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_SKIP_AUTH=false
|
||||
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"
|
||||
#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
|
||||
about: Create a new release for the Zoo Design Studio
|
||||
title: "Cut release v1.?.?"
|
||||
labels: [release]
|
||||
labels: [meta/release]
|
||||
---
|
||||
|
||||
> 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)
|
||||
* [ ] Create a project with a basic Text-to-CAD prompt
|
||||
* [ ] 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 ???
|
||||
|
||||
@ -27,7 +28,8 @@ Release builds URL: ???
|
||||
* [ ] Confirm the application opens (dismiss the updater)
|
||||
* [ ] Create a project with a basic Text-to-CAD prompt
|
||||
* [ ] 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 ???
|
||||
|
||||
@ -35,4 +37,5 @@ Release builds URL: ???
|
||||
* [ ] Confirm the application opens (dismiss the updater)
|
||||
* [ ] Create a project with a basic Text-to-CAD prompt
|
||||
* [ ] 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
|
||||
shell: bash
|
||||
run: |
|
||||
[ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./
|
||||
cp .github/workflows/nightly-rust-toolchain.toml rust-toolchain.toml
|
||||
- name: Install rust
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
cache-workspaces: rust
|
||||
components: rustfmt
|
||||
|
||||
- name: Run cargo fmt
|
||||
- name: Run nightly cargo fmt
|
||||
run: |
|
||||
cd rust
|
||||
cargo fmt -- --check
|
||||
cargo +nightly fmt -- --check
|
||||
shell: bash
|
||||
|
15
.github/workflows/e2e-tests.yml
vendored
@ -95,7 +95,8 @@ jobs:
|
||||
shell: bash
|
||||
run: npm run build:wasm
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- name: Upload compiled wasm artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: prepared-wasm
|
||||
path: |
|
||||
@ -176,7 +177,8 @@ jobs:
|
||||
CI_SUITE: e2e:snapshots
|
||||
TARGET: web
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- name: Upload playwright report
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ !cancelled() }}
|
||||
with:
|
||||
name: playwright-report-snapshot-${{ github.sha }}
|
||||
@ -290,7 +292,8 @@ jobs:
|
||||
CI_SUITE: e2e:web
|
||||
TARGET: web
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- name: Upload playwright report
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ !cancelled() && (success() || failure()) }}
|
||||
with:
|
||||
name: playwright-report-web-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||
@ -415,7 +418,8 @@ jobs:
|
||||
CI_SUITE: e2e:desktop
|
||||
TARGET: desktop
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- name: Upload test report
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: test-results-desktop-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||
@ -424,7 +428,8 @@ jobs:
|
||||
retention-days: 30
|
||||
overwrite: true
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- name: Upload playwright report
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
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
|
||||
involuteCircular(
|
||||
@sketch: Sketch,
|
||||
startRadius: number(Length),
|
||||
endRadius: number(Length),
|
||||
angle: number(Angle),
|
||||
startRadius?: number(Length),
|
||||
endRadius?: number(Length),
|
||||
startDiameter?: number(Length),
|
||||
endDiameter?: number(Length),
|
||||
reverse?: bool,
|
||||
tag?: TagDecl,
|
||||
): Sketch
|
||||
@ -25,9 +27,11 @@ involuteCircular(
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
|
@ -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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
|
@ -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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
|
||||
### Returns
|
||||
|
@ -24,7 +24,7 @@ verifying fit, and analyzing overlapping geometries in assemblies.
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `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
|
||||
|
||||
|
@ -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 |
|
||||
| `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
|
||||
|
||||
|
@ -21,7 +21,7 @@ union(
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `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
|
||||
|
||||
|
@ -4,7 +4,6 @@ import * as fsp from 'fs/promises'
|
||||
|
||||
import { executorInputPath, getUtils } from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
import { expectPixelColor } from '@e2e/playwright/fixtures/sceneFixture'
|
||||
|
||||
test.describe('Command bar tests', () => {
|
||||
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 submitButton = page.getByRole('button', { name: 'Submit command' })
|
||||
const submitButton = page.getByTestId('command-bar-submit')
|
||||
await continueButton.click()
|
||||
|
||||
// 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 ({
|
||||
page,
|
||||
homePage,
|
||||
|
@ -54,9 +54,7 @@ test(
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Click the checkbox
|
||||
const submitButton = page.getByText('Confirm Export')
|
||||
await expect(submitButton).toBeVisible()
|
||||
await page.keyboard.press('Enter')
|
||||
await cmdBar.submit()
|
||||
|
||||
// Expect it to succeed
|
||||
const errorToastMessage = page.getByText(`Error while exporting`)
|
||||
@ -119,9 +117,7 @@ test(
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Click the checkbox
|
||||
const submitButton = page.getByText('Confirm Export')
|
||||
await expect(submitButton).toBeVisible()
|
||||
await page.keyboard.press('Enter')
|
||||
await cmdBar.submit()
|
||||
|
||||
// Look out for the toast message
|
||||
const exportingToastMessage = page.getByText(`Exporting...`)
|
||||
|
@ -229,11 +229,12 @@ test.describe('Feature Tree pane', () => {
|
||||
const initialCode = `sketch001 = startSketchOn(XZ)
|
||||
|> circle(center = [0, 0], radius = 5)
|
||||
renamedExtrude = extrude(sketch001, length = ${initialInput})`
|
||||
const newConstantName = 'length001'
|
||||
const expectedCode = `${newConstantName} = 23
|
||||
const newParameterName = 'length001'
|
||||
const expectedCode = `${newParameterName} = 23
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> 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) => {
|
||||
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 cmdBar.variableCheckbox.click()
|
||||
await cmdBar.progressCmdBar()
|
||||
@ -296,13 +297,43 @@ test.describe('Feature Tree pane', () => {
|
||||
highlightedCode: '',
|
||||
diagnostics: [],
|
||||
activeLines: [
|
||||
`renamedExtrude = extrude(sketch001, length = ${newConstantName})`,
|
||||
`renamedExtrude = extrude(sketch001, length = ${newParameterName})`,
|
||||
],
|
||||
})
|
||||
await editor.expectEditor.toContain(expectedCode, {
|
||||
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 ({
|
||||
context,
|
||||
|
@ -118,15 +118,11 @@ export class CmdBarFixture {
|
||||
return
|
||||
}
|
||||
|
||||
const arrowButton = this.page.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
const arrowButton = this.page.getByTestId('command-bar-continue')
|
||||
if (await arrowButton.isVisible()) {
|
||||
await arrowButton.click()
|
||||
await this.continue()
|
||||
} else {
|
||||
await this.page
|
||||
.getByRole('button', { name: 'checkmark Submit command' })
|
||||
.click()
|
||||
await this.submit()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,14 +183,15 @@ export class EditorFixture {
|
||||
scrollToText(text: string, placeCursor?: boolean) {
|
||||
return this.page.evaluate(
|
||||
(args: { text: string; placeCursor?: boolean }) => {
|
||||
const editorView = window.editorManager.getEditorView()
|
||||
// error TS2339: Property 'docView' does not exist on type 'EditorView'.
|
||||
// Except it does so :shrug:
|
||||
// @ts-ignore
|
||||
let index = window.editorManager._editorView?.docView.view.state.doc
|
||||
const index = editorView?.docView.view.state.doc
|
||||
.toString()
|
||||
.indexOf(args.text)
|
||||
window.editorManager._editorView?.focus()
|
||||
window.editorManager._editorView?.dispatch({
|
||||
editorView?.focus()
|
||||
editorView?.dispatch({
|
||||
selection: window.EditorSelection.create([
|
||||
window.EditorSelection.cursor(index),
|
||||
]),
|
||||
|
@ -5,7 +5,7 @@ import type {
|
||||
FullResult,
|
||||
} from '@playwright/test/reporter'
|
||||
|
||||
class MyAPIReporter implements Reporter {
|
||||
class APIReporter implements Reporter {
|
||||
private pendingRequests: Promise<void>[] = []
|
||||
private allResults: Record<string, any>[] = []
|
||||
private blockingResults: Record<string, any>[] = []
|
||||
@ -32,7 +32,7 @@ class MyAPIReporter implements Reporter {
|
||||
'X-API-Key': process.env.TAB_API_KEY || '',
|
||||
}),
|
||||
body: JSON.stringify({
|
||||
project: 'https://github.com/KittyCAD/modeling-app',
|
||||
project: `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}`,
|
||||
branch:
|
||||
process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || '',
|
||||
commit: process.env.CI_COMMIT_SHA || process.env.GITHUB_SHA || '',
|
||||
@ -60,7 +60,7 @@ class MyAPIReporter implements Reporter {
|
||||
|
||||
const payload = {
|
||||
// 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',
|
||||
branch: process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || '',
|
||||
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,
|
||||
}) => {
|
||||
// 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 expectedOutput = `plane001 = offsetPlane(XZ, offset = 5)`
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
// FIXME: Since there is no KCL code loaded. We need to wait for the scene to load before we continue.
|
||||
// The engine may not be connected
|
||||
await page.waitForTimeout(15000)
|
||||
await scene.settled(cmdBar)
|
||||
|
||||
await test.step(`Look for the blue of the XZ plane`, async () => {
|
||||
//await scene.expectPixelColor([50, 51, 96], testPoint, 15) // FIXME
|
||||
@ -1829,7 +1828,6 @@ profile002 = startProfile(sketch002, at = [0, 0])
|
||||
currentArgKey: 'sketches',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
Sectional: '',
|
||||
Profiles: '',
|
||||
Path: '',
|
||||
},
|
||||
@ -1843,7 +1841,6 @@ profile002 = startProfile(sketch002, at = [0, 0])
|
||||
currentArgKey: 'path',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
Sectional: '',
|
||||
Profiles: '1 profile',
|
||||
Path: '',
|
||||
},
|
||||
@ -1856,7 +1853,6 @@ profile002 = startProfile(sketch002, at = [0, 0])
|
||||
currentArgKey: 'path',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
Sectional: '',
|
||||
Profiles: '1 profile',
|
||||
Path: '',
|
||||
},
|
||||
@ -1869,7 +1865,6 @@ profile002 = startProfile(sketch002, at = [0, 0])
|
||||
headerArguments: {
|
||||
Profiles: '1 profile',
|
||||
Path: '1 segment',
|
||||
Sectional: '',
|
||||
},
|
||||
stage: 'review',
|
||||
})
|
||||
@ -1894,6 +1889,9 @@ profile002 = startProfile(sketch002, at = [0, 0])
|
||||
0
|
||||
)
|
||||
await operationButton.dblclick({ button: 'left' })
|
||||
await page
|
||||
.getByRole('button', { name: 'sectional', exact: false })
|
||||
.click()
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Sweep',
|
||||
currentArgKey: 'sectional',
|
||||
@ -1971,7 +1969,6 @@ profile001 = ${circleCode}`
|
||||
currentArgKey: 'sketches',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
Sectional: '',
|
||||
Profiles: '',
|
||||
Path: '',
|
||||
},
|
||||
@ -1986,7 +1983,6 @@ profile001 = ${circleCode}`
|
||||
currentArgKey: 'path',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
Sectional: '',
|
||||
Profiles: '1 profile',
|
||||
Path: '',
|
||||
},
|
||||
@ -2000,7 +1996,6 @@ profile001 = ${circleCode}`
|
||||
currentArgKey: 'path',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
Sectional: '',
|
||||
Profiles: '1 profile',
|
||||
Path: '',
|
||||
},
|
||||
@ -2013,7 +2008,6 @@ profile001 = ${circleCode}`
|
||||
headerArguments: {
|
||||
Profiles: '1 profile',
|
||||
Path: '1 helix',
|
||||
Sectional: '',
|
||||
},
|
||||
stage: 'review',
|
||||
})
|
||||
@ -4734,7 +4728,6 @@ path001 = startProfile(sketch001, at = [0, 0])
|
||||
headerArguments: {
|
||||
Profiles: '',
|
||||
Path: '',
|
||||
Sectional: '',
|
||||
},
|
||||
highlightedHeaderArg: 'Profiles',
|
||||
commandName: 'Sweep',
|
||||
@ -4747,7 +4740,6 @@ path001 = startProfile(sketch001, at = [0, 0])
|
||||
headerArguments: {
|
||||
Profiles: '2 profiles',
|
||||
Path: '',
|
||||
Sectional: '',
|
||||
},
|
||||
highlightedHeaderArg: 'path',
|
||||
commandName: 'Sweep',
|
||||
@ -4760,7 +4752,6 @@ path001 = startProfile(sketch001, at = [0, 0])
|
||||
headerArguments: {
|
||||
Profiles: '2 profiles',
|
||||
Path: '1 segment',
|
||||
Sectional: '',
|
||||
},
|
||||
commandName: 'Sweep',
|
||||
})
|
||||
|
@ -475,6 +475,7 @@ test.describe('Can export from electron app', () => {
|
||||
},
|
||||
tronApp.projectDirName,
|
||||
page,
|
||||
cmdBar,
|
||||
method
|
||||
)
|
||||
)
|
||||
@ -779,9 +780,6 @@ test.describe(`Project management commands`, () => {
|
||||
const commandContinueButton = page.getByRole('button', {
|
||||
name: 'Continue',
|
||||
})
|
||||
const commandSubmitButton = page.getByRole('button', {
|
||||
name: 'Submit command',
|
||||
})
|
||||
const toastMessage = page.getByText(`Successfully renamed`)
|
||||
|
||||
await test.step(`Setup`, async () => {
|
||||
@ -800,8 +798,7 @@ test.describe(`Project management commands`, () => {
|
||||
await expect(commandContinueButton).toBeVisible()
|
||||
await commandContinueButton.click()
|
||||
|
||||
await expect(commandSubmitButton).toBeVisible()
|
||||
await commandSubmitButton.click()
|
||||
await cmdBar.submit()
|
||||
|
||||
await expect(toastMessage).toBeVisible()
|
||||
})
|
||||
@ -837,9 +834,6 @@ test.describe(`Project management commands`, () => {
|
||||
})
|
||||
const projectNameOption = page.getByRole('option', { name: projectName })
|
||||
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 noProjectsMessage = page.getByText('No projects found')
|
||||
|
||||
@ -859,8 +853,7 @@ test.describe(`Project management commands`, () => {
|
||||
await projectNameOption.click()
|
||||
|
||||
await expect(commandWarning).toBeVisible()
|
||||
await expect(commandSubmitButton).toBeVisible()
|
||||
await commandSubmitButton.click()
|
||||
await cmdBar.submit()
|
||||
|
||||
await expect(toastMessage).toBeVisible()
|
||||
})
|
||||
@ -894,9 +887,6 @@ test.describe(`Project management commands`, () => {
|
||||
const commandContinueButton = page.getByRole('button', {
|
||||
name: 'Continue',
|
||||
})
|
||||
const commandSubmitButton = page.getByRole('button', {
|
||||
name: 'Submit command',
|
||||
})
|
||||
const toastMessage = page.getByText(`Successfully renamed`)
|
||||
|
||||
await test.step(`Setup`, async () => {
|
||||
@ -914,8 +904,7 @@ test.describe(`Project management commands`, () => {
|
||||
await expect(commandContinueButton).toBeVisible()
|
||||
await commandContinueButton.click()
|
||||
|
||||
await expect(commandSubmitButton).toBeVisible()
|
||||
await commandSubmitButton.click()
|
||||
await cmdBar.submit()
|
||||
|
||||
await expect(toastMessage).toBeVisible()
|
||||
})
|
||||
@ -949,9 +938,6 @@ test.describe(`Project management commands`, () => {
|
||||
})
|
||||
const projectNameOption = page.getByRole('option', { name: projectName })
|
||||
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 noProjectsMessage = page.getByText('No projects found')
|
||||
|
||||
@ -967,8 +953,7 @@ test.describe(`Project management commands`, () => {
|
||||
await projectNameOption.click()
|
||||
|
||||
await expect(commandWarning).toBeVisible()
|
||||
await expect(commandSubmitButton).toBeVisible()
|
||||
await commandSubmitButton.click()
|
||||
await cmdBar.submit()
|
||||
|
||||
await expect(toastMessage).toBeVisible()
|
||||
})
|
||||
|
@ -1,6 +1,7 @@
|
||||
import path from 'path'
|
||||
import { bracket } from '@e2e/playwright/fixtures/bracket'
|
||||
import type { Page } from '@playwright/test'
|
||||
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
|
||||
import { reportRejection } from '@src/lib/trap'
|
||||
import * as fsp from 'fs/promises'
|
||||
|
||||
@ -421,10 +422,7 @@ extrude002 = extrude(profile002, length = 150)
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Click the checkbox
|
||||
const submitButton = page.getByText('Confirm Export')
|
||||
await expect(submitButton).toBeVisible()
|
||||
|
||||
await page.keyboard.press('Enter')
|
||||
await cmdBar.submit()
|
||||
|
||||
// Find the toast.
|
||||
// Look out for the toast message
|
||||
@ -461,8 +459,7 @@ extrude002 = extrude(profile002, length = 150)
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Click the checkbox
|
||||
await expect(submitButton).toBeVisible()
|
||||
await page.keyboard.press('Enter')
|
||||
await cmdBar.submit()
|
||||
|
||||
// Find the toast.
|
||||
// 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 ({
|
||||
page,
|
||||
homePage,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
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`)
|
||||
|
||||
await test.step('second export', async () => {
|
||||
await clickExportButton(page)
|
||||
await clickExportButton(page, cmdBar)
|
||||
|
||||
await expect(exportingToastMessage).toBeVisible()
|
||||
|
||||
await clickExportButton(page)
|
||||
await clickExportButton(page, cmdBar)
|
||||
|
||||
await test.step('The first export still succeeds', async () => {
|
||||
await Promise.all([
|
||||
@ -537,7 +535,7 @@ extrude002 = extrude(profile002, length = 150)
|
||||
|
||||
await test.step('Successful, unblocked export', async () => {
|
||||
// Try exporting again.
|
||||
await clickExportButton(page)
|
||||
await clickExportButton(page, cmdBar)
|
||||
|
||||
// Find the toast.
|
||||
// Look out for the toast message
|
||||
@ -575,7 +573,7 @@ extrude002 = extrude(profile002, length = 150)
|
||||
name: 'Projects',
|
||||
})
|
||||
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 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 () => {
|
||||
// export the model
|
||||
const exportButton = page.getByTestId('export-pane-button')
|
||||
@ -896,9 +894,6 @@ async function clickExportButton(page: Page) {
|
||||
await page.keyboard.press('Enter')
|
||||
|
||||
// Click the checkbox
|
||||
const submitButton = page.getByText('Confirm Export')
|
||||
await expect(submitButton).toBeVisible()
|
||||
|
||||
await page.keyboard.press('Enter')
|
||||
await cmdBar.submit()
|
||||
})
|
||||
}
|
||||
|
@ -1445,6 +1445,103 @@ solid001 = subtract([extrude001], tools = [extrude002])
|
||||
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 ({
|
||||
page,
|
||||
scene,
|
||||
|
@ -798,7 +798,7 @@ test('theme persists', async ({ page, context, homePage }) => {
|
||||
|
||||
await page.getByTestId('settings-close-button').click()
|
||||
|
||||
const networkToggle = page.getByTestId('network-toggle')
|
||||
const networkToggle = page.getByTestId(/network-toggle/)
|
||||
|
||||
// simulate network down
|
||||
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()
|
||||
|
||||
const networkToggle = page.getByTestId('network-toggle')
|
||||
const networkToggle = page.getByTestId(/network-toggle/)
|
||||
|
||||
// This is how we wait until the stream is online
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled({ timeout: 15000 })
|
||||
|
||||
const networkWidget = page.locator('[data-testid="network-toggle"]')
|
||||
await expect(networkWidget).toBeVisible()
|
||||
await networkWidget.hover()
|
||||
await expect(networkToggle).toBeVisible()
|
||||
await networkToggle.hover()
|
||||
|
||||
const networkPopover = page.locator('[data-testid="network-popover"]')
|
||||
await expect(networkPopover).not.toBeVisible()
|
||||
@ -44,7 +43,7 @@ test.describe('Test network related behaviors', () => {
|
||||
).toBeVisible()
|
||||
|
||||
// Click the network widget
|
||||
await networkWidget.click()
|
||||
await networkToggle.click()
|
||||
|
||||
// Check the modal opened.
|
||||
await expect(networkPopover).toBeVisible()
|
||||
@ -65,8 +64,8 @@ test.describe('Test network related behaviors', () => {
|
||||
// Expect the network to be down
|
||||
await expect(networkToggle).toContainText('Network health (Offline)')
|
||||
|
||||
// Click the network widget
|
||||
await networkWidget.click()
|
||||
// Click the network toggle
|
||||
await networkToggle.click()
|
||||
|
||||
// Check the modal opened.
|
||||
await expect(networkPopover).toBeVisible()
|
||||
@ -99,7 +98,7 @@ test.describe('Test network related behaviors', () => {
|
||||
'Engine disconnect & reconnect in sketch mode',
|
||||
{ tag: '@skipLocalEngine' },
|
||||
async ({ page, homePage, toolbar, scene, cmdBar }) => {
|
||||
const networkToggle = page.getByTestId('network-toggle')
|
||||
const networkToggle = page.getByTestId(/network-toggle/)
|
||||
const networkToggleConnectedText = page.getByText(
|
||||
'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',
|
||||
{ tag: ['@desktop', '@skipLocalEngine'] },
|
||||
async ({ page, homePage, scene, cmdBar, toolbar, tronApp }) => {
|
||||
const networkToggle = page.getByTestId('network-toggle')
|
||||
const networkToggle = page.getByTestId(/network-toggle/)
|
||||
const networkToggleConnectedText = page.getByText(
|
||||
'Network health (Strong)'
|
||||
)
|
||||
|
@ -22,6 +22,7 @@ export const token = process.env.token || ''
|
||||
import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration'
|
||||
|
||||
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 { TEST_SETTINGS, TEST_SETTINGS_KEY } from '@e2e/playwright/storageStates'
|
||||
import { test } from '@e2e/playwright/zoo-test'
|
||||
@ -37,7 +38,7 @@ export const headerMasks = (page: Page) => [
|
||||
]
|
||||
|
||||
export const lowerRightMasks = (page: Page) => [
|
||||
page.getByTestId('network-toggle'),
|
||||
page.getByTestId(/network-toggle/),
|
||||
page.getByTestId('billing-remaining-bar'),
|
||||
]
|
||||
|
||||
@ -158,10 +159,10 @@ async function openKclCodePanel(page: Page) {
|
||||
await page.evaluate(() => {
|
||||
// editorManager is available on the window object.
|
||||
//@ts-ignore this is in an entirely different context that tsc can't see.
|
||||
editorManager._editorView.dispatch({
|
||||
editorManager.getEditorView().dispatch({
|
||||
selection: {
|
||||
//@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,
|
||||
})
|
||||
@ -737,6 +738,7 @@ export const doExport = async (
|
||||
output: Models['OutputFormat3d_type'],
|
||||
rootDir: string,
|
||||
page: Page,
|
||||
cmdBar: CmdBarFixture,
|
||||
exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown'
|
||||
): Promise<Paths> => {
|
||||
if (exportFrom === 'dropdown') {
|
||||
@ -780,9 +782,7 @@ export const doExport = async (
|
||||
.click()
|
||||
await page.locator('#arg-form').waitFor({ state: 'detached' })
|
||||
}
|
||||
await expect(page.getByText('Confirm Export')).toBeVisible()
|
||||
|
||||
await page.getByRole('button', { name: 'Submit command' }).click()
|
||||
await cmdBar.submit()
|
||||
|
||||
await expect(page.getByText('Exported successfully')).toBeVisible()
|
||||
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
|
||||
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 () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
@ -50,11 +50,7 @@ test.describe('Testing constraints', () => {
|
||||
await page.waitForTimeout(100)
|
||||
await page.getByTestId('constraint-length').click()
|
||||
await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('20')
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
.click()
|
||||
await cmdBar.continue()
|
||||
|
||||
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)`
|
||||
@ -681,9 +677,6 @@ test.describe('Testing constraints', () => {
|
||||
.getByRole('textbox')
|
||||
const cmdBarKclVariableNameInput =
|
||||
page.getByPlaceholder('Variable name')
|
||||
const cmdBarSubmitButton = page.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
@ -736,7 +729,7 @@ part002 = startSketchOn(XZ)
|
||||
await page.waitForTimeout(500)
|
||||
const [ang, len] = value.split(', ')
|
||||
const changedCode = `|> angledLine(angle = ${ang}, length = ${len})`
|
||||
await cmdBarSubmitButton.click()
|
||||
await cmdBar.continue()
|
||||
await expect(page.locator('.cm-content')).toContainText(changedCode)
|
||||
|
||||
// checking active assures the cursor is where it should be
|
||||
@ -1101,11 +1094,7 @@ part002 = startSketchOn(XZ)
|
||||
await page.waitForTimeout(500)
|
||||
|
||||
await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('10')
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
.click()
|
||||
await cmdBar.continue()
|
||||
|
||||
await pollEditorLinesSelectedLength(page, 1)
|
||||
activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||
|
@ -8,37 +8,37 @@ test.describe('Testing Gizmo', () => {
|
||||
const cases = [
|
||||
{
|
||||
testDescription: 'top view',
|
||||
clickPosition: { x: 951, y: 385 },
|
||||
clickPosition: { x: 951, y: 402 },
|
||||
expectedCameraPosition: { x: 800, y: -152, z: 4886.02 },
|
||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
||||
},
|
||||
{
|
||||
testDescription: 'bottom view',
|
||||
clickPosition: { x: 951, y: 429 },
|
||||
clickPosition: { x: 951, y: 449 },
|
||||
expectedCameraPosition: { x: 800, y: -152, z: -4834.02 },
|
||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
||||
},
|
||||
{
|
||||
testDescription: 'right view',
|
||||
clickPosition: { x: 929, y: 417 },
|
||||
clickPosition: { x: 929, y: 435 },
|
||||
expectedCameraPosition: { x: 5660.02, y: -152, z: 26 },
|
||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
||||
},
|
||||
{
|
||||
testDescription: 'left view',
|
||||
clickPosition: { x: 974, y: 397 },
|
||||
clickPosition: { x: 974, y: 417 },
|
||||
expectedCameraPosition: { x: -4060.02, y: -152, z: 26 },
|
||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
||||
},
|
||||
{
|
||||
testDescription: 'back view',
|
||||
clickPosition: { x: 967, y: 421 },
|
||||
clickPosition: { x: 967, y: 441 },
|
||||
expectedCameraPosition: { x: 800, y: 4708.02, z: 26 },
|
||||
expectedCameraTarget: { x: 800, y: -152, z: 26 },
|
||||
},
|
||||
{
|
||||
testDescription: 'front view',
|
||||
clickPosition: { x: 935, y: 393 },
|
||||
clickPosition: { x: 935, y: 413 },
|
||||
expectedCameraPosition: { x: 800, y: -5012.02, 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
|
||||
test.fail(
|
||||
'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)
|
||||
await test.step(`Test setup`, async () => {
|
||||
await context.addInitScript((code) => {
|
||||
@ -52,9 +52,6 @@ test.describe('Testing loading external models', () => {
|
||||
name,
|
||||
})
|
||||
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 u.openKclCodePanel()
|
||||
@ -70,7 +67,7 @@ test.describe('Testing loading external models', () => {
|
||||
await expect(commandMethodOption('Create new file')).not.toBeVisible()
|
||||
await commandMethodOption('Overwrite').click()
|
||||
await expect(warningText).toBeVisible()
|
||||
await confirmButton.click()
|
||||
await cmdBar.submit()
|
||||
|
||||
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 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 { 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
|
||||
*/
|
||||
const _clickConstrained =
|
||||
(page: Page, editor: EditorFixture) =>
|
||||
(page: Page, editor: EditorFixture, cmdBar: CmdBarFixture) =>
|
||||
async ({
|
||||
hoverPos,
|
||||
constraintType,
|
||||
@ -93,11 +94,7 @@ test.describe('Testing segment overlays', () => {
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.waitForTimeout(500)
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
.click()
|
||||
await cmdBar.continue()
|
||||
await editor.expectEditor.toContain(expectFinal, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
@ -113,7 +110,7 @@ test.describe('Testing segment overlays', () => {
|
||||
* @param {number} options.steps - The number of steps to perform
|
||||
*/
|
||||
const _clickUnconstrained =
|
||||
(page: Page, editor: EditorFixture) =>
|
||||
(page: Page, editor: EditorFixture, cmdBar: CmdBarFixture) =>
|
||||
async ({
|
||||
hoverPos,
|
||||
constraintType,
|
||||
@ -163,11 +160,7 @@ test.describe('Testing segment overlays', () => {
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.waitForTimeout(500)
|
||||
await page
|
||||
.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
.click()
|
||||
await cmdBar.continue()
|
||||
await editor.expectEditor.toContain(expectAfterUnconstrained, {
|
||||
shouldNormalise: true,
|
||||
})
|
||||
@ -239,8 +232,8 @@ test.describe('Testing segment overlays', () => {
|
||||
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(14)
|
||||
|
||||
const clickUnconstrained = _clickUnconstrained(page, editor)
|
||||
const clickConstrained = _clickConstrained(page, editor)
|
||||
const clickUnconstrained = _clickUnconstrained(page, editor, cmdBar)
|
||||
const clickConstrained = _clickConstrained(page, editor, cmdBar)
|
||||
|
||||
await u.openAndClearDebugPanel()
|
||||
await u.sendCustomCmd({
|
||||
@ -664,7 +657,7 @@ profile002 = circle(sketch001, center = [345, 0], radius = 238.38)
|
||||
await expect(
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||
await cmdBar.continue()
|
||||
|
||||
// Verify the X constraint was added
|
||||
await editor.expectEditor.toContain('center = [xAbs001, 0]', {
|
||||
@ -682,7 +675,7 @@ profile002 = circle(sketch001, center = [345, 0], radius = 238.38)
|
||||
await expect(
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||
await cmdBar.continue()
|
||||
|
||||
// Verify the Y constraint was added
|
||||
await editor.expectEditor.toContain('center = [xAbs001, yAbs001]', {
|
||||
@ -700,7 +693,7 @@ profile002 = circle(sketch001, center = [345, 0], radius = 238.38)
|
||||
await expect(
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||
await cmdBar.continue()
|
||||
|
||||
// Verify all constraints were added
|
||||
await editor.expectEditor.toContain(
|
||||
@ -887,7 +880,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
|
||||
await expect(
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||
await cmdBar.continue()
|
||||
|
||||
// Verify the constraint was added
|
||||
await editor.expectEditor.toContain(
|
||||
@ -910,7 +903,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
|
||||
await expect(
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||
await cmdBar.continue()
|
||||
|
||||
// Verify both constraints were added
|
||||
await editor.expectEditor.toContain(
|
||||
@ -935,7 +928,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
|
||||
await expect(
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||
await cmdBar.continue()
|
||||
|
||||
// Verify the constraint was added
|
||||
await editor.expectEditor.toContain('endAbsolute = [xAbs002, 84.07]', {
|
||||
@ -955,7 +948,7 @@ profile003 = startProfile(sketch001, at = [64.39, 35.16])
|
||||
await expect(
|
||||
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
|
||||
).toBeFocused()
|
||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||
await cmdBar.continue()
|
||||
|
||||
// Verify all constraints were added
|
||||
await editor.expectEditor.toContain(
|
||||
|
@ -32,7 +32,7 @@ test('Units menu', async ({ page, homePage }) => {
|
||||
test(
|
||||
'Successful export shows a success toast',
|
||||
{ tag: '@skipLocalEngine' },
|
||||
async ({ page, homePage, tronApp }) => {
|
||||
async ({ page, homePage, cmdBar, tronApp }) => {
|
||||
// FYI this test doesn't work with only engine running locally
|
||||
// And you will need to have the KittyCAD CLI installed
|
||||
const u = await getUtils(page)
|
||||
@ -94,7 +94,8 @@ part001 = startSketchOn(-XZ)
|
||||
presentation: 'pretty',
|
||||
},
|
||||
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 ({
|
||||
page,
|
||||
homePage,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
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({
|
||||
timeout: 20_000,
|
||||
})
|
||||
await page.getByRole('button', { name: 'Continue' }).click()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Submit command' })
|
||||
).toBeVisible()
|
||||
await page.getByRole('button', { name: 'Submit command' }).click()
|
||||
await cmdBar.continue()
|
||||
await cmdBar.submit()
|
||||
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 expect(page.getByText('Confirm Extrude')).toBeVisible()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.submit()
|
||||
|
||||
const result2 = result.genNext`
|
||||
const sketch002 = extrude(sketch002, length = ${[5, 5]} + 7)`
|
||||
|
@ -111,7 +111,8 @@ commaSep1NoTrailingComma<term> { term ("," term)* }
|
||||
|
||||
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? }
|
||||
PropertyName { identifier }
|
||||
TagDeclarator { "$" identifier }
|
||||
|
@ -42,15 +42,15 @@ fn helicalGear(nTeeth, module, pressureAngle, helixAngle, gearHeight) {
|
||||
helicalGearSketch = startSketchOn(offsetPlane(XY, offset = offsetHeight))
|
||||
|> startProfile(at = polar(angle = helixCalc, length = baseDiameter / 2))
|
||||
|> involuteCircular(
|
||||
startRadius = baseDiameter / 2,
|
||||
endRadius = tipDiameter / 2,
|
||||
startDiameter = baseDiameter,
|
||||
endDiameter = tipDiameter,
|
||||
angle = helixCalc,
|
||||
tag = $seg01,
|
||||
)
|
||||
|> line(endAbsolute = polar(angle = 160 / nTeeth + helixCalc, length = tipDiameter / 2))
|
||||
|> involuteCircular(
|
||||
startRadius = baseDiameter / 2,
|
||||
endRadius = tipDiameter / 2,
|
||||
startDiameter = baseDiameter,
|
||||
endDiameter = tipDiameter,
|
||||
angle = -(4 * atan(segEndY(seg01) / segEndX(seg01)) - (3 * helixCalc)),
|
||||
reverse = true,
|
||||
)
|
||||
|
553
rust/Cargo.lock
generated
@ -29,8 +29,8 @@ debug = "line-tables-only"
|
||||
[workspace.dependencies]
|
||||
async-trait = "0.1.88"
|
||||
anyhow = { version = "1" }
|
||||
bson = { version = "2.13.0", features = ["uuid-1", "chrono"] }
|
||||
clap = { version = "4.5.36", features = ["derive"] }
|
||||
bson = { version = "2.15.0", features = ["uuid-1", "chrono"] }
|
||||
clap = { version = "4.5.40", features = ["derive"] }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
dashmap = { version = "6.1.0" }
|
||||
http = "1"
|
||||
@ -38,8 +38,8 @@ indexmap = "2.9.0"
|
||||
kittycad = { version = "0.3.37", default-features = false, features = ["js", "requests"] }
|
||||
kittycad-modeling-cmds = { version = "0.2.123", features = ["ts-rs", "websocket"] }
|
||||
lazy_static = "1.5.0"
|
||||
miette = "7.5.0"
|
||||
pyo3 = { version = "0.24.1" }
|
||||
miette = "7.6.0"
|
||||
pyo3 = { version = "0.24.2" }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = { version = "1" }
|
||||
slog = "2.7.0"
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
[package]
|
||||
name = "kcl-bumper"
|
||||
version = "0.1.81"
|
||||
version = "0.1.82"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/KittyCAD/modeling-api"
|
||||
rust-version = "1.76"
|
||||
@ -19,7 +19,7 @@ anyhow = { workspace = true }
|
||||
clap = { workspace = true, features = ["derive"] }
|
||||
semver = "1.0.25"
|
||||
serde = { workspace = true }
|
||||
toml_edit = "0.22.16"
|
||||
toml_edit = "0.22.26"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-derive-docs"
|
||||
description = "A tool for generating documentation from Rust derive macros"
|
||||
version = "0.1.81"
|
||||
version = "0.1.82"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
@ -14,7 +14,7 @@ bench = false
|
||||
[dependencies]
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = { version = "2.0.96", features = ["full"] }
|
||||
syn = { version = "2.0.103", features = ["full"] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-directory-test-macro"
|
||||
description = "A tool for generating tests from a directory of kcl files"
|
||||
version = "0.1.81"
|
||||
version = "0.1.82"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
@ -14,7 +14,7 @@ bench = false
|
||||
convert_case = "0.8.0"
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = { version = "2.0.96", features = ["full"] }
|
||||
syn = { version = "2.0.103", features = ["full"] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "kcl-language-server-release"
|
||||
version = "0.1.81"
|
||||
version = "0.1.82"
|
||||
edition = "2021"
|
||||
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||
publish = false
|
||||
@ -14,7 +14,7 @@ bench = false
|
||||
[dependencies]
|
||||
anyhow = { workspace = true }
|
||||
clap = { workspace = true, features = ["cargo", "derive", "env", "unicode"] }
|
||||
flate2 = "1.1.1"
|
||||
flate2 = "1.1.2"
|
||||
lazy_static = { workspace = true }
|
||||
log = { version = "0.4.27", features = ["serde"] }
|
||||
slog = { workspace = true }
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "kcl-language-server"
|
||||
description = "A language server for KCL."
|
||||
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||
version = "0.2.81"
|
||||
version = "0.2.82"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
# 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 }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
signal-hook = "0.3.17"
|
||||
tokio = { version = "1.44.2", features = ["full"] }
|
||||
signal-hook = "0.3.18"
|
||||
tokio = { version = "1.45.1", features = ["full"] }
|
||||
tower-lsp = { version = "0.20.0", features = ["proposed"] }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-lib"
|
||||
description = "KittyCAD Language implementation and tools"
|
||||
version = "0.2.81"
|
||||
version = "0.2.82"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
@ -25,8 +25,8 @@ async-recursion = "1.1.1"
|
||||
async-trait = { workspace = true }
|
||||
base64 = "0.22.1"
|
||||
bson = { workspace = true }
|
||||
chrono = "0.4.38"
|
||||
clap = { version = "4.5.36", default-features = false, optional = true, features = [
|
||||
chrono = "0.4.41"
|
||||
clap = { version = "4.5.40", default-features = false, optional = true, features = [
|
||||
"std",
|
||||
"derive",
|
||||
] }
|
||||
@ -42,11 +42,12 @@ gltf-json = "1.4.1"
|
||||
http = { workspace = true }
|
||||
image = { version = "0.25.6", default-features = false, features = ["png"] }
|
||||
indexmap = { workspace = true, features = ["serde", "rayon"] }
|
||||
itertools = "0.13.0"
|
||||
itertools = "0.14.0"
|
||||
kcl-derive-docs = { version = "0.1", path = "../kcl-derive-docs" }
|
||||
kittycad = { workspace = true }
|
||||
kittycad-modeling-cmds = { workspace = true }
|
||||
lazy_static = { workspace = true }
|
||||
libm = "0.2.15"
|
||||
measurements = "0.11.0"
|
||||
miette = { workspace = true }
|
||||
mime_guess = "2.0.5"
|
||||
@ -69,11 +70,11 @@ schemars = { version = "0.8.17", features = [
|
||||
] }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
sha2 = "0.10.8"
|
||||
tabled = { version = "0.18.0", optional = true }
|
||||
tempfile = "3.19"
|
||||
sha2 = "0.10.9"
|
||||
tabled = { version = "0.20.0", optional = true }
|
||||
tempfile = "3.20"
|
||||
thiserror = "2.0.0"
|
||||
toml = "0.8.19"
|
||||
toml = "0.8.22"
|
||||
ts-rs = { version = "10.1.0", features = [
|
||||
"uuid-impl",
|
||||
"url-impl",
|
||||
@ -82,7 +83,7 @@ ts-rs = { version = "10.1.0", features = [
|
||||
"no-serde-warnings",
|
||||
"serde-json-impl",
|
||||
] }
|
||||
tynm = "0.1.10"
|
||||
tynm = "0.2.0"
|
||||
url = { version = "2.5.4", features = ["serde"] }
|
||||
uuid = { workspace = true, features = ["v4", "v5", "js", "serde"] }
|
||||
validator = { version = "0.20.0", features = ["derive"] }
|
||||
@ -94,7 +95,6 @@ zip = { workspace = true }
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
console_error_panic_hook = { workspace = true }
|
||||
futures-lite = "2.6.0"
|
||||
instant = { version = "0.1.13", features = ["wasm-bindgen", "inaccurate"] }
|
||||
js-sys = { version = "0.3.72" }
|
||||
tokio = { workspace = true, features = ["sync", "time"] }
|
||||
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"] }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
instant = "0.1.13"
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
tokio-tungstenite = { version = "0.26.2", features = [
|
||||
tokio-tungstenite = { version = "0.27.0", features = [
|
||||
"rustls-tls-native-roots",
|
||||
] }
|
||||
tower-lsp = { workspace = true, features = ["proposed", "default"] }
|
||||
@ -131,15 +130,15 @@ tabled = ["dep:tabled"]
|
||||
approx = "0.5"
|
||||
base64 = "0.22.1"
|
||||
criterion = { version = "0.6.0", features = ["async_tokio"] }
|
||||
expectorate = "1.1.0"
|
||||
expectorate = "1.2.0"
|
||||
handlebars = "6.3.2"
|
||||
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" }
|
||||
miette = { version = "7.5.0", features = ["fancy"] }
|
||||
miette = { version = "7.6.0", features = ["fancy"] }
|
||||
pretty_assertions = "1.4.1"
|
||||
tokio = { version = "1.44.2", features = ["rt-multi-thread", "macros", "time"] }
|
||||
twenty-twenty = "0.8.0"
|
||||
tokio = { version = "1.45.1", features = ["rt-multi-thread", "macros", "time"] }
|
||||
twenty-twenty = "0.8.2"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
@ -1 +1,19 @@
|
||||
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 tokio::sync::RwLock;
|
||||
use uuid::Uuid;
|
||||
use web_time::Instant;
|
||||
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
@ -241,7 +242,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||
.unwrap_or_default()
|
||||
};
|
||||
|
||||
let current_time = instant::Instant::now();
|
||||
let current_time = Instant::now();
|
||||
while current_time.elapsed().as_secs() < 60 {
|
||||
let responses = self.responses().read().await.clone();
|
||||
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.
|
||||
#[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| {
|
||||
KclError::new_internal(KclErrorDetails::new(
|
||||
format!("Failed to sleep: {:?}", err),
|
||||
|
@ -668,9 +668,8 @@ impl From<GeometryWithImportedGeometry> for KclValue {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::exec::UnitType;
|
||||
|
||||
use super::*;
|
||||
use crate::exec::UnitType;
|
||||
|
||||
#[test]
|
||||
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.
|
||||
/// Taken from <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence#table>
|
||||
pub fn associativity(&self) -> Associativity {
|
||||
@ -3015,6 +3017,12 @@ impl BinaryOperator {
|
||||
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)]
|
||||
#[ts(export)]
|
||||
|
@ -3334,7 +3334,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
parsing::ast::types::{BodyItem, Expr, VariableKind},
|
||||
KclError, ModuleId,
|
||||
ModuleId,
|
||||
};
|
||||
|
||||
fn assert_reserved(word: &str) {
|
||||
@ -4398,14 +4398,10 @@ secondExtrude = startSketchOn(XY)
|
||||
#[test]
|
||||
fn test_parse_parens_unicode() {
|
||||
let result = crate::parsing::top_level_parse("(ޜ");
|
||||
let KclError::Lexical { details } = result.0.unwrap_err() else {
|
||||
panic!();
|
||||
};
|
||||
// TODO: Better errors when program cannot tokenize.
|
||||
let details = result.0.unwrap().1.pop().unwrap();
|
||||
// TODO: Highlight where the unmatched open parenthesis is.
|
||||
// https://github.com/KittyCAD/modeling-app/issues/696
|
||||
assert_eq!(details.message, "found unknown token 'ޜ'");
|
||||
assert_eq!(details.source_ranges[0].start(), 1);
|
||||
assert_eq!(details.source_ranges[0].end(), 2);
|
||||
assert_eq!(details.message, "Unexpected end of file. The compiler expected )");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -6,7 +6,7 @@ use winnow::{
|
||||
error::{ContextError, ParseError},
|
||||
prelude::*,
|
||||
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,
|
||||
};
|
||||
|
||||
@ -163,8 +163,8 @@ fn whitespace(i: &mut Input<'_>) -> ModalResult<Token> {
|
||||
}
|
||||
|
||||
fn inner_word(i: &mut Input<'_>) -> ModalResult<()> {
|
||||
one_of(('a'..='z', 'A'..='Z', '_')).parse_next(i)?;
|
||||
repeat::<_, _, (), _, _>(0.., one_of(('a'..='z', 'A'..='Z', '0'..='9', '_'))).parse_next(i)?;
|
||||
take_while(1.., |c: char| c.is_alphabetic() || c == '_').parse_next(i)?;
|
||||
take_while(0.., |c: char| c.is_alphabetic() || c.is_ascii_digit() || c == '_').parse_next(i)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -786,6 +786,7 @@ const things = "things"
|
||||
};
|
||||
assert_eq!(actual.tokens[0], expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_word_starting_with_keyword() {
|
||||
let module_id = ModuleId::default();
|
||||
@ -799,4 +800,18 @@ const things = "things"
|
||||
};
|
||||
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 insta::rounded_redaction;
|
||||
|
||||
use crate::{
|
||||
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(|| {
|
||||
assert_snapshot(test, "Variables in memory after executing", || {
|
||||
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(),
|
||||
})
|
||||
})
|
||||
@ -346,11 +334,6 @@ fn assert_artifact_snapshots(
|
||||
let result1 = catch_unwind(AssertUnwindSafe(|| {
|
||||
assert_snapshot(test, "Operations executed", || {
|
||||
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(),
|
||||
".**.functionSourceRange" => Vec::new(),
|
||||
".**.moduleId" => 0,
|
||||
@ -364,10 +347,6 @@ fn assert_artifact_snapshots(
|
||||
let result2 = catch_unwind(AssertUnwindSafe(|| {
|
||||
assert_snapshot(test, "Artifact 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(),
|
||||
});
|
||||
})
|
||||
@ -3626,3 +3605,24 @@ mod user_reported_union_2_bug {
|
||||
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,
|
||||
};
|
||||
|
||||
use super::{args::TyF64, DEFAULT_TOLERANCE};
|
||||
use super::{args::TyF64, DEFAULT_TOLERANCE_MM};
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
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),
|
||||
ModelingCmd::from(mcmd::BooleanUnion {
|
||||
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?;
|
||||
@ -122,7 +122,7 @@ pub(crate) async fn inner_intersect(
|
||||
ModelingCmdMeta::from_args_id(&args, solid_out_id),
|
||||
ModelingCmd::from(mcmd::BooleanIntersection {
|
||||
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?;
|
||||
@ -186,7 +186,7 @@ pub(crate) async fn inner_subtract(
|
||||
ModelingCmd::from(mcmd::BooleanSubtract {
|
||||
target_ids: solids.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?;
|
||||
|
@ -18,7 +18,7 @@ use kittycad_modeling_cmds::{
|
||||
};
|
||||
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::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
@ -79,7 +79,7 @@ async fn inner_extrude(
|
||||
) -> Result<Vec<Solid>, KclError> {
|
||||
// Extrude the element(s).
|
||||
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() {
|
||||
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 serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{args::TyF64, DEFAULT_TOLERANCE};
|
||||
use super::{args::TyF64, DEFAULT_TOLERANCE_MM};
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
@ -122,7 +122,7 @@ async fn inner_fillet(
|
||||
strategy: Default::default(),
|
||||
object_id: solid.id,
|
||||
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,
|
||||
}),
|
||||
)
|
||||
|
@ -6,7 +6,7 @@ use anyhow::Result;
|
||||
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, ModelingCmd};
|
||||
use kittycad_modeling_cmds as kcmc;
|
||||
|
||||
use super::{args::TyF64, DEFAULT_TOLERANCE};
|
||||
use super::{args::TyF64, DEFAULT_TOLERANCE_MM};
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
@ -84,7 +84,7 @@ async fn inner_loft(
|
||||
section_ids: sketches.iter().map(|group| group.id).collect(),
|
||||
base_curve_index,
|
||||
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,
|
||||
}),
|
||||
)
|
||||
|
@ -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> {
|
||||
let num: TyF64 = args.get_unlabeled_kw_arg("input", &RuntimeType::angle(), exec_state)?;
|
||||
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).
|
||||
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 = 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).
|
||||
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 = 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.
|
||||
@ -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).
|
||||
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 result = input.n.acos();
|
||||
let result = libm::acos(input.n);
|
||||
|
||||
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).
|
||||
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 result = input.n.asin();
|
||||
let result = libm::asin(input.n);
|
||||
|
||||
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).
|
||||
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 result = input.n.atan();
|
||||
let result = libm::atan(input.n);
|
||||
|
||||
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 x = args.get_kw_arg("x", &RuntimeType::length(), exec_state)?;
|
||||
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())))
|
||||
}
|
||||
@ -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 leg: TyF64 = args.get_kw_arg("leg", &RuntimeType::length(), exec_state)?;
|
||||
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(
|
||||
result,
|
||||
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 leg: TyF64 = args.get_kw_arg("leg", &RuntimeType::length(), exec_state)?;
|
||||
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(
|
||||
result,
|
||||
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`].
|
||||
const DEFAULT_TOLERANCE: f64 = 0.0000001;
|
||||
/// The default tolerance for modeling commands in millimeters.
|
||||
const DEFAULT_TOLERANCE_MM: f64 = 0.0000001;
|
||||
|
@ -9,7 +9,7 @@ use kcmc::{
|
||||
};
|
||||
use kittycad_modeling_cmds::{self as kcmc, shared::Point3d};
|
||||
|
||||
use super::{args::TyF64, DEFAULT_TOLERANCE};
|
||||
use super::{args::TyF64, DEFAULT_TOLERANCE_MM};
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
@ -133,7 +133,7 @@ async fn inner_revolve(
|
||||
let mut solids = Vec::new();
|
||||
for sketch in &sketches {
|
||||
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 {
|
||||
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.
|
||||
// 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[0] - tan_previous_point[0],
|
||||
));
|
||||
|
@ -305,7 +305,7 @@ async fn inner_polygon(
|
||||
radius.n
|
||||
} else {
|
||||
// circumscribed
|
||||
radius.n / half_angle.cos()
|
||||
radius.n / libm::cos(half_angle)
|
||||
};
|
||||
|
||||
let angle_step = std::f64::consts::TAU / num_sides as f64;
|
||||
@ -316,8 +316,8 @@ async fn inner_polygon(
|
||||
.map(|i| {
|
||||
let angle = angle_step * i as f64;
|
||||
[
|
||||
center_u[0] + radius_to_vertices * angle.cos(),
|
||||
center_u[1] + radius_to_vertices * angle.sin(),
|
||||
center_u[0] + radius_to_vertices * libm::cos(angle),
|
||||
center_u[1] + radius_to_vertices * libm::sin(angle),
|
||||
]
|
||||
})
|
||||
.collect();
|
||||
@ -415,16 +415,26 @@ pub(crate) fn get_radius(
|
||||
radius: Option<TyF64>,
|
||||
diameter: Option<TyF64>,
|
||||
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> {
|
||||
match (radius, diameter) {
|
||||
(Some(radius), None) => Ok(radius),
|
||||
(None, Some(diameter)) => Ok(TyF64::new(diameter.n / 2.0, diameter.ty)),
|
||||
(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],
|
||||
))),
|
||||
(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],
|
||||
))),
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ use parse_display::{Display, FromStr};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::shapes::get_radius;
|
||||
use super::shapes::{get_radius, get_radius_labelled};
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
use crate::execution::{Artifact, ArtifactId, CodeRef, StartSketchOnFace, StartSketchOnPlane};
|
||||
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> {
|
||||
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 end_radius: TyF64 = args.get_kw_arg("endRadius", &RuntimeType::length(), exec_state)?;
|
||||
let start_radius: Option<TyF64> = args.get_kw_arg_opt("startRadius", &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 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 new_sketch =
|
||||
inner_involute_circular(sketch, start_radius, end_radius, angle, reverse, tag, exec_state, args).await?;
|
||||
let new_sketch = inner_involute_circular(
|
||||
sketch,
|
||||
start_radius,
|
||||
end_radius,
|
||||
start_diameter,
|
||||
end_diameter,
|
||||
angle,
|
||||
reverse,
|
||||
tag,
|
||||
exec_state,
|
||||
args,
|
||||
)
|
||||
.await?;
|
||||
Ok(KclValue::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) {
|
||||
(
|
||||
radius * (angle.cos() + angle * angle.sin()),
|
||||
radius * (angle.sin() - angle * angle.cos()),
|
||||
radius * (libm::cos(angle) + angle * libm::sin(angle)),
|
||||
radius * (libm::sin(angle) - angle * libm::cos(angle)),
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn inner_involute_circular(
|
||||
sketch: Sketch,
|
||||
start_radius: TyF64,
|
||||
end_radius: TyF64,
|
||||
start_radius: Option<TyF64>,
|
||||
end_radius: Option<TyF64>,
|
||||
start_diameter: Option<TyF64>,
|
||||
end_diameter: Option<TyF64>,
|
||||
angle: TyF64,
|
||||
reverse: Option<bool>,
|
||||
tag: Option<TagNode>,
|
||||
@ -133,6 +148,22 @@ async fn inner_involute_circular(
|
||||
) -> Result<Sketch, KclError> {
|
||||
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
|
||||
.batch_modeling_cmd(
|
||||
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 (x, y) = involute_curve(start_radius, theta);
|
||||
|
||||
end.x = x * angle.to_radians().cos() - y * angle.to_radians().sin();
|
||||
end.y = x * angle.to_radians().sin() + y * angle.to_radians().cos();
|
||||
end.x = x * libm::cos(angle.to_radians()) - y * libm::sin(angle.to_radians());
|
||||
end.y = x * libm::sin(angle.to_radians()) + y * libm::cos(angle.to_radians());
|
||||
|
||||
end.x -= start_radius * angle.to_radians().cos();
|
||||
end.y -= start_radius * angle.to_radians().sin();
|
||||
end.x -= start_radius * libm::cos(angle.to_radians());
|
||||
end.y -= start_radius * libm::sin(angle.to_radians());
|
||||
|
||||
if reverse.unwrap_or_default() {
|
||||
end.x = -end.x;
|
||||
@ -500,8 +531,8 @@ async fn inner_angled_line_length(
|
||||
|
||||
//double check me on this one - mike
|
||||
let delta: [f64; 2] = [
|
||||
length * f64::cos(angle_degrees.to_radians()),
|
||||
length * f64::sin(angle_degrees.to_radians()),
|
||||
length * libm::cos(angle_degrees.to_radians()),
|
||||
length * libm::sin(angle_degrees.to_radians()),
|
||||
];
|
||||
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 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 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 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 new_sketch = straight_line(
|
||||
@ -1413,7 +1444,7 @@ async fn inner_tangential_arc_radius_angle(
|
||||
|
||||
// Calculate the end point from the angle and radius.
|
||||
// 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.x - tan_previous_point[0],
|
||||
));
|
||||
|
@ -6,7 +6,7 @@ use kittycad_modeling_cmds::{self as kcmc, shared::RelativeTo};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Serialize;
|
||||
|
||||
use super::{args::TyF64, DEFAULT_TOLERANCE};
|
||||
use super::{args::TyF64, DEFAULT_TOLERANCE_MM};
|
||||
use crate::{
|
||||
errors::KclError,
|
||||
execution::{
|
||||
@ -93,7 +93,7 @@ async fn inner_sweep(
|
||||
target: sketch.id.into(),
|
||||
trajectory,
|
||||
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,
|
||||
}),
|
||||
)
|
||||
|
@ -44,7 +44,7 @@ pub(crate) fn distance(a: Coords2d, b: Coords2d) -> f64 {
|
||||
pub(crate) fn between(a: Coords2d, b: Coords2d) -> Angle {
|
||||
let x = b[0] - a[0];
|
||||
let y = b[1] - a[1];
|
||||
normalize(Angle::from_radians(y.atan2(x)))
|
||||
normalize(Angle::from_radians(libm::atan2(y, x)))
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
let line2_point_b = [
|
||||
line2_point[0] + f64::cos(line2_angle.to_radians()) * 10.0,
|
||||
line2_point[1] + f64::sin(line2_angle.to_radians()) * 10.0,
|
||||
line2_point[0] + libm::cos(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)
|
||||
}
|
||||
@ -138,13 +138,13 @@ fn offset_line(offset: f64, p1: Coords2d, p2: Coords2d) -> [Coords2d; 2] {
|
||||
let direction = (p2[0] - p1[0]).signum();
|
||||
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]]]
|
||||
}
|
||||
|
||||
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 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 {
|
||||
-1.0
|
||||
} 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 {
|
||||
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 {
|
||||
-1.0
|
||||
} else {
|
||||
@ -174,13 +174,13 @@ pub(crate) fn arc_center_and_end(
|
||||
let end_angle = end_angle.to_radians();
|
||||
|
||||
let center = [
|
||||
-1.0 * (radius * start_angle.cos() - from[0]),
|
||||
-1.0 * (radius * start_angle.sin() - from[1]),
|
||||
-1.0 * (radius * libm::cos(start_angle) - from[0]),
|
||||
-1.0 * (radius * libm::sin(start_angle) - from[1]),
|
||||
];
|
||||
|
||||
let end = [
|
||||
center[0] + radius * end_angle.cos(),
|
||||
center[1] + radius * end_angle.sin(),
|
||||
center[0] + radius * libm::cos(end_angle),
|
||||
center[1] + radius * libm::sin(end_angle),
|
||||
];
|
||||
|
||||
(center, end)
|
||||
@ -357,7 +357,10 @@ mod tests {
|
||||
|
||||
let get_point = |radius: f64, t: f64| {
|
||||
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 {
|
||||
@ -442,7 +445,7 @@ fn get_slope(start: Coords2d, end: Coords2d) -> (f64, f64) {
|
||||
fn get_angle(point1: Coords2d, point2: Coords2d) -> f64 {
|
||||
let delta_x = point2[0] - point1[0];
|
||||
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 };
|
||||
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 shortest_arc_mid_point: Coords2d = [
|
||||
delta_ang.cos() * radius + center[0],
|
||||
delta_ang.sin() * radius + center[1],
|
||||
libm::cos(delta_ang) * radius + center[0],
|
||||
libm::sin(delta_ang) * radius + center[1],
|
||||
];
|
||||
let opposite_delta = delta_ang + PI;
|
||||
let longest_arc_mid_point: Coords2d = [
|
||||
opposite_delta.cos() * radius + center[0],
|
||||
opposite_delta.sin() * radius + center[1],
|
||||
libm::cos(opposite_delta) * radius + center[0],
|
||||
libm::sin(opposite_delta) * radius + center[1],
|
||||
];
|
||||
|
||||
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,
|
||||
);
|
||||
|
||||
let start_angle = (input.arc_start_point[1] - center[1]).atan2(input.arc_start_point[0] - center[0]);
|
||||
let end_angle = (input.arc_end_point[1] - center[1]).atan2(input.arc_end_point[0] - center[0]);
|
||||
let start_angle = libm::atan2(
|
||||
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 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
|
||||
* delta(Angle::from_radians(start_angle), Angle::from_radians(arc_mid_angle))
|
||||
.to_radians()
|
||||
@ -724,7 +730,7 @@ mod get_tangential_arc_to_info_tests {
|
||||
|
||||
#[test]
|
||||
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 {
|
||||
tan_previous_point: [2.0, -4.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 };
|
||||
// What is the 10.0 constant doing???
|
||||
[
|
||||
tangential_angle.to_radians().cos() * 10.0 + last_arc_end[0],
|
||||
tangential_angle.to_radians().sin() * 10.0 + last_arc_end[1],
|
||||
libm::cos(tangential_angle.to_radians()) * 10.0 + last_arc_end[0],
|
||||
libm::sin(tangential_angle.to_radians()) * 10.0 + last_arc_end[1],
|
||||
]
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ use std::fmt::Write;
|
||||
use crate::{
|
||||
parsing::{
|
||||
ast::types::{
|
||||
Annotation, ArrayExpression, ArrayRangeExpression, AscribedExpression, BinaryExpression, BinaryOperator,
|
||||
BinaryPart, BodyItem, CallExpressionKw, CommentStyle, DefaultParamVal, Expr, FormatOptions,
|
||||
Annotation, ArrayExpression, ArrayRangeExpression, AscribedExpression, Associativity, BinaryExpression,
|
||||
BinaryOperator, BinaryPart, BodyItem, CallExpressionKw, CommentStyle, DefaultParamVal, Expr, FormatOptions,
|
||||
FunctionExpression, IfExpression, ImportSelector, ImportStatement, ItemVisibility, LabeledArg, Literal,
|
||||
LiteralIdentifier, LiteralValue, MemberExpression, Node, NonCodeNode, NonCodeValue, ObjectExpression,
|
||||
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) => {
|
||||
self.precedence() > bin_exp.precedence()
|
||||
|| 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::Right))
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let should_wrap_left = match &self.left {
|
||||
BinaryPart::BinaryExpression(bin_exp) => self.precedence() > bin_exp.precedence(),
|
||||
let should_wrap_right = match &self.right {
|
||||
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,
|
||||
};
|
||||
|
||||
@ -2820,4 +2831,36 @@ yo = 'bing'
|
||||
let recasted = ast.recast(&FormatOptions::new(), 0);
|
||||
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,
|
||||
/// Angle to revolve (in degrees). Default is 360.
|
||||
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),
|
||||
/// If true, the extrusion will happen symmetrically around the sketch. Otherwise, the extrusion will happen on only one side of the sketch.
|
||||
symmetric?: bool,
|
||||
@ -961,7 +961,7 @@ export fn sweep(
|
||||
path: Sketch | Helix,
|
||||
/// If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components.
|
||||
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),
|
||||
/// What is the sweep relative to? Can be either 'sketchPlane' or 'trajectoryCurve'.
|
||||
relativeTo?: string = 'trajectoryCurve',
|
||||
@ -1047,7 +1047,7 @@ export fn loft(
|
||||
bezApproximateRational?: bool = false,
|
||||
/// This can be set to override the automatically determined topological base curve, which is usually the first section encountered.
|
||||
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),
|
||||
/// A named tag for the face at the start of the loft, i.e. the original sketch.
|
||||
tagStart?: TagDecl,
|
||||
@ -1499,12 +1499,20 @@ export fn profileStartY(
|
||||
export fn involuteCircular(
|
||||
/// Which sketch should this path be added to?
|
||||
@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.
|
||||
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.
|
||||
reverse?: bool = false,
|
||||
/// Create a new tag which refers to this line.
|
||||
|
@ -70,7 +70,7 @@ export fn fillet(
|
||||
radius: number(Length),
|
||||
/// The paths you want to fillet
|
||||
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),
|
||||
/// Create a new tag which refers to this fillet
|
||||
tag?: TagDecl,
|
||||
@ -799,7 +799,7 @@ export fn patternCircular3d(
|
||||
export fn union(
|
||||
/// The solids to union.
|
||||
@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),
|
||||
): [Solid; 1+] {}
|
||||
|
||||
@ -857,7 +857,7 @@ export fn union(
|
||||
export fn intersect(
|
||||
/// The solids to intersect.
|
||||
@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),
|
||||
): [Solid; 1+] {}
|
||||
|
||||
@ -917,7 +917,7 @@ export fn subtract(
|
||||
@solids: [Solid; 1+],
|
||||
/// The solids to subtract.
|
||||
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),
|
||||
): [Solid; 1+] {}
|
||||
|
||||
|
@ -7239,7 +7239,7 @@ description: Operations executed add_lots.kcl
|
||||
"name": "PI",
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 3.142,
|
||||
"value": 3.141592653589793,
|
||||
"ty": {
|
||||
"type": "Unknown"
|
||||
}
|
||||
@ -7255,7 +7255,7 @@ description: Operations executed add_lots.kcl
|
||||
"name": "E",
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 2.718,
|
||||
"value": 2.718281828459045,
|
||||
"ty": {
|
||||
"type": "Known",
|
||||
"type": "Count"
|
||||
@ -7272,7 +7272,7 @@ description: Operations executed add_lots.kcl
|
||||
"name": "TAU",
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 6.283,
|
||||
"value": 6.283185307179586,
|
||||
"ty": {
|
||||
"type": "Known",
|
||||
"type": "Count"
|
||||
|
@ -132,8 +132,8 @@ description: Artifact commands angled_line.kcl
|
||||
"segment": {
|
||||
"type": "line",
|
||||
"end": {
|
||||
"x": 3.762,
|
||||
"y": -11.763,
|
||||
"x": 3.761813572028026,
|
||||
"y": -11.763131328405109,
|
||||
"z": 0.0
|
||||
},
|
||||
"relative": true
|
||||
|
@ -94,7 +94,7 @@ description: Operations executed angled_line.kcl
|
||||
"name": "PI",
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 3.142,
|
||||
"value": 3.141592653589793,
|
||||
"ty": {
|
||||
"type": "Unknown"
|
||||
}
|
||||
@ -110,7 +110,7 @@ description: Operations executed angled_line.kcl
|
||||
"name": "E",
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 2.718,
|
||||
"value": 2.718281828459045,
|
||||
"ty": {
|
||||
"type": "Known",
|
||||
"type": "Count"
|
||||
@ -127,7 +127,7 @@ description: Operations executed angled_line.kcl
|
||||
"name": "TAU",
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 6.283,
|
||||
"value": 6.283185307179586,
|
||||
"ty": {
|
||||
"type": "Known",
|
||||
"type": "Count"
|
||||
|
@ -75,7 +75,7 @@ description: Variables in memory after executing angled_line.kcl
|
||||
"tag": null,
|
||||
"to": [
|
||||
19.93,
|
||||
15.04
|
||||
15.040000000000001
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
@ -89,7 +89,7 @@ description: Variables in memory after executing angled_line.kcl
|
||||
},
|
||||
"from": [
|
||||
19.93,
|
||||
15.04
|
||||
15.040000000000001
|
||||
],
|
||||
"tag": {
|
||||
"commentStart": 133,
|
||||
@ -100,7 +100,7 @@ description: Variables in memory after executing angled_line.kcl
|
||||
},
|
||||
"to": [
|
||||
23.08,
|
||||
5.19
|
||||
5.190000000000001
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
@ -114,12 +114,12 @@ description: Variables in memory after executing angled_line.kcl
|
||||
},
|
||||
"from": [
|
||||
23.08,
|
||||
5.19
|
||||
5.190000000000001
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
7.91,
|
||||
1.09
|
||||
7.909999999999998,
|
||||
1.0900000000000016
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
@ -132,13 +132,13 @@ description: Variables in memory after executing angled_line.kcl
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
7.91,
|
||||
1.09
|
||||
7.909999999999998,
|
||||
1.0900000000000016
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
11.672,
|
||||
-10.673
|
||||
11.671813572028025,
|
||||
-10.673131328405107
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
@ -151,13 +151,13 @@ description: Variables in memory after executing angled_line.kcl
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
11.672,
|
||||
-10.673
|
||||
11.671813572028025,
|
||||
-10.673131328405107
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
-1.348,
|
||||
-0.643
|
||||
-1.3481864279719744,
|
||||
-0.6431313284051079
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
@ -170,8 +170,8 @@ description: Variables in memory after executing angled_line.kcl
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
-1.348,
|
||||
-0.643
|
||||
-1.3481864279719744,
|
||||
-0.6431313284051079
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
|
@ -398,7 +398,7 @@ description: Operations executed any_type.kcl
|
||||
"name": "PI",
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 3.142,
|
||||
"value": 3.141592653589793,
|
||||
"ty": {
|
||||
"type": "Unknown"
|
||||
}
|
||||
@ -414,7 +414,7 @@ description: Operations executed any_type.kcl
|
||||
"name": "E",
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 2.718,
|
||||
"value": 2.718281828459045,
|
||||
"ty": {
|
||||
"type": "Known",
|
||||
"type": "Count"
|
||||
@ -431,7 +431,7 @@ description: Operations executed any_type.kcl
|
||||
"name": "TAU",
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 6.283,
|
||||
"value": 6.283185307179586,
|
||||
"ty": {
|
||||
"type": "Known",
|
||||
"type": "Count"
|
||||
|
@ -12,7 +12,7 @@ description: Operations executed argument_error.kcl
|
||||
"name": "PI",
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 3.142,
|
||||
"value": 3.141592653589793,
|
||||
"ty": {
|
||||
"type": "Unknown"
|
||||
}
|
||||
@ -28,7 +28,7 @@ description: Operations executed argument_error.kcl
|
||||
"name": "E",
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 2.718,
|
||||
"value": 2.718281828459045,
|
||||
"ty": {
|
||||
"type": "Known",
|
||||
"type": "Count"
|
||||
@ -45,7 +45,7 @@ description: Operations executed argument_error.kcl
|
||||
"name": "TAU",
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 6.283,
|
||||
"value": 6.283185307179586,
|
||||
"ty": {
|
||||
"type": "Known",
|
||||
"type": "Count"
|
||||
|
@ -12,7 +12,7 @@ description: Operations executed array_elem_pop.kcl
|
||||
"name": "PI",
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 3.142,
|
||||
"value": 3.141592653589793,
|
||||
"ty": {
|
||||
"type": "Unknown"
|
||||
}
|
||||
@ -28,7 +28,7 @@ description: Operations executed array_elem_pop.kcl
|
||||
"name": "E",
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 2.718,
|
||||
"value": 2.718281828459045,
|
||||
"ty": {
|
||||
"type": "Known",
|
||||
"type": "Count"
|
||||
@ -45,7 +45,7 @@ description: Operations executed array_elem_pop.kcl
|
||||
"name": "TAU",
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 6.283,
|
||||
"value": 6.283185307179586,
|
||||
"ty": {
|
||||
"type": "Known",
|
||||
"type": "Count"
|
||||
|
@ -12,7 +12,7 @@ description: Operations executed array_elem_pop_empty_fail.kcl
|
||||
"name": "PI",
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 3.142,
|
||||
"value": 3.141592653589793,
|
||||
"ty": {
|
||||
"type": "Unknown"
|
||||
}
|
||||
@ -28,7 +28,7 @@ description: Operations executed array_elem_pop_empty_fail.kcl
|
||||
"name": "E",
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 2.718,
|
||||
"value": 2.718281828459045,
|
||||
"ty": {
|
||||
"type": "Known",
|
||||
"type": "Count"
|
||||
@ -45,7 +45,7 @@ description: Operations executed array_elem_pop_empty_fail.kcl
|
||||
"name": "TAU",
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 6.283,
|
||||
"value": 6.283185307179586,
|
||||
"ty": {
|
||||
"type": "Known",
|
||||
"type": "Count"
|
||||
|
@ -12,7 +12,7 @@ description: Operations executed array_elem_pop_fail.kcl
|
||||
"name": "PI",
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 3.142,
|
||||
"value": 3.141592653589793,
|
||||
"ty": {
|
||||
"type": "Unknown"
|
||||
}
|
||||
@ -28,7 +28,7 @@ description: Operations executed array_elem_pop_fail.kcl
|
||||
"name": "E",
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 2.718,
|
||||
"value": 2.718281828459045,
|
||||
"ty": {
|
||||
"type": "Known",
|
||||
"type": "Count"
|
||||
@ -45,7 +45,7 @@ description: Operations executed array_elem_pop_fail.kcl
|
||||
"name": "TAU",
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 6.283,
|
||||
"value": 6.283185307179586,
|
||||
"ty": {
|
||||
"type": "Known",
|
||||
"type": "Count"
|
||||
|