Merge branch 'main' into achalmers/grid-scale2
@ -9,6 +9,7 @@ VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
|
|||||||
VITE_KC_SITE_APP_URL=https://app.dev.zoo.dev
|
VITE_KC_SITE_APP_URL=https://app.dev.zoo.dev
|
||||||
VITE_KC_SKIP_AUTH=false
|
VITE_KC_SKIP_AUTH=false
|
||||||
VITE_KC_CONNECTION_TIMEOUT_MS=5000
|
VITE_KC_CONNECTION_TIMEOUT_MS=5000
|
||||||
|
#VITE_WASM_URL="optional way of overriding the wasm url, particular for unit tests which need this if you running not on the default 3000 port"
|
||||||
#VITE_KC_DEV_TOKEN="optional token to skip auth in the app"
|
#VITE_KC_DEV_TOKEN="optional token to skip auth in the app"
|
||||||
#token="required token for playwright. TODO: clean up env vars in #3973"
|
#token="required token for playwright. TODO: clean up env vars in #3973"
|
||||||
|
|
||||||
|
3
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
* @KittyCAD/frontend
|
||||||
|
/src/ @KittyCAD/frontend
|
||||||
|
/rust/ @KittyCAD/kcl
|
11
.github/ISSUE_TEMPLATE/release.md
vendored
@ -2,7 +2,7 @@
|
|||||||
name: Release
|
name: Release
|
||||||
about: Create a new release for the Zoo Design Studio
|
about: Create a new release for the Zoo Design Studio
|
||||||
title: "Cut release v1.?.?"
|
title: "Cut release v1.?.?"
|
||||||
labels: [release]
|
labels: [meta/release]
|
||||||
---
|
---
|
||||||
|
|
||||||
> Instructions: https://github.com/KittyCAD/modeling-app/blob/main/CONTRIBUTING.md#shipping-releases
|
> Instructions: https://github.com/KittyCAD/modeling-app/blob/main/CONTRIBUTING.md#shipping-releases
|
||||||
@ -19,7 +19,8 @@ Release builds URL: ???
|
|||||||
* [ ] Confirm the application opens (dismiss the updater)
|
* [ ] Confirm the application opens (dismiss the updater)
|
||||||
* [ ] Create a project with a basic Text-to-CAD prompt
|
* [ ] Create a project with a basic Text-to-CAD prompt
|
||||||
* [ ] Confirm the result is viewable in an engine stream
|
* [ ] Confirm the result is viewable in an engine stream
|
||||||
* [ ] Open the application again and confirm the updater can downgrade
|
* [ ] Use 'Check for updates' to bring back the updater toast
|
||||||
|
* [ ] Confirm the app can update to the previous release
|
||||||
|
|
||||||
## macOS via ???
|
## macOS via ???
|
||||||
|
|
||||||
@ -27,7 +28,8 @@ Release builds URL: ???
|
|||||||
* [ ] Confirm the application opens (dismiss the updater)
|
* [ ] Confirm the application opens (dismiss the updater)
|
||||||
* [ ] Create a project with a basic Text-to-CAD prompt
|
* [ ] Create a project with a basic Text-to-CAD prompt
|
||||||
* [ ] Confirm the result is viewable in an engine stream
|
* [ ] Confirm the result is viewable in an engine stream
|
||||||
* [ ] Open the application again and confirm the updater can downgrade
|
* [ ] Use 'Check for updates' to bring back the updater toast
|
||||||
|
* [ ] Confirm the app can update to the previous release
|
||||||
|
|
||||||
## Linux via ???
|
## Linux via ???
|
||||||
|
|
||||||
@ -35,4 +37,5 @@ Release builds URL: ???
|
|||||||
* [ ] Confirm the application opens (dismiss the updater)
|
* [ ] Confirm the application opens (dismiss the updater)
|
||||||
* [ ] Create a project with a basic Text-to-CAD prompt
|
* [ ] Create a project with a basic Text-to-CAD prompt
|
||||||
* [ ] Confirm the result is viewable in an engine stream
|
* [ ] Confirm the result is viewable in an engine stream
|
||||||
* [ ] Open the application again and confirm the updater can downgrade
|
* [ ] Use 'Check for updates' to bring back the updater toast
|
||||||
|
* [ ] Confirm the app can update to the previous release
|
||||||
|
6
.github/workflows/cargo-fmt.yml
vendored
@ -31,15 +31,15 @@ jobs:
|
|||||||
- name: Use correct Rust toolchain
|
- name: Use correct Rust toolchain
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
[ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./
|
cp .github/workflows/nightly-rust-toolchain.toml rust-toolchain.toml
|
||||||
- name: Install rust
|
- name: Install rust
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
with:
|
with:
|
||||||
cache-workspaces: rust
|
cache-workspaces: rust
|
||||||
components: rustfmt
|
components: rustfmt
|
||||||
|
|
||||||
- name: Run cargo fmt
|
- name: Run nightly cargo fmt
|
||||||
run: |
|
run: |
|
||||||
cd rust
|
cd rust
|
||||||
cargo fmt -- --check
|
cargo +nightly fmt -- --check
|
||||||
shell: bash
|
shell: bash
|
||||||
|
15
.github/workflows/e2e-tests.yml
vendored
@ -95,7 +95,8 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: npm run build:wasm
|
run: npm run build:wasm
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- name: Upload compiled wasm artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: prepared-wasm
|
name: prepared-wasm
|
||||||
path: |
|
path: |
|
||||||
@ -176,7 +177,8 @@ jobs:
|
|||||||
CI_SUITE: e2e:snapshots
|
CI_SUITE: e2e:snapshots
|
||||||
TARGET: web
|
TARGET: web
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- name: Upload playwright report
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
if: ${{ !cancelled() }}
|
if: ${{ !cancelled() }}
|
||||||
with:
|
with:
|
||||||
name: playwright-report-snapshot-${{ github.sha }}
|
name: playwright-report-snapshot-${{ github.sha }}
|
||||||
@ -290,7 +292,8 @@ jobs:
|
|||||||
CI_SUITE: e2e:web
|
CI_SUITE: e2e:web
|
||||||
TARGET: web
|
TARGET: web
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- name: Upload playwright report
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
if: ${{ !cancelled() && (success() || failure()) }}
|
if: ${{ !cancelled() && (success() || failure()) }}
|
||||||
with:
|
with:
|
||||||
name: playwright-report-web-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
name: playwright-report-web-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
@ -415,7 +418,8 @@ jobs:
|
|||||||
CI_SUITE: e2e:desktop
|
CI_SUITE: e2e:desktop
|
||||||
TARGET: desktop
|
TARGET: desktop
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- name: Upload test report
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: test-results-desktop-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
name: test-results-desktop-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
@ -424,7 +428,8 @@ jobs:
|
|||||||
retention-days: 30
|
retention-days: 30
|
||||||
overwrite: true
|
overwrite: true
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- name: Upload playwright report
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
if: always()
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: playwright-report-desktop-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
name: playwright-report-desktop-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
|
3
.github/workflows/nightly-rust-toolchain.toml
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "nightly"
|
||||||
|
components = ["rustfmt"]
|
@ -10,9 +10,11 @@ Extend the current sketch with a new involute circular curve.
|
|||||||
```kcl
|
```kcl
|
||||||
involuteCircular(
|
involuteCircular(
|
||||||
@sketch: Sketch,
|
@sketch: Sketch,
|
||||||
startRadius: number(Length),
|
|
||||||
endRadius: number(Length),
|
|
||||||
angle: number(Angle),
|
angle: number(Angle),
|
||||||
|
startRadius?: number(Length),
|
||||||
|
endRadius?: number(Length),
|
||||||
|
startDiameter?: number(Length),
|
||||||
|
endDiameter?: number(Length),
|
||||||
reverse?: bool,
|
reverse?: bool,
|
||||||
tag?: TagDecl,
|
tag?: TagDecl,
|
||||||
): Sketch
|
): Sketch
|
||||||
@ -25,9 +27,11 @@ involuteCircular(
|
|||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `sketch` | [`Sketch`](/docs/kcl-std/types/std-types-Sketch) | Which sketch should this path be added to? | Yes |
|
| `sketch` | [`Sketch`](/docs/kcl-std/types/std-types-Sketch) | Which sketch should this path be added to? | Yes |
|
||||||
| `startRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, start_radius is the radius of the inner circle. | Yes |
|
|
||||||
| `endRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, end_radius is the radius of the outer circle. | Yes |
|
|
||||||
| `angle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | The angle to rotate the involute by. A value of zero will produce a curve with a tangent along the x-axis at the start point of the curve. | Yes |
|
| `angle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | The angle to rotate the involute by. A value of zero will produce a curve with a tangent along the x-axis at the start point of the curve. | Yes |
|
||||||
|
| `startRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, startRadius is the radius of the inner circle. Either `startRadius` or `startDiameter` must be given (but not both). | No |
|
||||||
|
| `endRadius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, endRadius is the radius of the outer circle. Either `endRadius` or `endDiameter` must be given (but not both). | No |
|
||||||
|
| `startDiameter` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, startDiameter describes the inner circle. Either `startRadius` or `startDiameter` must be given (but not both). | No |
|
||||||
|
| `endDiameter` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The involute is described between two circles, endDiameter describes the outer circle. Either `endRadius` or `endDiameter` must be given (but not both). | No |
|
||||||
| `reverse` | [`bool`](/docs/kcl-std/types/std-types-bool) | If reverse is true, the segment will start from the end of the involute, otherwise it will start from that start. | No |
|
| `reverse` | [`bool`](/docs/kcl-std/types/std-types-bool) | If reverse is true, the segment will start from the end of the involute, otherwise it will start from that start. | No |
|
||||||
| `tag` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | Create a new tag which refers to this line. | No |
|
| `tag` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | Create a new tag which refers to this line. | No |
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ The sketches need to be closed and on different planes that are parallel.
|
|||||||
| `vDegree` | [`number(_)`](/docs/kcl-std/types/std-types-number) | Degree of the interpolation. Must be greater than zero. For example, use 2 for quadratic, or 3 for cubic interpolation in the V direction. | No |
|
| `vDegree` | [`number(_)`](/docs/kcl-std/types/std-types-number) | Degree of the interpolation. Must be greater than zero. For example, use 2 for quadratic, or 3 for cubic interpolation in the V direction. | No |
|
||||||
| `bezApproximateRational` | [`bool`](/docs/kcl-std/types/std-types-bool) | Attempt to approximate rational curves (such as arcs) using a bezier. This will remove banding around interpolations between arcs and non-arcs. It may produce errors in other scenarios. Over time, this field won't be necessary. | No |
|
| `bezApproximateRational` | [`bool`](/docs/kcl-std/types/std-types-bool) | Attempt to approximate rational curves (such as arcs) using a bezier. This will remove banding around interpolations between arcs and non-arcs. It may produce errors in other scenarios. Over time, this field won't be necessary. | No |
|
||||||
| `baseCurveIndex` | [`number(_)`](/docs/kcl-std/types/std-types-number) | This can be set to override the automatically determined topological base curve, which is usually the first section encountered. | No |
|
| `baseCurveIndex` | [`number(_)`](/docs/kcl-std/types/std-types-number) | This can be set to override the automatically determined topological base curve, which is usually the first section encountered. | No |
|
||||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Tolerance for the loft operation. | No |
|
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No |
|
||||||
| `tagStart` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | A named tag for the face at the start of the loft, i.e. the original sketch. | No |
|
| `tagStart` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | A named tag for the face at the start of the loft, i.e. the original sketch. | No |
|
||||||
| `tagEnd` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | A named tag for the face at the end of the loft. | No |
|
| `tagEnd` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | A named tag for the face at the end of the loft. | No |
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ revolved around the same axis.
|
|||||||
| `sketches` | [`[Sketch; 1+]`](/docs/kcl-std/types/std-types-Sketch) | The sketch or set of sketches that should be revolved | Yes |
|
| `sketches` | [`[Sketch; 1+]`](/docs/kcl-std/types/std-types-Sketch) | The sketch or set of sketches that should be revolved | Yes |
|
||||||
| `axis` | [`Axis2d`](/docs/kcl-std/types/std-types-Axis2d) or [`Edge`](/docs/kcl-std/types/std-types-Edge) | Axis of revolution. | Yes |
|
| `axis` | [`Axis2d`](/docs/kcl-std/types/std-types-Axis2d) or [`Edge`](/docs/kcl-std/types/std-types-Edge) | Axis of revolution. | Yes |
|
||||||
| `angle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | Angle to revolve (in degrees). Default is 360. | No |
|
| `angle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | Angle to revolve (in degrees). Default is 360. | No |
|
||||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Tolerance for the revolve operation. | No |
|
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No |
|
||||||
| `symmetric` | [`bool`](/docs/kcl-std/types/std-types-bool) | If true, the extrusion will happen symmetrically around the sketch. Otherwise, the extrusion will happen on only one side of the sketch. | No |
|
| `symmetric` | [`bool`](/docs/kcl-std/types/std-types-bool) | If true, the extrusion will happen symmetrically around the sketch. Otherwise, the extrusion will happen on only one side of the sketch. | No |
|
||||||
| `bidirectionalAngle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | If specified, will also revolve in the opposite direction to 'angle' to the specified angle. If 'symmetric' is true, this value is ignored. | No |
|
| `bidirectionalAngle` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | If specified, will also revolve in the opposite direction to 'angle' to the specified angle. If 'symmetric' is true, this value is ignored. | No |
|
||||||
| `tagStart` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | A named tag for the face at the start of the revolve, i.e. the original sketch. | No |
|
| `tagStart` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | A named tag for the face at the start of the revolve, i.e. the original sketch. | No |
|
||||||
|
@ -35,7 +35,7 @@ swept along the same path.
|
|||||||
| `sketches` | [`[Sketch; 1+]`](/docs/kcl-std/types/std-types-Sketch) | The sketch or set of sketches that should be swept in space. | Yes |
|
| `sketches` | [`[Sketch; 1+]`](/docs/kcl-std/types/std-types-Sketch) | The sketch or set of sketches that should be swept in space. | Yes |
|
||||||
| `path` | [`Sketch`](/docs/kcl-std/types/std-types-Sketch) or [`Helix`](/docs/kcl-std/types/std-types-Helix) | The path to sweep the sketch along. | Yes |
|
| `path` | [`Sketch`](/docs/kcl-std/types/std-types-Sketch) or [`Helix`](/docs/kcl-std/types/std-types-Helix) | The path to sweep the sketch along. | Yes |
|
||||||
| `sectional` | [`bool`](/docs/kcl-std/types/std-types-bool) | If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components. | No |
|
| `sectional` | [`bool`](/docs/kcl-std/types/std-types-bool) | If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components. | No |
|
||||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Tolerance for this operation. | No |
|
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No |
|
||||||
| `relativeTo` | [`string`](/docs/kcl-std/types/std-types-string) | What is the sweep relative to? Can be either 'sketchPlane' or 'trajectoryCurve'. | No |
|
| `relativeTo` | [`string`](/docs/kcl-std/types/std-types-string) | What is the sweep relative to? Can be either 'sketchPlane' or 'trajectoryCurve'. | No |
|
||||||
| `tagStart` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | A named tag for the face at the start of the sweep, i.e. the original sketch. | No |
|
| `tagStart` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | A named tag for the face at the start of the sweep, i.e. the original sketch. | No |
|
||||||
| `tagEnd` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | A named tag for the face at the end of the sweep. | No |
|
| `tagEnd` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | A named tag for the face at the end of the sweep. | No |
|
||||||
|
@ -28,7 +28,7 @@ will smoothly blend the transition.
|
|||||||
| `solid` | [`Solid`](/docs/kcl-std/types/std-types-Solid) | The solid whose edges should be filletted | Yes |
|
| `solid` | [`Solid`](/docs/kcl-std/types/std-types-Solid) | The solid whose edges should be filletted | Yes |
|
||||||
| `radius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The radius of the fillet | Yes |
|
| `radius` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The radius of the fillet | Yes |
|
||||||
| `tags` | [`[Edge; 1+]`](/docs/kcl-std/types/std-types-Edge) | The paths you want to fillet | Yes |
|
| `tags` | [`[Edge; 1+]`](/docs/kcl-std/types/std-types-Edge) | The paths you want to fillet | Yes |
|
||||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The tolerance for this fillet | No |
|
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No |
|
||||||
| `tag` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | Create a new tag which refers to this fillet | No |
|
| `tag` | [`TagDecl`](/docs/kcl-std/types/std-types-TagDecl) | Create a new tag which refers to this fillet | No |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
@ -24,7 +24,7 @@ verifying fit, and analyzing overlapping geometries in assemblies.
|
|||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `solids` | `[Solid; 2+]` | The solids to intersect. | Yes |
|
| `solids` | `[Solid; 2+]` | The solids to intersect. | Yes |
|
||||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The tolerance to use for the intersection operation. | No |
|
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ and complex multi-body part modeling.
|
|||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `solids` | [`[Solid; 1+]`](/docs/kcl-std/types/std-types-Solid) | The solids to use as the base to subtract from. | Yes |
|
| `solids` | [`[Solid; 1+]`](/docs/kcl-std/types/std-types-Solid) | The solids to use as the base to subtract from. | Yes |
|
||||||
| `tools` | [`[Solid]`](/docs/kcl-std/types/std-types-Solid) | The solids to subtract. | Yes |
|
| `tools` | [`[Solid]`](/docs/kcl-std/types/std-types-Solid) | The solids to subtract. | Yes |
|
||||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The tolerance to use for the subtraction operation. | No |
|
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ union(
|
|||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `solids` | `[Solid; 2+]` | The solids to union. | Yes |
|
| `solids` | `[Solid; 2+]` | The solids to union. | Yes |
|
||||||
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The tolerance to use for the union operation. | No |
|
| `tolerance` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters. | No |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ import * as fsp from 'fs/promises'
|
|||||||
|
|
||||||
import { executorInputPath, getUtils } from '@e2e/playwright/test-utils'
|
import { executorInputPath, getUtils } from '@e2e/playwright/test-utils'
|
||||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||||
import { expectPixelColor } from '@e2e/playwright/fixtures/sceneFixture'
|
|
||||||
|
|
||||||
test.describe('Command bar tests', () => {
|
test.describe('Command bar tests', () => {
|
||||||
test('Extrude from command bar selects extrude line after', async ({
|
test('Extrude from command bar selects extrude line after', async ({
|
||||||
@ -308,7 +307,7 @@ test.describe('Command bar tests', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const continueButton = page.getByRole('button', { name: 'Continue' })
|
const continueButton = page.getByRole('button', { name: 'Continue' })
|
||||||
const submitButton = page.getByRole('button', { name: 'Submit command' })
|
const submitButton = page.getByTestId('command-bar-submit')
|
||||||
await continueButton.click()
|
await continueButton.click()
|
||||||
|
|
||||||
// Review step and argument hotkeys
|
// Review step and argument hotkeys
|
||||||
@ -515,47 +514,6 @@ test.describe('Command bar tests', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test(
|
|
||||||
`Zoom to fit to shared model on web`,
|
|
||||||
{ tag: ['@web'] },
|
|
||||||
async ({ page, scene }) => {
|
|
||||||
if (process.env.TARGET !== 'web') {
|
|
||||||
// This test is web-only
|
|
||||||
// TODO: re-enable on CI as part of a new @web test suite
|
|
||||||
return
|
|
||||||
}
|
|
||||||
await test.step(`Prepare and navigate to home page with query params`, async () => {
|
|
||||||
// a quad in the top left corner of the XZ plane (which is out of the current view)
|
|
||||||
const code = `sketch001 = startSketchOn(XZ)
|
|
||||||
profile001 = startProfile(sketch001, at = [-484.34, 484.95])
|
|
||||||
|> yLine(length = -69.1)
|
|
||||||
|> xLine(length = 66.84)
|
|
||||||
|> yLine(length = 71.37)
|
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
||||||
|> close()
|
|
||||||
`
|
|
||||||
const targetURL = `?create-file=true&name=test&units=mm&code=${encodeURIComponent(btoa(code))}&ask-open-desktop=true`
|
|
||||||
await page.goto(page.url() + targetURL)
|
|
||||||
expect(page.url()).toContain(targetURL)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Submit the command`, async () => {
|
|
||||||
await page.getByTestId('continue-to-web-app-button').click()
|
|
||||||
|
|
||||||
await scene.connectionEstablished()
|
|
||||||
|
|
||||||
// This makes SystemIOMachineActors.createKCLFile run after EngineStream/firstPlay
|
|
||||||
await page.waitForTimeout(3000)
|
|
||||||
|
|
||||||
await page.getByTestId('command-bar-submit').click()
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Ensure we created the project and are in the modeling scene`, async () => {
|
|
||||||
await expectPixelColor(page, [252, 252, 252], { x: 600, y: 260 }, 8)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(`Can add and edit a named parameter or constant`, async ({
|
test(`Can add and edit a named parameter or constant`, async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
homePage,
|
||||||
|
@ -54,9 +54,7 @@ test(
|
|||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
// Click the checkbox
|
// Click the checkbox
|
||||||
const submitButton = page.getByText('Confirm Export')
|
await cmdBar.submit()
|
||||||
await expect(submitButton).toBeVisible()
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
|
|
||||||
// Expect it to succeed
|
// Expect it to succeed
|
||||||
const errorToastMessage = page.getByText(`Error while exporting`)
|
const errorToastMessage = page.getByText(`Error while exporting`)
|
||||||
@ -119,9 +117,7 @@ test(
|
|||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
// Click the checkbox
|
// Click the checkbox
|
||||||
const submitButton = page.getByText('Confirm Export')
|
await cmdBar.submit()
|
||||||
await expect(submitButton).toBeVisible()
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
|
|
||||||
// Look out for the toast message
|
// Look out for the toast message
|
||||||
const exportingToastMessage = page.getByText(`Exporting...`)
|
const exportingToastMessage = page.getByText(`Exporting...`)
|
||||||
|
@ -229,11 +229,12 @@ test.describe('Feature Tree pane', () => {
|
|||||||
const initialCode = `sketch001 = startSketchOn(XZ)
|
const initialCode = `sketch001 = startSketchOn(XZ)
|
||||||
|> circle(center = [0, 0], radius = 5)
|
|> circle(center = [0, 0], radius = 5)
|
||||||
renamedExtrude = extrude(sketch001, length = ${initialInput})`
|
renamedExtrude = extrude(sketch001, length = ${initialInput})`
|
||||||
const newConstantName = 'length001'
|
const newParameterName = 'length001'
|
||||||
const expectedCode = `${newConstantName} = 23
|
const expectedCode = `${newParameterName} = 23
|
||||||
sketch001 = startSketchOn(XZ)
|
sketch001 = startSketchOn(XZ)
|
||||||
|> circle(center = [0, 0], radius = 5)
|
|> circle(center = [0, 0], radius = 5)
|
||||||
renamedExtrude = extrude(sketch001, length = ${newConstantName})`
|
renamedExtrude = extrude(sketch001, length = ${newParameterName})`
|
||||||
|
const editedParameterValue = '23 * 2'
|
||||||
|
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
const testDir = join(dir, 'test-sample')
|
const testDir = join(dir, 'test-sample')
|
||||||
@ -279,7 +280,7 @@ test.describe('Feature Tree pane', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Add a named constant for distance argument and submit', async () => {
|
await test.step('Add a parameter for distance argument and submit', async () => {
|
||||||
await expect(cmdBar.currentArgumentInput).toBeVisible()
|
await expect(cmdBar.currentArgumentInput).toBeVisible()
|
||||||
await cmdBar.variableCheckbox.click()
|
await cmdBar.variableCheckbox.click()
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
@ -296,13 +297,43 @@ test.describe('Feature Tree pane', () => {
|
|||||||
highlightedCode: '',
|
highlightedCode: '',
|
||||||
diagnostics: [],
|
diagnostics: [],
|
||||||
activeLines: [
|
activeLines: [
|
||||||
`renamedExtrude = extrude(sketch001, length = ${newConstantName})`,
|
`renamedExtrude = extrude(sketch001, length = ${newParameterName})`,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
await editor.expectEditor.toContain(expectedCode, {
|
await editor.expectEditor.toContain(expectedCode, {
|
||||||
shouldNormalise: true,
|
shouldNormalise: true,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await test.step('Edit the parameter via the feature tree', async () => {
|
||||||
|
const parameter = await toolbar.getFeatureTreeOperation('Parameter', 0)
|
||||||
|
await parameter.dblclick()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
commandName: 'Edit parameter',
|
||||||
|
currentArgKey: 'value',
|
||||||
|
currentArgValue: '23',
|
||||||
|
headerArguments: {
|
||||||
|
Name: newParameterName,
|
||||||
|
Value: '23',
|
||||||
|
},
|
||||||
|
stage: 'arguments',
|
||||||
|
highlightedHeaderArg: 'value',
|
||||||
|
})
|
||||||
|
await cmdBar.argumentInput
|
||||||
|
.locator('[contenteditable]')
|
||||||
|
.fill(editedParameterValue)
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
commandName: 'Edit parameter',
|
||||||
|
headerArguments: {
|
||||||
|
Name: newParameterName,
|
||||||
|
Value: '46', // Shows calculated result
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await editor.expectEditor.toContain(editedParameterValue)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
test(`User can edit an offset plane operation from the feature tree`, async ({
|
test(`User can edit an offset plane operation from the feature tree`, async ({
|
||||||
context,
|
context,
|
||||||
|
@ -118,15 +118,11 @@ export class CmdBarFixture {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const arrowButton = this.page.getByRole('button', {
|
const arrowButton = this.page.getByTestId('command-bar-continue')
|
||||||
name: 'arrow right Continue',
|
|
||||||
})
|
|
||||||
if (await arrowButton.isVisible()) {
|
if (await arrowButton.isVisible()) {
|
||||||
await arrowButton.click()
|
await this.continue()
|
||||||
} else {
|
} else {
|
||||||
await this.page
|
await this.submit()
|
||||||
.getByRole('button', { name: 'checkmark Submit command' })
|
|
||||||
.click()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,14 +183,15 @@ export class EditorFixture {
|
|||||||
scrollToText(text: string, placeCursor?: boolean) {
|
scrollToText(text: string, placeCursor?: boolean) {
|
||||||
return this.page.evaluate(
|
return this.page.evaluate(
|
||||||
(args: { text: string; placeCursor?: boolean }) => {
|
(args: { text: string; placeCursor?: boolean }) => {
|
||||||
|
const editorView = window.editorManager.getEditorView()
|
||||||
// error TS2339: Property 'docView' does not exist on type 'EditorView'.
|
// error TS2339: Property 'docView' does not exist on type 'EditorView'.
|
||||||
// Except it does so :shrug:
|
// Except it does so :shrug:
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let index = window.editorManager._editorView?.docView.view.state.doc
|
const index = editorView?.docView.view.state.doc
|
||||||
.toString()
|
.toString()
|
||||||
.indexOf(args.text)
|
.indexOf(args.text)
|
||||||
window.editorManager._editorView?.focus()
|
editorView?.focus()
|
||||||
window.editorManager._editorView?.dispatch({
|
editorView?.dispatch({
|
||||||
selection: window.EditorSelection.create([
|
selection: window.EditorSelection.create([
|
||||||
window.EditorSelection.cursor(index),
|
window.EditorSelection.cursor(index),
|
||||||
]),
|
]),
|
||||||
|
@ -5,7 +5,7 @@ import type {
|
|||||||
FullResult,
|
FullResult,
|
||||||
} from '@playwright/test/reporter'
|
} from '@playwright/test/reporter'
|
||||||
|
|
||||||
class MyAPIReporter implements Reporter {
|
class APIReporter implements Reporter {
|
||||||
private pendingRequests: Promise<void>[] = []
|
private pendingRequests: Promise<void>[] = []
|
||||||
private allResults: Record<string, any>[] = []
|
private allResults: Record<string, any>[] = []
|
||||||
private blockingResults: Record<string, any>[] = []
|
private blockingResults: Record<string, any>[] = []
|
||||||
@ -32,7 +32,7 @@ class MyAPIReporter implements Reporter {
|
|||||||
'X-API-Key': process.env.TAB_API_KEY || '',
|
'X-API-Key': process.env.TAB_API_KEY || '',
|
||||||
}),
|
}),
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
project: 'https://github.com/KittyCAD/modeling-app',
|
project: `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}`,
|
||||||
branch:
|
branch:
|
||||||
process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || '',
|
process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || '',
|
||||||
commit: process.env.CI_COMMIT_SHA || process.env.GITHUB_SHA || '',
|
commit: process.env.CI_COMMIT_SHA || process.env.GITHUB_SHA || '',
|
||||||
@ -60,7 +60,7 @@ class MyAPIReporter implements Reporter {
|
|||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
// Required information
|
// Required information
|
||||||
project: 'https://github.com/KittyCAD/modeling-app',
|
project: `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}`,
|
||||||
suite: process.env.CI_SUITE || 'e2e',
|
suite: process.env.CI_SUITE || 'e2e',
|
||||||
branch: process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || '',
|
branch: process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || '',
|
||||||
commit: process.env.CI_COMMIT_SHA || process.env.GITHUB_SHA || '',
|
commit: process.env.CI_COMMIT_SHA || process.env.GITHUB_SHA || '',
|
||||||
@ -124,4 +124,4 @@ class MyAPIReporter implements Reporter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MyAPIReporter
|
export default APIReporter
|
||||||
|
@ -1083,14 +1083,13 @@ openSketch = startSketchOn(XY)
|
|||||||
cmdBar,
|
cmdBar,
|
||||||
}) => {
|
}) => {
|
||||||
// One dumb hardcoded screen pixel value
|
// One dumb hardcoded screen pixel value
|
||||||
const testPoint = { x: 700, y: 150 }
|
const testPoint = { x: 700, y: 200 }
|
||||||
|
// TODO: replace the testPoint selection with a feature tree click once that's supported #7544
|
||||||
const [clickOnXzPlane] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
const [clickOnXzPlane] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
const expectedOutput = `plane001 = offsetPlane(XZ, offset = 5)`
|
const expectedOutput = `plane001 = offsetPlane(XZ, offset = 5)`
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
// FIXME: Since there is no KCL code loaded. We need to wait for the scene to load before we continue.
|
await scene.settled(cmdBar)
|
||||||
// The engine may not be connected
|
|
||||||
await page.waitForTimeout(15000)
|
|
||||||
|
|
||||||
await test.step(`Look for the blue of the XZ plane`, async () => {
|
await test.step(`Look for the blue of the XZ plane`, async () => {
|
||||||
//await scene.expectPixelColor([50, 51, 96], testPoint, 15) // FIXME
|
//await scene.expectPixelColor([50, 51, 96], testPoint, 15) // FIXME
|
||||||
@ -1829,7 +1828,6 @@ profile002 = startProfile(sketch002, at = [0, 0])
|
|||||||
currentArgKey: 'sketches',
|
currentArgKey: 'sketches',
|
||||||
currentArgValue: '',
|
currentArgValue: '',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Sectional: '',
|
|
||||||
Profiles: '',
|
Profiles: '',
|
||||||
Path: '',
|
Path: '',
|
||||||
},
|
},
|
||||||
@ -1843,7 +1841,6 @@ profile002 = startProfile(sketch002, at = [0, 0])
|
|||||||
currentArgKey: 'path',
|
currentArgKey: 'path',
|
||||||
currentArgValue: '',
|
currentArgValue: '',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Sectional: '',
|
|
||||||
Profiles: '1 profile',
|
Profiles: '1 profile',
|
||||||
Path: '',
|
Path: '',
|
||||||
},
|
},
|
||||||
@ -1856,7 +1853,6 @@ profile002 = startProfile(sketch002, at = [0, 0])
|
|||||||
currentArgKey: 'path',
|
currentArgKey: 'path',
|
||||||
currentArgValue: '',
|
currentArgValue: '',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Sectional: '',
|
|
||||||
Profiles: '1 profile',
|
Profiles: '1 profile',
|
||||||
Path: '',
|
Path: '',
|
||||||
},
|
},
|
||||||
@ -1869,7 +1865,6 @@ profile002 = startProfile(sketch002, at = [0, 0])
|
|||||||
headerArguments: {
|
headerArguments: {
|
||||||
Profiles: '1 profile',
|
Profiles: '1 profile',
|
||||||
Path: '1 segment',
|
Path: '1 segment',
|
||||||
Sectional: '',
|
|
||||||
},
|
},
|
||||||
stage: 'review',
|
stage: 'review',
|
||||||
})
|
})
|
||||||
@ -1894,6 +1889,9 @@ profile002 = startProfile(sketch002, at = [0, 0])
|
|||||||
0
|
0
|
||||||
)
|
)
|
||||||
await operationButton.dblclick({ button: 'left' })
|
await operationButton.dblclick({ button: 'left' })
|
||||||
|
await page
|
||||||
|
.getByRole('button', { name: 'sectional', exact: false })
|
||||||
|
.click()
|
||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
commandName: 'Sweep',
|
commandName: 'Sweep',
|
||||||
currentArgKey: 'sectional',
|
currentArgKey: 'sectional',
|
||||||
@ -1971,7 +1969,6 @@ profile001 = ${circleCode}`
|
|||||||
currentArgKey: 'sketches',
|
currentArgKey: 'sketches',
|
||||||
currentArgValue: '',
|
currentArgValue: '',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Sectional: '',
|
|
||||||
Profiles: '',
|
Profiles: '',
|
||||||
Path: '',
|
Path: '',
|
||||||
},
|
},
|
||||||
@ -1986,7 +1983,6 @@ profile001 = ${circleCode}`
|
|||||||
currentArgKey: 'path',
|
currentArgKey: 'path',
|
||||||
currentArgValue: '',
|
currentArgValue: '',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Sectional: '',
|
|
||||||
Profiles: '1 profile',
|
Profiles: '1 profile',
|
||||||
Path: '',
|
Path: '',
|
||||||
},
|
},
|
||||||
@ -2000,7 +1996,6 @@ profile001 = ${circleCode}`
|
|||||||
currentArgKey: 'path',
|
currentArgKey: 'path',
|
||||||
currentArgValue: '',
|
currentArgValue: '',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Sectional: '',
|
|
||||||
Profiles: '1 profile',
|
Profiles: '1 profile',
|
||||||
Path: '',
|
Path: '',
|
||||||
},
|
},
|
||||||
@ -2013,7 +2008,6 @@ profile001 = ${circleCode}`
|
|||||||
headerArguments: {
|
headerArguments: {
|
||||||
Profiles: '1 profile',
|
Profiles: '1 profile',
|
||||||
Path: '1 helix',
|
Path: '1 helix',
|
||||||
Sectional: '',
|
|
||||||
},
|
},
|
||||||
stage: 'review',
|
stage: 'review',
|
||||||
})
|
})
|
||||||
@ -4734,7 +4728,6 @@ path001 = startProfile(sketch001, at = [0, 0])
|
|||||||
headerArguments: {
|
headerArguments: {
|
||||||
Profiles: '',
|
Profiles: '',
|
||||||
Path: '',
|
Path: '',
|
||||||
Sectional: '',
|
|
||||||
},
|
},
|
||||||
highlightedHeaderArg: 'Profiles',
|
highlightedHeaderArg: 'Profiles',
|
||||||
commandName: 'Sweep',
|
commandName: 'Sweep',
|
||||||
@ -4747,7 +4740,6 @@ path001 = startProfile(sketch001, at = [0, 0])
|
|||||||
headerArguments: {
|
headerArguments: {
|
||||||
Profiles: '2 profiles',
|
Profiles: '2 profiles',
|
||||||
Path: '',
|
Path: '',
|
||||||
Sectional: '',
|
|
||||||
},
|
},
|
||||||
highlightedHeaderArg: 'path',
|
highlightedHeaderArg: 'path',
|
||||||
commandName: 'Sweep',
|
commandName: 'Sweep',
|
||||||
@ -4760,7 +4752,6 @@ path001 = startProfile(sketch001, at = [0, 0])
|
|||||||
headerArguments: {
|
headerArguments: {
|
||||||
Profiles: '2 profiles',
|
Profiles: '2 profiles',
|
||||||
Path: '1 segment',
|
Path: '1 segment',
|
||||||
Sectional: '',
|
|
||||||
},
|
},
|
||||||
commandName: 'Sweep',
|
commandName: 'Sweep',
|
||||||
})
|
})
|
||||||
|
@ -475,6 +475,7 @@ test.describe('Can export from electron app', () => {
|
|||||||
},
|
},
|
||||||
tronApp.projectDirName,
|
tronApp.projectDirName,
|
||||||
page,
|
page,
|
||||||
|
cmdBar,
|
||||||
method
|
method
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -779,9 +780,6 @@ test.describe(`Project management commands`, () => {
|
|||||||
const commandContinueButton = page.getByRole('button', {
|
const commandContinueButton = page.getByRole('button', {
|
||||||
name: 'Continue',
|
name: 'Continue',
|
||||||
})
|
})
|
||||||
const commandSubmitButton = page.getByRole('button', {
|
|
||||||
name: 'Submit command',
|
|
||||||
})
|
|
||||||
const toastMessage = page.getByText(`Successfully renamed`)
|
const toastMessage = page.getByText(`Successfully renamed`)
|
||||||
|
|
||||||
await test.step(`Setup`, async () => {
|
await test.step(`Setup`, async () => {
|
||||||
@ -800,8 +798,7 @@ test.describe(`Project management commands`, () => {
|
|||||||
await expect(commandContinueButton).toBeVisible()
|
await expect(commandContinueButton).toBeVisible()
|
||||||
await commandContinueButton.click()
|
await commandContinueButton.click()
|
||||||
|
|
||||||
await expect(commandSubmitButton).toBeVisible()
|
await cmdBar.submit()
|
||||||
await commandSubmitButton.click()
|
|
||||||
|
|
||||||
await expect(toastMessage).toBeVisible()
|
await expect(toastMessage).toBeVisible()
|
||||||
})
|
})
|
||||||
@ -837,9 +834,6 @@ test.describe(`Project management commands`, () => {
|
|||||||
})
|
})
|
||||||
const projectNameOption = page.getByRole('option', { name: projectName })
|
const projectNameOption = page.getByRole('option', { name: projectName })
|
||||||
const commandWarning = page.getByText('Are you sure you want to delete?')
|
const commandWarning = page.getByText('Are you sure you want to delete?')
|
||||||
const commandSubmitButton = page.getByRole('button', {
|
|
||||||
name: 'Submit command',
|
|
||||||
})
|
|
||||||
const toastMessage = page.getByText(`Successfully deleted`)
|
const toastMessage = page.getByText(`Successfully deleted`)
|
||||||
const noProjectsMessage = page.getByText('No projects found')
|
const noProjectsMessage = page.getByText('No projects found')
|
||||||
|
|
||||||
@ -859,8 +853,7 @@ test.describe(`Project management commands`, () => {
|
|||||||
await projectNameOption.click()
|
await projectNameOption.click()
|
||||||
|
|
||||||
await expect(commandWarning).toBeVisible()
|
await expect(commandWarning).toBeVisible()
|
||||||
await expect(commandSubmitButton).toBeVisible()
|
await cmdBar.submit()
|
||||||
await commandSubmitButton.click()
|
|
||||||
|
|
||||||
await expect(toastMessage).toBeVisible()
|
await expect(toastMessage).toBeVisible()
|
||||||
})
|
})
|
||||||
@ -894,9 +887,6 @@ test.describe(`Project management commands`, () => {
|
|||||||
const commandContinueButton = page.getByRole('button', {
|
const commandContinueButton = page.getByRole('button', {
|
||||||
name: 'Continue',
|
name: 'Continue',
|
||||||
})
|
})
|
||||||
const commandSubmitButton = page.getByRole('button', {
|
|
||||||
name: 'Submit command',
|
|
||||||
})
|
|
||||||
const toastMessage = page.getByText(`Successfully renamed`)
|
const toastMessage = page.getByText(`Successfully renamed`)
|
||||||
|
|
||||||
await test.step(`Setup`, async () => {
|
await test.step(`Setup`, async () => {
|
||||||
@ -914,8 +904,7 @@ test.describe(`Project management commands`, () => {
|
|||||||
await expect(commandContinueButton).toBeVisible()
|
await expect(commandContinueButton).toBeVisible()
|
||||||
await commandContinueButton.click()
|
await commandContinueButton.click()
|
||||||
|
|
||||||
await expect(commandSubmitButton).toBeVisible()
|
await cmdBar.submit()
|
||||||
await commandSubmitButton.click()
|
|
||||||
|
|
||||||
await expect(toastMessage).toBeVisible()
|
await expect(toastMessage).toBeVisible()
|
||||||
})
|
})
|
||||||
@ -949,9 +938,6 @@ test.describe(`Project management commands`, () => {
|
|||||||
})
|
})
|
||||||
const projectNameOption = page.getByRole('option', { name: projectName })
|
const projectNameOption = page.getByRole('option', { name: projectName })
|
||||||
const commandWarning = page.getByText('Are you sure you want to delete?')
|
const commandWarning = page.getByText('Are you sure you want to delete?')
|
||||||
const commandSubmitButton = page.getByRole('button', {
|
|
||||||
name: 'Submit command',
|
|
||||||
})
|
|
||||||
const toastMessage = page.getByText(`Successfully deleted`)
|
const toastMessage = page.getByText(`Successfully deleted`)
|
||||||
const noProjectsMessage = page.getByText('No projects found')
|
const noProjectsMessage = page.getByText('No projects found')
|
||||||
|
|
||||||
@ -967,8 +953,7 @@ test.describe(`Project management commands`, () => {
|
|||||||
await projectNameOption.click()
|
await projectNameOption.click()
|
||||||
|
|
||||||
await expect(commandWarning).toBeVisible()
|
await expect(commandWarning).toBeVisible()
|
||||||
await expect(commandSubmitButton).toBeVisible()
|
await cmdBar.submit()
|
||||||
await commandSubmitButton.click()
|
|
||||||
|
|
||||||
await expect(toastMessage).toBeVisible()
|
await expect(toastMessage).toBeVisible()
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { bracket } from '@e2e/playwright/fixtures/bracket'
|
import { bracket } from '@e2e/playwright/fixtures/bracket'
|
||||||
import type { Page } from '@playwright/test'
|
import type { Page } from '@playwright/test'
|
||||||
|
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||||
import { reportRejection } from '@src/lib/trap'
|
import { reportRejection } from '@src/lib/trap'
|
||||||
import * as fsp from 'fs/promises'
|
import * as fsp from 'fs/promises'
|
||||||
|
|
||||||
@ -421,10 +422,7 @@ extrude002 = extrude(profile002, length = 150)
|
|||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
// Click the checkbox
|
// Click the checkbox
|
||||||
const submitButton = page.getByText('Confirm Export')
|
await cmdBar.submit()
|
||||||
await expect(submitButton).toBeVisible()
|
|
||||||
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
|
|
||||||
// Find the toast.
|
// Find the toast.
|
||||||
// Look out for the toast message
|
// Look out for the toast message
|
||||||
@ -461,8 +459,7 @@ extrude002 = extrude(profile002, length = 150)
|
|||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
// Click the checkbox
|
// Click the checkbox
|
||||||
await expect(submitButton).toBeVisible()
|
await cmdBar.submit()
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
|
|
||||||
// Find the toast.
|
// Find the toast.
|
||||||
// Look out for the toast message
|
// Look out for the toast message
|
||||||
@ -482,6 +479,7 @@ extrude002 = extrude(profile002, length = 150)
|
|||||||
test('ensure you CAN export while an export is already going', async ({
|
test('ensure you CAN export while an export is already going', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
homePage,
|
||||||
|
cmdBar,
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await test.step('Set up the code and durations', async () => {
|
await test.step('Set up the code and durations', async () => {
|
||||||
@ -516,11 +514,11 @@ extrude002 = extrude(profile002, length = 150)
|
|||||||
const successToastMessage = page.getByText(`Exported successfully`)
|
const successToastMessage = page.getByText(`Exported successfully`)
|
||||||
|
|
||||||
await test.step('second export', async () => {
|
await test.step('second export', async () => {
|
||||||
await clickExportButton(page)
|
await clickExportButton(page, cmdBar)
|
||||||
|
|
||||||
await expect(exportingToastMessage).toBeVisible()
|
await expect(exportingToastMessage).toBeVisible()
|
||||||
|
|
||||||
await clickExportButton(page)
|
await clickExportButton(page, cmdBar)
|
||||||
|
|
||||||
await test.step('The first export still succeeds', async () => {
|
await test.step('The first export still succeeds', async () => {
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@ -537,7 +535,7 @@ extrude002 = extrude(profile002, length = 150)
|
|||||||
|
|
||||||
await test.step('Successful, unblocked export', async () => {
|
await test.step('Successful, unblocked export', async () => {
|
||||||
// Try exporting again.
|
// Try exporting again.
|
||||||
await clickExportButton(page)
|
await clickExportButton(page, cmdBar)
|
||||||
|
|
||||||
// Find the toast.
|
// Find the toast.
|
||||||
// Look out for the toast message
|
// Look out for the toast message
|
||||||
@ -575,7 +573,7 @@ extrude002 = extrude(profile002, length = 150)
|
|||||||
name: 'Projects',
|
name: 'Projects',
|
||||||
})
|
})
|
||||||
const projectLink = page.getByRole('link', { name: 'bracket' })
|
const projectLink = page.getByRole('link', { name: 'bracket' })
|
||||||
const networkHealthIndicator = page.getByTestId('network-toggle')
|
const networkHealthIndicator = page.getByTestId(/network-toggle/)
|
||||||
|
|
||||||
await test.step('Check the home page', async () => {
|
await test.step('Check the home page', async () => {
|
||||||
await expect(projectsHeading).toBeVisible()
|
await expect(projectsHeading).toBeVisible()
|
||||||
@ -880,7 +878,7 @@ s2 = startSketchOn(XY)
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
async function clickExportButton(page: Page) {
|
async function clickExportButton(page: Page, cmdBar: CmdBarFixture) {
|
||||||
await test.step('Running export flow', async () => {
|
await test.step('Running export flow', async () => {
|
||||||
// export the model
|
// export the model
|
||||||
const exportButton = page.getByTestId('export-pane-button')
|
const exportButton = page.getByTestId('export-pane-button')
|
||||||
@ -896,9 +894,6 @@ async function clickExportButton(page: Page) {
|
|||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
// Click the checkbox
|
// Click the checkbox
|
||||||
const submitButton = page.getByText('Confirm Export')
|
await cmdBar.submit()
|
||||||
await expect(submitButton).toBeVisible()
|
|
||||||
|
|
||||||
await page.keyboard.press('Enter')
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1445,6 +1445,103 @@ solid001 = subtract([extrude001], tools = [extrude002])
|
|||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Can edit a tangentialArc defined by angle and radius', async ({
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
scene,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
const viewportSize = { width: 1500, height: 750 }
|
||||||
|
await page.setBodyDimensions(viewportSize)
|
||||||
|
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`@settings(defaultLengthUnit=in)
|
||||||
|
sketch001 = startSketchOn(XZ)
|
||||||
|
|> startProfile(at = [-10, -10])
|
||||||
|
|> line(end = [20.0, 10.0])
|
||||||
|
|> tangentialArc(angle = 60deg, radius=10.0)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await toolbar.waitForFeatureTreeToBeBuilt()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
|
await (await toolbar.getFeatureTreeOperation('Sketch', 0)).dblclick()
|
||||||
|
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
|
await page.mouse.move(1200, 139)
|
||||||
|
await page.mouse.down()
|
||||||
|
await page.mouse.move(870, 250)
|
||||||
|
await page.mouse.up()
|
||||||
|
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
`tangentialArc(angle = 234.01deg, radius = 4.08)`,
|
||||||
|
{ shouldNormalise: true }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Can undo with closed code pane', async ({
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
scene,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
const viewportSize = { width: 1500, height: 750 }
|
||||||
|
await page.setBodyDimensions(viewportSize)
|
||||||
|
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`@settings(defaultLengthUnit=in)
|
||||||
|
sketch001 = startSketchOn(XZ)
|
||||||
|
|> startProfile(at = [-10, -10])
|
||||||
|
|> line(end = [20.0, 10.0])
|
||||||
|
|> tangentialArc(end = [5.49, 8.37])`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await toolbar.waitForFeatureTreeToBeBuilt()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
|
await (await toolbar.getFeatureTreeOperation('Sketch', 0)).dblclick()
|
||||||
|
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
|
await page.mouse.move(1200, 139)
|
||||||
|
await page.mouse.down()
|
||||||
|
await page.mouse.move(870, 250)
|
||||||
|
await page.mouse.up()
|
||||||
|
|
||||||
|
await editor.expectEditor.toContain(`tangentialArc(end=[-5.85,4.32])`, {
|
||||||
|
shouldNormalise: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
await u.closeKclCodePanel()
|
||||||
|
|
||||||
|
// Undo the last change
|
||||||
|
await page.keyboard.down('Control')
|
||||||
|
await page.keyboard.press('KeyZ')
|
||||||
|
await page.keyboard.up('Control')
|
||||||
|
|
||||||
|
await u.openKclCodePanel()
|
||||||
|
await editor.expectEditor.toContain(`tangentialArc(end = [5.49, 8.37])`, {
|
||||||
|
shouldNormalise: true,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
test('Can delete a single segment line with keyboard', async ({
|
test('Can delete a single segment line with keyboard', async ({
|
||||||
page,
|
page,
|
||||||
scene,
|
scene,
|
||||||
|
@ -798,7 +798,7 @@ test('theme persists', async ({ page, context, homePage }) => {
|
|||||||
|
|
||||||
await page.getByTestId('settings-close-button').click()
|
await page.getByTestId('settings-close-button').click()
|
||||||
|
|
||||||
const networkToggle = page.getByTestId('network-toggle')
|
const networkToggle = page.getByTestId(/network-toggle/)
|
||||||
|
|
||||||
// simulate network down
|
// simulate network down
|
||||||
await u.emulateNetworkConditions({
|
await u.emulateNetworkConditions({
|
||||||
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 135 KiB |
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 118 KiB |
Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 135 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 67 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 65 KiB |
117
e2e/playwright/temporary-workspace.spec.ts
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||||
|
import { stringToBase64 } from '@src/lib/base64'
|
||||||
|
|
||||||
|
test.describe('Temporary workspace', () => {
|
||||||
|
test(
|
||||||
|
'Opening a share link creates a temporary environment that is not saved',
|
||||||
|
{ tag: ['@web'] },
|
||||||
|
async ({ page, editor, scene, cmdBar, homePage }) => {
|
||||||
|
await test.step('Pre-condition: editor is empty', async () => {
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
await editor.expectEditor.toContain('')
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Go to share link, check new content present, make a change', async () => {
|
||||||
|
const code = `sketch001 = startSketchOn(XY)
|
||||||
|
profile001 = startProfile(sketch001, at = [-124.89, -186.4])
|
||||||
|
|> line(end = [391.31, 444.04])
|
||||||
|
|> line(end = [96.21, -493.07])
|
||||||
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|
|> close()
|
||||||
|
extrude001 = extrude(profile001, length = 5)
|
||||||
|
`
|
||||||
|
|
||||||
|
const codeQueryParam = encodeURIComponent(stringToBase64(code))
|
||||||
|
const targetURL = `?create-file=true&browser=test&code=${codeQueryParam}&ask-open-desktop=true`
|
||||||
|
await page.goto(page.url() + targetURL)
|
||||||
|
await expect.poll(() => page.url()).toContain(targetURL)
|
||||||
|
const button = page.getByRole('button', { name: 'Continue to web app' })
|
||||||
|
await button.click()
|
||||||
|
|
||||||
|
await editor.expectEditor.toContain(code, { shouldNormalise: true })
|
||||||
|
await editor.scrollToText('-124.89', true)
|
||||||
|
await page.keyboard.press('9')
|
||||||
|
await page.keyboard.press('9')
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Post-condition: empty editor once again (original state)', async () => {
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
const code = await page.evaluate(() =>
|
||||||
|
window.localStorage.getItem('persistCode')
|
||||||
|
)
|
||||||
|
await expect(code).toContain('')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
'Opening a sample link creates a temporary environment that is not saved',
|
||||||
|
{ tag: ['@web'] },
|
||||||
|
async ({ page, editor, scene, cmdBar, homePage }) => {
|
||||||
|
await test.step('Pre-condition: editor is empty', async () => {
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
await editor.expectEditor.toContain('')
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Load sample, make an edit', async () => {
|
||||||
|
await page.goto(
|
||||||
|
`${page.url()}/?cmd=add-kcl-file-to-project&groupId=application&projectName=browser&source=kcl-samples&sample=brake-rotor/main.kcl`
|
||||||
|
)
|
||||||
|
|
||||||
|
await editor.scrollToText('114.3', true)
|
||||||
|
await page.keyboard.press('9')
|
||||||
|
await page.keyboard.press('9')
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Post-condition: empty editor once again (original state)', async () => {
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
const code = await page.evaluate(() =>
|
||||||
|
window.localStorage.getItem('persistCode')
|
||||||
|
)
|
||||||
|
await expect(code).toContain('')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
'Hitting save will save the temporary workspace',
|
||||||
|
{ tag: ['@web'] },
|
||||||
|
async ({ page, editor, scene, cmdBar, homePage }) => {
|
||||||
|
const buttonSaveTemporaryWorkspace = page.getByTestId('tws-save')
|
||||||
|
|
||||||
|
await test.step('Pre-condition: editor is empty', async () => {
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
await editor.expectEditor.toContain('')
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Load sample, make an edit, *save*', async () => {
|
||||||
|
await page.goto(
|
||||||
|
`${page.url()}/?cmd=add-kcl-file-to-project&groupId=application&projectName=browser&source=kcl-samples&sample=brake-rotor/main.kcl`
|
||||||
|
)
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
|
await editor.scrollToText('114.3')
|
||||||
|
await editor.replaceCode('114.3', '999.9133')
|
||||||
|
await editor.expectEditor.toContain('999.9133')
|
||||||
|
|
||||||
|
await buttonSaveTemporaryWorkspace.click()
|
||||||
|
await expect(buttonSaveTemporaryWorkspace).not.toBeVisible()
|
||||||
|
|
||||||
|
await editor.expectEditor.toContain('999.9133')
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Post-condition: has the edits in localStorage', async () => {
|
||||||
|
const code = await page.evaluate(() =>
|
||||||
|
window.localStorage.getItem('persistCode')
|
||||||
|
)
|
||||||
|
await expect(code).toContain('999.9133')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
@ -24,16 +24,15 @@ test.describe('Test network related behaviors', () => {
|
|||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
const networkToggle = page.getByTestId('network-toggle')
|
const networkToggle = page.getByTestId(/network-toggle/)
|
||||||
|
|
||||||
// This is how we wait until the stream is online
|
// This is how we wait until the stream is online
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
).not.toBeDisabled({ timeout: 15000 })
|
).not.toBeDisabled({ timeout: 15000 })
|
||||||
|
|
||||||
const networkWidget = page.locator('[data-testid="network-toggle"]')
|
await expect(networkToggle).toBeVisible()
|
||||||
await expect(networkWidget).toBeVisible()
|
await networkToggle.hover()
|
||||||
await networkWidget.hover()
|
|
||||||
|
|
||||||
const networkPopover = page.locator('[data-testid="network-popover"]')
|
const networkPopover = page.locator('[data-testid="network-popover"]')
|
||||||
await expect(networkPopover).not.toBeVisible()
|
await expect(networkPopover).not.toBeVisible()
|
||||||
@ -44,7 +43,7 @@ test.describe('Test network related behaviors', () => {
|
|||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
|
|
||||||
// Click the network widget
|
// Click the network widget
|
||||||
await networkWidget.click()
|
await networkToggle.click()
|
||||||
|
|
||||||
// Check the modal opened.
|
// Check the modal opened.
|
||||||
await expect(networkPopover).toBeVisible()
|
await expect(networkPopover).toBeVisible()
|
||||||
@ -65,8 +64,8 @@ test.describe('Test network related behaviors', () => {
|
|||||||
// Expect the network to be down
|
// Expect the network to be down
|
||||||
await expect(networkToggle).toContainText('Network health (Offline)')
|
await expect(networkToggle).toContainText('Network health (Offline)')
|
||||||
|
|
||||||
// Click the network widget
|
// Click the network toggle
|
||||||
await networkWidget.click()
|
await networkToggle.click()
|
||||||
|
|
||||||
// Check the modal opened.
|
// Check the modal opened.
|
||||||
await expect(networkPopover).toBeVisible()
|
await expect(networkPopover).toBeVisible()
|
||||||
@ -99,7 +98,7 @@ test.describe('Test network related behaviors', () => {
|
|||||||
'Engine disconnect & reconnect in sketch mode',
|
'Engine disconnect & reconnect in sketch mode',
|
||||||
{ tag: '@skipLocalEngine' },
|
{ tag: '@skipLocalEngine' },
|
||||||
async ({ page, homePage, toolbar, scene, cmdBar }) => {
|
async ({ page, homePage, toolbar, scene, cmdBar }) => {
|
||||||
const networkToggle = page.getByTestId('network-toggle')
|
const networkToggle = page.getByTestId(/network-toggle/)
|
||||||
const networkToggleConnectedText = page.getByText(
|
const networkToggleConnectedText = page.getByText(
|
||||||
'Network health (Strong)'
|
'Network health (Strong)'
|
||||||
)
|
)
|
||||||
@ -286,7 +285,7 @@ profile001 = startProfile(sketch001, at = [12.34, -12.34])
|
|||||||
'Paused stream freezes view frame, unpause reconnect is seamless to user',
|
'Paused stream freezes view frame, unpause reconnect is seamless to user',
|
||||||
{ tag: ['@desktop', '@skipLocalEngine'] },
|
{ tag: ['@desktop', '@skipLocalEngine'] },
|
||||||
async ({ page, homePage, scene, cmdBar, toolbar, tronApp }) => {
|
async ({ page, homePage, scene, cmdBar, toolbar, tronApp }) => {
|
||||||
const networkToggle = page.getByTestId('network-toggle')
|
const networkToggle = page.getByTestId(/network-toggle/)
|
||||||
const networkToggleConnectedText = page.getByText(
|
const networkToggleConnectedText = page.getByText(
|
||||||
'Network health (Strong)'
|
'Network health (Strong)'
|
||||||
)
|
)
|
||||||
|
@ -22,6 +22,7 @@ export const token = process.env.token || ''
|
|||||||
import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration'
|
import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration'
|
||||||
|
|
||||||
import type { ElectronZoo } from '@e2e/playwright/fixtures/fixtureSetup'
|
import type { ElectronZoo } from '@e2e/playwright/fixtures/fixtureSetup'
|
||||||
|
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||||
import { isErrorWhitelisted } from '@e2e/playwright/lib/console-error-whitelist'
|
import { isErrorWhitelisted } from '@e2e/playwright/lib/console-error-whitelist'
|
||||||
import { TEST_SETTINGS, TEST_SETTINGS_KEY } from '@e2e/playwright/storageStates'
|
import { TEST_SETTINGS, TEST_SETTINGS_KEY } from '@e2e/playwright/storageStates'
|
||||||
import { test } from '@e2e/playwright/zoo-test'
|
import { test } from '@e2e/playwright/zoo-test'
|
||||||
@ -37,7 +38,7 @@ export const headerMasks = (page: Page) => [
|
|||||||
]
|
]
|
||||||
|
|
||||||
export const lowerRightMasks = (page: Page) => [
|
export const lowerRightMasks = (page: Page) => [
|
||||||
page.getByTestId('network-toggle'),
|
page.getByTestId(/network-toggle/),
|
||||||
page.getByTestId('billing-remaining-bar'),
|
page.getByTestId('billing-remaining-bar'),
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -158,10 +159,10 @@ async function openKclCodePanel(page: Page) {
|
|||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
// editorManager is available on the window object.
|
// editorManager is available on the window object.
|
||||||
//@ts-ignore this is in an entirely different context that tsc can't see.
|
//@ts-ignore this is in an entirely different context that tsc can't see.
|
||||||
editorManager._editorView.dispatch({
|
editorManager.getEditorView().dispatch({
|
||||||
selection: {
|
selection: {
|
||||||
//@ts-ignore this is in an entirely different context that tsc can't see.
|
//@ts-ignore this is in an entirely different context that tsc can't see.
|
||||||
anchor: editorManager._editorView.docView.length,
|
anchor: editorManager.getEditorView().docView.length,
|
||||||
},
|
},
|
||||||
scrollIntoView: true,
|
scrollIntoView: true,
|
||||||
})
|
})
|
||||||
@ -737,6 +738,7 @@ export const doExport = async (
|
|||||||
output: Models['OutputFormat3d_type'],
|
output: Models['OutputFormat3d_type'],
|
||||||
rootDir: string,
|
rootDir: string,
|
||||||
page: Page,
|
page: Page,
|
||||||
|
cmdBar: CmdBarFixture,
|
||||||
exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown'
|
exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown'
|
||||||
): Promise<Paths> => {
|
): Promise<Paths> => {
|
||||||
if (exportFrom === 'dropdown') {
|
if (exportFrom === 'dropdown') {
|
||||||
@ -780,9 +782,7 @@ export const doExport = async (
|
|||||||
.click()
|
.click()
|
||||||
await page.locator('#arg-form').waitFor({ state: 'detached' })
|
await page.locator('#arg-form').waitFor({ state: 'detached' })
|
||||||
}
|
}
|
||||||
await expect(page.getByText('Confirm Export')).toBeVisible()
|
await cmdBar.submit()
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Submit command' }).click()
|
|
||||||
|
|
||||||
await expect(page.getByText('Exported successfully')).toBeVisible()
|
await expect(page.getByText('Exported successfully')).toBeVisible()
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||||
|
|
||||||
test.describe('Testing constraints', () => {
|
test.describe('Testing constraints', () => {
|
||||||
test('Can constrain line length', async ({ page, homePage }) => {
|
test('Can constrain line length', async ({ page, homePage, cmdBar }) => {
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
@ -50,11 +50,7 @@ test.describe('Testing constraints', () => {
|
|||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await page.getByTestId('constraint-length').click()
|
await page.getByTestId('constraint-length').click()
|
||||||
await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('20')
|
await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('20')
|
||||||
await page
|
await cmdBar.continue()
|
||||||
.getByRole('button', {
|
|
||||||
name: 'arrow right Continue',
|
|
||||||
})
|
|
||||||
.click()
|
|
||||||
|
|
||||||
await expect(page.locator('.cm-content')).toHaveText(
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
`length001 = 20sketch001 = startSketchOn(XY) |> startProfile(at = [-10, -10]) |> line(end = [20, 0]) |> angledLine(angle = 90, length = length001) |> xLine(length = -20)`
|
`length001 = 20sketch001 = startSketchOn(XY) |> startProfile(at = [-10, -10]) |> line(end = [20, 0]) |> angledLine(angle = 90, length = length001) |> xLine(length = -20)`
|
||||||
@ -681,9 +677,6 @@ test.describe('Testing constraints', () => {
|
|||||||
.getByRole('textbox')
|
.getByRole('textbox')
|
||||||
const cmdBarKclVariableNameInput =
|
const cmdBarKclVariableNameInput =
|
||||||
page.getByPlaceholder('Variable name')
|
page.getByPlaceholder('Variable name')
|
||||||
const cmdBarSubmitButton = page.getByRole('button', {
|
|
||||||
name: 'arrow right Continue',
|
|
||||||
})
|
|
||||||
|
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@ -736,7 +729,7 @@ part002 = startSketchOn(XZ)
|
|||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
const [ang, len] = value.split(', ')
|
const [ang, len] = value.split(', ')
|
||||||
const changedCode = `|> angledLine(angle = ${ang}, length = ${len})`
|
const changedCode = `|> angledLine(angle = ${ang}, length = ${len})`
|
||||||
await cmdBarSubmitButton.click()
|
await cmdBar.continue()
|
||||||
await expect(page.locator('.cm-content')).toContainText(changedCode)
|
await expect(page.locator('.cm-content')).toContainText(changedCode)
|
||||||
|
|
||||||
// checking active assures the cursor is where it should be
|
// checking active assures the cursor is where it should be
|
||||||
@ -1101,11 +1094,7 @@ part002 = startSketchOn(XZ)
|
|||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('10')
|
await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('10')
|
||||||
await page
|
await cmdBar.continue()
|
||||||
.getByRole('button', {
|
|
||||||
name: 'arrow right Continue',
|
|
||||||
})
|
|
||||||
.click()
|
|
||||||
|
|
||||||
await pollEditorLinesSelectedLength(page, 1)
|
await pollEditorLinesSelectedLength(page, 1)
|
||||||
activeLinesContent = await page.locator('.cm-activeLine').all()
|
activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||||
|
@ -8,37 +8,37 @@ test.describe('Testing Gizmo', () => {
|
|||||||
const cases = [
|
const cases = [
|
||||||
{
|
{
|
||||||
testDescription: 'top view',
|
testDescription: 'top view',
|
||||||
clickPosition: { x: 951, y: 385 },
|
clickPosition: { x: 951, y: 402 },
|
||||||
expectedCameraPosition: { x: 800, y: -152, z: 4886.02 },
|
expectedCameraPosition: { x: 800, y: -152, z: 4886.02 },
|
||||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testDescription: 'bottom view',
|
testDescription: 'bottom view',
|
||||||
clickPosition: { x: 951, y: 429 },
|
clickPosition: { x: 951, y: 449 },
|
||||||
expectedCameraPosition: { x: 800, y: -152, z: -4834.02 },
|
expectedCameraPosition: { x: 800, y: -152, z: -4834.02 },
|
||||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testDescription: 'right view',
|
testDescription: 'right view',
|
||||||
clickPosition: { x: 929, y: 417 },
|
clickPosition: { x: 929, y: 435 },
|
||||||
expectedCameraPosition: { x: 5660.02, y: -152, z: 26 },
|
expectedCameraPosition: { x: 5660.02, y: -152, z: 26 },
|
||||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testDescription: 'left view',
|
testDescription: 'left view',
|
||||||
clickPosition: { x: 974, y: 397 },
|
clickPosition: { x: 974, y: 417 },
|
||||||
expectedCameraPosition: { x: -4060.02, y: -152, z: 26 },
|
expectedCameraPosition: { x: -4060.02, y: -152, z: 26 },
|
||||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testDescription: 'back view',
|
testDescription: 'back view',
|
||||||
clickPosition: { x: 967, y: 421 },
|
clickPosition: { x: 967, y: 441 },
|
||||||
expectedCameraPosition: { x: 800, y: 4708.02, z: 26 },
|
expectedCameraPosition: { x: 800, y: 4708.02, z: 26 },
|
||||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testDescription: 'front view',
|
testDescription: 'front view',
|
||||||
clickPosition: { x: 935, y: 393 },
|
clickPosition: { x: 935, y: 413 },
|
||||||
expectedCameraPosition: { x: 800, y: -5012.02, z: 26 },
|
expectedCameraPosition: { x: 800, y: -5012.02, z: 26 },
|
||||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
||||||
},
|
},
|
||||||
|
@ -21,7 +21,7 @@ test.describe('Testing loading external models', () => {
|
|||||||
// We have no more web tests
|
// We have no more web tests
|
||||||
test.fail(
|
test.fail(
|
||||||
'Web: should overwrite current code, cannot create new file',
|
'Web: should overwrite current code, cannot create new file',
|
||||||
async ({ editor, context, page, homePage }) => {
|
async ({ editor, context, page, homePage, cmdBar }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await test.step(`Test setup`, async () => {
|
await test.step(`Test setup`, async () => {
|
||||||
await context.addInitScript((code) => {
|
await context.addInitScript((code) => {
|
||||||
@ -52,9 +52,6 @@ test.describe('Testing loading external models', () => {
|
|||||||
name,
|
name,
|
||||||
})
|
})
|
||||||
const warningText = page.getByText('Overwrite current file with sample?')
|
const warningText = page.getByText('Overwrite current file with sample?')
|
||||||
const confirmButton = page.getByRole('button', {
|
|
||||||
name: 'Submit command',
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Precondition: check the initial code`, async () => {
|
await test.step(`Precondition: check the initial code`, async () => {
|
||||||
await u.openKclCodePanel()
|
await u.openKclCodePanel()
|
||||||
@ -70,7 +67,7 @@ test.describe('Testing loading external models', () => {
|
|||||||
await expect(commandMethodOption('Create new file')).not.toBeVisible()
|
await expect(commandMethodOption('Create new file')).not.toBeVisible()
|
||||||
await commandMethodOption('Overwrite').click()
|
await commandMethodOption('Overwrite').click()
|
||||||
await expect(warningText).toBeVisible()
|
await expect(warningText).toBeVisible()
|
||||||
await confirmButton.click()
|
await cmdBar.submit()
|
||||||
|
|
||||||
await editor.expectEditor.toContain('// ' + newSample.title)
|
await editor.expectEditor.toContain('// ' + newSample.title)
|
||||||
})
|
})
|
||||||
|
@ -3,6 +3,7 @@ import type { LineInputsType } from '@src/lang/std/sketchcombos'
|
|||||||
import { uuidv4 } from '@src/lib/utils'
|
import { uuidv4 } from '@src/lib/utils'
|
||||||
|
|
||||||
import type { EditorFixture } from '@e2e/playwright/fixtures/editorFixture'
|
import type { EditorFixture } from '@e2e/playwright/fixtures/editorFixture'
|
||||||
|
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||||
import { deg, getUtils, wiggleMove } from '@e2e/playwright/test-utils'
|
import { deg, getUtils, wiggleMove } from '@e2e/playwright/test-utils'
|
||||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||||
|
|
||||||
@ -18,7 +19,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
* @param {number} options.steps - The number of steps to perform
|
* @param {number} options.steps - The number of steps to perform
|
||||||
*/
|
*/
|
||||||
const _clickConstrained =
|
const _clickConstrained =
|
||||||
(page: Page, editor: EditorFixture) =>
|
(page: Page, editor: EditorFixture, cmdBar: CmdBarFixture) =>
|
||||||
async ({
|
async ({
|
||||||
hoverPos,
|
hoverPos,
|
||||||
constraintType,
|
constraintType,
|
||||||
@ -93,11 +94,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||||
).toBeFocused()
|
).toBeFocused()
|
||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
await page
|
await cmdBar.continue()
|
||||||
.getByRole('button', {
|
|
||||||
name: 'arrow right Continue',
|
|
||||||
})
|
|
||||||
.click()
|
|
||||||
await editor.expectEditor.toContain(expectFinal, {
|
await editor.expectEditor.toContain(expectFinal, {
|
||||||
shouldNormalise: true,
|
shouldNormalise: true,
|
||||||
})
|
})
|
||||||
@ -113,7 +110,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
* @param {number} options.steps - The number of steps to perform
|
* @param {number} options.steps - The number of steps to perform
|
||||||
*/
|
*/
|
||||||
const _clickUnconstrained =
|
const _clickUnconstrained =
|
||||||
(page: Page, editor: EditorFixture) =>
|
(page: Page, editor: EditorFixture, cmdBar: CmdBarFixture) =>
|
||||||
async ({
|
async ({
|
||||||
hoverPos,
|
hoverPos,
|
||||||
constraintType,
|
constraintType,
|
||||||
@ -163,11 +160,7 @@ test.describe('Testing segment overlays', () => {
|
|||||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||||
).toBeFocused()
|
).toBeFocused()
|
||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
await page
|
await cmdBar.continue()
|
||||||
.getByRole('button', {
|
|
||||||
name: 'arrow right Continue',
|
|
||||||
})
|
|
||||||
.click()
|
|
||||||
await editor.expectEditor.toContain(expectAfterUnconstrained, {
|
await editor.expectEditor.toContain(expectAfterUnconstrained, {
|
||||||
shouldNormalise: true,
|
shouldNormalise: true,
|
||||||
})
|
})
|
||||||
@ -239,8 +232,8 @@ test.describe('Testing segment overlays', () => {
|
|||||||
|
|
||||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(14)
|
await expect(page.getByTestId('segment-overlay')).toHaveCount(14)
|
||||||
|
|
||||||
const clickUnconstrained = _clickUnconstrained(page, editor)
|
const clickUnconstrained = _clickUnconstrained(page, editor, cmdBar)
|
||||||
const clickConstrained = _clickConstrained(page, editor)
|
const clickConstrained = _clickConstrained(page, editor, cmdBar)
|
||||||
|
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
await u.sendCustomCmd({
|
await u.sendCustomCmd({
|
||||||
@ -664,7 +657,7 @@ profile002 = circle(sketch001, center = [345, 0], radius = 238.38)
|
|||||||
await expect(
|
await expect(
|
||||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||||
).toBeFocused()
|
).toBeFocused()
|
||||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
await cmdBar.continue()
|
||||||
|
|
||||||
// Verify the X constraint was added
|
// Verify the X constraint was added
|
||||||
await editor.expectEditor.toContain('center = [xAbs001, 0]', {
|
await editor.expectEditor.toContain('center = [xAbs001, 0]', {
|
||||||
@ -682,7 +675,7 @@ profile002 = circle(sketch001, center = [345, 0], radius = 238.38)
|
|||||||
await expect(
|
await expect(
|
||||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||||
).toBeFocused()
|
).toBeFocused()
|
||||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
await cmdBar.continue()
|
||||||
|
|
||||||
// Verify the Y constraint was added
|
// Verify the Y constraint was added
|
||||||
await editor.expectEditor.toContain('center = [xAbs001, yAbs001]', {
|
await editor.expectEditor.toContain('center = [xAbs001, yAbs001]', {
|
||||||
@ -700,7 +693,7 @@ profile002 = circle(sketch001, center = [345, 0], radius = 238.38)
|
|||||||
await expect(
|
await expect(
|
||||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||||
).toBeFocused()
|
).toBeFocused()
|
||||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
await cmdBar.continue()
|
||||||
|
|
||||||
// Verify all constraints were added
|
// Verify all constraints were added
|
||||||
await editor.expectEditor.toContain(
|
await editor.expectEditor.toContain(
|
||||||
@ -887,7 +880,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
|
|||||||
await expect(
|
await expect(
|
||||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||||
).toBeFocused()
|
).toBeFocused()
|
||||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
await cmdBar.continue()
|
||||||
|
|
||||||
// Verify the constraint was added
|
// Verify the constraint was added
|
||||||
await editor.expectEditor.toContain(
|
await editor.expectEditor.toContain(
|
||||||
@ -910,7 +903,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
|
|||||||
await expect(
|
await expect(
|
||||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||||
).toBeFocused()
|
).toBeFocused()
|
||||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
await cmdBar.continue()
|
||||||
|
|
||||||
// Verify both constraints were added
|
// Verify both constraints were added
|
||||||
await editor.expectEditor.toContain(
|
await editor.expectEditor.toContain(
|
||||||
@ -935,7 +928,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
|
|||||||
await expect(
|
await expect(
|
||||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||||
).toBeFocused()
|
).toBeFocused()
|
||||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
await cmdBar.continue()
|
||||||
|
|
||||||
// Verify the constraint was added
|
// Verify the constraint was added
|
||||||
await editor.expectEditor.toContain('endAbsolute = [xAbs002, 84.07]', {
|
await editor.expectEditor.toContain('endAbsolute = [xAbs002, 84.07]', {
|
||||||
@ -955,7 +948,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
|
|||||||
await expect(
|
await expect(
|
||||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||||
).toBeFocused()
|
).toBeFocused()
|
||||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
await cmdBar.continue()
|
||||||
|
|
||||||
// Verify all constraints were added
|
// Verify all constraints were added
|
||||||
await editor.expectEditor.toContain(
|
await editor.expectEditor.toContain(
|
||||||
|
@ -32,7 +32,7 @@ test('Units menu', async ({ page, homePage }) => {
|
|||||||
test(
|
test(
|
||||||
'Successful export shows a success toast',
|
'Successful export shows a success toast',
|
||||||
{ tag: '@skipLocalEngine' },
|
{ tag: '@skipLocalEngine' },
|
||||||
async ({ page, homePage, tronApp }) => {
|
async ({ page, homePage, cmdBar, tronApp }) => {
|
||||||
// FYI this test doesn't work with only engine running locally
|
// FYI this test doesn't work with only engine running locally
|
||||||
// And you will need to have the KittyCAD CLI installed
|
// And you will need to have the KittyCAD CLI installed
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
@ -94,7 +94,8 @@ part001 = startSketchOn(-XZ)
|
|||||||
presentation: 'pretty',
|
presentation: 'pretty',
|
||||||
},
|
},
|
||||||
tronApp?.projectDirName,
|
tronApp?.projectDirName,
|
||||||
page
|
page,
|
||||||
|
cmdBar
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -254,6 +255,7 @@ test('First escape in tool pops you out of tool, second exits sketch mode', asyn
|
|||||||
test('Basic default modeling and sketch hotkeys work', async ({
|
test('Basic default modeling and sketch hotkeys work', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
homePage,
|
||||||
|
cmdBar,
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await test.step(`Set up test`, async () => {
|
await test.step(`Set up test`, async () => {
|
||||||
@ -397,11 +399,8 @@ test('Basic default modeling and sketch hotkeys work', async ({
|
|||||||
await expect(page.getByRole('button', { name: 'Continue' })).toBeVisible({
|
await expect(page.getByRole('button', { name: 'Continue' })).toBeVisible({
|
||||||
timeout: 20_000,
|
timeout: 20_000,
|
||||||
})
|
})
|
||||||
await page.getByRole('button', { name: 'Continue' }).click()
|
await cmdBar.continue()
|
||||||
await expect(
|
await cmdBar.submit()
|
||||||
page.getByRole('button', { name: 'Submit command' })
|
|
||||||
).toBeVisible()
|
|
||||||
await page.getByRole('button', { name: 'Submit command' }).click()
|
|
||||||
await expect(page.locator('.cm-content')).toContainText('extrude(')
|
await expect(page.locator('.cm-content')).toContainText('extrude(')
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -575,8 +574,7 @@ profile001 = startProfile(sketch002, at = [-12.34, 12.34])
|
|||||||
|
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
await expect(page.getByText('Confirm Extrude')).toBeVisible()
|
await cmdBar.submit()
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
|
|
||||||
const result2 = result.genNext`
|
const result2 = result.genNext`
|
||||||
const sketch002 = extrude(sketch002, length = ${[5, 5]} + 7)`
|
const sketch002 = extrude(sketch002, length = ${[5, 5]} + 7)`
|
||||||
|
@ -111,7 +111,8 @@ commaSep1NoTrailingComma<term> { term ("," term)* }
|
|||||||
|
|
||||||
PipeSubstitution { "%" }
|
PipeSubstitution { "%" }
|
||||||
|
|
||||||
identifier { (@asciiLetter | "_") (@asciiLetter | @digit | "_")* }
|
// Includes non-whitespace unicode characters.
|
||||||
|
identifier { $[a-zA-Z_\u{a1}-\u{167f}\u{1681}-\u{1fff}\u{200e}-\u{2027}\u{202a}-\u{202e}\u{2030}-\u{205e}\u{2061}-\u{2fff}\u{3001}-\u{fefe}\u{ff00}-\u{10ffff}] $[a-zA-Z0-9_\u{a1}-\u{167f}\u{1681}-\u{1fff}\u{200e}-\u{2027}\u{202a}-\u{202e}\u{2030}-\u{205e}\u{2061}-\u{2fff}\u{3001}-\u{fefe}\u{ff00}-\u{10ffff}]* }
|
||||||
AnnotationName { "@" identifier? }
|
AnnotationName { "@" identifier? }
|
||||||
PropertyName { identifier }
|
PropertyName { identifier }
|
||||||
TagDeclarator { "$" identifier }
|
TagDeclarator { "$" identifier }
|
||||||
|
@ -42,15 +42,15 @@ fn helicalGear(nTeeth, module, pressureAngle, helixAngle, gearHeight) {
|
|||||||
helicalGearSketch = startSketchOn(offsetPlane(XY, offset = offsetHeight))
|
helicalGearSketch = startSketchOn(offsetPlane(XY, offset = offsetHeight))
|
||||||
|> startProfile(at = polar(angle = helixCalc, length = baseDiameter / 2))
|
|> startProfile(at = polar(angle = helixCalc, length = baseDiameter / 2))
|
||||||
|> involuteCircular(
|
|> involuteCircular(
|
||||||
startRadius = baseDiameter / 2,
|
startDiameter = baseDiameter,
|
||||||
endRadius = tipDiameter / 2,
|
endDiameter = tipDiameter,
|
||||||
angle = helixCalc,
|
angle = helixCalc,
|
||||||
tag = $seg01,
|
tag = $seg01,
|
||||||
)
|
)
|
||||||
|> line(endAbsolute = polar(angle = 160 / nTeeth + helixCalc, length = tipDiameter / 2))
|
|> line(endAbsolute = polar(angle = 160 / nTeeth + helixCalc, length = tipDiameter / 2))
|
||||||
|> involuteCircular(
|
|> involuteCircular(
|
||||||
startRadius = baseDiameter / 2,
|
startDiameter = baseDiameter,
|
||||||
endRadius = tipDiameter / 2,
|
endDiameter = tipDiameter,
|
||||||
angle = -(4 * atan(segEndY(seg01) / segEndX(seg01)) - (3 * helixCalc)),
|
angle = -(4 * atan(segEndY(seg01) / segEndX(seg01)) - (3 * helixCalc)),
|
||||||
reverse = true,
|
reverse = true,
|
||||||
)
|
)
|
||||||
|
553
rust/Cargo.lock
generated
@ -29,8 +29,8 @@ debug = "line-tables-only"
|
|||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
async-trait = "0.1.88"
|
async-trait = "0.1.88"
|
||||||
anyhow = { version = "1" }
|
anyhow = { version = "1" }
|
||||||
bson = { version = "2.13.0", features = ["uuid-1", "chrono"] }
|
bson = { version = "2.15.0", features = ["uuid-1", "chrono"] }
|
||||||
clap = { version = "4.5.36", features = ["derive"] }
|
clap = { version = "4.5.40", features = ["derive"] }
|
||||||
console_error_panic_hook = "0.1.7"
|
console_error_panic_hook = "0.1.7"
|
||||||
dashmap = { version = "6.1.0" }
|
dashmap = { version = "6.1.0" }
|
||||||
http = "1"
|
http = "1"
|
||||||
@ -38,8 +38,8 @@ indexmap = "2.9.0"
|
|||||||
kittycad = { version = "0.3.37", default-features = false, features = ["js", "requests"] }
|
kittycad = { version = "0.3.37", default-features = false, features = ["js", "requests"] }
|
||||||
kittycad-modeling-cmds = { version = "0.2.123", features = ["ts-rs", "websocket"] }
|
kittycad-modeling-cmds = { version = "0.2.123", features = ["ts-rs", "websocket"] }
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
miette = "7.5.0"
|
miette = "7.6.0"
|
||||||
pyo3 = { version = "0.24.1" }
|
pyo3 = { version = "0.24.2" }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = { version = "1" }
|
serde_json = { version = "1" }
|
||||||
slog = "2.7.0"
|
slog = "2.7.0"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "kcl-bumper"
|
name = "kcl-bumper"
|
||||||
version = "0.1.81"
|
version = "0.1.82"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
repository = "https://github.com/KittyCAD/modeling-api"
|
repository = "https://github.com/KittyCAD/modeling-api"
|
||||||
rust-version = "1.76"
|
rust-version = "1.76"
|
||||||
@ -19,7 +19,7 @@ anyhow = { workspace = true }
|
|||||||
clap = { workspace = true, features = ["derive"] }
|
clap = { workspace = true, features = ["derive"] }
|
||||||
semver = "1.0.25"
|
semver = "1.0.25"
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
toml_edit = "0.22.16"
|
toml_edit = "0.22.26"
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-derive-docs"
|
name = "kcl-derive-docs"
|
||||||
description = "A tool for generating documentation from Rust derive macros"
|
description = "A tool for generating documentation from Rust derive macros"
|
||||||
version = "0.1.81"
|
version = "0.1.82"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
@ -14,7 +14,7 @@ bench = false
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
proc-macro2 = "1"
|
proc-macro2 = "1"
|
||||||
quote = "1"
|
quote = "1"
|
||||||
syn = { version = "2.0.96", features = ["full"] }
|
syn = { version = "2.0.103", features = ["full"] }
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-directory-test-macro"
|
name = "kcl-directory-test-macro"
|
||||||
description = "A tool for generating tests from a directory of kcl files"
|
description = "A tool for generating tests from a directory of kcl files"
|
||||||
version = "0.1.81"
|
version = "0.1.82"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
@ -14,7 +14,7 @@ bench = false
|
|||||||
convert_case = "0.8.0"
|
convert_case = "0.8.0"
|
||||||
proc-macro2 = "1"
|
proc-macro2 = "1"
|
||||||
quote = "1"
|
quote = "1"
|
||||||
syn = { version = "2.0.96", features = ["full"] }
|
syn = { version = "2.0.103", features = ["full"] }
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-language-server-release"
|
name = "kcl-language-server-release"
|
||||||
version = "0.1.81"
|
version = "0.1.82"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||||
publish = false
|
publish = false
|
||||||
@ -14,7 +14,7 @@ bench = false
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
clap = { workspace = true, features = ["cargo", "derive", "env", "unicode"] }
|
clap = { workspace = true, features = ["cargo", "derive", "env", "unicode"] }
|
||||||
flate2 = "1.1.1"
|
flate2 = "1.1.2"
|
||||||
lazy_static = { workspace = true }
|
lazy_static = { workspace = true }
|
||||||
log = { version = "0.4.27", features = ["serde"] }
|
log = { version = "0.4.27", features = ["serde"] }
|
||||||
slog = { workspace = true }
|
slog = { workspace = true }
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
name = "kcl-language-server"
|
name = "kcl-language-server"
|
||||||
description = "A language server for KCL."
|
description = "A language server for KCL."
|
||||||
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||||
version = "0.2.81"
|
version = "0.2.82"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
@ -31,8 +31,8 @@ slog-term = { workspace = true }
|
|||||||
tracing-subscriber = { workspace = true }
|
tracing-subscriber = { workspace = true }
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
signal-hook = "0.3.17"
|
signal-hook = "0.3.18"
|
||||||
tokio = { version = "1.44.2", features = ["full"] }
|
tokio = { version = "1.45.1", features = ["full"] }
|
||||||
tower-lsp = { version = "0.20.0", features = ["proposed"] }
|
tower-lsp = { version = "0.20.0", features = ["proposed"] }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-lib"
|
name = "kcl-lib"
|
||||||
description = "KittyCAD Language implementation and tools"
|
description = "KittyCAD Language implementation and tools"
|
||||||
version = "0.2.81"
|
version = "0.2.82"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
@ -25,8 +25,8 @@ async-recursion = "1.1.1"
|
|||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
bson = { workspace = true }
|
bson = { workspace = true }
|
||||||
chrono = "0.4.38"
|
chrono = "0.4.41"
|
||||||
clap = { version = "4.5.36", default-features = false, optional = true, features = [
|
clap = { version = "4.5.40", default-features = false, optional = true, features = [
|
||||||
"std",
|
"std",
|
||||||
"derive",
|
"derive",
|
||||||
] }
|
] }
|
||||||
@ -42,11 +42,12 @@ gltf-json = "1.4.1"
|
|||||||
http = { workspace = true }
|
http = { workspace = true }
|
||||||
image = { version = "0.25.6", default-features = false, features = ["png"] }
|
image = { version = "0.25.6", default-features = false, features = ["png"] }
|
||||||
indexmap = { workspace = true, features = ["serde", "rayon"] }
|
indexmap = { workspace = true, features = ["serde", "rayon"] }
|
||||||
itertools = "0.13.0"
|
itertools = "0.14.0"
|
||||||
kcl-derive-docs = { version = "0.1", path = "../kcl-derive-docs" }
|
kcl-derive-docs = { version = "0.1", path = "../kcl-derive-docs" }
|
||||||
kittycad = { workspace = true }
|
kittycad = { workspace = true }
|
||||||
kittycad-modeling-cmds = { workspace = true }
|
kittycad-modeling-cmds = { workspace = true }
|
||||||
lazy_static = { workspace = true }
|
lazy_static = { workspace = true }
|
||||||
|
libm = "0.2.15"
|
||||||
measurements = "0.11.0"
|
measurements = "0.11.0"
|
||||||
miette = { workspace = true }
|
miette = { workspace = true }
|
||||||
mime_guess = "2.0.5"
|
mime_guess = "2.0.5"
|
||||||
@ -69,11 +70,11 @@ schemars = { version = "0.8.17", features = [
|
|||||||
] }
|
] }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
sha2 = "0.10.8"
|
sha2 = "0.10.9"
|
||||||
tabled = { version = "0.18.0", optional = true }
|
tabled = { version = "0.20.0", optional = true }
|
||||||
tempfile = "3.19"
|
tempfile = "3.20"
|
||||||
thiserror = "2.0.0"
|
thiserror = "2.0.0"
|
||||||
toml = "0.8.19"
|
toml = "0.8.22"
|
||||||
ts-rs = { version = "10.1.0", features = [
|
ts-rs = { version = "10.1.0", features = [
|
||||||
"uuid-impl",
|
"uuid-impl",
|
||||||
"url-impl",
|
"url-impl",
|
||||||
@ -82,7 +83,7 @@ ts-rs = { version = "10.1.0", features = [
|
|||||||
"no-serde-warnings",
|
"no-serde-warnings",
|
||||||
"serde-json-impl",
|
"serde-json-impl",
|
||||||
] }
|
] }
|
||||||
tynm = "0.1.10"
|
tynm = "0.2.0"
|
||||||
url = { version = "2.5.4", features = ["serde"] }
|
url = { version = "2.5.4", features = ["serde"] }
|
||||||
uuid = { workspace = true, features = ["v4", "v5", "js", "serde"] }
|
uuid = { workspace = true, features = ["v4", "v5", "js", "serde"] }
|
||||||
validator = { version = "0.20.0", features = ["derive"] }
|
validator = { version = "0.20.0", features = ["derive"] }
|
||||||
@ -94,7 +95,6 @@ zip = { workspace = true }
|
|||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
console_error_panic_hook = { workspace = true }
|
console_error_panic_hook = { workspace = true }
|
||||||
futures-lite = "2.6.0"
|
futures-lite = "2.6.0"
|
||||||
instant = { version = "0.1.13", features = ["wasm-bindgen", "inaccurate"] }
|
|
||||||
js-sys = { version = "0.3.72" }
|
js-sys = { version = "0.3.72" }
|
||||||
tokio = { workspace = true, features = ["sync", "time"] }
|
tokio = { workspace = true, features = ["sync", "time"] }
|
||||||
tower-lsp = { workspace = true, features = ["runtime-agnostic"] }
|
tower-lsp = { workspace = true, features = ["runtime-agnostic"] }
|
||||||
@ -105,9 +105,8 @@ wasm-timer = { package = "zduny-wasm-timer", version = "0.2.5" }
|
|||||||
web-sys = { version = "0.3.76", features = ["console"] }
|
web-sys = { version = "0.3.76", features = ["console"] }
|
||||||
|
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
instant = "0.1.13"
|
|
||||||
tokio = { workspace = true, features = ["full"] }
|
tokio = { workspace = true, features = ["full"] }
|
||||||
tokio-tungstenite = { version = "0.26.2", features = [
|
tokio-tungstenite = { version = "0.27.0", features = [
|
||||||
"rustls-tls-native-roots",
|
"rustls-tls-native-roots",
|
||||||
] }
|
] }
|
||||||
tower-lsp = { workspace = true, features = ["proposed", "default"] }
|
tower-lsp = { workspace = true, features = ["proposed", "default"] }
|
||||||
@ -131,15 +130,15 @@ tabled = ["dep:tabled"]
|
|||||||
approx = "0.5"
|
approx = "0.5"
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
criterion = { version = "0.6.0", features = ["async_tokio"] }
|
criterion = { version = "0.6.0", features = ["async_tokio"] }
|
||||||
expectorate = "1.1.0"
|
expectorate = "1.2.0"
|
||||||
handlebars = "6.3.2"
|
handlebars = "6.3.2"
|
||||||
image = { version = "0.25.6", default-features = false, features = ["png"] }
|
image = { version = "0.25.6", default-features = false, features = ["png"] }
|
||||||
insta = { version = "1.42.2", features = ["json", "filters", "redactions"] }
|
insta = { version = "1.43.1", features = ["json", "filters", "redactions"] }
|
||||||
kcl-directory-test-macro = { version = "0.1", path = "../kcl-directory-test-macro" }
|
kcl-directory-test-macro = { version = "0.1", path = "../kcl-directory-test-macro" }
|
||||||
miette = { version = "7.5.0", features = ["fancy"] }
|
miette = { version = "7.6.0", features = ["fancy"] }
|
||||||
pretty_assertions = "1.4.1"
|
pretty_assertions = "1.4.1"
|
||||||
tokio = { version = "1.44.2", features = ["rt-multi-thread", "macros", "time"] }
|
tokio = { version = "1.45.1", features = ["rt-multi-thread", "macros", "time"] }
|
||||||
twenty-twenty = "0.8.0"
|
twenty-twenty = "0.8.2"
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
@ -1 +1,19 @@
|
|||||||
enum-variant-size-threshold = 48
|
enum-variant-size-threshold = 48
|
||||||
|
|
||||||
|
disallowed-methods = [
|
||||||
|
{ path = "f64::sin", reason = "Use trig functions from libm crate instead, to ensure FP math works the same across OSs and platforms."},
|
||||||
|
{ path = "f64::cos", reason = "Use trig functions from libm crate instead, to ensure FP math works the same across OSs and platforms."},
|
||||||
|
{ path = "f64::tan", reason = "Use trig functions from libm crate instead, to ensure FP math works the same across OSs and platforms."},
|
||||||
|
{ path = "f64::asin", reason = "Use trig functions from libm crate instead, to ensure FP math works the same across OSs and platforms."},
|
||||||
|
{ path = "f64::acos", reason = "Use trig functions from libm crate instead, to ensure FP math works the same across OSs and platforms."},
|
||||||
|
{ path = "f64::atan", reason = "Use trig functions from libm crate instead, to ensure FP math works the same across OSs and platforms."},
|
||||||
|
{ path = "f64::atan2", reason = "Use trig functions from libm crate instead, to ensure FP math works the same across OSs and platforms."},
|
||||||
|
{ path = "f32::sin", reason = "Use trig functions from libm crate instead, to ensure FP math works the same across OSs and platforms."},
|
||||||
|
{ path = "f32::cos", reason = "Use trig functions from libm crate instead, to ensure FP math works the same across OSs and platforms."},
|
||||||
|
{ path = "f32::tan", reason = "Use trig functions from libm crate instead, to ensure FP math works the same across OSs and platforms."},
|
||||||
|
{ path = "f32::asin", reason = "Use trig functions from libm crate instead, to ensure FP math works the same across OSs and platforms."},
|
||||||
|
{ path = "f32::acos", reason = "Use trig functions from libm crate instead, to ensure FP math works the same across OSs and platforms."},
|
||||||
|
{ path = "f32::atan", reason = "Use trig functions from libm crate instead, to ensure FP math works the same across OSs and platforms."},
|
||||||
|
{ path = "f32::atan2", reason = "Use trig functions from libm crate instead, to ensure FP math works the same across OSs and platforms."},
|
||||||
|
]
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ use schemars::JsonSchema;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
use web_time::Instant;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
@ -241,7 +242,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let current_time = instant::Instant::now();
|
let current_time = Instant::now();
|
||||||
while current_time.elapsed().as_secs() < 60 {
|
while current_time.elapsed().as_secs() < 60 {
|
||||||
let responses = self.responses().read().await.clone();
|
let responses = self.responses().read().await.clone();
|
||||||
let Some(resp) = responses.get(&id) else {
|
let Some(resp) = responses.get(&id) else {
|
||||||
@ -249,7 +250,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
// No seriously WE DO NOT WANT TO PAUSE THE WHOLE APP ON THE JS SIDE.
|
// No seriously WE DO NOT WANT TO PAUSE THE WHOLE APP ON THE JS SIDE.
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
{
|
{
|
||||||
let duration = instant::Duration::from_millis(1);
|
let duration = web_time::Duration::from_millis(1);
|
||||||
wasm_timer::Delay::new(duration).await.map_err(|err| {
|
wasm_timer::Delay::new(duration).await.map_err(|err| {
|
||||||
KclError::new_internal(KclErrorDetails::new(
|
KclError::new_internal(KclErrorDetails::new(
|
||||||
format!("Failed to sleep: {:?}", err),
|
format!("Failed to sleep: {:?}", err),
|
||||||
|
@ -668,9 +668,8 @@ impl From<GeometryWithImportedGeometry> for KclValue {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::exec::UnitType;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::exec::UnitType;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_human_friendly_type() {
|
fn test_human_friendly_type() {
|
||||||
|
@ -3005,6 +3005,8 @@ impl BinaryOperator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The operator associativity of the operator (as in the parsing sense, not the mathematical sense of associativity).
|
||||||
|
///
|
||||||
/// Follow JS definitions of each operator.
|
/// Follow JS definitions of each operator.
|
||||||
/// Taken from <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence#table>
|
/// Taken from <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence#table>
|
||||||
pub fn associativity(&self) -> Associativity {
|
pub fn associativity(&self) -> Associativity {
|
||||||
@ -3015,6 +3017,12 @@ impl BinaryOperator {
|
|||||||
Self::And | Self::Or => Associativity::Left,
|
Self::And | Self::Or => Associativity::Left,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether an operator is mathematically associative. If it is, then the operator associativity (given by the
|
||||||
|
/// `associativity` method) is mostly irrelevant.
|
||||||
|
pub fn associative(&self) -> bool {
|
||||||
|
matches!(self, Self::Add | Self::Mul | Self::And | Self::Or)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
|
@ -3334,7 +3334,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
parsing::ast::types::{BodyItem, Expr, VariableKind},
|
parsing::ast::types::{BodyItem, Expr, VariableKind},
|
||||||
KclError, ModuleId,
|
ModuleId,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn assert_reserved(word: &str) {
|
fn assert_reserved(word: &str) {
|
||||||
@ -4398,14 +4398,10 @@ secondExtrude = startSketchOn(XY)
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_parse_parens_unicode() {
|
fn test_parse_parens_unicode() {
|
||||||
let result = crate::parsing::top_level_parse("(ޜ");
|
let result = crate::parsing::top_level_parse("(ޜ");
|
||||||
let KclError::Lexical { details } = result.0.unwrap_err() else {
|
let details = result.0.unwrap().1.pop().unwrap();
|
||||||
panic!();
|
// TODO: Highlight where the unmatched open parenthesis is.
|
||||||
};
|
|
||||||
// TODO: Better errors when program cannot tokenize.
|
|
||||||
// https://github.com/KittyCAD/modeling-app/issues/696
|
// https://github.com/KittyCAD/modeling-app/issues/696
|
||||||
assert_eq!(details.message, "found unknown token 'ޜ'");
|
assert_eq!(details.message, "Unexpected end of file. The compiler expected )");
|
||||||
assert_eq!(details.source_ranges[0].start(), 1);
|
|
||||||
assert_eq!(details.source_ranges[0].end(), 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -6,7 +6,7 @@ use winnow::{
|
|||||||
error::{ContextError, ParseError},
|
error::{ContextError, ParseError},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
stream::{Location, Stream},
|
stream::{Location, Stream},
|
||||||
token::{any, none_of, one_of, take_till, take_until},
|
token::{any, none_of, take_till, take_until, take_while},
|
||||||
LocatingSlice, Stateful,
|
LocatingSlice, Stateful,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -163,8 +163,8 @@ fn whitespace(i: &mut Input<'_>) -> ModalResult<Token> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn inner_word(i: &mut Input<'_>) -> ModalResult<()> {
|
fn inner_word(i: &mut Input<'_>) -> ModalResult<()> {
|
||||||
one_of(('a'..='z', 'A'..='Z', '_')).parse_next(i)?;
|
take_while(1.., |c: char| c.is_alphabetic() || c == '_').parse_next(i)?;
|
||||||
repeat::<_, _, (), _, _>(0.., one_of(('a'..='z', 'A'..='Z', '0'..='9', '_'))).parse_next(i)?;
|
take_while(0.., |c: char| c.is_alphabetic() || c.is_ascii_digit() || c == '_').parse_next(i)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -786,6 +786,7 @@ const things = "things"
|
|||||||
};
|
};
|
||||||
assert_eq!(actual.tokens[0], expected);
|
assert_eq!(actual.tokens[0], expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_word_starting_with_keyword() {
|
fn test_word_starting_with_keyword() {
|
||||||
let module_id = ModuleId::default();
|
let module_id = ModuleId::default();
|
||||||
@ -799,4 +800,18 @@ const things = "things"
|
|||||||
};
|
};
|
||||||
assert_eq!(actual.tokens[0], expected);
|
assert_eq!(actual.tokens[0], expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn non_english_identifiers() {
|
||||||
|
let module_id = ModuleId::default();
|
||||||
|
let actual = lex("亞當", module_id).unwrap();
|
||||||
|
let expected = Token {
|
||||||
|
token_type: TokenType::Word,
|
||||||
|
value: "亞當".to_owned(),
|
||||||
|
start: 0,
|
||||||
|
end: 6,
|
||||||
|
module_id,
|
||||||
|
};
|
||||||
|
assert_eq!(actual.tokens[0], expected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use insta::rounded_redaction;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::KclError,
|
errors::KclError,
|
||||||
@ -262,17 +261,6 @@ async fn execute_test(test: &Test, render_to_png: bool, export_step: bool) {
|
|||||||
let mem_result = catch_unwind(AssertUnwindSafe(|| {
|
let mem_result = catch_unwind(AssertUnwindSafe(|| {
|
||||||
assert_snapshot(test, "Variables in memory after executing", || {
|
assert_snapshot(test, "Variables in memory after executing", || {
|
||||||
insta::assert_json_snapshot!("program_memory", outcome.variables, {
|
insta::assert_json_snapshot!("program_memory", outcome.variables, {
|
||||||
".**.value" => rounded_redaction(3),
|
|
||||||
".**[].value" => rounded_redaction(3),
|
|
||||||
".**.from[]" => rounded_redaction(3),
|
|
||||||
".**.to[]" => rounded_redaction(3),
|
|
||||||
".**.center[]" => rounded_redaction(3),
|
|
||||||
".**[].x[]" => rounded_redaction(3),
|
|
||||||
".**[].y[]" => rounded_redaction(3),
|
|
||||||
".**[].z[]" => rounded_redaction(3),
|
|
||||||
".**.x" => rounded_redaction(3),
|
|
||||||
".**.y" => rounded_redaction(3),
|
|
||||||
".**.z" => rounded_redaction(3),
|
|
||||||
".**.sourceRange" => Vec::new(),
|
".**.sourceRange" => Vec::new(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -346,11 +334,6 @@ fn assert_artifact_snapshots(
|
|||||||
let result1 = catch_unwind(AssertUnwindSafe(|| {
|
let result1 = catch_unwind(AssertUnwindSafe(|| {
|
||||||
assert_snapshot(test, "Operations executed", || {
|
assert_snapshot(test, "Operations executed", || {
|
||||||
insta::assert_json_snapshot!("ops", module_operations, {
|
insta::assert_json_snapshot!("ops", module_operations, {
|
||||||
".*[].*.unlabeledArg.*.value.**[].from[]" => rounded_redaction(3),
|
|
||||||
".*[].*.unlabeledArg.*.value.**[].to[]" => rounded_redaction(3),
|
|
||||||
".*[].**.value.value" => rounded_redaction(3),
|
|
||||||
".*[].*.labeledArgs.*.value.**[].from[]" => rounded_redaction(3),
|
|
||||||
".*[].*.labeledArgs.*.value.**[].to[]" => rounded_redaction(3),
|
|
||||||
".**.sourceRange" => Vec::new(),
|
".**.sourceRange" => Vec::new(),
|
||||||
".**.functionSourceRange" => Vec::new(),
|
".**.functionSourceRange" => Vec::new(),
|
||||||
".**.moduleId" => 0,
|
".**.moduleId" => 0,
|
||||||
@ -364,10 +347,6 @@ fn assert_artifact_snapshots(
|
|||||||
let result2 = catch_unwind(AssertUnwindSafe(|| {
|
let result2 = catch_unwind(AssertUnwindSafe(|| {
|
||||||
assert_snapshot(test, "Artifact commands", || {
|
assert_snapshot(test, "Artifact commands", || {
|
||||||
insta::assert_json_snapshot!("artifact_commands", module_commands, {
|
insta::assert_json_snapshot!("artifact_commands", module_commands, {
|
||||||
".*[].command.**.value" => rounded_redaction(3),
|
|
||||||
".*[].command.**.x" => rounded_redaction(3),
|
|
||||||
".*[].command.**.y" => rounded_redaction(3),
|
|
||||||
".*[].command.**.z" => rounded_redaction(3),
|
|
||||||
".**.range" => Vec::new(),
|
".**.range" => Vec::new(),
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
@ -3626,3 +3605,24 @@ mod user_reported_union_2_bug {
|
|||||||
super::execute(TEST_NAME, false).await
|
super::execute(TEST_NAME, false).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mod non_english_identifiers {
|
||||||
|
const TEST_NAME: &str = "non_english_identifiers";
|
||||||
|
|
||||||
|
/// Test parsing KCL.
|
||||||
|
#[test]
|
||||||
|
fn parse() {
|
||||||
|
super::parse(TEST_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn unparse() {
|
||||||
|
super::unparse(TEST_NAME).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test that KCL is executed correctly.
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn kcl_test_execute() {
|
||||||
|
super::execute(TEST_NAME, true).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,7 +9,7 @@ use kittycad_modeling_cmds::{
|
|||||||
websocket::OkWebSocketResponseData,
|
websocket::OkWebSocketResponseData,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{args::TyF64, DEFAULT_TOLERANCE};
|
use super::{args::TyF64, DEFAULT_TOLERANCE_MM};
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{types::RuntimeType, ExecState, KclValue, ModelingCmdMeta, Solid},
|
execution::{types::RuntimeType, ExecState, KclValue, ModelingCmdMeta, Solid},
|
||||||
@ -57,7 +57,7 @@ pub(crate) async fn inner_union(
|
|||||||
ModelingCmdMeta::from_args_id(&args, solid_out_id),
|
ModelingCmdMeta::from_args_id(&args, solid_out_id),
|
||||||
ModelingCmd::from(mcmd::BooleanUnion {
|
ModelingCmd::from(mcmd::BooleanUnion {
|
||||||
solid_ids: solids.iter().map(|s| s.id).collect(),
|
solid_ids: solids.iter().map(|s| s.id).collect(),
|
||||||
tolerance: LengthUnit(tolerance.map(|t| t.n).unwrap_or(DEFAULT_TOLERANCE)),
|
tolerance: LengthUnit(tolerance.map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE_MM)),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@ -122,7 +122,7 @@ pub(crate) async fn inner_intersect(
|
|||||||
ModelingCmdMeta::from_args_id(&args, solid_out_id),
|
ModelingCmdMeta::from_args_id(&args, solid_out_id),
|
||||||
ModelingCmd::from(mcmd::BooleanIntersection {
|
ModelingCmd::from(mcmd::BooleanIntersection {
|
||||||
solid_ids: solids.iter().map(|s| s.id).collect(),
|
solid_ids: solids.iter().map(|s| s.id).collect(),
|
||||||
tolerance: LengthUnit(tolerance.map(|t| t.n).unwrap_or(DEFAULT_TOLERANCE)),
|
tolerance: LengthUnit(tolerance.map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE_MM)),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@ -186,7 +186,7 @@ pub(crate) async fn inner_subtract(
|
|||||||
ModelingCmd::from(mcmd::BooleanSubtract {
|
ModelingCmd::from(mcmd::BooleanSubtract {
|
||||||
target_ids: solids.iter().map(|s| s.id).collect(),
|
target_ids: solids.iter().map(|s| s.id).collect(),
|
||||||
tool_ids: tools.iter().map(|s| s.id).collect(),
|
tool_ids: tools.iter().map(|s| s.id).collect(),
|
||||||
tolerance: LengthUnit(tolerance.map(|t| t.n).unwrap_or(DEFAULT_TOLERANCE)),
|
tolerance: LengthUnit(tolerance.map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE_MM)),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -18,7 +18,7 @@ use kittycad_modeling_cmds::{
|
|||||||
};
|
};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use super::{args::TyF64, utils::point_to_mm, DEFAULT_TOLERANCE};
|
use super::{args::TyF64, utils::point_to_mm, DEFAULT_TOLERANCE_MM};
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{
|
execution::{
|
||||||
@ -79,7 +79,7 @@ async fn inner_extrude(
|
|||||||
) -> Result<Vec<Solid>, KclError> {
|
) -> Result<Vec<Solid>, KclError> {
|
||||||
// Extrude the element(s).
|
// Extrude the element(s).
|
||||||
let mut solids = Vec::new();
|
let mut solids = Vec::new();
|
||||||
let tolerance = LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE));
|
let tolerance = LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE_MM));
|
||||||
|
|
||||||
if symmetric.unwrap_or(false) && bidirectional_length.is_some() {
|
if symmetric.unwrap_or(false) && bidirectional_length.is_some() {
|
||||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||||
|
@ -6,7 +6,7 @@ use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, shared::CutType, ModelingC
|
|||||||
use kittycad_modeling_cmds as kcmc;
|
use kittycad_modeling_cmds as kcmc;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::{args::TyF64, DEFAULT_TOLERANCE};
|
use super::{args::TyF64, DEFAULT_TOLERANCE_MM};
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{
|
execution::{
|
||||||
@ -122,7 +122,7 @@ async fn inner_fillet(
|
|||||||
strategy: Default::default(),
|
strategy: Default::default(),
|
||||||
object_id: solid.id,
|
object_id: solid.id,
|
||||||
radius: LengthUnit(radius.to_mm()),
|
radius: LengthUnit(radius.to_mm()),
|
||||||
tolerance: LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE)),
|
tolerance: LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE_MM)),
|
||||||
cut_type: CutType::Fillet,
|
cut_type: CutType::Fillet,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
@ -6,7 +6,7 @@ use anyhow::Result;
|
|||||||
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, ModelingCmd};
|
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, ModelingCmd};
|
||||||
use kittycad_modeling_cmds as kcmc;
|
use kittycad_modeling_cmds as kcmc;
|
||||||
|
|
||||||
use super::{args::TyF64, DEFAULT_TOLERANCE};
|
use super::{args::TyF64, DEFAULT_TOLERANCE_MM};
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{
|
execution::{
|
||||||
@ -84,7 +84,7 @@ async fn inner_loft(
|
|||||||
section_ids: sketches.iter().map(|group| group.id).collect(),
|
section_ids: sketches.iter().map(|group| group.id).collect(),
|
||||||
base_curve_index,
|
base_curve_index,
|
||||||
bez_approximate_rational,
|
bez_approximate_rational,
|
||||||
tolerance: LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE)),
|
tolerance: LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE_MM)),
|
||||||
v_degree,
|
v_degree,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
@ -34,21 +34,21 @@ pub async fn rem(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kcl
|
|||||||
pub async fn cos(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn cos(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let num: TyF64 = args.get_unlabeled_kw_arg("input", &RuntimeType::angle(), exec_state)?;
|
let num: TyF64 = args.get_unlabeled_kw_arg("input", &RuntimeType::angle(), exec_state)?;
|
||||||
let num = num.to_radians();
|
let num = num.to_radians();
|
||||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(num.cos(), exec_state.current_default_units())))
|
Ok(args.make_user_val_from_f64_with_type(TyF64::new(libm::cos(num), exec_state.current_default_units())))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the sine of a number (in radians).
|
/// Compute the sine of a number (in radians).
|
||||||
pub async fn sin(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn sin(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let num: TyF64 = args.get_unlabeled_kw_arg("input", &RuntimeType::angle(), exec_state)?;
|
let num: TyF64 = args.get_unlabeled_kw_arg("input", &RuntimeType::angle(), exec_state)?;
|
||||||
let num = num.to_radians();
|
let num = num.to_radians();
|
||||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(num.sin(), exec_state.current_default_units())))
|
Ok(args.make_user_val_from_f64_with_type(TyF64::new(libm::sin(num), exec_state.current_default_units())))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the tangent of a number (in radians).
|
/// Compute the tangent of a number (in radians).
|
||||||
pub async fn tan(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn tan(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let num: TyF64 = args.get_unlabeled_kw_arg("input", &RuntimeType::angle(), exec_state)?;
|
let num: TyF64 = args.get_unlabeled_kw_arg("input", &RuntimeType::angle(), exec_state)?;
|
||||||
let num = num.to_radians();
|
let num = num.to_radians();
|
||||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(num.tan(), exec_state.current_default_units())))
|
Ok(args.make_user_val_from_f64_with_type(TyF64::new(libm::tan(num), exec_state.current_default_units())))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compute the square root of a number.
|
/// Compute the square root of a number.
|
||||||
@ -164,7 +164,7 @@ pub async fn pow(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kcl
|
|||||||
/// Compute the arccosine of a number (in radians).
|
/// Compute the arccosine of a number (in radians).
|
||||||
pub async fn acos(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn acos(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let input: TyF64 = args.get_unlabeled_kw_arg("input", &RuntimeType::count(), exec_state)?;
|
let input: TyF64 = args.get_unlabeled_kw_arg("input", &RuntimeType::count(), exec_state)?;
|
||||||
let result = input.n.acos();
|
let result = libm::acos(input.n);
|
||||||
|
|
||||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
|
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
|
||||||
}
|
}
|
||||||
@ -172,7 +172,7 @@ pub async fn acos(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
|||||||
/// Compute the arcsine of a number (in radians).
|
/// Compute the arcsine of a number (in radians).
|
||||||
pub async fn asin(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn asin(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let input: TyF64 = args.get_unlabeled_kw_arg("input", &RuntimeType::count(), exec_state)?;
|
let input: TyF64 = args.get_unlabeled_kw_arg("input", &RuntimeType::count(), exec_state)?;
|
||||||
let result = input.n.asin();
|
let result = libm::asin(input.n);
|
||||||
|
|
||||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
|
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
|
||||||
}
|
}
|
||||||
@ -180,7 +180,7 @@ pub async fn asin(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
|||||||
/// Compute the arctangent of a number (in radians).
|
/// Compute the arctangent of a number (in radians).
|
||||||
pub async fn atan(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn atan(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let input: TyF64 = args.get_unlabeled_kw_arg("input", &RuntimeType::count(), exec_state)?;
|
let input: TyF64 = args.get_unlabeled_kw_arg("input", &RuntimeType::count(), exec_state)?;
|
||||||
let result = input.n.atan();
|
let result = libm::atan(input.n);
|
||||||
|
|
||||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
|
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
|
||||||
}
|
}
|
||||||
@ -190,7 +190,7 @@ pub async fn atan2(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
|||||||
let y = args.get_kw_arg("y", &RuntimeType::length(), exec_state)?;
|
let y = args.get_kw_arg("y", &RuntimeType::length(), exec_state)?;
|
||||||
let x = args.get_kw_arg("x", &RuntimeType::length(), exec_state)?;
|
let x = args.get_kw_arg("x", &RuntimeType::length(), exec_state)?;
|
||||||
let (y, x, _) = NumericType::combine_eq_coerce(y, x);
|
let (y, x, _) = NumericType::combine_eq_coerce(y, x);
|
||||||
let result = y.atan2(x);
|
let result = libm::atan2(y, x);
|
||||||
|
|
||||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
|
Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
|
||||||
}
|
}
|
||||||
@ -246,7 +246,7 @@ pub async fn leg_angle_x(exec_state: &mut ExecState, args: Args) -> Result<KclVa
|
|||||||
let hypotenuse: TyF64 = args.get_kw_arg("hypotenuse", &RuntimeType::length(), exec_state)?;
|
let hypotenuse: TyF64 = args.get_kw_arg("hypotenuse", &RuntimeType::length(), exec_state)?;
|
||||||
let leg: TyF64 = args.get_kw_arg("leg", &RuntimeType::length(), exec_state)?;
|
let leg: TyF64 = args.get_kw_arg("leg", &RuntimeType::length(), exec_state)?;
|
||||||
let (hypotenuse, leg, _ty) = NumericType::combine_eq_coerce(hypotenuse, leg);
|
let (hypotenuse, leg, _ty) = NumericType::combine_eq_coerce(hypotenuse, leg);
|
||||||
let result = (leg.min(hypotenuse) / hypotenuse).acos().to_degrees();
|
let result = libm::acos(leg.min(hypotenuse) / hypotenuse).to_degrees();
|
||||||
Ok(KclValue::from_number_with_type(
|
Ok(KclValue::from_number_with_type(
|
||||||
result,
|
result,
|
||||||
NumericType::degrees(),
|
NumericType::degrees(),
|
||||||
@ -259,7 +259,7 @@ pub async fn leg_angle_y(exec_state: &mut ExecState, args: Args) -> Result<KclVa
|
|||||||
let hypotenuse: TyF64 = args.get_kw_arg("hypotenuse", &RuntimeType::length(), exec_state)?;
|
let hypotenuse: TyF64 = args.get_kw_arg("hypotenuse", &RuntimeType::length(), exec_state)?;
|
||||||
let leg: TyF64 = args.get_kw_arg("leg", &RuntimeType::length(), exec_state)?;
|
let leg: TyF64 = args.get_kw_arg("leg", &RuntimeType::length(), exec_state)?;
|
||||||
let (hypotenuse, leg, _ty) = NumericType::combine_eq_coerce(hypotenuse, leg);
|
let (hypotenuse, leg, _ty) = NumericType::combine_eq_coerce(hypotenuse, leg);
|
||||||
let result = (leg.min(hypotenuse) / hypotenuse).asin().to_degrees();
|
let result = libm::asin(leg.min(hypotenuse) / hypotenuse).to_degrees();
|
||||||
Ok(KclValue::from_number_with_type(
|
Ok(KclValue::from_number_with_type(
|
||||||
result,
|
result,
|
||||||
NumericType::degrees(),
|
NumericType::degrees(),
|
||||||
|
@ -442,5 +442,5 @@ pub(crate) fn std_ty(path: &str, fn_name: &str) -> (PrimitiveType, StdFnProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The default tolerance for modeling commands in [`kittycad_modeling_cmds::length_unit::LengthUnit`].
|
/// The default tolerance for modeling commands in millimeters.
|
||||||
const DEFAULT_TOLERANCE: f64 = 0.0000001;
|
const DEFAULT_TOLERANCE_MM: f64 = 0.0000001;
|
||||||
|
@ -9,7 +9,7 @@ use kcmc::{
|
|||||||
};
|
};
|
||||||
use kittycad_modeling_cmds::{self as kcmc, shared::Point3d};
|
use kittycad_modeling_cmds::{self as kcmc, shared::Point3d};
|
||||||
|
|
||||||
use super::{args::TyF64, DEFAULT_TOLERANCE};
|
use super::{args::TyF64, DEFAULT_TOLERANCE_MM};
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{
|
execution::{
|
||||||
@ -133,7 +133,7 @@ async fn inner_revolve(
|
|||||||
let mut solids = Vec::new();
|
let mut solids = Vec::new();
|
||||||
for sketch in &sketches {
|
for sketch in &sketches {
|
||||||
let id = exec_state.next_uuid();
|
let id = exec_state.next_uuid();
|
||||||
let tolerance = tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE);
|
let tolerance = tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE_MM);
|
||||||
|
|
||||||
let direction = match &axis {
|
let direction = match &axis {
|
||||||
Axis2dOrEdgeReference::Axis { direction, origin } => {
|
Axis2dOrEdgeReference::Axis { direction, origin } => {
|
||||||
|
@ -250,7 +250,7 @@ async fn inner_tangent_to_end(tag: &TagIdentifier, exec_state: &mut ExecState, a
|
|||||||
|
|
||||||
// Calculate the end point from the angle and radius.
|
// Calculate the end point from the angle and radius.
|
||||||
// atan2 outputs radians.
|
// atan2 outputs radians.
|
||||||
let previous_end_tangent = Angle::from_radians(f64::atan2(
|
let previous_end_tangent = Angle::from_radians(libm::atan2(
|
||||||
from[1] - tan_previous_point[1],
|
from[1] - tan_previous_point[1],
|
||||||
from[0] - tan_previous_point[0],
|
from[0] - tan_previous_point[0],
|
||||||
));
|
));
|
||||||
|
@ -305,7 +305,7 @@ async fn inner_polygon(
|
|||||||
radius.n
|
radius.n
|
||||||
} else {
|
} else {
|
||||||
// circumscribed
|
// circumscribed
|
||||||
radius.n / half_angle.cos()
|
radius.n / libm::cos(half_angle)
|
||||||
};
|
};
|
||||||
|
|
||||||
let angle_step = std::f64::consts::TAU / num_sides as f64;
|
let angle_step = std::f64::consts::TAU / num_sides as f64;
|
||||||
@ -316,8 +316,8 @@ async fn inner_polygon(
|
|||||||
.map(|i| {
|
.map(|i| {
|
||||||
let angle = angle_step * i as f64;
|
let angle = angle_step * i as f64;
|
||||||
[
|
[
|
||||||
center_u[0] + radius_to_vertices * angle.cos(),
|
center_u[0] + radius_to_vertices * libm::cos(angle),
|
||||||
center_u[1] + radius_to_vertices * angle.sin(),
|
center_u[1] + radius_to_vertices * libm::sin(angle),
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@ -415,16 +415,26 @@ pub(crate) fn get_radius(
|
|||||||
radius: Option<TyF64>,
|
radius: Option<TyF64>,
|
||||||
diameter: Option<TyF64>,
|
diameter: Option<TyF64>,
|
||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
|
) -> Result<TyF64, KclError> {
|
||||||
|
get_radius_labelled(radius, diameter, source_range, "radius", "diameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_radius_labelled(
|
||||||
|
radius: Option<TyF64>,
|
||||||
|
diameter: Option<TyF64>,
|
||||||
|
source_range: SourceRange,
|
||||||
|
label_radius: &'static str,
|
||||||
|
label_diameter: &'static str,
|
||||||
) -> Result<TyF64, KclError> {
|
) -> Result<TyF64, KclError> {
|
||||||
match (radius, diameter) {
|
match (radius, diameter) {
|
||||||
(Some(radius), None) => Ok(radius),
|
(Some(radius), None) => Ok(radius),
|
||||||
(None, Some(diameter)) => Ok(TyF64::new(diameter.n / 2.0, diameter.ty)),
|
(None, Some(diameter)) => Ok(TyF64::new(diameter.n / 2.0, diameter.ty)),
|
||||||
(None, None) => Err(KclError::new_type(KclErrorDetails::new(
|
(None, None) => Err(KclError::new_type(KclErrorDetails::new(
|
||||||
"This function needs either `diameter` or `radius`".to_string(),
|
format!("This function needs either `{label_diameter}` or `{label_radius}`"),
|
||||||
vec![source_range],
|
vec![source_range],
|
||||||
))),
|
))),
|
||||||
(Some(_), Some(_)) => Err(KclError::new_type(KclErrorDetails::new(
|
(Some(_), Some(_)) => Err(KclError::new_type(KclErrorDetails::new(
|
||||||
"You cannot specify both `diameter` and `radius`, please remove one".to_string(),
|
format!("You cannot specify both `{label_diameter}` and `{label_radius}`, please remove one"),
|
||||||
vec![source_range],
|
vec![source_range],
|
||||||
))),
|
))),
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ use parse_display::{Display, FromStr};
|
|||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::shapes::get_radius;
|
use super::shapes::{get_radius, get_radius_labelled};
|
||||||
#[cfg(feature = "artifact-graph")]
|
#[cfg(feature = "artifact-graph")]
|
||||||
use crate::execution::{Artifact, ArtifactId, CodeRef, StartSketchOnFace, StartSketchOnPlane};
|
use crate::execution::{Artifact, ArtifactId, CodeRef, StartSketchOnFace, StartSketchOnPlane};
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -101,13 +101,26 @@ pub const NEW_TAG_KW: &str = "tag";
|
|||||||
pub async fn involute_circular(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn involute_circular(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let sketch = args.get_unlabeled_kw_arg("sketch", &RuntimeType::sketch(), exec_state)?;
|
let sketch = args.get_unlabeled_kw_arg("sketch", &RuntimeType::sketch(), exec_state)?;
|
||||||
|
|
||||||
let start_radius: TyF64 = args.get_kw_arg("startRadius", &RuntimeType::length(), exec_state)?;
|
let start_radius: Option<TyF64> = args.get_kw_arg_opt("startRadius", &RuntimeType::length(), exec_state)?;
|
||||||
let end_radius: TyF64 = args.get_kw_arg("endRadius", &RuntimeType::length(), exec_state)?;
|
let end_radius: Option<TyF64> = args.get_kw_arg_opt("endRadius", &RuntimeType::length(), exec_state)?;
|
||||||
|
let start_diameter: Option<TyF64> = args.get_kw_arg_opt("startDiameter", &RuntimeType::length(), exec_state)?;
|
||||||
|
let end_diameter: Option<TyF64> = args.get_kw_arg_opt("endDiameter", &RuntimeType::length(), exec_state)?;
|
||||||
let angle: TyF64 = args.get_kw_arg("angle", &RuntimeType::angle(), exec_state)?;
|
let angle: TyF64 = args.get_kw_arg("angle", &RuntimeType::angle(), exec_state)?;
|
||||||
let reverse = args.get_kw_arg_opt("reverse", &RuntimeType::bool(), exec_state)?;
|
let reverse = args.get_kw_arg_opt("reverse", &RuntimeType::bool(), exec_state)?;
|
||||||
let tag = args.get_kw_arg_opt("tag", &RuntimeType::tag_decl(), exec_state)?;
|
let tag = args.get_kw_arg_opt("tag", &RuntimeType::tag_decl(), exec_state)?;
|
||||||
let new_sketch =
|
let new_sketch = inner_involute_circular(
|
||||||
inner_involute_circular(sketch, start_radius, end_radius, angle, reverse, tag, exec_state, args).await?;
|
sketch,
|
||||||
|
start_radius,
|
||||||
|
end_radius,
|
||||||
|
start_diameter,
|
||||||
|
end_diameter,
|
||||||
|
angle,
|
||||||
|
reverse,
|
||||||
|
tag,
|
||||||
|
exec_state,
|
||||||
|
args,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
Ok(KclValue::Sketch {
|
Ok(KclValue::Sketch {
|
||||||
value: Box::new(new_sketch),
|
value: Box::new(new_sketch),
|
||||||
})
|
})
|
||||||
@ -115,16 +128,18 @@ pub async fn involute_circular(exec_state: &mut ExecState, args: Args) -> Result
|
|||||||
|
|
||||||
fn involute_curve(radius: f64, angle: f64) -> (f64, f64) {
|
fn involute_curve(radius: f64, angle: f64) -> (f64, f64) {
|
||||||
(
|
(
|
||||||
radius * (angle.cos() + angle * angle.sin()),
|
radius * (libm::cos(angle) + angle * libm::sin(angle)),
|
||||||
radius * (angle.sin() - angle * angle.cos()),
|
radius * (libm::sin(angle) - angle * libm::cos(angle)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn inner_involute_circular(
|
async fn inner_involute_circular(
|
||||||
sketch: Sketch,
|
sketch: Sketch,
|
||||||
start_radius: TyF64,
|
start_radius: Option<TyF64>,
|
||||||
end_radius: TyF64,
|
end_radius: Option<TyF64>,
|
||||||
|
start_diameter: Option<TyF64>,
|
||||||
|
end_diameter: Option<TyF64>,
|
||||||
angle: TyF64,
|
angle: TyF64,
|
||||||
reverse: Option<bool>,
|
reverse: Option<bool>,
|
||||||
tag: Option<TagNode>,
|
tag: Option<TagNode>,
|
||||||
@ -133,6 +148,22 @@ async fn inner_involute_circular(
|
|||||||
) -> Result<Sketch, KclError> {
|
) -> Result<Sketch, KclError> {
|
||||||
let id = exec_state.next_uuid();
|
let id = exec_state.next_uuid();
|
||||||
|
|
||||||
|
let longer_args_dot_source_range = args.source_range;
|
||||||
|
let start_radius = get_radius_labelled(
|
||||||
|
start_radius,
|
||||||
|
start_diameter,
|
||||||
|
args.source_range,
|
||||||
|
"startRadius",
|
||||||
|
"startDiameter",
|
||||||
|
)?;
|
||||||
|
let end_radius = get_radius_labelled(
|
||||||
|
end_radius,
|
||||||
|
end_diameter,
|
||||||
|
longer_args_dot_source_range,
|
||||||
|
"endRadius",
|
||||||
|
"endDiameter",
|
||||||
|
)?;
|
||||||
|
|
||||||
exec_state
|
exec_state
|
||||||
.batch_modeling_cmd(
|
.batch_modeling_cmd(
|
||||||
ModelingCmdMeta::from_args_id(&args, id),
|
ModelingCmdMeta::from_args_id(&args, id),
|
||||||
@ -157,11 +188,11 @@ async fn inner_involute_circular(
|
|||||||
let theta = f64::sqrt(end_radius * end_radius - start_radius * start_radius) / start_radius;
|
let theta = f64::sqrt(end_radius * end_radius - start_radius * start_radius) / start_radius;
|
||||||
let (x, y) = involute_curve(start_radius, theta);
|
let (x, y) = involute_curve(start_radius, theta);
|
||||||
|
|
||||||
end.x = x * angle.to_radians().cos() - y * angle.to_radians().sin();
|
end.x = x * libm::cos(angle.to_radians()) - y * libm::sin(angle.to_radians());
|
||||||
end.y = x * angle.to_radians().sin() + y * angle.to_radians().cos();
|
end.y = x * libm::sin(angle.to_radians()) + y * libm::cos(angle.to_radians());
|
||||||
|
|
||||||
end.x -= start_radius * angle.to_radians().cos();
|
end.x -= start_radius * libm::cos(angle.to_radians());
|
||||||
end.y -= start_radius * angle.to_radians().sin();
|
end.y -= start_radius * libm::sin(angle.to_radians());
|
||||||
|
|
||||||
if reverse.unwrap_or_default() {
|
if reverse.unwrap_or_default() {
|
||||||
end.x = -end.x;
|
end.x = -end.x;
|
||||||
@ -500,8 +531,8 @@ async fn inner_angled_line_length(
|
|||||||
|
|
||||||
//double check me on this one - mike
|
//double check me on this one - mike
|
||||||
let delta: [f64; 2] = [
|
let delta: [f64; 2] = [
|
||||||
length * f64::cos(angle_degrees.to_radians()),
|
length * libm::cos(angle_degrees.to_radians()),
|
||||||
length * f64::sin(angle_degrees.to_radians()),
|
length * libm::sin(angle_degrees.to_radians()),
|
||||||
];
|
];
|
||||||
let relative = true;
|
let relative = true;
|
||||||
|
|
||||||
@ -601,7 +632,7 @@ async fn inner_angled_line_to_x(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let x_component = x_to.to_length_units(from.units) - from.x;
|
let x_component = x_to.to_length_units(from.units) - from.x;
|
||||||
let y_component = x_component * f64::tan(angle_degrees.to_radians());
|
let y_component = x_component * libm::tan(angle_degrees.to_radians());
|
||||||
let y_to = from.y + y_component;
|
let y_to = from.y + y_component;
|
||||||
|
|
||||||
let new_sketch = straight_line(
|
let new_sketch = straight_line(
|
||||||
@ -668,7 +699,7 @@ async fn inner_angled_line_to_y(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let y_component = y_to.to_length_units(from.units) - from.y;
|
let y_component = y_to.to_length_units(from.units) - from.y;
|
||||||
let x_component = y_component / f64::tan(angle_degrees.to_radians());
|
let x_component = y_component / libm::tan(angle_degrees.to_radians());
|
||||||
let x_to = from.x + x_component;
|
let x_to = from.x + x_component;
|
||||||
|
|
||||||
let new_sketch = straight_line(
|
let new_sketch = straight_line(
|
||||||
@ -1413,7 +1444,7 @@ async fn inner_tangential_arc_radius_angle(
|
|||||||
|
|
||||||
// Calculate the end point from the angle and radius.
|
// Calculate the end point from the angle and radius.
|
||||||
// atan2 outputs radians.
|
// atan2 outputs radians.
|
||||||
let previous_end_tangent = Angle::from_radians(f64::atan2(
|
let previous_end_tangent = Angle::from_radians(libm::atan2(
|
||||||
from.y - tan_previous_point[1],
|
from.y - tan_previous_point[1],
|
||||||
from.x - tan_previous_point[0],
|
from.x - tan_previous_point[0],
|
||||||
));
|
));
|
||||||
|
@ -6,7 +6,7 @@ use kittycad_modeling_cmds::{self as kcmc, shared::RelativeTo};
|
|||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use super::{args::TyF64, DEFAULT_TOLERANCE};
|
use super::{args::TyF64, DEFAULT_TOLERANCE_MM};
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::KclError,
|
errors::KclError,
|
||||||
execution::{
|
execution::{
|
||||||
@ -93,7 +93,7 @@ async fn inner_sweep(
|
|||||||
target: sketch.id.into(),
|
target: sketch.id.into(),
|
||||||
trajectory,
|
trajectory,
|
||||||
sectional: sectional.unwrap_or(false),
|
sectional: sectional.unwrap_or(false),
|
||||||
tolerance: LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE)),
|
tolerance: LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE_MM)),
|
||||||
relative_to,
|
relative_to,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
@ -44,7 +44,7 @@ pub(crate) fn distance(a: Coords2d, b: Coords2d) -> f64 {
|
|||||||
pub(crate) fn between(a: Coords2d, b: Coords2d) -> Angle {
|
pub(crate) fn between(a: Coords2d, b: Coords2d) -> Angle {
|
||||||
let x = b[0] - a[0];
|
let x = b[0] - a[0];
|
||||||
let y = b[1] - a[1];
|
let y = b[1] - a[1];
|
||||||
normalize(Angle::from_radians(y.atan2(x)))
|
normalize(Angle::from_radians(libm::atan2(y, x)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Normalize the angle
|
/// Normalize the angle
|
||||||
@ -97,8 +97,8 @@ pub(crate) fn normalize_rad(angle: f64) -> f64 {
|
|||||||
|
|
||||||
fn calculate_intersection_of_two_lines(line1: &[Coords2d; 2], line2_angle: f64, line2_point: Coords2d) -> Coords2d {
|
fn calculate_intersection_of_two_lines(line1: &[Coords2d; 2], line2_angle: f64, line2_point: Coords2d) -> Coords2d {
|
||||||
let line2_point_b = [
|
let line2_point_b = [
|
||||||
line2_point[0] + f64::cos(line2_angle.to_radians()) * 10.0,
|
line2_point[0] + libm::cos(line2_angle.to_radians()) * 10.0,
|
||||||
line2_point[1] + f64::sin(line2_angle.to_radians()) * 10.0,
|
line2_point[1] + libm::sin(line2_angle.to_radians()) * 10.0,
|
||||||
];
|
];
|
||||||
intersect(line1[0], line1[1], line2_point, line2_point_b)
|
intersect(line1[0], line1[1], line2_point, line2_point_b)
|
||||||
}
|
}
|
||||||
@ -138,13 +138,13 @@ fn offset_line(offset: f64, p1: Coords2d, p2: Coords2d) -> [Coords2d; 2] {
|
|||||||
let direction = (p2[0] - p1[0]).signum();
|
let direction = (p2[0] - p1[0]).signum();
|
||||||
return [[p1[0], p1[1] + offset * direction], [p2[0], p2[1] + offset * direction]];
|
return [[p1[0], p1[1] + offset * direction], [p2[0], p2[1] + offset * direction]];
|
||||||
}
|
}
|
||||||
let x_offset = offset / f64::sin(f64::atan2(p1[1] - p2[1], p1[0] - p2[0]));
|
let x_offset = offset / libm::sin(libm::atan2(p1[1] - p2[1], p1[0] - p2[0]));
|
||||||
[[p1[0] + x_offset, p1[1]], [p2[0] + x_offset, p2[1]]]
|
[[p1[0] + x_offset, p1[1]], [p2[0] + x_offset, p2[1]]]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_y_component(angle: Angle, x: f64) -> Coords2d {
|
pub(crate) fn get_y_component(angle: Angle, x: f64) -> Coords2d {
|
||||||
let normalised_angle = ((angle.to_degrees() % 360.0) + 360.0) % 360.0; // between 0 and 360
|
let normalised_angle = ((angle.to_degrees() % 360.0) + 360.0) % 360.0; // between 0 and 360
|
||||||
let y = x * f64::tan(normalised_angle.to_radians());
|
let y = x * libm::tan(normalised_angle.to_radians());
|
||||||
let sign = if normalised_angle > 90.0 && normalised_angle <= 270.0 {
|
let sign = if normalised_angle > 90.0 && normalised_angle <= 270.0 {
|
||||||
-1.0
|
-1.0
|
||||||
} else {
|
} else {
|
||||||
@ -155,7 +155,7 @@ pub(crate) fn get_y_component(angle: Angle, x: f64) -> Coords2d {
|
|||||||
|
|
||||||
pub(crate) fn get_x_component(angle: Angle, y: f64) -> Coords2d {
|
pub(crate) fn get_x_component(angle: Angle, y: f64) -> Coords2d {
|
||||||
let normalised_angle = ((angle.to_degrees() % 360.0) + 360.0) % 360.0; // between 0 and 360
|
let normalised_angle = ((angle.to_degrees() % 360.0) + 360.0) % 360.0; // between 0 and 360
|
||||||
let x = y / f64::tan(normalised_angle.to_radians());
|
let x = y / libm::tan(normalised_angle.to_radians());
|
||||||
let sign = if normalised_angle > 180.0 && normalised_angle <= 360.0 {
|
let sign = if normalised_angle > 180.0 && normalised_angle <= 360.0 {
|
||||||
-1.0
|
-1.0
|
||||||
} else {
|
} else {
|
||||||
@ -174,13 +174,13 @@ pub(crate) fn arc_center_and_end(
|
|||||||
let end_angle = end_angle.to_radians();
|
let end_angle = end_angle.to_radians();
|
||||||
|
|
||||||
let center = [
|
let center = [
|
||||||
-1.0 * (radius * start_angle.cos() - from[0]),
|
-1.0 * (radius * libm::cos(start_angle) - from[0]),
|
||||||
-1.0 * (radius * start_angle.sin() - from[1]),
|
-1.0 * (radius * libm::sin(start_angle) - from[1]),
|
||||||
];
|
];
|
||||||
|
|
||||||
let end = [
|
let end = [
|
||||||
center[0] + radius * end_angle.cos(),
|
center[0] + radius * libm::cos(end_angle),
|
||||||
center[1] + radius * end_angle.sin(),
|
center[1] + radius * libm::sin(end_angle),
|
||||||
];
|
];
|
||||||
|
|
||||||
(center, end)
|
(center, end)
|
||||||
@ -357,7 +357,10 @@ mod tests {
|
|||||||
|
|
||||||
let get_point = |radius: f64, t: f64| {
|
let get_point = |radius: f64, t: f64| {
|
||||||
let angle = t * TAU;
|
let angle = t * TAU;
|
||||||
[center[0] + radius * angle.cos(), center[1] + radius * angle.sin()]
|
[
|
||||||
|
center[0] + radius * libm::cos(angle),
|
||||||
|
center[1] + radius * libm::sin(angle),
|
||||||
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
for radius in radius_array {
|
for radius in radius_array {
|
||||||
@ -442,7 +445,7 @@ fn get_slope(start: Coords2d, end: Coords2d) -> (f64, f64) {
|
|||||||
fn get_angle(point1: Coords2d, point2: Coords2d) -> f64 {
|
fn get_angle(point1: Coords2d, point2: Coords2d) -> f64 {
|
||||||
let delta_x = point2[0] - point1[0];
|
let delta_x = point2[0] - point1[0];
|
||||||
let delta_y = point2[1] - point1[1];
|
let delta_y = point2[1] - point1[1];
|
||||||
let angle = delta_y.atan2(delta_x);
|
let angle = libm::atan2(delta_y, delta_x);
|
||||||
|
|
||||||
let result = if angle < 0.0 { angle + 2.0 * PI } else { angle };
|
let result = if angle < 0.0 { angle + 2.0 * PI } else { angle };
|
||||||
result * (180.0 / PI)
|
result * (180.0 / PI)
|
||||||
@ -484,13 +487,13 @@ fn get_mid_point(
|
|||||||
);
|
);
|
||||||
let delta_ang = delta_ang / 2.0 + deg2rad(angle_from_center_to_arc_start);
|
let delta_ang = delta_ang / 2.0 + deg2rad(angle_from_center_to_arc_start);
|
||||||
let shortest_arc_mid_point: Coords2d = [
|
let shortest_arc_mid_point: Coords2d = [
|
||||||
delta_ang.cos() * radius + center[0],
|
libm::cos(delta_ang) * radius + center[0],
|
||||||
delta_ang.sin() * radius + center[1],
|
libm::sin(delta_ang) * radius + center[1],
|
||||||
];
|
];
|
||||||
let opposite_delta = delta_ang + PI;
|
let opposite_delta = delta_ang + PI;
|
||||||
let longest_arc_mid_point: Coords2d = [
|
let longest_arc_mid_point: Coords2d = [
|
||||||
opposite_delta.cos() * radius + center[0],
|
libm::cos(opposite_delta) * radius + center[0],
|
||||||
opposite_delta.sin() * radius + center[1],
|
libm::sin(opposite_delta) * radius + center[1],
|
||||||
];
|
];
|
||||||
|
|
||||||
let rotation_direction_original_points = is_points_ccw(&[tan_previous_point, arc_start_point, arc_end_point]);
|
let rotation_direction_original_points = is_points_ccw(&[tan_previous_point, arc_start_point, arc_end_point]);
|
||||||
@ -592,11 +595,14 @@ pub fn get_tangential_arc_to_info(input: TangentialArcInfoInput) -> TangentialAr
|
|||||||
input.obtuse,
|
input.obtuse,
|
||||||
);
|
);
|
||||||
|
|
||||||
let start_angle = (input.arc_start_point[1] - center[1]).atan2(input.arc_start_point[0] - center[0]);
|
let start_angle = libm::atan2(
|
||||||
let end_angle = (input.arc_end_point[1] - center[1]).atan2(input.arc_end_point[0] - center[0]);
|
input.arc_start_point[1] - center[1],
|
||||||
|
input.arc_start_point[0] - center[0],
|
||||||
|
);
|
||||||
|
let end_angle = libm::atan2(input.arc_end_point[1] - center[1], input.arc_end_point[0] - center[0]);
|
||||||
let ccw = is_points_ccw(&[input.arc_start_point, arc_mid_point, input.arc_end_point]);
|
let ccw = is_points_ccw(&[input.arc_start_point, arc_mid_point, input.arc_end_point]);
|
||||||
|
|
||||||
let arc_mid_angle = (arc_mid_point[1] - center[1]).atan2(arc_mid_point[0] - center[0]);
|
let arc_mid_angle = libm::atan2(arc_mid_point[1] - center[1], arc_mid_point[0] - center[0]);
|
||||||
let start_to_mid_arc_length = radius
|
let start_to_mid_arc_length = radius
|
||||||
* delta(Angle::from_radians(start_angle), Angle::from_radians(arc_mid_angle))
|
* delta(Angle::from_radians(start_angle), Angle::from_radians(arc_mid_angle))
|
||||||
.to_radians()
|
.to_radians()
|
||||||
@ -724,7 +730,7 @@ mod get_tangential_arc_to_info_tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_tangential_arc_to_info_obtuse_with_wrap_around() {
|
fn test_get_tangential_arc_to_info_obtuse_with_wrap_around() {
|
||||||
let arc_end = (std::f64::consts::PI / 4.0).cos() * 2.0;
|
let arc_end = libm::cos(std::f64::consts::PI / 4.0) * 2.0;
|
||||||
let result = get_tangential_arc_to_info(TangentialArcInfoInput {
|
let result = get_tangential_arc_to_info(TangentialArcInfoInput {
|
||||||
tan_previous_point: [2.0, -4.0],
|
tan_previous_point: [2.0, -4.0],
|
||||||
arc_start_point: [2.0, 0.0],
|
arc_start_point: [2.0, 0.0],
|
||||||
@ -803,7 +809,7 @@ pub(crate) fn get_tangent_point_from_previous_arc(
|
|||||||
let tangential_angle = angle_from_old_center_to_arc_start + if last_arc_ccw { -90.0 } else { 90.0 };
|
let tangential_angle = angle_from_old_center_to_arc_start + if last_arc_ccw { -90.0 } else { 90.0 };
|
||||||
// What is the 10.0 constant doing???
|
// What is the 10.0 constant doing???
|
||||||
[
|
[
|
||||||
tangential_angle.to_radians().cos() * 10.0 + last_arc_end[0],
|
libm::cos(tangential_angle.to_radians()) * 10.0 + last_arc_end[0],
|
||||||
tangential_angle.to_radians().sin() * 10.0 + last_arc_end[1],
|
libm::sin(tangential_angle.to_radians()) * 10.0 + last_arc_end[1],
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@ use std::fmt::Write;
|
|||||||
use crate::{
|
use crate::{
|
||||||
parsing::{
|
parsing::{
|
||||||
ast::types::{
|
ast::types::{
|
||||||
Annotation, ArrayExpression, ArrayRangeExpression, AscribedExpression, BinaryExpression, BinaryOperator,
|
Annotation, ArrayExpression, ArrayRangeExpression, AscribedExpression, Associativity, BinaryExpression,
|
||||||
BinaryPart, BodyItem, CallExpressionKw, CommentStyle, DefaultParamVal, Expr, FormatOptions,
|
BinaryOperator, BinaryPart, BodyItem, CallExpressionKw, CommentStyle, DefaultParamVal, Expr, FormatOptions,
|
||||||
FunctionExpression, IfExpression, ImportSelector, ImportStatement, ItemVisibility, LabeledArg, Literal,
|
FunctionExpression, IfExpression, ImportSelector, ImportStatement, ItemVisibility, LabeledArg, Literal,
|
||||||
LiteralIdentifier, LiteralValue, MemberExpression, Node, NonCodeNode, NonCodeValue, ObjectExpression,
|
LiteralIdentifier, LiteralValue, MemberExpression, Node, NonCodeNode, NonCodeValue, ObjectExpression,
|
||||||
Parameter, PipeExpression, Program, TagDeclarator, TypeDeclaration, UnaryExpression, VariableDeclaration,
|
Parameter, PipeExpression, Program, TagDeclarator, TypeDeclaration, UnaryExpression, VariableDeclaration,
|
||||||
@ -710,17 +710,28 @@ impl BinaryExpression {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let should_wrap_right = match &self.right {
|
// It would be better to always preserve the user's parentheses but since we've dropped that
|
||||||
|
// info from the AST, we bracket expressions as necessary.
|
||||||
|
let should_wrap_left = match &self.left {
|
||||||
BinaryPart::BinaryExpression(bin_exp) => {
|
BinaryPart::BinaryExpression(bin_exp) => {
|
||||||
self.precedence() > bin_exp.precedence()
|
self.precedence() > bin_exp.precedence()
|
||||||
|| self.operator == BinaryOperator::Sub
|
|| ((self.precedence() == bin_exp.precedence())
|
||||||
|| self.operator == BinaryOperator::Div
|
&& (!(self.operator.associative() && self.operator == bin_exp.operator)
|
||||||
|
&& self.operator.associativity() == Associativity::Right))
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let should_wrap_left = match &self.left {
|
let should_wrap_right = match &self.right {
|
||||||
BinaryPart::BinaryExpression(bin_exp) => self.precedence() > bin_exp.precedence(),
|
BinaryPart::BinaryExpression(bin_exp) => {
|
||||||
|
self.precedence() > bin_exp.precedence()
|
||||||
|
// These two lines preserve previous reformatting behaviour.
|
||||||
|
|| self.operator == BinaryOperator::Sub
|
||||||
|
|| self.operator == BinaryOperator::Div
|
||||||
|
|| ((self.precedence() == bin_exp.precedence())
|
||||||
|
&& (!(self.operator.associative() && self.operator == bin_exp.operator)
|
||||||
|
&& self.operator.associativity() == Associativity::Left))
|
||||||
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2820,4 +2831,36 @@ yo = 'bing'
|
|||||||
let recasted = ast.recast(&FormatOptions::new(), 0);
|
let recasted = ast.recast(&FormatOptions::new(), 0);
|
||||||
assert_eq!(recasted, code);
|
assert_eq!(recasted, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn paren_precedence() {
|
||||||
|
let code = r#"x = 1 - 2 - 3
|
||||||
|
x = (1 - 2) - 3
|
||||||
|
x = 1 - (2 - 3)
|
||||||
|
x = 1 + 2 + 3
|
||||||
|
x = (1 + 2) + 3
|
||||||
|
x = 1 + (2 + 3)
|
||||||
|
x = 2 * (y % 2)
|
||||||
|
x = (2 * y) % 2
|
||||||
|
x = 2 % (y * 2)
|
||||||
|
x = (2 % y) * 2
|
||||||
|
x = 2 * y % 2
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let expected = r#"x = 1 - 2 - 3
|
||||||
|
x = 1 - 2 - 3
|
||||||
|
x = 1 - (2 - 3)
|
||||||
|
x = 1 + 2 + 3
|
||||||
|
x = 1 + 2 + 3
|
||||||
|
x = 1 + 2 + 3
|
||||||
|
x = 2 * (y % 2)
|
||||||
|
x = 2 * y % 2
|
||||||
|
x = 2 % (y * 2)
|
||||||
|
x = 2 % y * 2
|
||||||
|
x = 2 * y % 2
|
||||||
|
"#;
|
||||||
|
let ast = crate::parsing::top_level_parse(code).unwrap();
|
||||||
|
let recasted = ast.recast(&FormatOptions::new(), 0);
|
||||||
|
assert_eq!(recasted, expected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -622,7 +622,7 @@ export fn revolve(
|
|||||||
axis: Axis2d | Edge,
|
axis: Axis2d | Edge,
|
||||||
/// Angle to revolve (in degrees). Default is 360.
|
/// Angle to revolve (in degrees). Default is 360.
|
||||||
angle?: number(Angle),
|
angle?: number(Angle),
|
||||||
/// Tolerance for the revolve operation.
|
/// Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters.
|
||||||
tolerance?: number(Length),
|
tolerance?: number(Length),
|
||||||
/// If true, the extrusion will happen symmetrically around the sketch. Otherwise, the extrusion will happen on only one side of the sketch.
|
/// If true, the extrusion will happen symmetrically around the sketch. Otherwise, the extrusion will happen on only one side of the sketch.
|
||||||
symmetric?: bool,
|
symmetric?: bool,
|
||||||
@ -961,7 +961,7 @@ export fn sweep(
|
|||||||
path: Sketch | Helix,
|
path: Sketch | Helix,
|
||||||
/// If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components.
|
/// If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components.
|
||||||
sectional?: bool,
|
sectional?: bool,
|
||||||
/// Tolerance for this operation.
|
/// Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters.
|
||||||
tolerance?: number(Length),
|
tolerance?: number(Length),
|
||||||
/// What is the sweep relative to? Can be either 'sketchPlane' or 'trajectoryCurve'.
|
/// What is the sweep relative to? Can be either 'sketchPlane' or 'trajectoryCurve'.
|
||||||
relativeTo?: string = 'trajectoryCurve',
|
relativeTo?: string = 'trajectoryCurve',
|
||||||
@ -1047,7 +1047,7 @@ export fn loft(
|
|||||||
bezApproximateRational?: bool = false,
|
bezApproximateRational?: bool = false,
|
||||||
/// This can be set to override the automatically determined topological base curve, which is usually the first section encountered.
|
/// This can be set to override the automatically determined topological base curve, which is usually the first section encountered.
|
||||||
baseCurveIndex?: number(Count),
|
baseCurveIndex?: number(Count),
|
||||||
/// Tolerance for the loft operation.
|
/// Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters.
|
||||||
tolerance?: number(Length),
|
tolerance?: number(Length),
|
||||||
/// A named tag for the face at the start of the loft, i.e. the original sketch.
|
/// A named tag for the face at the start of the loft, i.e. the original sketch.
|
||||||
tagStart?: TagDecl,
|
tagStart?: TagDecl,
|
||||||
@ -1499,12 +1499,20 @@ export fn profileStartY(
|
|||||||
export fn involuteCircular(
|
export fn involuteCircular(
|
||||||
/// Which sketch should this path be added to?
|
/// Which sketch should this path be added to?
|
||||||
@sketch: Sketch,
|
@sketch: Sketch,
|
||||||
/// The involute is described between two circles, start_radius is the radius of the inner circle.
|
|
||||||
startRadius: number(Length),
|
|
||||||
/// The involute is described between two circles, end_radius is the radius of the outer circle.
|
|
||||||
endRadius: number(Length),
|
|
||||||
/// The angle to rotate the involute by. A value of zero will produce a curve with a tangent along the x-axis at the start point of the curve.
|
/// The angle to rotate the involute by. A value of zero will produce a curve with a tangent along the x-axis at the start point of the curve.
|
||||||
angle: number(Angle),
|
angle: number(Angle),
|
||||||
|
/// The involute is described between two circles, startRadius is the radius of the inner circle.
|
||||||
|
/// Either `startRadius` or `startDiameter` must be given (but not both).
|
||||||
|
startRadius?: number(Length),
|
||||||
|
/// The involute is described between two circles, endRadius is the radius of the outer circle.
|
||||||
|
/// Either `endRadius` or `endDiameter` must be given (but not both).
|
||||||
|
endRadius?: number(Length),
|
||||||
|
/// The involute is described between two circles, startDiameter describes the inner circle.
|
||||||
|
/// Either `startRadius` or `startDiameter` must be given (but not both).
|
||||||
|
startDiameter?: number(Length),
|
||||||
|
/// The involute is described between two circles, endDiameter describes the outer circle.
|
||||||
|
/// Either `endRadius` or `endDiameter` must be given (but not both).
|
||||||
|
endDiameter?: number(Length),
|
||||||
/// If reverse is true, the segment will start from the end of the involute, otherwise it will start from that start.
|
/// If reverse is true, the segment will start from the end of the involute, otherwise it will start from that start.
|
||||||
reverse?: bool = false,
|
reverse?: bool = false,
|
||||||
/// Create a new tag which refers to this line.
|
/// Create a new tag which refers to this line.
|
||||||
|
@ -70,7 +70,7 @@ export fn fillet(
|
|||||||
radius: number(Length),
|
radius: number(Length),
|
||||||
/// The paths you want to fillet
|
/// The paths you want to fillet
|
||||||
tags: [Edge; 1+],
|
tags: [Edge; 1+],
|
||||||
/// The tolerance for this fillet
|
/// Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters.
|
||||||
tolerance?: number(Length),
|
tolerance?: number(Length),
|
||||||
/// Create a new tag which refers to this fillet
|
/// Create a new tag which refers to this fillet
|
||||||
tag?: TagDecl,
|
tag?: TagDecl,
|
||||||
@ -799,7 +799,7 @@ export fn patternCircular3d(
|
|||||||
export fn union(
|
export fn union(
|
||||||
/// The solids to union.
|
/// The solids to union.
|
||||||
@solids: [Solid; 2+],
|
@solids: [Solid; 2+],
|
||||||
/// The tolerance to use for the union operation.
|
/// Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters.
|
||||||
tolerance?: number(Length),
|
tolerance?: number(Length),
|
||||||
): [Solid; 1+] {}
|
): [Solid; 1+] {}
|
||||||
|
|
||||||
@ -857,7 +857,7 @@ export fn union(
|
|||||||
export fn intersect(
|
export fn intersect(
|
||||||
/// The solids to intersect.
|
/// The solids to intersect.
|
||||||
@solids: [Solid; 2+],
|
@solids: [Solid; 2+],
|
||||||
/// The tolerance to use for the intersection operation.
|
/// Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters.
|
||||||
tolerance?: number(Length),
|
tolerance?: number(Length),
|
||||||
): [Solid; 1+] {}
|
): [Solid; 1+] {}
|
||||||
|
|
||||||
@ -917,7 +917,7 @@ export fn subtract(
|
|||||||
@solids: [Solid; 1+],
|
@solids: [Solid; 1+],
|
||||||
/// The solids to subtract.
|
/// The solids to subtract.
|
||||||
tools: [Solid],
|
tools: [Solid],
|
||||||
/// The tolerance to use for the subtraction operation.
|
/// Defines the smallest distance below which two entities are considered coincident, intersecting, coplanar, or similar. For most use cases, it should not be changed from its default value of 10^-7 millimeters.
|
||||||
tolerance?: number(Length),
|
tolerance?: number(Length),
|
||||||
): [Solid; 1+] {}
|
): [Solid; 1+] {}
|
||||||
|
|
||||||
|
@ -7239,7 +7239,7 @@ description: Operations executed add_lots.kcl
|
|||||||
"name": "PI",
|
"name": "PI",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 3.142,
|
"value": 3.141592653589793,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Unknown"
|
"type": "Unknown"
|
||||||
}
|
}
|
||||||
@ -7255,7 +7255,7 @@ description: Operations executed add_lots.kcl
|
|||||||
"name": "E",
|
"name": "E",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 2.718,
|
"value": 2.718281828459045,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Known",
|
"type": "Known",
|
||||||
"type": "Count"
|
"type": "Count"
|
||||||
@ -7272,7 +7272,7 @@ description: Operations executed add_lots.kcl
|
|||||||
"name": "TAU",
|
"name": "TAU",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 6.283,
|
"value": 6.283185307179586,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Known",
|
"type": "Known",
|
||||||
"type": "Count"
|
"type": "Count"
|
||||||
|
@ -132,8 +132,8 @@ description: Artifact commands angled_line.kcl
|
|||||||
"segment": {
|
"segment": {
|
||||||
"type": "line",
|
"type": "line",
|
||||||
"end": {
|
"end": {
|
||||||
"x": 3.762,
|
"x": 3.761813572028026,
|
||||||
"y": -11.763,
|
"y": -11.763131328405109,
|
||||||
"z": 0.0
|
"z": 0.0
|
||||||
},
|
},
|
||||||
"relative": true
|
"relative": true
|
||||||
|
@ -94,7 +94,7 @@ description: Operations executed angled_line.kcl
|
|||||||
"name": "PI",
|
"name": "PI",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 3.142,
|
"value": 3.141592653589793,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Unknown"
|
"type": "Unknown"
|
||||||
}
|
}
|
||||||
@ -110,7 +110,7 @@ description: Operations executed angled_line.kcl
|
|||||||
"name": "E",
|
"name": "E",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 2.718,
|
"value": 2.718281828459045,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Known",
|
"type": "Known",
|
||||||
"type": "Count"
|
"type": "Count"
|
||||||
@ -127,7 +127,7 @@ description: Operations executed angled_line.kcl
|
|||||||
"name": "TAU",
|
"name": "TAU",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 6.283,
|
"value": 6.283185307179586,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Known",
|
"type": "Known",
|
||||||
"type": "Count"
|
"type": "Count"
|
||||||
|
@ -75,7 +75,7 @@ description: Variables in memory after executing angled_line.kcl
|
|||||||
"tag": null,
|
"tag": null,
|
||||||
"to": [
|
"to": [
|
||||||
19.93,
|
19.93,
|
||||||
15.04
|
15.040000000000001
|
||||||
],
|
],
|
||||||
"type": "ToPoint",
|
"type": "ToPoint",
|
||||||
"units": {
|
"units": {
|
||||||
@ -89,7 +89,7 @@ description: Variables in memory after executing angled_line.kcl
|
|||||||
},
|
},
|
||||||
"from": [
|
"from": [
|
||||||
19.93,
|
19.93,
|
||||||
15.04
|
15.040000000000001
|
||||||
],
|
],
|
||||||
"tag": {
|
"tag": {
|
||||||
"commentStart": 133,
|
"commentStart": 133,
|
||||||
@ -100,7 +100,7 @@ description: Variables in memory after executing angled_line.kcl
|
|||||||
},
|
},
|
||||||
"to": [
|
"to": [
|
||||||
23.08,
|
23.08,
|
||||||
5.19
|
5.190000000000001
|
||||||
],
|
],
|
||||||
"type": "ToPoint",
|
"type": "ToPoint",
|
||||||
"units": {
|
"units": {
|
||||||
@ -114,12 +114,12 @@ description: Variables in memory after executing angled_line.kcl
|
|||||||
},
|
},
|
||||||
"from": [
|
"from": [
|
||||||
23.08,
|
23.08,
|
||||||
5.19
|
5.190000000000001
|
||||||
],
|
],
|
||||||
"tag": null,
|
"tag": null,
|
||||||
"to": [
|
"to": [
|
||||||
7.91,
|
7.909999999999998,
|
||||||
1.09
|
1.0900000000000016
|
||||||
],
|
],
|
||||||
"type": "ToPoint",
|
"type": "ToPoint",
|
||||||
"units": {
|
"units": {
|
||||||
@ -132,13 +132,13 @@ description: Variables in memory after executing angled_line.kcl
|
|||||||
"sourceRange": []
|
"sourceRange": []
|
||||||
},
|
},
|
||||||
"from": [
|
"from": [
|
||||||
7.91,
|
7.909999999999998,
|
||||||
1.09
|
1.0900000000000016
|
||||||
],
|
],
|
||||||
"tag": null,
|
"tag": null,
|
||||||
"to": [
|
"to": [
|
||||||
11.672,
|
11.671813572028025,
|
||||||
-10.673
|
-10.673131328405107
|
||||||
],
|
],
|
||||||
"type": "ToPoint",
|
"type": "ToPoint",
|
||||||
"units": {
|
"units": {
|
||||||
@ -151,13 +151,13 @@ description: Variables in memory after executing angled_line.kcl
|
|||||||
"sourceRange": []
|
"sourceRange": []
|
||||||
},
|
},
|
||||||
"from": [
|
"from": [
|
||||||
11.672,
|
11.671813572028025,
|
||||||
-10.673
|
-10.673131328405107
|
||||||
],
|
],
|
||||||
"tag": null,
|
"tag": null,
|
||||||
"to": [
|
"to": [
|
||||||
-1.348,
|
-1.3481864279719744,
|
||||||
-0.643
|
-0.6431313284051079
|
||||||
],
|
],
|
||||||
"type": "ToPoint",
|
"type": "ToPoint",
|
||||||
"units": {
|
"units": {
|
||||||
@ -170,8 +170,8 @@ description: Variables in memory after executing angled_line.kcl
|
|||||||
"sourceRange": []
|
"sourceRange": []
|
||||||
},
|
},
|
||||||
"from": [
|
"from": [
|
||||||
-1.348,
|
-1.3481864279719744,
|
||||||
-0.643
|
-0.6431313284051079
|
||||||
],
|
],
|
||||||
"tag": null,
|
"tag": null,
|
||||||
"to": [
|
"to": [
|
||||||
|
@ -398,7 +398,7 @@ description: Operations executed any_type.kcl
|
|||||||
"name": "PI",
|
"name": "PI",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 3.142,
|
"value": 3.141592653589793,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Unknown"
|
"type": "Unknown"
|
||||||
}
|
}
|
||||||
@ -414,7 +414,7 @@ description: Operations executed any_type.kcl
|
|||||||
"name": "E",
|
"name": "E",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 2.718,
|
"value": 2.718281828459045,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Known",
|
"type": "Known",
|
||||||
"type": "Count"
|
"type": "Count"
|
||||||
@ -431,7 +431,7 @@ description: Operations executed any_type.kcl
|
|||||||
"name": "TAU",
|
"name": "TAU",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 6.283,
|
"value": 6.283185307179586,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Known",
|
"type": "Known",
|
||||||
"type": "Count"
|
"type": "Count"
|
||||||
|
@ -12,7 +12,7 @@ description: Operations executed argument_error.kcl
|
|||||||
"name": "PI",
|
"name": "PI",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 3.142,
|
"value": 3.141592653589793,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Unknown"
|
"type": "Unknown"
|
||||||
}
|
}
|
||||||
@ -28,7 +28,7 @@ description: Operations executed argument_error.kcl
|
|||||||
"name": "E",
|
"name": "E",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 2.718,
|
"value": 2.718281828459045,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Known",
|
"type": "Known",
|
||||||
"type": "Count"
|
"type": "Count"
|
||||||
@ -45,7 +45,7 @@ description: Operations executed argument_error.kcl
|
|||||||
"name": "TAU",
|
"name": "TAU",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 6.283,
|
"value": 6.283185307179586,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Known",
|
"type": "Known",
|
||||||
"type": "Count"
|
"type": "Count"
|
||||||
|
@ -12,7 +12,7 @@ description: Operations executed array_elem_pop.kcl
|
|||||||
"name": "PI",
|
"name": "PI",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 3.142,
|
"value": 3.141592653589793,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Unknown"
|
"type": "Unknown"
|
||||||
}
|
}
|
||||||
@ -28,7 +28,7 @@ description: Operations executed array_elem_pop.kcl
|
|||||||
"name": "E",
|
"name": "E",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 2.718,
|
"value": 2.718281828459045,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Known",
|
"type": "Known",
|
||||||
"type": "Count"
|
"type": "Count"
|
||||||
@ -45,7 +45,7 @@ description: Operations executed array_elem_pop.kcl
|
|||||||
"name": "TAU",
|
"name": "TAU",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 6.283,
|
"value": 6.283185307179586,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Known",
|
"type": "Known",
|
||||||
"type": "Count"
|
"type": "Count"
|
||||||
|
@ -12,7 +12,7 @@ description: Operations executed array_elem_pop_empty_fail.kcl
|
|||||||
"name": "PI",
|
"name": "PI",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 3.142,
|
"value": 3.141592653589793,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Unknown"
|
"type": "Unknown"
|
||||||
}
|
}
|
||||||
@ -28,7 +28,7 @@ description: Operations executed array_elem_pop_empty_fail.kcl
|
|||||||
"name": "E",
|
"name": "E",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 2.718,
|
"value": 2.718281828459045,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Known",
|
"type": "Known",
|
||||||
"type": "Count"
|
"type": "Count"
|
||||||
@ -45,7 +45,7 @@ description: Operations executed array_elem_pop_empty_fail.kcl
|
|||||||
"name": "TAU",
|
"name": "TAU",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 6.283,
|
"value": 6.283185307179586,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Known",
|
"type": "Known",
|
||||||
"type": "Count"
|
"type": "Count"
|
||||||
|
@ -12,7 +12,7 @@ description: Operations executed array_elem_pop_fail.kcl
|
|||||||
"name": "PI",
|
"name": "PI",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 3.142,
|
"value": 3.141592653589793,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Unknown"
|
"type": "Unknown"
|
||||||
}
|
}
|
||||||
@ -28,7 +28,7 @@ description: Operations executed array_elem_pop_fail.kcl
|
|||||||
"name": "E",
|
"name": "E",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 2.718,
|
"value": 2.718281828459045,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Known",
|
"type": "Known",
|
||||||
"type": "Count"
|
"type": "Count"
|
||||||
@ -45,7 +45,7 @@ description: Operations executed array_elem_pop_fail.kcl
|
|||||||
"name": "TAU",
|
"name": "TAU",
|
||||||
"value": {
|
"value": {
|
||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 6.283,
|
"value": 6.283185307179586,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Known",
|
"type": "Known",
|
||||||
"type": "Count"
|
"type": "Count"
|
||||||
|