Compare commits

..

27 Commits

Author SHA1 Message Date
083103144a add test
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2025-05-20 11:04:53 -07:00
1dafbf105e Identify distinct test suites (#7109) 2025-05-20 12:56:55 -04:00
773f013115 [Fix]: When loading the modeling page the user's settings for camera projection was ignored (#7111)
* fix: initialization of user camera projection is now used again, ope

* fix: removing comment
2025-05-20 12:46:53 -04:00
c5cd460595 Show error when trying to export at non-top-level (#7110) 2025-05-20 12:43:11 -04:00
845352046b Modeling machine unit tests (#7098)
* successfully transition to sketch idle

* get constraint to mod code

* clean up

* remove .only

* Fixed tsc

---------

Co-authored-by: lee-at-zoo-corp <lee@zoo.dev>
2025-05-20 12:22:52 -04:00
597f1087f9 Fix enter key loop in sweep commands (#7112)
* use effect for focus of command palette submit button, not autoFocus

autoFocus is being overridden by the Headless UI Dialog component's
focus management here
https://headlessui.com/v1/react/dialog#focus-management (we do not have
access to pass back initialFocus in this case). So we can use an effect
to imperatively focus the button when this component is mounted.

* Update sweep tests to submit the command with Enter
2025-05-20 16:04:56 +00:00
511334683a test: Add regression test for importing only at the top level (#7104)
Add regression test for importing only at the top level
2025-05-20 11:43:48 -04:00
223a4ad45d Style the experimental badge to match the website (#7100)
* Style the experimental badge to match the website

* Match the styling of the rest of the app

We don't use all apps monospace fonts anywhere.

* Bring back all caps
2025-05-20 14:57:53 +00:00
edf31ec1d3 Make the textarea command bar input run its resize initially (#7103)
We have a hook to auto-grow the textarea input but it wasn't running
once initially. This is noticable on the onboarding, where we show the
user a long Text-to-CAD Edit prompt that overflows.
2025-05-20 10:56:03 -04:00
1539557005 Always update snapshots if needed (#7105) 2025-05-20 14:34:26 +00:00
1d3ba4e3ac [Fix]: Remove console logs (#7102)
* fix: these got in when they should not have

* fix: spacemacs found wrong eslint again biome thing..
2025-05-20 14:12:32 +00:00
4110aa00db Only update snapshots for tests that repeatedly fail (#7101)
* Only update snapshots for tests that repeatedly fail

* Let TAB silence snapshot capture failures as well
2025-05-20 14:00:33 +00:00
7eb52cda36 Fix: creating a dir ending with .kcl panics the app (#7099)
* Fix: creating a dir ending with .kcl panics the app
Fixes #7082

* Update snapshots

* Update snapshots

* Add test

* tag: ['@electron', '@macos', '@windows']

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-05-20 13:53:26 +00:00
7872fb9cbd Update snapshots on CI (#7069) 2025-05-20 06:00:31 -04:00
651181e62c Restrict subdirectory imports to main.kcl (#7094)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-05-20 18:13:17 +12:00
max
38a245f2fc fix typos in the kcl samples (#7078)
typos
2025-05-20 05:47:33 +00:00
1b4289f93f allow nested files imported (#7090)
* allow nested files

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix test

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* disallow bad things

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* add playwright test on windows

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* add playwright test on windows

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix test

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* Update rust/kcl-lib/tests/nested_windows_main_kcl/unparsed@main.kcl.snap

Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
2025-05-19 22:42:25 -04:00
d0697c24fd Change Sketch to use the units of the module (#7076)
* Change Sketch to use the units of the module

* Update output
2025-05-19 20:20:47 -04:00
8c24e29081 [Fix]: P2E base path is always the project directory, P2E when completed stays in your current file (#7091)
* fix: fixes for p2e

* fix: yep tsc fixes

* fix: fixing reject workflow and navigate
2025-05-19 20:05:38 -04:00
2b9d26e2ff Make Text-to-CAD Edit the default workflow in the toolbar (#7092)
We want users to make edits first and foremost within projects, so we're
going to surface it as the default workflow button in the toolbar. WIP
until I verify that tests are okay with this.
2025-05-19 19:58:18 -04:00
ab148a7654 Fix: Esc key doesn't work in Text-to-CAD prompt (#7089)
* Fix: Esc key doesn't work in Text-to-CAD prompt
Fixes #7086

* Add e2e test because why not
2025-05-19 23:31:33 +00:00
553e650fbe Add brake disc to samples. (#7059)
* Add brake disc.

* Update kcl-samples simulation test output

* Update public/kcl-samples/brake-rotor/main.kcl

Co-authored-by: Adam Chalmers <adam.chalmers@zoo.dev>

* Update public/kcl-samples/brake-rotor/main.kcl

Co-authored-by: Adam Chalmers <adam.chalmers@zoo.dev>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Adam Chalmers <adam.chalmers@zoo.dev>
Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
Co-authored-by: Jess Frazelle <github@jessfraz.com>
2025-05-19 15:24:37 -07:00
9690a24c68 Avoid a spin-lock when auth is never resolved (#7084) 2025-05-19 18:16:07 -04:00
978d5d44a2 rotate a named axis (#7087)
updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2025-05-19 22:11:35 +00:00
9df476543a turn on the revolve test (#7075)
* turn on the revolve test

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2025-05-19 14:51:44 -07:00
cf303ebe97 Declare pattern transform functions in KCL (#7057)
* Declare pattern transform using KCL

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Boolean function param defaults

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Parse empty record types in fn types

Signed-off-by: Nick Cameron <nrc@ncameron.org>

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-05-20 08:25:29 +12:00
b1d1d89ca5 Include link to the new book (#7056)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-05-19 14:49:23 -05:00
184 changed files with 37308 additions and 19845 deletions

View File

@ -6,6 +6,7 @@ if [ -z "${TAB_API_URL:-}" ] || [ -z "${TAB_API_KEY:-}" ]; then
fi fi
project="https://github.com/KittyCAD/modeling-app" project="https://github.com/KittyCAD/modeling-app"
suite="${CI_SUITE:-unit}"
branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME:-}}" branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME:-}}"
commit="${CI_COMMIT_SHA:-${GITHUB_SHA:-}}" commit="${CI_COMMIT_SHA:-${GITHUB_SHA:-}}"
@ -13,6 +14,7 @@ echo "Uploading batch results"
curl --silent --request POST \ curl --silent --request POST \
--header "X-API-Key: ${TAB_API_KEY}" \ --header "X-API-Key: ${TAB_API_KEY}" \
--form "project=${project}" \ --form "project=${project}" \
--form "suite=${suite}" \
--form "branch=${branch}" \ --form "branch=${branch}" \
--form "commit=${commit}" \ --form "commit=${commit}" \
--form "tests=@test-results/junit.xml" \ --form "tests=@test-results/junit.xml" \

View File

@ -193,6 +193,7 @@ jobs:
TAB_API_KEY: ${{ secrets.TAB_API_KEY }} TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }} CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
CI_PR_NUMBER: ${{ github.event.pull_request.number }} CI_PR_NUMBER: ${{ github.event.pull_request.number }}
CI_SUITE: unit:kcl
run-internal-kcl-samples: run-internal-kcl-samples:
name: cargo test (internal-kcl-samples) name: cargo test (internal-kcl-samples)
runs-on: runs-on:

View File

@ -143,7 +143,7 @@ jobs:
- name: Install browsers - name: Install browsers
run: npm run playwright install --with-deps run: npm run playwright install --with-deps
- name: Capture snapshots - name: Test snapshots
uses: nick-fields/retry@v3.0.2 uses: nick-fields/retry@v3.0.2
with: with:
shell: bash shell: bash
@ -156,6 +156,19 @@ jobs:
TAB_API_KEY: ${{ secrets.TAB_API_KEY }} TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }} CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
CI_PR_NUMBER: ${{ github.event.pull_request.number }} CI_PR_NUMBER: ${{ github.event.pull_request.number }}
CI_SUITE: snapshots
TARGET: web
- name: Update snapshots
if: always()
run: npm run test:snapshots -- --last-failed --update-snapshots
env:
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
TAB_API_URL: ${{ secrets.TAB_API_URL }}
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
CI_PR_NUMBER: ${{ github.event.pull_request.number }}
CI_SUITE: snapshots
TARGET: web TARGET: web
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
@ -173,7 +186,7 @@ jobs:
id: git-check id: git-check
run: | run: |
git add e2e/playwright/snapshot-tests.spec.ts-snapshots e2e/playwright/snapshots git add e2e/playwright/snapshot-tests.spec.ts-snapshots e2e/playwright/snapshots
if git status | grep -q "Changes to be committed" if git status | grep --quiet "Changes to be committed"
then echo "modified=true" >> $GITHUB_OUTPUT then echo "modified=true" >> $GITHUB_OUTPUT
else echo "modified=false" >> $GITHUB_OUTPUT else echo "modified=false" >> $GITHUB_OUTPUT
fi fi

View File

@ -4,7 +4,7 @@ excerpt: "Documentation of the KCL language for the Zoo Design Studio."
layout: manual layout: manual
--- ---
This is a reference for KCL. If you are learning KCL, you may prefer the [guide]() which explains This is a reference for KCL. If you are learning KCL, you may prefer the [guide](https://zoo.dev/docs/kcl-book/intro.html) which explains
things in a more tutorial fashion. See also our documentation of the [standard library](/docs/kcl-std). things in a more tutorial fashion. See also our documentation of the [standard library](/docs/kcl-std).
## Topics ## Topics

View File

@ -27,9 +27,6 @@ import increment from "util.kcl"
answer = increment(41) answer = increment(41)
``` ```
Imported files _must_ be in the same project so that units are uniform across
modules. This means that it must be in the same directory.
Import statements must be at the top-level of a file. It is not allowed to have Import statements must be at the top-level of a file. It is not allowed to have
an `import` statement inside a function or in the body of an ifelse. an `import` statement inside a function or in the body of an ifelse.
@ -58,6 +55,9 @@ Imported symbols can be renamed for convenience or to avoid name collisions.
import increment as inc, decrement as dec from "util.kcl" import increment as inc, decrement as dec from "util.kcl"
``` ```
You can import files from the current directory or from subdirectories, but if importing from a
subdirectory you can only import `main.kcl`.
--- ---
## Functions vs `clone` ## Functions vs `clone`
@ -229,6 +229,19 @@ The final statement is what's important because it's the return value of the
entire module. The module is expected to return a single object that can be used entire module. The module is expected to return a single object that can be used
as a variable by the file that imports it. as a variable by the file that imports it.
The name of the file or subdirectory is used as the name of the variable within the importing program.
If you want to use a different name, you can do so by using the `as` keyword:
```kcl,norun
import "cube.kcl" // Introduces a new variable called `cube`.
import "cube.kcl" as block // Introduces a new variable called `block`.
import "cube/main.kcl" // Introduces a new variable called `cube`.
import "cube/main.kcl" as block // Introduces a new variable called `block`.
```
If the filename includes hyphens (`-`) or starts with an underscore (`_`), then you must specify a
variable name.
--- ---
## Multiple instances of the same import ## Multiple instances of the same import

View File

@ -1,19 +1,19 @@
--- ---
title: "patternTransform2d" title: "patternTransform2d"
subtitle: "Function in std::sketch" subtitle: "Function in std::sketch"
excerpt: "Just like patternTransform, but works on 2D sketches not 3D solids." excerpt: "Just like `patternTransform`, but works on 2D sketches not 3D solids."
layout: manual layout: manual
--- ---
Just like patternTransform, but works on 2D sketches not 3D solids. Just like `patternTransform`, but works on 2D sketches not 3D solids.
```kcl ```kcl
patternTransform2d( patternTransform2d(
@sketches: [Sketch], @sketches: [Sketch; 1+],
instances: number, instances: number(_),
transform: FunctionSource, transform: fn(number(_)): { },
useOriginal?: bool, useOriginal?: boolean,
): [Sketch] ): [Sketch; 1+]
``` ```
@ -22,14 +22,14 @@ patternTransform2d(
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `sketches` | [`[Sketch]`](/docs/kcl-std/types/std-types-Sketch) | The sketch(es) to duplicate | Yes | | `sketches` | [`[Sketch; 1+]`](/docs/kcl-std/types/std-types-Sketch) | The sketch(es) to duplicate. | Yes |
| `instances` | [`number`](/docs/kcl-std/types/std-types-number) | The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | Yes | | `instances` | [`number(_)`](/docs/kcl-std/types/std-types-number) | The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | Yes |
| `transform` | `FunctionSource` | How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples. | Yes | | `transform` | [`fn(number(_)): { }`](/docs/kcl-std/types/std-types-fn) | How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples. | Yes |
| `useOriginal` | [`bool`](/docs/kcl-std/types/std-types-bool) | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false. | No | | `useOriginal` | `boolean` | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. | No |
### Returns ### Returns
[`[Sketch]`](/docs/kcl-std/types/std-types-Sketch) [`[Sketch; 1+]`](/docs/kcl-std/types/std-types-Sketch)
### Examples ### Examples

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -65,7 +65,7 @@ layout: manual
* [`line`](/docs/kcl-std/line) * [`line`](/docs/kcl-std/line)
* [`loft`](/docs/kcl-std/loft) * [`loft`](/docs/kcl-std/loft)
* [`patternCircular2d`](/docs/kcl-std/patternCircular2d) * [`patternCircular2d`](/docs/kcl-std/patternCircular2d)
* [`patternTransform2d`](/docs/kcl-std/patternTransform2d) * [`patternTransform2d`](/docs/kcl-std/functions/std-sketch-patternTransform2d)
* [`polygon`](/docs/kcl-std/polygon) * [`polygon`](/docs/kcl-std/polygon)
* [`profileStart`](/docs/kcl-std/profileStart) * [`profileStart`](/docs/kcl-std/profileStart)
* [`profileStartX`](/docs/kcl-std/profileStartX) * [`profileStartX`](/docs/kcl-std/profileStartX)
@ -94,7 +94,7 @@ layout: manual
* [`intersect`](/docs/kcl-std/intersect) * [`intersect`](/docs/kcl-std/intersect)
* [`patternCircular3d`](/docs/kcl-std/patternCircular3d) * [`patternCircular3d`](/docs/kcl-std/patternCircular3d)
* [`patternLinear3d`](/docs/kcl-std/patternLinear3d) * [`patternLinear3d`](/docs/kcl-std/patternLinear3d)
* [`patternTransform`](/docs/kcl-std/patternTransform) * [`patternTransform`](/docs/kcl-std/functions/std-solid-patternTransform)
* [`shell`](/docs/kcl-std/functions/std-solid-shell) * [`shell`](/docs/kcl-std/functions/std-solid-shell)
* [`subtract`](/docs/kcl-std/subtract) * [`subtract`](/docs/kcl-std/subtract)
* [`union`](/docs/kcl-std/union) * [`union`](/docs/kcl-std/union)

View File

@ -30,7 +30,7 @@ This module contains functions for creating and manipulating sketches, and makin
* [`line`](/docs/kcl-std/line) * [`line`](/docs/kcl-std/line)
* [`loft`](/docs/kcl-std/loft) * [`loft`](/docs/kcl-std/loft)
* [`patternCircular2d`](/docs/kcl-std/patternCircular2d) * [`patternCircular2d`](/docs/kcl-std/patternCircular2d)
* [`patternTransform2d`](/docs/kcl-std/patternTransform2d) * [`patternTransform2d`](/docs/kcl-std/functions/std-sketch-patternTransform2d)
* [`polygon`](/docs/kcl-std/polygon) * [`polygon`](/docs/kcl-std/polygon)
* [`profileStart`](/docs/kcl-std/profileStart) * [`profileStart`](/docs/kcl-std/profileStart)
* [`profileStartX`](/docs/kcl-std/profileStartX) * [`profileStartX`](/docs/kcl-std/profileStartX)

View File

@ -18,7 +18,7 @@ This module contains functions for modifying solids, e.g., by adding a fillet or
* [`intersect`](/docs/kcl-std/intersect) * [`intersect`](/docs/kcl-std/intersect)
* [`patternCircular3d`](/docs/kcl-std/patternCircular3d) * [`patternCircular3d`](/docs/kcl-std/patternCircular3d)
* [`patternLinear3d`](/docs/kcl-std/patternLinear3d) * [`patternLinear3d`](/docs/kcl-std/patternLinear3d)
* [`patternTransform`](/docs/kcl-std/patternTransform) * [`patternTransform`](/docs/kcl-std/functions/std-solid-patternTransform)
* [`shell`](/docs/kcl-std/functions/std-solid-shell) * [`shell`](/docs/kcl-std/functions/std-solid-shell)
* [`subtract`](/docs/kcl-std/subtract) * [`subtract`](/docs/kcl-std/subtract)
* [`union`](/docs/kcl-std/union) * [`union`](/docs/kcl-std/union)

View File

@ -11,7 +11,7 @@ Contains frequently used constants, functions for interacting with the KittyCAD
The standard library is organised into modules (listed below), but most things are always available in KCL programs. The standard library is organised into modules (listed below), but most things are always available in KCL programs.
You might also want the [KCL language reference](/docs/kcl-lang) or the [KCL guide](). You might also want the [KCL language reference](/docs/kcl-lang) or the [KCL guide](https://zoo.dev/docs/kcl-book/intro.html).
## Modules ## Modules

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -684,4 +684,33 @@ c = 3 + a`
highlightedHeaderArg: 'value', highlightedHeaderArg: 'value',
}) })
}) })
test('Text-to-CAD command can be closed with escape while in prompt', async ({
page,
homePage,
cmdBar,
}) => {
await homePage.expectState({
projectCards: [],
sortBy: 'last-modified-desc',
})
await homePage.textToCadBtn.click()
await cmdBar.expectState({
stage: 'arguments',
commandName: 'Text-to-CAD Create',
currentArgKey: 'prompt',
currentArgValue: '',
headerArguments: {
Method: 'New project',
NewProjectName: 'untitled',
Prompt: '',
},
highlightedHeaderArg: 'prompt',
})
await page.keyboard.press('Escape')
await cmdBar.toBeClosed()
await cmdBar.expectState({
stage: 'commandBarClosed',
})
})
}) })

View File

@ -238,6 +238,26 @@ test.describe('when using the file tree to', () => {
} }
) )
test(
`create new folders and that doesn't trigger a navigation`,
{ tag: ['@electron', '@macos', '@windows'] },
async ({ page, homePage, scene, toolbar, cmdBar }) => {
await homePage.goToModelingScene()
await scene.settled(cmdBar)
await toolbar.openPane('files')
const { createNewFolder } = await getUtils(page, test)
await createNewFolder('folder')
await createNewFolder('folder.kcl')
await test.step(`Postcondition: folders are created and we didn't navigate`, async () => {
await toolbar.expectFileTreeState(['folder', 'folder.kcl', 'main.kcl'])
await expect(toolbar.fileName).toHaveText('main.kcl')
})
}
)
test( test(
'deleting all files recreates a default main.kcl with no code', 'deleting all files recreates a default main.kcl with no code',
{ tag: '@electron' }, { tag: '@electron' },

View File

@ -105,14 +105,19 @@ export class CmdBarFixture {
expectState = async (expected: CmdBarSerialised) => { expectState = async (expected: CmdBarSerialised) => {
return expect.poll(() => this._serialiseCmdBar()).toEqual(expected) return expect.poll(() => this._serialiseCmdBar()).toEqual(expected)
} }
/** The method will use buttons OR press enter randomly to progress the cmdbar, /**
* this could have unexpected results depending on what's focused * This method is used to progress the command bar to the next step, defaulting to clicking the next button.
* * Optionally, with the `shouldUseKeyboard` parameter, it will hit `Enter` to progress.
* TODO: This method assumes the user has a valid input to the current stage, * * TODO: This method assumes the user has a valid input to the current stage,
* and assumes we are past the `pickCommand` step. * and assumes we are past the `pickCommand` step.
*/ */
progressCmdBar = async (shouldFuzzProgressMethod = true) => { progressCmdBar = async (shouldUseKeyboard = false) => {
await this.page.waitForTimeout(2000) await this.page.waitForTimeout(2000)
if (shouldUseKeyboard) {
await this.page.keyboard.press('Enter')
return
}
const arrowButton = this.page.getByRole('button', { const arrowButton = this.page.getByRole('button', {
name: 'arrow right Continue', name: 'arrow right Continue',
}) })
@ -308,6 +313,11 @@ export class CmdBarFixture {
await expect(this.cmdBarElement).toBeVisible({ timeout: 10_000 }) await expect(this.cmdBarElement).toBeVisible({ timeout: 10_000 })
} }
async toBeClosed() {
// Check that the command bar is closed
await expect(this.cmdBarElement).not.toBeVisible({ timeout: 10_000 })
}
async expectArgValue(value: string) { async expectArgValue(value: string) {
// Check the placeholder project name exists // Check the placeholder project name exists
const actualArgument = await this.cmdBarElement const actualArgument = await this.cmdBarElement

View File

@ -26,6 +26,7 @@ export class HomePageFixture {
sortByNameBtn!: Locator sortByNameBtn!: Locator
appHeader!: Locator appHeader!: Locator
tutorialBtn!: Locator tutorialBtn!: Locator
textToCadBtn!: Locator
constructor(page: Page) { constructor(page: Page) {
this.page = page this.page = page
@ -47,6 +48,7 @@ export class HomePageFixture {
this.sortByNameBtn = this.page.getByTestId('home-sort-by-name') this.sortByNameBtn = this.page.getByTestId('home-sort-by-name')
this.appHeader = this.page.getByTestId('app-header') this.appHeader = this.page.getByTestId('app-header')
this.tutorialBtn = this.page.getByTestId('home-tutorial-button') this.tutorialBtn = this.page.getByTestId('home-tutorial-button')
this.textToCadBtn = this.page.getByTestId('home-text-to-cad')
} }
private _serialiseSortBy = async (): Promise< private _serialiseSortBy = async (): Promise<

View File

@ -61,6 +61,7 @@ class MyAPIReporter implements Reporter {
const payload = { const payload = {
// Required information // Required information
project: 'https://github.com/KittyCAD/modeling-app', project: 'https://github.com/KittyCAD/modeling-app',
suite: process.env.CI_SUITE || 'e2e',
branch: process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || '', branch: process.env.GITHUB_HEAD_REF || process.env.GITHUB_REF_NAME || '',
commit: process.env.CI_COMMIT_SHA || process.env.GITHUB_SHA || '', commit: process.env.CI_COMMIT_SHA || process.env.GITHUB_SHA || '',
test: test.titlePath().slice(2).join(' '), test: test.titlePath().slice(2).join(' '),

View File

@ -1855,7 +1855,11 @@ sketch002 = startSketchOn(XZ)
}, },
stage: 'review', stage: 'review',
}) })
await cmdBar.progressCmdBar() // Confirm we can submit from the review step with just `Enter`
await cmdBar.progressCmdBar(true)
await cmdBar.expectState({
stage: 'commandBarClosed',
})
}) })
await test.step(`Confirm code is added to the editor, scene has changed`, async () => { await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
@ -1995,7 +1999,7 @@ profile001 = ${circleCode}`
}, },
stage: 'review', stage: 'review',
}) })
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar(true)
await editor.expectEditor.toContain(sweepDeclaration) await editor.expectEditor.toContain(sweepDeclaration)
}) })

View File

@ -2064,3 +2064,59 @@ test(
}) })
} }
) )
test(
'nested dir import works on windows',
{ tag: ['@electron', '@windows'] },
async ({ scene, cmdBar, context, page }, testInfo) => {
// Skip if on non-windows
if (process.platform !== 'win32') {
test.skip()
}
await context.folderSetupFn(async (dir) => {
const bracketDir = path.join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true })
const nestedDir = path.join(bracketDir, 'nested')
await fsp.mkdir(nestedDir, { recursive: true })
await fsp.copyFile(
executorInputPath('cylinder-inches.kcl'),
path.join(nestedDir, 'main.kcl')
)
await fsp.writeFile(
path.join(bracketDir, 'main.kcl'),
`import 'nested\\main.kcl' as thing
thing`
)
})
await page.setBodyDimensions({ width: 1200, height: 500 })
const u = await getUtils(page)
const pointOnModel = { x: 630, y: 280 }
await test.step('Opening the bracket project should load the stream', async () => {
// expect to see the text bracket
await expect(page.getByText('bracket')).toBeVisible()
await page.getByText('bracket').click()
await scene.settled(cmdBar)
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).toBeEnabled({
timeout: 20_000,
})
// gray at this pixel means the stream has loaded in the most
// user way we can verify it (pixel color)
await expect
.poll(() => u.getGreatestPixDiff(pointOnModel, [125, 125, 125]), {
timeout: 10_000,
})
.toBeLessThan(15)
})
}
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

After

Width:  |  Height:  |  Size: 133 KiB

View File

@ -557,6 +557,14 @@ export async function getUtils(page: Page, test_?: typeof test) {
}) })
}, },
createNewFolder: async (name: string) => {
return test?.step(`Create a folder named ${name}`, async () => {
await page.getByTestId('create-folder-button').click()
await page.getByTestId('tree-input-field').fill(name)
await page.keyboard.press('Enter')
})
},
cloneFile: async (name: string) => { cloneFile: async (name: string) => {
return test?.step(`Cloning file '${name}'`, async () => { return test?.step(`Cloning file '${name}'`, async () => {
await page await page

View File

@ -37,6 +37,8 @@ When you submit a PR to add or modify KCL samples, images will be generated and
[![bottle](screenshots/bottle.png)](bottle/main.kcl) [![bottle](screenshots/bottle.png)](bottle/main.kcl)
#### [bracket](bracket/main.kcl) ([screenshot](screenshots/bracket.png)) #### [bracket](bracket/main.kcl) ([screenshot](screenshots/bracket.png))
[![bracket](screenshots/bracket.png)](bracket/main.kcl) [![bracket](screenshots/bracket.png)](bracket/main.kcl)
#### [brake-rotor](brake-rotor/main.kcl) ([screenshot](screenshots/brake-rotor.png))
[![brake-rotor](screenshots/brake-rotor.png)](brake-rotor/main.kcl)
#### [car-wheel-assembly](car-wheel-assembly/main.kcl) ([screenshot](screenshots/car-wheel-assembly.png)) #### [car-wheel-assembly](car-wheel-assembly/main.kcl) ([screenshot](screenshots/car-wheel-assembly.png))
[![car-wheel-assembly](screenshots/car-wheel-assembly.png)](car-wheel-assembly/main.kcl) [![car-wheel-assembly](screenshots/car-wheel-assembly.png)](car-wheel-assembly/main.kcl)
#### [cold-plate](cold-plate/main.kcl) ([screenshot](screenshots/cold-plate.png)) #### [cold-plate](cold-plate/main.kcl) ([screenshot](screenshots/cold-plate.png))

View File

@ -0,0 +1,179 @@
// A 320mm vented brake disc (rotor), with straight vanes, 30mm thick. The disc bell should accommodate 5 M12 wheel studs on a 114.3mm pitch circle diameter.
@settings(defaultLengthUnit = mm)
// Define parameters.
dDisc = 320
dPitchCircle = 114.3
dBore = 64
nStuds = 5
dStudDrilling = 12.5 // M12
hFrictionSurface = 60
tDiscHalf = 10
// Vent parameters.
tVent = 10
wVent = 6
rVentFillet = 2
nVentBosses = 36
// Drilling parameters.
dDrillDia = 6
aBase = 90
aSweep = 30
nArcs = 12
// Bell parameters.
aDraftBell = 5
tBell = 5 // Wall thickness.
hBellAboveDiscFace = 40
hBellSubflush = 4
wUndercut = 8
fn drillHole(activeSketch, t) {
// Sketch a vent hole at line parameter value t on an arc drawn across the disc surface.
rInner = dDisc / 2 - hFrictionSurface
rOuter = dDisc / 2
aStart = aBase
aEnd = aBase - aSweep
// Linear interpolation of radius.
rCurrent = rInner + t * (rOuter - rInner)
// Linear interpolation of angle.
aCurrent = aStart + t * (aEnd - aStart)
// Calculate position.
xCenter = rCurrent * cos(aCurrent)
yCenter = rCurrent * sin(aCurrent)
// Draw.
drillCircle = circle(activeSketch, center = [xCenter, yCenter], radius = dDrillDia / 2)
return drillCircle
}
fn createDiscHalf(plane, dDiscParam, hFrictionSurfaceParam, tDiscHalfParam) {
// Create a disc half with a vent hole pattern.
sketchFace = startSketchOn(plane)
profileFace = circle(sketchFace, center = [0, 0], radius = dDiscParam / 2)
|> subtract2d(tool = circle(sketchFace, center = [0, 0], radius = dDiscParam / 2 - hFrictionSurfaceParam))
// Create three circles at t = 0, 0.5, and 1
hole1 = drillHole(activeSketch = sketchFace, t = 0.2)
hole2 = drillHole(activeSketch = sketchFace, t = 0.5)
hole3 = drillHole(activeSketch = sketchFace, t = 0.8)
// Pattern and cut.
holes = patternCircular2d(
[hole1, hole2, hole3],
instances = nArcs,
center = [0, 0],
arcDegrees = 360,
rotateDuplicates = true,
)
profileDrilled = subtract2d(profileFace, tool = holes)
// Extrude.
discHalf = extrude(profileFace, length = tDiscHalfParam)
return discHalf
}
// ---------------------------------------------------------------------------------------------------------------------
// Create inboard half.
discInboard = createDiscHalf(
plane = XY,
dDiscParam = dDisc,
hFrictionSurfaceParam = hFrictionSurface,
tDiscHalfParam = tDiscHalf,
)
// Create vents.
planeVent = offsetPlane(XY, offset = tDiscHalf)
sketchVent = startSketchOn(planeVent)
profileVent = startProfile(sketchVent, at = [-wVent, dDisc / 2])
|> angledLine(angle = 0, length = wVent, tag = $rectangleSegmentA001)
|> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = hFrictionSurface, tag = $seg02)
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $seg03)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg01)
|> close()
ventPad = extrude(profileVent, length = tVent)
|> fillet(
radius = rVentFillet,
tags = [
getCommonEdge(faces = [seg01, rectangleSegmentA001]),
getCommonEdge(faces = [seg02, rectangleSegmentA001]),
getCommonEdge(faces = [seg01, seg03]),
getCommonEdge(faces = [seg03, seg02])
],
)
ventSet = patternCircular3d(
ventPad,
instances = nVentBosses,
axis = [0, 0, 1],
center = [0, 0, tDiscHalf],
arcDegrees = 360,
rotateDuplicates = true,
)
// Create outboard half.
planeOutboard = offsetPlane(XY, offset = tDiscHalf + tVent)
discOutboard = createDiscHalf(
plane = planeOutboard,
dDiscParam = dDisc,
hFrictionSurfaceParam = hFrictionSurface,
tDiscHalfParam = tDiscHalf,
)
// Now create bell.
rCenter = dDisc / 2 - hFrictionSurface - wUndercut
rBore = dBore / 2
lDraftExterior = hBellAboveDiscFace / tan(90 - aDraftBell)
lDraftInterior = (hBellAboveDiscFace - tBell) / tan(90 - aDraftBell)
// Inner and outer radius of outboard face of disc bell.
rOuter = rCenter - lDraftExterior - rBore
rInner = rOuter + lDraftExterior - (tBell + lDraftInterior)
sketchDiscBell = startSketchOn(-YZ)
bodyDiscBell = startProfile(
sketchDiscBell,
at = [
-dDisc / 2 + hFrictionSurface,
tDiscHalf * 2 + tVent
],
)
|> arc(
%,
angleStart = -180,
angleEnd = 0,
radius = wUndercut / 2,
)
|> line(end = [lDraftExterior, hBellAboveDiscFace])
|> xLine(length = rOuter, tag = $seg04)
|> yLine(length = -tBell)
|> xLine(length = -rInner)
|> line(end = [-lDraftInterior, -hBellAboveDiscFace])
|> line(end = [0, -2]) // Wall thickness.
|> xLine(length = -1 * (tBell + wUndercut))
|> close(%)
|> revolve(axis = Y)
// Drill lug holes.
sketchLugs = startSketchOn(bodyDiscBell, face = seg04)
profileStud = circle(sketchLugs, center = [0, dPitchCircle / 2], radius = dStudDrilling / 2)
|> patternCircular2d(
%,
instances = nStuds,
center = [0, 0],
arcDegrees = 360,
rotateDuplicates = true,
)
clearance = 2 // Some margin on negative extrude.
lugs = extrude(profileStud, length = -1 * (tBell + clearance))

View File

@ -74,6 +74,16 @@
"main.kcl" "main.kcl"
] ]
}, },
{
"file": "main.kcl",
"pathFromProjectDirectoryToFirstFile": "brake-rotor/main.kcl",
"multipleFiles": false,
"title": "A 320mm vented brake disc (rotor), with straight vanes, 30mm thick. The disc bell should accommodate 5 M12 wheel studs on a 114.3mm pitch circle diameter.",
"description": "",
"files": [
"main.kcl"
]
},
{ {
"file": "main.kcl", "file": "main.kcl",
"pathFromProjectDirectoryToFirstFile": "car-wheel-assembly/main.kcl", "pathFromProjectDirectoryToFirstFile": "car-wheel-assembly/main.kcl",

View File

@ -72,25 +72,25 @@ leftSpacerShape = boxModuleFn(width = leftSpacerWidth)
// Module for power switch including front plate and red rocker button // Module for power switch including front plate and red rocker button
switchPosition = leftSpacerPosition + leftSpacerWidth / 2 + moduleWidth / 2 switchPosition = leftSpacerPosition + leftSpacerWidth / 2 + moduleWidth / 2
swtichWidth = moduleWidth switchWidth = moduleWidth
// Switch Body // Switch Body
switchBody = boxModuleFn(width = moduleWidth) switchBody = boxModuleFn(width = moduleWidth)
// Switch Plate // Switch Plate
swtichPlateWidth = 20 switchPlateWidth = 20
switchPlateHeight = 30 switchPlateHeight = 30
switchPlateThickness = 3 switchPlateThickness = 3
switchPlateShape = startSketchOn(switchBody, face = END) switchPlateShape = startSketchOn(switchBody, face = END)
|> startProfile( |> startProfile(
%, %,
at = [ at = [
-swtichPlateWidth / 2, -switchPlateWidth / 2,
-switchPlateHeight / 2 -switchPlateHeight / 2
], ],
) )
|> yLine(length = switchPlateHeight) |> yLine(length = switchPlateHeight)
|> xLine(length = swtichPlateWidth) |> xLine(length = switchPlateWidth)
|> yLine(length = -switchPlateHeight) |> yLine(length = -switchPlateHeight)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
@ -104,8 +104,8 @@ switchPlateBody = extrude(switchPlateShape, length = switchPlateThickness)
// Switch Button // Switch Button
switchButtonHeight = 26 switchButtonHeight = 26
swtichButtonWidth = 15 switchButtonWidth = 15
switchButtonShape = startSketchOn(offsetPlane(-YZ, offset = -swtichButtonWidth / 2)) switchButtonShape = startSketchOn(offsetPlane(-YZ, offset = -switchButtonWidth / 2))
|> startProfile( |> startProfile(
%, %,
at = [ at = [
@ -121,7 +121,7 @@ switchButtonShape = startSketchOn(offsetPlane(-YZ, offset = -swtichButtonWidth /
]) ])
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
switchButtonBody = extrude(switchButtonShape, length = swtichButtonWidth) switchButtonBody = extrude(switchButtonShape, length = switchButtonWidth)
|> translate( |> translate(
%, %,
x = switchPosition, x = switchPosition,
@ -132,7 +132,7 @@ switchButtonBody = extrude(switchButtonShape, length = swtichButtonWidth)
// Spacer between switch and plug modules for layout alignment // Spacer between switch and plug modules for layout alignment
secondSpacerWidth = moduleWidth / 2 secondSpacerWidth = moduleWidth / 2
secondSpacerPosition = switchPosition + swtichWidth / 2 + secondSpacerWidth / 2 secondSpacerPosition = switchPosition + switchWidth / 2 + secondSpacerWidth / 2
secondSpacerBody = boxModuleFn(width = secondSpacerWidth) secondSpacerBody = boxModuleFn(width = secondSpacerWidth)
|> translate( |> translate(
%, %,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 95 KiB

View File

@ -53,8 +53,8 @@ baseSlab = boxFn(plane = XY, width = slabWidth, height = -baseThickness)
|> appearance(%, color = "#dbd7d2") |> appearance(%, color = "#dbd7d2")
// Create ground platform beneath the base // Create ground platform beneath the base
goundSize = 50 groundSize = 50
groundBody = boxFn(plane = offsetPlane(XY, offset = -baseThickness), width = goundSize, height = -5) groundBody = boxFn(plane = offsetPlane(XY, offset = -baseThickness), width = groundSize, height = -5)
|> appearance(%, color = "#3a3631") |> appearance(%, color = "#3a3631")
// Create a single slab with handrail height to be reused with pattern // Create a single slab with handrail height to be reused with pattern

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

View File

@ -281,7 +281,14 @@ impl ExecutorContext {
// Track exports. // Track exports.
if let ItemVisibility::Export = variable_declaration.visibility { if let ItemVisibility::Export = variable_declaration.visibility {
exec_state.mod_local.module_exports.push(var_name); if matches!(body_type, BodyType::Root) {
exec_state.mod_local.module_exports.push(var_name);
} else {
exec_state.err(CompilationError::err(
variable_declaration.as_source_range(),
"Exports are only supported at the top-level of a file. Remove `export` or move it to the top-level.",
));
}
} }
// Variable declaration can be the return value of a module. // Variable declaration can be the return value of a module.
last_expr = matches!(body_type, BodyType::Root).then_some(value); last_expr = matches!(body_type, BodyType::Root).then_some(value);

View File

@ -35,31 +35,28 @@ impl Default for TypedPath {
impl From<&String> for TypedPath { impl From<&String> for TypedPath {
fn from(path: &String) -> Self { fn from(path: &String) -> Self {
#[cfg(target_arch = "wasm32")] TypedPath::new(path)
{
TypedPath(typed_path::TypedPath::derive(path).to_path_buf())
}
#[cfg(not(target_arch = "wasm32"))]
{
TypedPath(std::path::PathBuf::from(path))
}
} }
} }
impl From<&str> for TypedPath { impl From<&str> for TypedPath {
fn from(path: &str) -> Self { fn from(path: &str) -> Self {
TypedPath::new(path)
}
}
impl TypedPath {
pub fn new(path: &str) -> Self {
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
{ {
TypedPath(typed_path::TypedPath::derive(path).to_path_buf()) TypedPath(typed_path::TypedPath::derive(path).to_path_buf())
} }
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
{ {
TypedPath(std::path::PathBuf::from(path)) TypedPath(normalise_import(path))
} }
} }
}
impl TypedPath {
pub fn extension(&self) -> Option<&str> { pub fn extension(&self) -> Option<&str> {
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
{ {
@ -85,6 +82,17 @@ impl TypedPath {
} }
} }
pub fn join_typed(&self, path: &TypedPath) -> Self {
#[cfg(target_arch = "wasm32")]
{
TypedPath(self.0.join(path.0.to_path()))
}
#[cfg(not(target_arch = "wasm32"))]
{
TypedPath(self.0.join(&path.0))
}
}
pub fn parent(&self) -> Option<Self> { pub fn parent(&self) -> Option<Self> {
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
{ {
@ -206,3 +214,19 @@ impl schemars::JsonSchema for TypedPath {
gen.subschema_for::<std::path::PathBuf>() gen.subschema_for::<std::path::PathBuf>()
} }
} }
/// Turn `nested\foo\bar\main.kcl` or `nested/foo/bar/main.kcl`
/// into a PathBuf that works on the host OS.
///
/// * Does **not** touch `..` or symlinks call `canonicalize()` if you need that.
/// * Returns an owned `PathBuf` only when normalisation was required.
fn normalise_import<S: AsRef<str>>(raw: S) -> std::path::PathBuf {
let s = raw.as_ref();
// On Unix we need to swap `\` → `/`. On Windows we leave it alone.
// (Windows happily consumes `/`)
if cfg!(unix) && s.contains('\\') {
std::path::PathBuf::from(s.replace('\\', "/"))
} else {
std::path::Path::new(s).to_path_buf()
}
}

View File

@ -155,9 +155,8 @@ impl RuntimeType {
.map(RuntimeType::Union), .map(RuntimeType::Union),
Type::Object { properties } => properties Type::Object { properties } => properties
.into_iter() .into_iter()
.map(|p| { .map(|(id, ty)| {
RuntimeType::from_parsed(p.type_.unwrap().inner, exec_state, source_range) RuntimeType::from_parsed(ty.inner, exec_state, source_range).map(|ty| (id.name.clone(), ty))
.map(|ty| (p.identifier.inner.name, ty))
}) })
.collect::<Result<Vec<_>, CompilationError>>() .collect::<Result<Vec<_>, CompilationError>>()
.map(RuntimeType::Object), .map(RuntimeType::Object),

View File

@ -185,9 +185,9 @@ impl ModulePath {
match path { match path {
ImportPath::Kcl { filename: path } | ImportPath::Foreign { path } => { ImportPath::Kcl { filename: path } | ImportPath::Foreign { path } => {
let resolved_path = if let Some(project_dir) = project_directory { let resolved_path = if let Some(project_dir) = project_directory {
project_dir.join(path) project_dir.join_typed(path)
} else { } else {
TypedPath::from(path) path.clone()
}; };
ModulePath::Local { value: resolved_path } ModulePath::Local { value: resolved_path }
} }

View File

@ -212,8 +212,9 @@ impl Type {
Type::Object { properties } => { Type::Object { properties } => {
hasher.update(b"FnArgType::Object"); hasher.update(b"FnArgType::Object");
hasher.update(properties.len().to_ne_bytes()); hasher.update(properties.len().to_ne_bytes());
for prop in properties.iter_mut() { for (id, ty) in properties.iter_mut() {
hasher.update(prop.compute_digest()); hasher.update(id.compute_digest());
hasher.update(ty.compute_digest());
} }
} }
} }

View File

@ -34,7 +34,7 @@ use crate::{
}, },
parsing::{ast::digest::Digest, token::NumericSuffix, PIPE_OPERATOR}, parsing::{ast::digest::Digest, token::NumericSuffix, PIPE_OPERATOR},
source_range::SourceRange, source_range::SourceRange,
ModuleId, ModuleId, TypedPath,
}; };
mod condition; mod condition;
@ -1741,8 +1741,8 @@ impl ImportSelector {
#[ts(export)] #[ts(export)]
#[serde(tag = "type")] #[serde(tag = "type")]
pub enum ImportPath { pub enum ImportPath {
Kcl { filename: String }, Kcl { filename: TypedPath },
Foreign { path: String }, Foreign { path: TypedPath },
Std { path: Vec<String> }, Std { path: Vec<String> },
} }
@ -1811,16 +1811,25 @@ impl ImportStatement {
match &self.path { match &self.path {
ImportPath::Kcl { filename: s } | ImportPath::Foreign { path: s } => { ImportPath::Kcl { filename: s } | ImportPath::Foreign { path: s } => {
let mut parts = s.split('.'); let name = s.to_string_lossy();
let path = parts.next()?; if name.ends_with("/main.kcl") || name.ends_with("\\main.kcl") {
let _ext = parts.next()?; let name = &name[..name.len() - 9];
let rest = parts.next(); let start = name.rfind(['/', '\\']).map(|s| s + 1).unwrap_or(0);
return Some(name[start..].to_owned());
}
if rest.is_some() { let name = s.file_name().map(|f| f.to_string())?;
if name.contains('\\') || name.contains('/') {
return None; return None;
} }
path.rsplit(&['/', '\\']).next().map(str::to_owned) // Remove the extension if it exists.
let extension = s.extension();
Some(if let Some(extension) = extension {
name.trim_end_matches(extension).trim_end_matches('.').to_string()
} else {
name
})
} }
ImportPath::Std { path } => path.last().cloned(), ImportPath::Std { path } => path.last().cloned(),
} }
@ -3315,7 +3324,7 @@ pub enum Type {
}, },
// An object type. // An object type.
Object { Object {
properties: Vec<Parameter>, properties: Vec<(Node<Identifier>, Node<Type>)>,
}, },
} }
@ -3348,10 +3357,8 @@ impl fmt::Display for Type {
} else { } else {
write!(f, ",")?; write!(f, ",")?;
} }
write!(f, " {}:", p.identifier.name)?; write!(f, " {}:", p.0.name)?;
if let Some(ty) = &p.type_ { write!(f, " {}", p.1)?;
write!(f, " {}", ty.inner)?;
}
} }
write!(f, " }}") write!(f, " }}")
} }
@ -3988,7 +3995,7 @@ cylinder = startSketchOn(-XZ)
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_parse_type_args_object_on_functions() { async fn test_parse_type_args_object_on_functions() {
let some_program_string = r#"fn thing(arg0: [number], arg1: {thing: number, things: [string], more?: string}, tag?: string) { let some_program_string = r#"fn thing(arg0: [number], arg1: {thing: number, things: [string], more: string}, tag?: string) {
return arg0 return arg0
}"#; }"#;
let module_id = ModuleId::default(); let module_id = ModuleId::default();
@ -4015,8 +4022,8 @@ cylinder = startSketchOn(-XZ)
params[1].type_.as_ref().unwrap().inner, params[1].type_.as_ref().unwrap().inner,
Type::Object { Type::Object {
properties: vec![ properties: vec![
Parameter { (
identifier: Node::new( Node::new(
Identifier { Identifier {
name: "thing".to_owned(), name: "thing".to_owned(),
digest: None, digest: None,
@ -4025,18 +4032,15 @@ cylinder = startSketchOn(-XZ)
37, 37,
module_id, module_id,
), ),
type_: Some(Node::new( Node::new(
Type::Primitive(PrimitiveType::Number(NumericSuffix::None)), Type::Primitive(PrimitiveType::Number(NumericSuffix::None)),
39, 39,
45, 45,
module_id module_id
)), ),
default_value: None, ),
labeled: true, (
digest: None, Node::new(
},
Parameter {
identifier: Node::new(
Identifier { Identifier {
name: "things".to_owned(), name: "things".to_owned(),
digest: None, digest: None,
@ -4045,7 +4049,7 @@ cylinder = startSketchOn(-XZ)
53, 53,
module_id, module_id,
), ),
type_: Some(Node::new( Node::new(
Type::Array { Type::Array {
ty: Box::new(Type::Primitive(PrimitiveType::String)), ty: Box::new(Type::Primitive(PrimitiveType::String)),
len: ArrayLen::None len: ArrayLen::None
@ -4053,13 +4057,10 @@ cylinder = startSketchOn(-XZ)
56, 56,
62, 62,
module_id module_id
)), )
default_value: None, ),
labeled: true, (
digest: None Node::new(
},
Parameter {
identifier: Node::new(
Identifier { Identifier {
name: "more".to_owned(), name: "more".to_owned(),
digest: None digest: None
@ -4068,11 +4069,8 @@ cylinder = startSketchOn(-XZ)
69, 69,
module_id, module_id,
), ),
type_: Some(Node::new(Type::Primitive(PrimitiveType::String), 72, 78, module_id)), Node::new(Type::Primitive(PrimitiveType::String), 71, 77, module_id),
labeled: true, )
default_value: Some(DefaultParamVal::none()),
digest: None
}
] ]
} }
); );
@ -4343,4 +4341,20 @@ startSketchOn(XY)
"# "#
); );
} }
#[test]
fn module_name() {
#[track_caller]
fn assert_mod_name(stmt: &str, name: &str) {
let tokens = crate::parsing::token::lex(stmt, ModuleId::default()).unwrap();
let stmt = crate::parsing::parser::import_stmt(&mut tokens.as_slice()).unwrap();
assert_eq!(stmt.module_name().unwrap(), name);
}
assert_mod_name("import 'foo.kcl'", "foo");
assert_mod_name("import 'foo.kcl' as bar", "bar");
assert_mod_name("import 'main.kcl'", "main");
assert_mod_name("import 'foo/main.kcl'", "foo");
assert_mod_name("import 'foo\\bar\\main.kcl'", "bar");
}
} }

View File

@ -35,7 +35,7 @@ use crate::{
token::{Token, TokenSlice, TokenType}, token::{Token, TokenSlice, TokenType},
PIPE_OPERATOR, PIPE_SUBSTITUTION_OPERATOR, PIPE_OPERATOR, PIPE_SUBSTITUTION_OPERATOR,
}, },
SourceRange, IMPORT_FILE_EXTENSIONS, SourceRange, TypedPath, IMPORT_FILE_EXTENSIONS,
}; };
thread_local! { thread_local! {
@ -436,7 +436,7 @@ fn pipe_expression(i: &mut TokenSlice) -> PResult<Node<PipeExpression>> {
)) ))
} }
fn bool_value(i: &mut TokenSlice) -> PResult<BoxNode<Literal>> { fn bool_value(i: &mut TokenSlice) -> PResult<Node<Literal>> {
let (value, token) = any let (value, token) = any
.try_map(|token: Token| match token.token_type { .try_map(|token: Token| match token.token_type {
TokenType::Keyword if token.value == "true" => Ok((true, token)), TokenType::Keyword if token.value == "true" => Ok((true, token)),
@ -448,7 +448,7 @@ fn bool_value(i: &mut TokenSlice) -> PResult<BoxNode<Literal>> {
}) })
.context(expected("a boolean literal (either true or false)")) .context(expected("a boolean literal (either true or false)"))
.parse_next(i)?; .parse_next(i)?;
Ok(Box::new(Node::new( Ok(Node::new(
Literal { Literal {
value: LiteralValue::Bool(value), value: LiteralValue::Bool(value),
raw: value.to_string(), raw: value.to_string(),
@ -457,11 +457,11 @@ fn bool_value(i: &mut TokenSlice) -> PResult<BoxNode<Literal>> {
token.start, token.start,
token.end, token.end,
token.module_id, token.module_id,
))) ))
} }
fn literal(i: &mut TokenSlice) -> PResult<BoxNode<Literal>> { fn literal(i: &mut TokenSlice) -> PResult<BoxNode<Literal>> {
alt((string_literal, unsigned_number_literal)) alt((string_literal, unsigned_number_literal, bool_value))
.map(Box::new) .map(Box::new)
.context(expected("a KCL literal, like 'myPart' or 3")) .context(expected("a KCL literal, like 'myPart' or 3"))
.parse_next(i) .parse_next(i)
@ -1729,7 +1729,7 @@ fn glob(i: &mut TokenSlice) -> PResult<Token> {
.parse_next(i) .parse_next(i)
} }
fn import_stmt(i: &mut TokenSlice) -> PResult<BoxNode<ImportStatement>> { pub(super) fn import_stmt(i: &mut TokenSlice) -> PResult<BoxNode<ImportStatement>> {
let (visibility, visibility_token) = opt(terminated(item_visibility, whitespace)) let (visibility, visibility_token) = opt(terminated(item_visibility, whitespace))
.parse_next(i)? .parse_next(i)?
.map_or((ItemVisibility::Default, None), |pair| (pair.0, Some(pair.1))); .map_or((ItemVisibility::Default, None), |pair| (pair.0, Some(pair.1)));
@ -1862,18 +1862,50 @@ fn validate_path_string(path_string: String, var_name: bool, path_range: SourceR
let path = if path_string.ends_with(".kcl") { let path = if path_string.ends_with(".kcl") {
if path_string if path_string
.chars() .chars()
.any(|c| !c.is_ascii_alphanumeric() && c != '_' && c != '-' && c != '.') .any(|c| !c.is_ascii_alphanumeric() && c != '_' && c != '-' && c != '.' && c != '/' && c != '\\')
{ {
return Err(ErrMode::Cut( return Err(ErrMode::Cut(
CompilationError::fatal( CompilationError::fatal(
path_range, path_range,
"import path may only contain alphanumeric characters, underscore, hyphen, and period. KCL files in other directories are not yet supported.", "import path may only contain alphanumeric characters, `_`, `-`, `.`, `/`, and `\\`.",
) )
.into(), .into(),
)); ));
} }
ImportPath::Kcl { filename: path_string } if path_string.starts_with("..") {
return Err(ErrMode::Cut(
CompilationError::fatal(
path_range,
"import path may not start with '..'. Cannot traverse to something outside the bounds of your project. If this path is inside your project please find a better way to reference it.",
)
.into(),
));
}
// Make sure they are not using an absolute path.
if path_string.starts_with('/') || path_string.starts_with('\\') {
return Err(ErrMode::Cut(
CompilationError::fatal(
path_range,
"import path may not start with '/' or '\\'. Cannot traverse to something outside the bounds of your project. If this path is inside your project please find a better way to reference it.",
)
.into(),
));
}
if (path_string.contains('/') || path_string.contains('\\'))
&& !(path_string.ends_with("/main.kcl") || path_string.ends_with("\\main.kcl"))
{
return Err(ErrMode::Cut(
CompilationError::fatal(path_range, "import path to a subdirectory must only refer to main.kcl.")
.into(),
));
}
ImportPath::Kcl {
filename: TypedPath::new(&path_string),
}
} else if path_string.starts_with("std::") { } else if path_string.starts_with("std::") {
ParseContext::warn(CompilationError::err( ParseContext::warn(CompilationError::err(
path_range, path_range,
@ -1910,7 +1942,9 @@ fn validate_path_string(path_string: String, var_name: bool, path_range: SourceR
format!("unsupported import path format. KCL files can be imported from the current project, CAD files with the following formats are supported: {}", IMPORT_FILE_EXTENSIONS.join(", ")), format!("unsupported import path format. KCL files can be imported from the current project, CAD files with the following formats are supported: {}", IMPORT_FILE_EXTENSIONS.join(", ")),
)) ))
} }
ImportPath::Foreign { path: path_string } ImportPath::Foreign {
path: TypedPath::new(&path_string),
}
} else { } else {
return Err(ErrMode::Cut( return Err(ErrMode::Cut(
CompilationError::fatal( CompilationError::fatal(
@ -2051,7 +2085,7 @@ fn unnecessarily_bracketed(i: &mut TokenSlice) -> PResult<Expr> {
fn expr_allowed_in_pipe_expr(i: &mut TokenSlice) -> PResult<Expr> { fn expr_allowed_in_pipe_expr(i: &mut TokenSlice) -> PResult<Expr> {
alt(( alt((
member_expression.map(Box::new).map(Expr::MemberExpression), member_expression.map(Box::new).map(Expr::MemberExpression),
bool_value.map(Expr::Literal), bool_value.map(Box::new).map(Expr::Literal),
tag.map(Box::new).map(Expr::TagDeclarator), tag.map(Box::new).map(Expr::TagDeclarator),
literal.map(Expr::Literal), literal.map(Expr::Literal),
fn_call_kw.map(Box::new).map(Expr::CallExpressionKw), fn_call_kw.map(Box::new).map(Expr::CallExpressionKw),
@ -2070,7 +2104,7 @@ fn expr_allowed_in_pipe_expr(i: &mut TokenSlice) -> PResult<Expr> {
fn possible_operands(i: &mut TokenSlice) -> PResult<Expr> { fn possible_operands(i: &mut TokenSlice) -> PResult<Expr> {
let mut expr = alt(( let mut expr = alt((
unary_expression.map(Box::new).map(Expr::UnaryExpression), unary_expression.map(Box::new).map(Expr::UnaryExpression),
bool_value.map(Expr::Literal), bool_value.map(Box::new).map(Expr::Literal),
member_expression.map(Box::new).map(Expr::MemberExpression), member_expression.map(Box::new).map(Expr::MemberExpression),
literal.map(Expr::Literal), literal.map(Expr::Literal),
fn_call_kw.map(Box::new).map(Expr::CallExpressionKw), fn_call_kw.map(Box::new).map(Expr::CallExpressionKw),
@ -2780,27 +2814,31 @@ fn labeled_argument(i: &mut TokenSlice) -> PResult<LabeledArg> {
.parse_next(i) .parse_next(i)
} }
fn record_ty_field(i: &mut TokenSlice) -> PResult<(Node<Identifier>, Node<Type>)> {
(identifier, colon, opt(whitespace), type_)
.map(|(id, _, _, ty)| (id, ty))
.parse_next(i)
}
/// Parse a type in various positions. /// Parse a type in various positions.
fn type_(i: &mut TokenSlice) -> PResult<Node<Type>> { fn type_(i: &mut TokenSlice) -> PResult<Node<Type>> {
let type_ = alt(( let type_ = alt((
// Object types // Object types
// TODO it is buggy to treat object fields like parameters since the parameters parser assumes a terminating `)`. (
(open_brace, parameters, close_brace).try_map(|(open, params, close)| { open_brace,
for p in &params { opt(whitespace),
if p.type_.is_none() { separated(0.., record_ty_field, comma_sep),
return Err(CompilationError::fatal( opt(whitespace),
p.identifier.as_source_range(), close_brace,
"Missing type for field in record type", )
)); .try_map(|(open, _, params, _, close)| {
} Ok(Node::new(
} Type::Object { properties: params },
Ok(Node::new( open.start,
Type::Object { properties: params }, close.end,
open.start, open.module_id,
close.end, ))
open.module_id, }),
))
}),
// Array types // Array types
array_type, array_type,
// Primitive or union types // Primitive or union types
@ -4530,9 +4568,24 @@ e
fn bad_imports() { fn bad_imports() {
assert_err( assert_err(
r#"import cube from "../cube.kcl""#, r#"import cube from "../cube.kcl""#,
"import path may only contain alphanumeric characters, underscore, hyphen, and period. KCL files in other directories are not yet supported.", "import path may not start with '..'. Cannot traverse to something outside the bounds of your project. If this path is inside your project please find a better way to reference it.",
[17, 30], [17, 30],
); );
assert_err(
r#"import cube from "/cube.kcl""#,
"import path may not start with '/' or '\\'. Cannot traverse to something outside the bounds of your project. If this path is inside your project please find a better way to reference it.",
[17, 28],
);
assert_err(
r#"import cube from "C:\cube.kcl""#,
"import path may only contain alphanumeric characters, `_`, `-`, `.`, `/`, and `\\`.",
[17, 30],
);
assert_err(
r#"import cube from "cube/cube.kcl""#,
"import path to a subdirectory must only refer to main.kcl.",
[17, 32],
);
assert_err( assert_err(
r#"import * as foo from "dsfs""#, r#"import * as foo from "dsfs""#,
"as is not the 'from' keyword", "as is not the 'from' keyword",
@ -4866,6 +4919,15 @@ let myBox = box(p=[0,0], h=-3, l=-16, w=-10)
|> line(%, tag = $var01)"#; |> line(%, tag = $var01)"#;
assert_no_err(some_program_string); assert_no_err(some_program_string);
} }
#[test]
fn test_parse_param_bool_default() {
let some_program_string = r#"fn patternTransform(
use_original?: boolean = false,
) {}"#;
assert_no_err(some_program_string);
}
#[test] #[test]
fn parse_function_types() { fn parse_function_types() {
let code = r#"foo = x: fn let code = r#"foo = x: fn
@ -4875,6 +4937,8 @@ fn foo(x: fn(a, b: number(mm), c: d): number(Angle)): fn { return 0 }
type fn type fn
type foo = fn type foo = fn
type foo = fn(a: string, b: { f: fn(): any }) type foo = fn(a: string, b: { f: fn(): any })
type foo = fn(a: string, b: {})
type foo = fn(a: string, b: { })
type foo = fn([fn]) type foo = fn([fn])
type foo = fn(fn, f: fn(number(_))): [fn([any]): string] type foo = fn(fn, f: fn(number(_))): [fn([any]): string]
"#; "#;

View File

@ -996,6 +996,27 @@ mod import_cycle1 {
super::execute(TEST_NAME, false).await super::execute(TEST_NAME, false).await
} }
} }
mod import_only_at_top_level {
const TEST_NAME: &str = "import_only_at_top_level";
/// 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, false).await
}
}
mod import_function_not_sketch { mod import_function_not_sketch {
const TEST_NAME: &str = "import_function_not_sketch"; const TEST_NAME: &str = "import_function_not_sketch";
@ -1164,6 +1185,27 @@ mod import_foreign {
super::execute(TEST_NAME, false).await super::execute(TEST_NAME, false).await
} }
} }
mod export_var_only_at_top_level {
const TEST_NAME: &str = "export_var_only_at_top_level";
/// 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, false).await
}
}
mod assembly_non_default_units { mod assembly_non_default_units {
const TEST_NAME: &str = "assembly_non_default_units"; const TEST_NAME: &str = "assembly_non_default_units";
@ -3188,7 +3230,6 @@ mod revolve_colinear {
/// Test that KCL is executed correctly. /// Test that KCL is executed correctly.
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
#[ignore] // until https://github.com/KittyCAD/engine/pull/3417 lands
async fn kcl_test_execute() { async fn kcl_test_execute() {
super::execute(TEST_NAME, true).await super::execute(TEST_NAME, true).await
} }
@ -3277,3 +3318,66 @@ mod subtract_regression10 {
super::execute(TEST_NAME, true).await super::execute(TEST_NAME, true).await
} }
} }
mod nested_main_kcl {
const TEST_NAME: &str = "nested_main_kcl";
/// 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
}
}
mod nested_windows_main_kcl {
const TEST_NAME: &str = "nested_windows_main_kcl";
/// 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
}
}
mod nested_assembly {
const TEST_NAME: &str = "nested_assembly";
/// 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
}
}

View File

@ -9,6 +9,7 @@ use kittycad_modeling_cmds as kcmc;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Serialize; use serde::Serialize;
pub use crate::execution::fn_call::Args;
use crate::{ use crate::{
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
execution::{ execution::{
@ -27,8 +28,6 @@ use crate::{
ModuleId, ModuleId,
}; };
pub use crate::execution::fn_call::Args;
const ERROR_STRING_SKETCH_TO_SOLID_HELPER: &str = const ERROR_STRING_SKETCH_TO_SOLID_HELPER: &str =
"You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`"; "You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`";

View File

@ -80,8 +80,6 @@ lazy_static! {
Box::new(crate::std::patterns::PatternLinear3D), Box::new(crate::std::patterns::PatternLinear3D),
Box::new(crate::std::patterns::PatternCircular2D), Box::new(crate::std::patterns::PatternCircular2D),
Box::new(crate::std::patterns::PatternCircular3D), Box::new(crate::std::patterns::PatternCircular3D),
Box::new(crate::std::patterns::PatternTransform),
Box::new(crate::std::patterns::PatternTransform2D),
Box::new(crate::std::edge::GetOppositeEdge), Box::new(crate::std::edge::GetOppositeEdge),
Box::new(crate::std::edge::GetNextAdjacentEdge), Box::new(crate::std::edge::GetNextAdjacentEdge),
Box::new(crate::std::edge::GetPreviousAdjacentEdge), Box::new(crate::std::edge::GetPreviousAdjacentEdge),
@ -280,6 +278,14 @@ pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProp
|e, a| Box::pin(crate::std::clone::clone(e, a)), |e, a| Box::pin(crate::std::clone::clone(e, a)),
StdFnProps::default("std::clone").include_in_feature_tree(), StdFnProps::default("std::clone").include_in_feature_tree(),
), ),
("solid", "patternTransform") => (
|e, a| Box::pin(crate::std::patterns::pattern_transform(e, a)),
StdFnProps::default("std::solid::patternTransform").include_in_feature_tree(),
),
("sketch", "patternTransform2d") => (
|e, a| Box::pin(crate::std::patterns::pattern_transform_2d(e, a)),
StdFnProps::default("std::sketch::patternTransform2d"),
),
_ => unreachable!(), _ => unreachable!(),
} }
} }

View File

@ -15,6 +15,7 @@ use kittycad_modeling_cmds::{
use serde::Serialize; use serde::Serialize;
use uuid::Uuid; use uuid::Uuid;
use super::axis_or_reference::Axis3dOrPoint3d;
use crate::{ use crate::{
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
execution::{ execution::{
@ -31,8 +32,6 @@ use crate::{
ExecutorContext, SourceRange, ExecutorContext, SourceRange,
}; };
use super::axis_or_reference::Axis3dOrPoint3d;
const MUST_HAVE_ONE_INSTANCE: &str = "There must be at least 1 instance of your geometry"; const MUST_HAVE_ONE_INSTANCE: &str = "There must be at least 1 instance of your geometry";
/// Repeat some 3D solid, changing each repetition slightly. /// Repeat some 3D solid, changing each repetition slightly.
@ -57,202 +56,6 @@ pub async fn pattern_transform_2d(exec_state: &mut ExecState, args: Args) -> Res
Ok(sketches.into()) Ok(sketches.into())
} }
/// Repeat a 3-dimensional solid, changing it each time.
///
/// Replicates the 3D solid, applying a transformation function to each replica.
/// Transformation function could alter rotation, scale, visibility, position, etc.
///
/// The `patternTransform` call itself takes a number for how many total instances of
/// the shape should be. For example, if you use a circle with `patternTransform(instances = 4, transform = f)`
/// then there will be 4 circles: the original, and 3 created by replicating the original and
/// calling the transform function on each.
///
/// The transform function takes a single parameter: an integer representing which
/// number replication the transform is for. E.g. the first replica to be transformed
/// will be passed the argument `1`. This simplifies your math: the transform function can
/// rely on id `0` being the original instance passed into the `patternTransform`. See the examples.
///
/// The transform function returns a transform object. All properties of the object are optional,
/// they each default to "no change". So the overall transform object defaults to "no change" too.
/// Its properties are:
///
/// - `translate` (3D point)
///
/// Translates the replica, moving its position in space.
///
/// - `replicate` (bool)
///
/// If false, this ID will not actually copy the object. It'll be skipped.
///
/// - `scale` (3D point)
///
/// Stretches the object, multiplying its width in the given dimension by the point's component in
/// that direction.
///
/// - `rotation` (object, with the following properties)
///
/// - `rotation.axis` (a 3D point, defaults to the Z axis)
///
/// - `rotation.angle` (number of degrees)
///
/// - `rotation.origin` (either "local" i.e. rotate around its own center, "global" i.e. rotate around the scene's center, or a 3D point, defaults to "local")
///
/// ```no_run
/// // Each instance will be shifted along the X axis.
/// fn transform(@id) {
/// return { translate = [4 * id, 0, 0] }
/// }
///
/// // Sketch 4 cylinders.
/// sketch001 = startSketchOn(XZ)
/// |> circle(center = [0, 0], radius = 2)
/// |> extrude(length = 5)
/// |> patternTransform(instances = 4, transform = transform)
/// ```
/// ```no_run
/// // Each instance will be shifted along the X axis,
/// // with a gap between the original (at x = 0) and the first replica
/// // (at x = 8). This is because `id` starts at 1.
/// fn transform(@id) {
/// return { translate = [4 * (1+id), 0, 0] }
/// }
///
/// sketch001 = startSketchOn(XZ)
/// |> circle(center = [0, 0], radius = 2)
/// |> extrude(length = 5)
/// |> patternTransform(instances = 4, transform = transform)
/// ```
/// ```no_run
/// fn cube(length, center) {
/// l = length/2
/// x = center[0]
/// y = center[1]
/// p0 = [-l + x, -l + y]
/// p1 = [-l + x, l + y]
/// p2 = [ l + x, l + y]
/// p3 = [ l + x, -l + y]
///
/// return startSketchOn(XY)
/// |> startProfile(at = p0)
/// |> line(endAbsolute = p1)
/// |> line(endAbsolute = p2)
/// |> line(endAbsolute = p3)
/// |> line(endAbsolute = p0)
/// |> close()
/// |> extrude(length = length)
/// }
///
/// width = 20
/// fn transform(@i) {
/// return {
/// // Move down each time.
/// translate = [0, 0, -i * width],
/// // Make the cube longer, wider and flatter each time.
/// scale = [pow(1.1, exp = i), pow(1.1, exp = i), pow(0.9, exp = i)],
/// // Turn by 15 degrees each time.
/// rotation = {
/// angle = 15 * i,
/// origin = "local",
/// }
/// }
/// }
///
/// myCubes =
/// cube(length = width, center = [100,0])
/// |> patternTransform(instances = 25, transform = transform)
/// ```
///
/// ```no_run
/// fn cube(length, center) {
/// l = length/2
/// x = center[0]
/// y = center[1]
/// p0 = [-l + x, -l + y]
/// p1 = [-l + x, l + y]
/// p2 = [ l + x, l + y]
/// p3 = [ l + x, -l + y]
///
/// return startSketchOn(XY)
/// |> startProfile(at = p0)
/// |> line(endAbsolute = p1)
/// |> line(endAbsolute = p2)
/// |> line(endAbsolute = p3)
/// |> line(endAbsolute = p0)
/// |> close()
/// |> extrude(length = length)
/// }
///
/// width = 20
/// fn transform(@i) {
/// return {
/// translate = [0, 0, -i * width],
/// rotation = {
/// angle = 90 * i,
/// // Rotate around the overall scene's origin.
/// origin = "global",
/// }
/// }
/// }
/// myCubes =
/// cube(length = width, center = [100,100])
/// |> patternTransform(instances = 4, transform = transform)
/// ```
/// ```no_run
/// // Parameters
/// r = 50 // base radius
/// h = 10 // layer height
/// t = 0.005 // taper factor [0-1)
/// // Defines how to modify each layer of the vase.
/// // Each replica is shifted up the Z axis, and has a smoothly-varying radius
/// fn transform(@replicaId) {
/// scale = r * abs(1 - (t * replicaId)) * (5 + cos((replicaId / 8): number(rad)))
/// return {
/// translate = [0, 0, replicaId * 10],
/// scale = [scale, scale, 0],
/// }
/// }
/// // Each layer is just a pretty thin cylinder.
/// fn layer() {
/// return startSketchOn(XY) // or some other plane idk
/// |> circle(center = [0, 0], radius = 1, tag = $tag1)
/// |> extrude(length = h)
/// }
/// // The vase is 100 layers tall.
/// // The 100 layers are replica of each other, with a slight transformation applied to each.
/// vase = layer() |> patternTransform(instances = 100, transform = transform)
/// ```
/// ```
/// fn transform(@i) {
/// // Transform functions can return multiple transforms. They'll be applied in order.
/// return [
/// { translate = [30 * i, 0, 0] },
/// { rotation = { angle = 45 * i } },
/// ]
/// }
/// startSketchOn(XY)
/// |> startProfile(at = [0, 0])
/// |> polygon(
/// radius = 10,
/// numSides = 4,
/// center = [0, 0],
/// inscribed = false,
/// )
/// |> extrude(length = 4)
/// |> patternTransform(instances = 3, transform = transform)
/// ```
#[stdlib {
name = "patternTransform",
feature_tree_operation = true,
keywords = true,
unlabeled_first = true,
args = {
solids = { docs = "The solid(s) to duplicate" },
instances = { docs = "The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect." },
transform = { docs = "How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples." },
use_original = { docs = "If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false." },
},
tags = ["solid"]
}]
async fn inner_pattern_transform<'a>( async fn inner_pattern_transform<'a>(
solids: Vec<Solid>, solids: Vec<Solid>,
instances: u32, instances: u32,
@ -283,30 +86,6 @@ async fn inner_pattern_transform<'a>(
.await .await
} }
/// Just like patternTransform, but works on 2D sketches not 3D solids.
/// ```no_run
/// // Each instance will be shifted along the X axis.
/// fn transform(@id) {
/// return { translate = [4 * id, 0] }
/// }
///
/// // Sketch 4 circles.
/// sketch001 = startSketchOn(XZ)
/// |> circle(center = [0, 0], radius= 2)
/// |> patternTransform2d(instances = 4, transform = transform)
/// ```
#[stdlib {
name = "patternTransform2d",
keywords = true,
unlabeled_first = true,
args = {
sketches = { docs = "The sketch(es) to duplicate" },
instances = { docs = "The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect." },
transform = { docs = "How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples." },
use_original = { docs = "If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false." },
},
tags = ["sketch"]
}]
async fn inner_pattern_transform_2d<'a>( async fn inner_pattern_transform_2d<'a>(
sketches: Vec<Sketch>, sketches: Vec<Sketch>,
instances: u32, instances: u32,
@ -1228,7 +1007,16 @@ pub async fn pattern_circular_3d(exec_state: &mut ExecState, args: Args) -> Resu
// If instances is 1, this has no effect. // If instances is 1, this has no effect.
let instances: u32 = args.get_kw_arg_typed("instances", &RuntimeType::count(), exec_state)?; let instances: u32 = args.get_kw_arg_typed("instances", &RuntimeType::count(), exec_state)?;
// The axis around which to make the pattern. This is a 3D vector. // The axis around which to make the pattern. This is a 3D vector.
let axis: [TyF64; 3] = args.get_kw_arg_typed("axis", &RuntimeType::point3d(), exec_state)?; let axis: Axis3dOrPoint3d = args.get_kw_arg_typed(
"axis",
&RuntimeType::Union(vec![
RuntimeType::Primitive(PrimitiveType::Axis3d),
RuntimeType::point3d(),
]),
exec_state,
)?;
let axis = axis.to_point3d();
// The center about which to make the pattern. This is a 3D vector. // The center about which to make the pattern. This is a 3D vector.
let center: [TyF64; 3] = args.get_kw_arg_typed("center", &RuntimeType::point3d(), exec_state)?; let center: [TyF64; 3] = args.get_kw_arg_typed("center", &RuntimeType::point3d(), exec_state)?;
// The arc angle (in degrees) to place the repetitions. Must be greater than 0. // The arc angle (in degrees) to place the repetitions. Must be greater than 0.
@ -1260,6 +1048,24 @@ pub async fn pattern_circular_3d(exec_state: &mut ExecState, args: Args) -> Resu
/// solid with respect to the center of the circle is maintained. /// solid with respect to the center of the circle is maintained.
/// ///
/// ```no_run /// ```no_run
/// /// Pattern using a named axis.
///
/// exampleSketch = startSketchOn(XZ)
/// |> circle(center = [0, 0], radius = 1)
///
/// example = extrude(exampleSketch, length = -5)
/// |> patternCircular3d(
/// axis = X,
/// center = [10, -20, 0],
/// instances = 11,
/// arcDegrees = 360,
/// rotateDuplicates = true
/// )
/// ```
///
/// ```no_run
/// /// Pattern using a raw axis.
///
/// exampleSketch = startSketchOn(XZ) /// exampleSketch = startSketchOn(XZ)
/// |> circle(center = [0, 0], radius = 1) /// |> circle(center = [0, 0], radius = 1)
/// ///

View File

@ -26,7 +26,7 @@ use crate::{
args::{Args, TyF64}, args::{Args, TyF64},
utils::{ utils::{
arc_center_and_end, get_tangential_arc_to_info, get_x_component, get_y_component, arc_center_and_end, get_tangential_arc_to_info, get_x_component, get_y_component,
intersection_with_parallel_line, point_to_len_unit, point_to_mm, untype_point, untyped_point_to_mm, intersection_with_parallel_line, point_to_len_unit, point_to_mm, untyped_point_to_mm,
TangentialArcInfoInput, TangentialArcInfoInput,
}, },
}, },
@ -1396,12 +1396,14 @@ pub(crate) async fn inner_start_profile(
]) ])
.await?; .await?;
let (to, ty) = untype_point(at); // Convert to the units of the module. This is what the frontend expects.
let units = exec_state.length_unit();
let to = point_to_len_unit(at, units);
let current_path = BasePath { let current_path = BasePath {
from: to, from: to,
to, to,
tag: tag.clone(), tag: tag.clone(),
units: ty.expect_length(), units,
geo_meta: GeoMeta { geo_meta: GeoMeta {
id: move_pen_id, id: move_pen_id,
metadata: args.source_range.into(), metadata: args.source_range.into(),
@ -1414,7 +1416,7 @@ pub(crate) async fn inner_start_profile(
artifact_id: path_id.into(), artifact_id: path_id.into(),
on: sketch_surface.clone(), on: sketch_surface.clone(),
paths: vec![], paths: vec![],
units: ty.expect_length(), units,
mirror: Default::default(), mirror: Default::default(),
meta: vec![args.source_range.into()], meta: vec![args.source_range.into()],
tags: if let Some(tag) = &tag { tags: if let Some(tag) = &tag {

View File

@ -11,11 +11,13 @@ use kcmc::{
}; };
use kittycad_modeling_cmds as kcmc; use kittycad_modeling_cmds as kcmc;
use super::args::TyF64;
use crate::{ use crate::{
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
execution::{types::RuntimeType, ExecState, KclValue, SolidOrSketchOrImportedGeometry}, execution::{
std::Args, types::{PrimitiveType, RuntimeType},
ExecState, KclValue, SolidOrSketchOrImportedGeometry,
},
std::{args::TyF64, axis_or_reference::Axis3dOrPoint3d, Args},
}; };
/// Scale a solid or a sketch. /// Scale a solid or a sketch.
@ -446,7 +448,15 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
let roll: Option<TyF64> = args.get_kw_arg_opt_typed("roll", &RuntimeType::degrees(), exec_state)?; let roll: Option<TyF64> = args.get_kw_arg_opt_typed("roll", &RuntimeType::degrees(), exec_state)?;
let pitch: Option<TyF64> = args.get_kw_arg_opt_typed("pitch", &RuntimeType::degrees(), exec_state)?; let pitch: Option<TyF64> = args.get_kw_arg_opt_typed("pitch", &RuntimeType::degrees(), exec_state)?;
let yaw: Option<TyF64> = args.get_kw_arg_opt_typed("yaw", &RuntimeType::degrees(), exec_state)?; let yaw: Option<TyF64> = args.get_kw_arg_opt_typed("yaw", &RuntimeType::degrees(), exec_state)?;
let axis: Option<[TyF64; 3]> = args.get_kw_arg_opt_typed("axis", &RuntimeType::point3d(), exec_state)?; let axis: Option<Axis3dOrPoint3d> = args.get_kw_arg_opt_typed(
"axis",
&RuntimeType::Union(vec![
RuntimeType::Primitive(PrimitiveType::Axis3d),
RuntimeType::point3d(),
]),
exec_state,
)?;
let axis = axis.map(|a| a.to_point3d());
let angle: Option<TyF64> = args.get_kw_arg_opt_typed("angle", &RuntimeType::degrees(), exec_state)?; let angle: Option<TyF64> = args.get_kw_arg_opt_typed("angle", &RuntimeType::degrees(), exec_state)?;
let global = args.get_kw_arg_opt("global")?; let global = args.get_kw_arg_opt("global")?;
@ -642,7 +652,51 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
/// ``` /// ```
/// ///
/// ```no_run /// ```no_run
/// // Rotate a pipe about an axis with an angle. /// // Rotate a pipe about a named axis with an angle.
///
/// // Create a path for the sweep.
/// sweepPath = startSketchOn(XZ)
/// |> startProfile(at = [0.05, 0.05])
/// |> line(end = [0, 7])
/// |> tangentialArc(angle = 90, radius = 5)
/// |> line(end = [-3, 0])
/// |> tangentialArc(angle = -90, radius = 5)
/// |> line(end = [0, 7])
///
/// // Create a hole for the pipe.
/// pipeHole = startSketchOn(XY)
/// |> circle(
/// center = [0, 0],
/// radius = 1.5,
/// )
///
/// sweepSketch = startSketchOn(XY)
/// |> circle(
/// center = [0, 0],
/// radius = 2,
/// )
/// |> subtract2d(tool = pipeHole)
/// |> sweep(path = sweepPath)
/// |> rotate(
/// axis = Z,
/// angle = 90,
/// )
/// ```
///
/// ```no_run
/// // Rotate an imported model.
///
/// import "tests/inputs/cube.sldprt" as cube
///
/// cube
/// |> rotate(
/// axis = [0, 0, 1.0],
/// angle = 9,
/// )
/// ```
///
/// ```no_run
/// // Rotate a pipe about a raw axis with an angle.
/// ///
/// // Create a path for the sweep. /// // Create a path for the sweep.
/// sweepPath = startSketchOn(XZ) /// sweepPath = startSketchOn(XZ)
@ -673,18 +727,6 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
/// ) /// )
/// ``` /// ```
/// ///
/// ```no_run
/// // Rotate an imported model.
///
/// import "tests/inputs/cube.sldprt" as cube
///
/// cube
/// |> rotate(
/// axis = [0, 0, 1.0],
/// angle = 9,
/// )
/// ```
///
/// ``` /// ```
/// // Sweep two sketches along the same path. /// // Sweep two sketches along the same path.
/// ///

View File

@ -6,7 +6,7 @@
/// The standard library is organised into modules (listed below), but most things are always available /// The standard library is organised into modules (listed below), but most things are always available
/// in KCL programs. /// in KCL programs.
/// ///
/// You might also want the [KCL language reference](/docs/kcl-lang) or the [KCL guide](). /// You might also want the [KCL language reference](/docs/kcl-lang) or the [KCL guide](https://zoo.dev/docs/kcl-book/intro.html).
@no_std @no_std
@settings(defaultLengthUnit = mm, kclVersion = 1.0) @settings(defaultLengthUnit = mm, kclVersion = 1.0)

View File

@ -278,3 +278,28 @@ export fn revolve(
/// A named tag for the face at the end of the revolve. /// A named tag for the face at the end of the revolve.
tagEnd?: tag, tagEnd?: tag,
): [Solid; 1+] {} ): [Solid; 1+] {}
/// Just like `patternTransform`, but works on 2D sketches not 3D solids.
///
/// ```kcl
/// // Each instance will be shifted along the X axis.
/// fn transform(@id) {
/// return { translate = [4 * id, 0] }
/// }
///
/// // Sketch 4 circles.
/// sketch001 = startSketchOn(XZ)
/// |> circle(center = [0, 0], radius = 2)
/// |> patternTransform2d(instances = 4, transform = transform)
/// ```
@(impl = std_rust)
export fn patternTransform2d(
/// The sketch(es) to duplicate.
@sketches: [Sketch; 1+],
/// The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect.
instances: number(Count),
/// How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples.
transform: fn(number(Count)): {},
/// If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid.
useOriginal?: boolean = false,
): [Sketch; 1+] {}

View File

@ -365,3 +365,202 @@ export fn hollow(
/// The thickness of the remaining shell /// The thickness of the remaining shell
thickness: number(Length), thickness: number(Length),
): Solid {} ): Solid {}
/// Repeat a 3-dimensional solid, changing it each time.
///
/// Replicates the 3D solid, applying a transformation function to each replica.
/// Transformation function could alter rotation, scale, visibility, position, etc.
///
/// The `patternTransform` call itself takes a number for how many total instances of
/// the shape should be. For example, if you use a circle with `patternTransform(instances = 4, transform = f)`
/// then there will be 4 circles: the original, and 3 created by replicating the original and
/// calling the transform function on each.
///
/// The transform function takes a single parameter: an integer representing which
/// number replication the transform is for. E.g. the first replica to be transformed
/// will be passed the argument `1`. This simplifies your math: the transform function can
/// rely on id `0` being the original instance passed into the `patternTransform`. See the examples.
///
/// The transform function returns a transform object. All properties of the object are optional,
/// they each default to "no change". So the overall transform object defaults to "no change" too.
/// Its properties are:
///
/// - `translate` (3D point)
///
/// Translates the replica, moving its position in space.
///
/// - `replicate` (bool)
///
/// If false, this ID will not actually copy the object. It'll be skipped.
///
/// - `scale` (3D point)
///
/// Stretches the object, multiplying its width in the given dimension by the point's component in
/// that direction.
///
/// - `rotation` (object, with the following properties)
///
/// - `rotation.axis` (a 3D point, defaults to the Z axis)
///
/// - `rotation.angle` (number of degrees)
///
/// - `rotation.origin` (either "local" i.e. rotate around its own center, "global" i.e. rotate around the scene's center, or a 3D point, defaults to "local")
///
/// ```kcl
/// // Each instance will be shifted along the X axis.
/// fn transform(@id) {
/// return { translate = [4 * id, 0, 0] }
/// }
///
/// // Sketch 4 cylinders.
/// sketch001 = startSketchOn(XZ)
/// |> circle(center = [0, 0], radius = 2)
/// |> extrude(length = 5)
/// |> patternTransform(instances = 4, transform = transform)
/// ```
///
/// ```kcl
/// // Each instance will be shifted along the X axis,
/// // with a gap between the original (at x = 0) and the first replica
/// // (at x = 8). This is because `id` starts at 1.
/// fn transform(@id) {
/// return { translate = [4 * (1+id), 0, 0] }
/// }
///
/// sketch001 = startSketchOn(XZ)
/// |> circle(center = [0, 0], radius = 2)
/// |> extrude(length = 5)
/// |> patternTransform(instances = 4, transform = transform)
/// ```
///
/// ```kcl
/// fn cube(length, center) {
/// l = length/2
/// x = center[0]
/// y = center[1]
/// p0 = [-l + x, -l + y]
/// p1 = [-l + x, l + y]
/// p2 = [ l + x, l + y]
/// p3 = [ l + x, -l + y]
///
/// return startSketchOn(XY)
/// |> startProfile(at = p0)
/// |> line(endAbsolute = p1)
/// |> line(endAbsolute = p2)
/// |> line(endAbsolute = p3)
/// |> line(endAbsolute = p0)
/// |> close()
/// |> extrude(length = length)
/// }
///
/// width = 20
/// fn transform(@i) {
/// return {
/// // Move down each time.
/// translate = [0, 0, -i * width],
/// // Make the cube longer, wider and flatter each time.
/// scale = [pow(1.1, exp = i), pow(1.1, exp = i), pow(0.9, exp = i)],
/// // Turn by 15 degrees each time.
/// rotation = {
/// angle = 15 * i,
/// origin = "local",
/// }
/// }
/// }
///
/// myCubes =
/// cube(length = width, center = [100,0])
/// |> patternTransform(instances = 25, transform = transform)
/// ```
///
/// ```kcl
/// fn cube(length, center) {
/// l = length/2
/// x = center[0]
/// y = center[1]
/// p0 = [-l + x, -l + y]
/// p1 = [-l + x, l + y]
/// p2 = [ l + x, l + y]
/// p3 = [ l + x, -l + y]
///
/// return startSketchOn(XY)
/// |> startProfile(at = p0)
/// |> line(endAbsolute = p1)
/// |> line(endAbsolute = p2)
/// |> line(endAbsolute = p3)
/// |> line(endAbsolute = p0)
/// |> close()
/// |> extrude(length = length)
/// }
///
/// width = 20
/// fn transform(@i) {
/// return {
/// translate = [0, 0, -i * width],
/// rotation = {
/// angle = 90 * i,
/// // Rotate around the overall scene's origin.
/// origin = "global",
/// }
/// }
/// }
/// myCubes =
/// cube(length = width, center = [100,100])
/// |> patternTransform(instances = 4, transform = transform)
/// ```
///
/// ```kcl
/// // Parameters
/// r = 50 // base radius
/// h = 10 // layer height
/// t = 0.005 // taper factor [0-1)
/// // Defines how to modify each layer of the vase.
/// // Each replica is shifted up the Z axis, and has a smoothly-varying radius
/// fn transform(@replicaId) {
/// scale = r * abs(1 - (t * replicaId)) * (5 + cos((replicaId / 8): number(rad)))
/// return {
/// translate = [0, 0, replicaId * 10],
/// scale = [scale, scale, 0],
/// }
/// }
/// // Each layer is just a pretty thin cylinder.
/// fn layer() {
/// return startSketchOn(XY) // or some other plane idk
/// |> circle(center = [0, 0], radius = 1, tag = $tag1)
/// |> extrude(length = h)
/// }
/// // The vase is 100 layers tall.
/// // The 100 layers are replica of each other, with a slight transformation applied to each.
/// vase = layer() |> patternTransform(instances = 100, transform = transform)
/// ```
///
/// ```kcl
/// fn transform(@i) {
/// // Transform functions can return multiple transforms. They'll be applied in order.
/// return [
/// { translate = [30 * i, 0, 0] },
/// { rotation = { angle = 45 * i } },
/// ]
/// }
/// startSketchOn(XY)
/// |> startProfile(at = [0, 0])
/// |> polygon(
/// radius = 10,
/// numSides = 4,
/// center = [0, 0],
/// inscribed = false,
/// )
/// |> extrude(length = 4)
/// |> patternTransform(instances = 3, transform = transform)
/// ```
@(impl = std_rust)
export fn patternTransform(
/// The solid(s) to duplicate.
@solids: [Solid; 1+],
/// The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect.
instances: number(Count),
/// How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples.
transform: fn(number(Count)): {},
/// If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid.
useOriginal?: boolean = false,
): [Solid; 1+] {}

View File

@ -296,15 +296,15 @@ flowchart LR
33 --- 92 33 --- 92
33 --- 105 33 --- 105
37 --- 65 37 --- 65
37 x--> 84 37 x--> 80
37 --- 86 37 --- 86
37 --- 99 37 --- 99
38 --- 66 38 --- 66
38 x--> 84 38 x--> 80
38 --- 87 38 --- 87
38 --- 100 38 --- 100
39 --- 67 39 --- 67
39 x--> 84 39 x--> 80
39 --- 88 39 --- 88
39 --- 101 39 --- 101
46 --- 75 46 --- 75
@ -409,9 +409,6 @@ flowchart LR
77 --- 96 77 --- 96
77 --- 109 77 --- 109
110 <--x 77 110 <--x 77
86 <--x 80
87 <--x 80
88 <--x 80
92 <--x 82 92 <--x 82
93 <--x 82 93 <--x 82
94 <--x 82 94 <--x 82
@ -419,6 +416,9 @@ flowchart LR
96 <--x 83 96 <--x 83
97 <--x 83 97 <--x 83
98 <--x 83 98 <--x 83
86 <--x 84
87 <--x 84
88 <--x 84
89 <--x 85 89 <--x 85
90 <--x 85 90 <--x 85
91 <--x 85 91 <--x 85

View File

@ -337,6 +337,15 @@ description: Artifact commands error_revolve_on_edge_get_edge.kcl
"opposite": "None" "opposite": "None"
} }
}, },
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "solid3d_get_adjacency_info",
"object_id": "[uuid]",
"edge_id": "[uuid]"
}
},
{ {
"cmdId": "[uuid]", "cmdId": "[uuid]",
"range": [], "range": [],

View File

@ -32,28 +32,30 @@ flowchart LR
%% [ProgramBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }] %% [ProgramBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }]
15["Sweep Extrusion<br>[169, 189, 0]"] 15["Sweep Extrusion<br>[169, 189, 0]"]
%% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 6 }] %% [ProgramBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 6 }]
16[Wall] 16["Sweep RevolveAboutEdge<br>[367, 406, 0]"]
%% face_code_ref=Missing NodePath %% [ProgramBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 6 }]
17[Wall] 17[Wall]
%% face_code_ref=Missing NodePath %% face_code_ref=Missing NodePath
18[Wall] 18[Wall]
%% face_code_ref=Missing NodePath %% face_code_ref=Missing NodePath
19[Wall] 19[Wall]
%% face_code_ref=Missing NodePath
20[Wall]
%% face_code_ref=[ProgramBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }] %% face_code_ref=[ProgramBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }]
20["Cap Start"] 21["Cap Start"]
%% face_code_ref=Missing NodePath %% face_code_ref=Missing NodePath
21["Cap End"] 22["Cap End"]
%% face_code_ref=Missing NodePath %% face_code_ref=Missing NodePath
22["SweepEdge Opposite"]
23["SweepEdge Opposite"] 23["SweepEdge Opposite"]
24["SweepEdge Opposite"] 24["SweepEdge Opposite"]
25["SweepEdge Opposite"] 25["SweepEdge Opposite"]
26["SweepEdge Adjacent"] 26["SweepEdge Opposite"]
27["SweepEdge Adjacent"] 27["SweepEdge Adjacent"]
28["SweepEdge Adjacent"] 28["SweepEdge Adjacent"]
29["SweepEdge Adjacent"] 29["SweepEdge Adjacent"]
30["SweepEdge Adjacent"]
1 --- 3 1 --- 3
19 x--> 2 20 x--> 2
3 --- 5 3 --- 5
3 --- 6 3 --- 6
3 --- 7 3 --- 7
@ -65,24 +67,24 @@ flowchart LR
4 --- 11 4 --- 11
4 --- 12 4 --- 12
4 --- 14 4 --- 14
19 --- 4 4 ---- 16
5 --- 18 20 --- 4
5 x--> 20 5 --- 19
5 --- 25 5 x--> 21
5 --- 29 5 --- 26
6 --- 16 5 --- 30
6 x--> 20 6 --- 17
6 --- 24 6 x--> 21
6 --- 28 6 --- 25
7 --- 19 6 --- 29
7 x--> 20 7 --- 20
7 --- 23 7 x--> 21
7 --- 27 7 --- 24
8 --- 17 7 --- 28
8 x--> 20 8 --- 18
8 --- 22 8 x--> 21
8 --- 26 8 --- 23
15 --- 16 8 --- 27
15 --- 17 15 --- 17
15 --- 18 15 --- 18
15 --- 19 15 --- 19
@ -96,20 +98,21 @@ flowchart LR
15 --- 27 15 --- 27
15 --- 28 15 --- 28
15 --- 29 15 --- 29
16 --- 24 15 --- 30
16 --- 28 17 --- 25
29 <--x 16 17 --- 29
17 --- 22 30 <--x 17
17 --- 26 18 --- 23
27 <--x 17 18 --- 27
18 --- 25 28 <--x 18
26 <--x 18 19 --- 26
18 --- 29 27 <--x 19
19 --- 23 19 --- 30
19 --- 27 20 --- 24
28 <--x 19 20 --- 28
22 <--x 21 29 <--x 20
23 <--x 21 23 <--x 22
24 <--x 21 24 <--x 22
25 <--x 21 25 <--x 22
26 <--x 22
``` ```

View File

@ -4,23 +4,11 @@ description: Error from executing error_revolve_on_edge_get_edge.kcl
--- ---
KCL Engine error KCL Engine error
× engine: Solid3D revolve failed: sketch profile must lie entirely on one × engine: Entity found but it was of the wrong type
side of the revolution axis No such edge exists, cannot retrieve adjacency information.
╭─[15:6] ╭─[15:6]
14 │ |> close() 14 │ |> close()
15 │ |> revolve(axis = revolveAxis, angle = 90) 15 │ |> revolve(axis = revolveAxis, angle = 90)
· ───────────────────┬─────────────────── · ───────────────────┬───────────────────
· ╰── tests/error_revolve_on_edge_get_edge/input.kcl
· ╰── tests/error_revolve_on_edge_get_edge/input.kcl · ╰── tests/error_revolve_on_edge_get_edge/input.kcl
╰──── ╰────
╰─▶ KCL Engine error
× engine: Solid3D revolve failed: sketch profile must lie entirely on
│ one side of the revolution axis
╭─[15:6]
14 │ |> close()
15 │ |> revolve(axis = revolveAxis, angle = 90)
· ───────────────────┬───────────────────
· ╰── tests/error_revolve_on_edge_get_edge/
input.kcl
╰────

View File

@ -110,7 +110,6 @@ description: Operations executed error_revolve_on_edge_get_edge.kcl
"sourceRange": [] "sourceRange": []
} }
}, },
"sourceRange": [], "sourceRange": []
"isError": true
} }
] ]

View File

@ -0,0 +1,5 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Artifact commands export_only_at_top_level.kcl
---
[]

View File

@ -0,0 +1,6 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Artifact graph flowchart export_only_at_top_level.kcl
extension: md
snapshot_kind: binary
---

View File

@ -0,0 +1,3 @@
```mermaid
flowchart LR
```

View File

@ -0,0 +1,148 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of parsing export_only_at_top_level.kcl
---
{
"Ok": {
"body": [
{
"commentStart": 0,
"declaration": {
"commentStart": 0,
"end": 0,
"id": {
"commentStart": 0,
"end": 0,
"name": "main",
"start": 0,
"type": "Identifier"
},
"init": {
"body": {
"body": [
{
"commentStart": 0,
"declaration": {
"commentStart": 0,
"end": 0,
"id": {
"commentStart": 0,
"end": 0,
"name": "x",
"start": 0,
"type": "Identifier"
},
"init": {
"commentStart": 0,
"end": 0,
"raw": "2",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 2.0,
"suffix": "None"
}
},
"start": 0,
"type": "VariableDeclarator"
},
"end": 0,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration",
"visibility": "export"
},
{
"argument": {
"commentStart": 0,
"end": 0,
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "ReturnStatement",
"type": "ReturnStatement"
}
],
"commentStart": 0,
"end": 0,
"start": 0
},
"commentStart": 0,
"end": 0,
"params": [],
"start": 0,
"type": "FunctionExpression",
"type": "FunctionExpression"
},
"start": 0,
"type": "VariableDeclarator"
},
"end": 0,
"kind": "fn",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"commentStart": 0,
"end": 0,
"expression": {
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "main",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
},
"start": 0,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
}
],
"commentStart": 0,
"end": 0,
"nonCodeMeta": {
"nonCodeNodes": {
"0": [
{
"commentStart": 0,
"end": 0,
"start": 0,
"type": "NonCodeNode",
"value": {
"type": "newLine"
}
}
]
},
"startNodes": []
},
"start": 0
}
}

View File

@ -0,0 +1,15 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Error from executing export_only_at_top_level.kcl
---
KCL Semantic error
× semantic: Exports are only supported at the top-level of a file. Remove
│ `export` or move it to the top-level.
╭─[2:3]
1 │ fn main() {
2 │ export x = 2
· ──────┬─────
· ╰── main
3 │ return 0
╰────

View File

@ -0,0 +1,6 @@
fn main() {
export x = 2
return 0
}
main()

View File

@ -0,0 +1,5 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Operations executed export_only_at_top_level.kcl
---
[]

View File

@ -0,0 +1,10 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing export_only_at_top_level.kcl
---
fn main() {
export x = 2
return 0
}
main()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

View File

@ -0,0 +1,32 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Artifact commands import_only_at_top_level.kcl
---
[
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "edge_lines_visible",
"hidden": false
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_visible",
"object_id": "[uuid]",
"hidden": true
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_visible",
"object_id": "[uuid]",
"hidden": true
}
}
]

View File

@ -0,0 +1,6 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Artifact graph flowchart import_only_at_top_level.kcl
extension: md
snapshot_kind: binary
---

View File

@ -0,0 +1,3 @@
```mermaid
flowchart LR
```

View File

@ -0,0 +1,129 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of parsing import_only_at_top_level.kcl
---
{
"Ok": {
"body": [
{
"commentStart": 0,
"declaration": {
"commentStart": 0,
"end": 0,
"id": {
"commentStart": 0,
"end": 0,
"name": "main",
"start": 0,
"type": "Identifier"
},
"init": {
"body": {
"body": [
{
"commentStart": 0,
"end": 0,
"path": {
"type": "Kcl",
"filename": "empty.kcl"
},
"selector": {
"type": "None",
"alias": null
},
"start": 0,
"type": "ImportStatement",
"type": "ImportStatement"
},
{
"argument": {
"commentStart": 0,
"end": 0,
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "ReturnStatement",
"type": "ReturnStatement"
}
],
"commentStart": 0,
"end": 0,
"start": 0
},
"commentStart": 0,
"end": 0,
"params": [],
"start": 0,
"type": "FunctionExpression",
"type": "FunctionExpression"
},
"start": 0,
"type": "VariableDeclarator"
},
"end": 0,
"kind": "fn",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"commentStart": 0,
"end": 0,
"expression": {
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "main",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
},
"start": 0,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
}
],
"commentStart": 0,
"end": 0,
"nonCodeMeta": {
"nonCodeNodes": {
"0": [
{
"commentStart": 0,
"end": 0,
"start": 0,
"type": "NonCodeNode",
"value": {
"type": "newLine"
}
}
]
},
"startNodes": []
},
"start": 0
}
}

View File

@ -0,0 +1,30 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Error from executing import_only_at_top_level.kcl
---
KCL Semantic error
× semantic: Imports are only supported at the top-level of a file.
╭─[2:3]
1 │ fn main() {
2 │ import "empty.kcl"
· ─────────┬────────
· ╰── tests/import_only_at_top_level/input.kcl
3 │ return 0
╰────
╭─[6:1]
5 │
6 │ main()
· ───┬──
· ╰── tests/import_only_at_top_level/input.kcl
╰────
╰─▶ KCL Semantic error
× semantic: Imports are only supported at the top-level of a file.
╭─[2:3]
1 │ fn main() {
2 │ import "empty.kcl"
· ─────────┬────────
· ╰── tests/import_only_at_top_level/input.kcl
3 │ return 0
╰────

View File

@ -0,0 +1,6 @@
fn main() {
import "empty.kcl"
return 0
}
main()

View File

@ -0,0 +1,32 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Operations executed import_only_at_top_level.kcl
---
[
{
"type": "GroupBegin",
"group": {
"type": "ModuleInstance",
"name": "empty.kcl",
"moduleId": 0
},
"sourceRange": []
},
{
"type": "GroupBegin",
"group": {
"type": "FunctionCall",
"name": "main",
"functionSourceRange": [],
"unlabeledArg": null,
"labeledArgs": {}
},
"sourceRange": []
},
{
"type": "GroupEnd"
},
{
"type": "GroupEnd"
}
]

View File

@ -0,0 +1,10 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing import_only_at_top_level.kcl
---
fn main() {
import "empty.kcl"
return 0
}
main()

View File

@ -0,0 +1,5 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing tests/import_only_at_top_level/empty.kcl
---

View File

@ -661,19 +661,19 @@ flowchart LR
84 --- 144 84 --- 144
84 --- 237 84 --- 237
86 --- 168 86 --- 168
86 x--> 188 86 x--> 189
86 --- 212 86 --- 212
86 --- 256 86 --- 256
88 --- 169 88 --- 169
88 x--> 188 88 x--> 189
88 --- 213 88 --- 213
88 --- 257 88 --- 257
90 --- 167 90 --- 167
90 x--> 188 90 x--> 189
90 --- 214 90 --- 214
90 --- 258 90 --- 258
92 --- 170 92 --- 170
92 x--> 188 92 x--> 189
92 --- 215 92 --- 215
92 --- 259 92 --- 259
119 --- 133 119 --- 133
@ -955,10 +955,10 @@ flowchart LR
218 <--x 186 218 <--x 186
219 <--x 186 219 <--x 186
194 <--x 187 194 <--x 187
212 <--x 189 212 <--x 188
213 <--x 189 213 <--x 188
214 <--x 189 214 <--x 188
215 <--x 189 215 <--x 188
220 <--x 275 220 <--x 275
223 <--x 270 223 <--x 270
224 <--x 274 224 <--x 274

View File

@ -177,7 +177,7 @@ flowchart LR
22 --- 44 22 --- 44
22 --- 62 22 --- 62
23 --- 38 23 --- 38
23 x--> 52 23 x--> 49
23 --- 53 23 --- 53
23 --- 56 23 --- 56
24 --- 46 24 --- 46
@ -226,7 +226,7 @@ flowchart LR
62 <--x 45 62 <--x 45
46 --- 55 46 --- 55
46 --- 63 46 --- 63
53 <--x 49
54 <--x 50 54 <--x 50
55 <--x 51 55 <--x 51
53 <--x 52
``` ```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 98 KiB

View File

@ -327,67 +327,67 @@ flowchart LR
11 ---- 54 11 ---- 54
11 --- 62 11 --- 62
12 --- 83 12 --- 83
12 x--> 100 12 x--> 91
12 --- 113 12 --- 113
12 --- 137 12 --- 137
13 --- 78 13 --- 78
13 x--> 100 13 x--> 91
13 --- 114 13 --- 114
13 --- 138 13 --- 138
14 --- 77 14 --- 77
14 x--> 100 14 x--> 91
14 --- 115 14 --- 115
14 --- 139 14 --- 139
15 --- 79 15 --- 79
15 x--> 100 15 x--> 91
15 --- 116 15 --- 116
15 --- 140 15 --- 140
16 --- 75 16 --- 75
16 x--> 100 16 x--> 91
16 --- 117 16 --- 117
16 --- 141 16 --- 141
17 --- 74 17 --- 74
17 x--> 100 17 x--> 91
17 --- 118 17 --- 118
17 --- 142 17 --- 142
18 --- 82 18 --- 82
18 x--> 100 18 x--> 91
18 --- 119 18 --- 119
18 --- 143 18 --- 143
19 --- 85 19 --- 85
19 x--> 100 19 x--> 91
19 --- 120 19 --- 120
19 --- 144 19 --- 144
20 --- 84 20 --- 84
20 x--> 100 20 x--> 91
20 --- 121 20 --- 121
20 --- 145 20 --- 145
21 --- 72 21 --- 72
21 x--> 100 21 x--> 91
21 --- 122 21 --- 122
21 --- 146 21 --- 146
22 --- 73 22 --- 73
22 x--> 100 22 x--> 91
22 --- 123 22 --- 123
22 --- 147 22 --- 147
23 --- 86 23 --- 86
23 x--> 100 23 x--> 91
23 --- 124 23 --- 124
23 --- 148 23 --- 148
24 --- 80 24 --- 80
24 x--> 100 24 x--> 91
24 --- 125 24 --- 125
24 --- 149 24 --- 149
25 --- 76 25 --- 76
25 x--> 100 25 x--> 91
25 --- 126 25 --- 126
25 --- 150 25 --- 150
26 --- 71 26 --- 71
26 x--> 100 26 x--> 91
26 --- 127 26 --- 127
26 --- 151 26 --- 151
27 --- 81 27 --- 81
27 x--> 100 27 x--> 91
27 --- 128 27 --- 128
27 --- 152 27 --- 152
29 --- 70 29 --- 70
@ -587,24 +587,24 @@ flowchart LR
110 <--x 88 110 <--x 88
112 <--x 89 112 <--x 89
108 <--x 90 108 <--x 90
113 <--x 91
114 <--x 91
115 <--x 91
116 <--x 91
117 <--x 91
118 <--x 91
119 <--x 91
120 <--x 91
121 <--x 91
122 <--x 91
123 <--x 91
124 <--x 91
125 <--x 91
126 <--x 91
127 <--x 91
128 <--x 91
111 <--x 92 111 <--x 92
106 <--x 93 106 <--x 93
105 <--x 94 105 <--x 94
107 <--x 95 107 <--x 95
113 <--x 100
114 <--x 100
115 <--x 100
116 <--x 100
117 <--x 100
118 <--x 100
119 <--x 100
120 <--x 100
121 <--x 100
122 <--x 100
123 <--x 100
124 <--x 100
125 <--x 100
126 <--x 100
127 <--x 100
128 <--x 100
``` ```

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,6 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Artifact graph flowchart brake-rotor.kcl
extension: md
snapshot_kind: binary
---

View File

@ -0,0 +1,430 @@
```mermaid
flowchart LR
subgraph path8 [Path]
8["Path<br>[1124, 1197, 0]"]
%% [ProgramBodyItem { index: 20 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 8 }, VariableDeclarationDeclaration, VariableDeclarationInit]
26["Segment<br>[1124, 1197, 0]"]
%% [ProgramBodyItem { index: 20 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 8 }, VariableDeclarationDeclaration, VariableDeclarationInit]
48[Solid2d]
end
subgraph path9 [Path]
9["Path<br>[1124, 1197, 0]"]
%% [ProgramBodyItem { index: 20 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 8 }, VariableDeclarationDeclaration, VariableDeclarationInit]
24["Segment<br>[1124, 1197, 0]"]
%% [ProgramBodyItem { index: 20 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 8 }, VariableDeclarationDeclaration, VariableDeclarationInit]
49[Solid2d]
end
subgraph path10 [Path]
10["Path<br>[1124, 1197, 0]"]
%% [ProgramBodyItem { index: 20 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 8 }, VariableDeclarationDeclaration, VariableDeclarationInit]
21["Segment<br>[1124, 1197, 0]"]
%% [ProgramBodyItem { index: 20 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 8 }, VariableDeclarationDeclaration, VariableDeclarationInit]
51[Solid2d]
end
subgraph path11 [Path]
11["Path<br>[1124, 1197, 0]"]
%% [ProgramBodyItem { index: 20 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 8 }, VariableDeclarationDeclaration, VariableDeclarationInit]
25["Segment<br>[1124, 1197, 0]"]
%% [ProgramBodyItem { index: 20 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 8 }, VariableDeclarationDeclaration, VariableDeclarationInit]
52[Solid2d]
end
subgraph path12 [Path]
12["Path<br>[1124, 1197, 0]"]
%% [ProgramBodyItem { index: 20 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 8 }, VariableDeclarationDeclaration, VariableDeclarationInit]
23["Segment<br>[1124, 1197, 0]"]
%% [ProgramBodyItem { index: 20 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 8 }, VariableDeclarationDeclaration, VariableDeclarationInit]
53[Solid2d]
end
subgraph path13 [Path]
13["Path<br>[1124, 1197, 0]"]
%% [ProgramBodyItem { index: 20 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 8 }, VariableDeclarationDeclaration, VariableDeclarationInit]
22["Segment<br>[1124, 1197, 0]"]
%% [ProgramBodyItem { index: 20 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 8 }, VariableDeclarationDeclaration, VariableDeclarationInit]
57[Solid2d]
end
subgraph path14 [Path]
14["Path<br>[1402, 1462, 0]"]
%% [ProgramBodyItem { index: 21 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }]
27["Segment<br>[1402, 1462, 0]"]
%% [ProgramBodyItem { index: 21 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }]
46[Solid2d]
end
subgraph path15 [Path]
15["Path<br>[1402, 1462, 0]"]
%% [ProgramBodyItem { index: 21 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }]
28["Segment<br>[1402, 1462, 0]"]
%% [ProgramBodyItem { index: 21 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }]
54[Solid2d]
end
subgraph path16 [Path]
16["Path<br>[1488, 1572, 0]"]
%% [ProgramBodyItem { index: 21 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }, CallKwArg { index: 0 }]
29["Segment<br>[1488, 1572, 0]"]
%% [ProgramBodyItem { index: 21 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }, CallKwArg { index: 0 }]
55[Solid2d]
end
subgraph path17 [Path]
17["Path<br>[1488, 1572, 0]"]
%% [ProgramBodyItem { index: 21 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }, CallKwArg { index: 0 }]
30["Segment<br>[1488, 1572, 0]"]
%% [ProgramBodyItem { index: 21 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 1 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }, CallKwArg { index: 0 }]
58[Solid2d]
end
subgraph path18 [Path]
18["Path<br>[2526, 2576, 0]"]
%% [ProgramBodyItem { index: 25 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }]
31["Segment<br>[2582, 2648, 0]"]
%% [ProgramBodyItem { index: 25 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }]
32["Segment<br>[2654, 2748, 0]"]
%% [ProgramBodyItem { index: 25 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 2 }]
33["Segment<br>[2754, 2856, 0]"]
%% [ProgramBodyItem { index: 25 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 3 }]
34["Segment<br>[2862, 2932, 0]"]
%% [ProgramBodyItem { index: 25 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 4 }]
35["Segment<br>[2938, 2945, 0]"]
%% [ProgramBodyItem { index: 25 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 5 }]
47[Solid2d]
end
subgraph path19 [Path]
19["Path<br>[4126, 4264, 0]"]
%% [ProgramBodyItem { index: 37 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }]
36["Segment<br>[4270, 4369, 0]"]
%% [ProgramBodyItem { index: 37 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }]
37["Segment<br>[4375, 4423, 0]"]
%% [ProgramBodyItem { index: 37 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 2 }]
38["Segment<br>[4429, 4465, 0]"]
%% [ProgramBodyItem { index: 37 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 3 }]
39["Segment<br>[4471, 4493, 0]"]
%% [ProgramBodyItem { index: 37 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 4 }]
40["Segment<br>[4499, 4522, 0]"]
%% [ProgramBodyItem { index: 37 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 5 }]
41["Segment<br>[4528, 4578, 0]"]
%% [ProgramBodyItem { index: 37 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 6 }]
42["Segment<br>[4584, 4603, 0]"]
%% [ProgramBodyItem { index: 37 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 7 }]
43["Segment<br>[4628, 4668, 0]"]
%% [ProgramBodyItem { index: 37 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 8 }]
44["Segment<br>[4674, 4682, 0]"]
%% [ProgramBodyItem { index: 37 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 9 }]
56[Solid2d]
end
subgraph path20 [Path]
20["Path<br>[4796, 4874, 0]"]
%% [ProgramBodyItem { index: 39 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }]
45["Segment<br>[4796, 4874, 0]"]
%% [ProgramBodyItem { index: 39 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }]
50[Solid2d]
end
1["Plane<br>[1365, 1385, 0]"]
%% [ProgramBodyItem { index: 21 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit]
2["Plane<br>[2438, 2473, 0]"]
%% [ProgramBodyItem { index: 23 }, VariableDeclarationDeclaration, VariableDeclarationInit]
3["Plane<br>[3498, 3541, 0]"]
%% [ProgramBodyItem { index: 28 }, VariableDeclarationDeclaration, VariableDeclarationInit]
4["Plane<br>[4092, 4110, 0]"]
%% [ProgramBodyItem { index: 36 }, VariableDeclarationDeclaration, VariableDeclarationInit]
5["StartSketchOnPlane<br>[1365, 1385, 0]"]
%% [ProgramBodyItem { index: 21 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 0 }, VariableDeclarationDeclaration, VariableDeclarationInit]
6["StartSketchOnPlane<br>[2487, 2511, 0]"]
%% [ProgramBodyItem { index: 24 }, VariableDeclarationDeclaration, VariableDeclarationInit]
7["StartSketchOnFace<br>[4740, 4781, 0]"]
%% [ProgramBodyItem { index: 38 }, VariableDeclarationDeclaration, VariableDeclarationInit]
59["Sweep Extrusion<br>[2053, 2098, 0]"]
%% [ProgramBodyItem { index: 21 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 7 }, VariableDeclarationDeclaration, VariableDeclarationInit]
60["Sweep Extrusion<br>[2053, 2098, 0]"]
%% [ProgramBodyItem { index: 21 }, VariableDeclarationDeclaration, VariableDeclarationInit, FunctionExpressionBody, FunctionExpressionBodyItem { index: 7 }, VariableDeclarationDeclaration, VariableDeclarationInit]
61["Sweep Extrusion<br>[2957, 2993, 0]"]
%% [ProgramBodyItem { index: 26 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 0 }]
62["Sweep Revolve<br>[4688, 4705, 0]"]
%% [ProgramBodyItem { index: 37 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 10 }]
63["Sweep Extrusion<br>[5082, 5137, 0]"]
%% [ProgramBodyItem { index: 41 }, VariableDeclarationDeclaration, VariableDeclarationInit]
64["Sweep Extrusion<br>[5082, 5137, 0]"]
%% [ProgramBodyItem { index: 41 }, VariableDeclarationDeclaration, VariableDeclarationInit]
65["Sweep Extrusion<br>[5082, 5137, 0]"]
%% [ProgramBodyItem { index: 41 }, VariableDeclarationDeclaration, VariableDeclarationInit]
66["Sweep Extrusion<br>[5082, 5137, 0]"]
%% [ProgramBodyItem { index: 41 }, VariableDeclarationDeclaration, VariableDeclarationInit]
67["Sweep Extrusion<br>[5082, 5137, 0]"]
%% [ProgramBodyItem { index: 41 }, VariableDeclarationDeclaration, VariableDeclarationInit]
68[Wall]
%% face_code_ref=Missing NodePath
69[Wall]
%% face_code_ref=Missing NodePath
70[Wall]
%% face_code_ref=Missing NodePath
71[Wall]
%% face_code_ref=Missing NodePath
72[Wall]
%% face_code_ref=Missing NodePath
73[Wall]
%% face_code_ref=Missing NodePath
74[Wall]
%% face_code_ref=Missing NodePath
75[Wall]
%% face_code_ref=Missing NodePath
76[Wall]
%% face_code_ref=Missing NodePath
77[Wall]
%% face_code_ref=Missing NodePath
78[Wall]
%% face_code_ref=Missing NodePath
79[Wall]
%% face_code_ref=Missing NodePath
80[Wall]
%% face_code_ref=Missing NodePath
81[Wall]
%% face_code_ref=Missing NodePath
82[Wall]
%% face_code_ref=[ProgramBodyItem { index: 38 }, VariableDeclarationDeclaration, VariableDeclarationInit]
83[Wall]
%% face_code_ref=Missing NodePath
84["Cap Start"]
%% face_code_ref=Missing NodePath
85["Cap Start"]
%% face_code_ref=Missing NodePath
86["Cap Start"]
%% face_code_ref=Missing NodePath
87["Cap Start"]
%% face_code_ref=Missing NodePath
88["Cap End"]
%% face_code_ref=Missing NodePath
89["Cap End"]
%% face_code_ref=Missing NodePath
90["Cap End"]
%% face_code_ref=Missing NodePath
91["SweepEdge Opposite"]
92["SweepEdge Opposite"]
93["SweepEdge Opposite"]
94["SweepEdge Opposite"]
95["SweepEdge Opposite"]
96["SweepEdge Opposite"]
97["SweepEdge Opposite"]
98["SweepEdge Adjacent"]
99["SweepEdge Adjacent"]
100["SweepEdge Adjacent"]
101["SweepEdge Adjacent"]
102["SweepEdge Adjacent"]
103["SweepEdge Adjacent"]
104["SweepEdge Adjacent"]
105["SweepEdge Adjacent"]
106["SweepEdge Adjacent"]
107["SweepEdge Adjacent"]
108["SweepEdge Adjacent"]
109["SweepEdge Adjacent"]
110["SweepEdge Adjacent"]
111["SweepEdge Adjacent"]
112["SweepEdge Adjacent"]
113["EdgeCut Fillet<br>[2999, 3289, 0]"]
%% [ProgramBodyItem { index: 26 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }]
114["EdgeCut Fillet<br>[2999, 3289, 0]"]
%% [ProgramBodyItem { index: 26 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }]
115["EdgeCut Fillet<br>[2999, 3289, 0]"]
%% [ProgramBodyItem { index: 26 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }]
116["EdgeCut Fillet<br>[2999, 3289, 0]"]
%% [ProgramBodyItem { index: 26 }, VariableDeclarationDeclaration, VariableDeclarationInit, PipeBodyItem { index: 1 }]
1 --- 8
1 --- 9
1 --- 13
1 --- 15
1 --- 16
2 <--x 6
2 --- 18
3 <--x 5
3 --- 10
3 --- 11
3 --- 12
3 --- 14
3 --- 17
4 --- 19
82 x--> 7
8 --- 26
8 --- 48
9 --- 24
9 --- 49
10 --- 21
10 --- 51
11 --- 25
11 --- 52
12 --- 23
12 --- 53
13 --- 22
13 --- 57
14 --- 27
14 --- 46
14 ---- 60
15 --- 28
15 --- 54
15 ---- 59
16 --- 29
16 --- 55
17 --- 30
17 --- 58
18 --- 31
18 --- 32
18 --- 33
18 --- 34
18 --- 35
18 --- 47
18 ---- 61
19 --- 36
19 --- 37
19 --- 38
19 --- 39
19 --- 40
19 --- 41
19 --- 42
19 --- 43
19 --- 44
19 --- 56
19 ---- 62
20 --- 45
20 --- 50
20 ---- 64
82 --- 20
27 --- 83
27 x--> 87
27 --- 97
27 --- 112
28 --- 68
28 x--> 85
28 --- 91
28 --- 98
31 --- 70
31 x--> 84
31 --- 95
31 --- 102
32 --- 71
32 x--> 84
32 --- 94
32 --- 101
33 --- 69
33 x--> 84
33 --- 93
33 --- 100
34 --- 72
34 x--> 84
34 --- 92
34 --- 99
62 <--x 36
36 --- 78
36 x--> 104
62 <--x 37
37 --- 77
37 --- 104
62 <--x 38
38 --- 82
38 --- 105
62 <--x 39
39 --- 79
39 --- 106
62 <--x 40
40 --- 74
40 --- 107
62 <--x 41
41 --- 81
41 --- 108
62 <--x 42
42 --- 76
42 --- 109
62 <--x 43
43 --- 75
43 --- 110
62 <--x 44
44 --- 80
44 --- 111
45 --- 73
45 x--> 82
45 --- 96
45 --- 103
59 --- 68
59 --- 85
59 --- 89
59 --- 91
59 --- 98
60 --- 83
60 --- 87
60 --- 90
60 --- 97
60 --- 112
61 --- 69
61 --- 70
61 --- 71
61 --- 72
61 --- 84
61 --- 88
61 --- 92
61 --- 93
61 --- 94
61 --- 95
61 --- 99
61 --- 100
61 --- 101
61 --- 102
62 --- 74
62 --- 75
62 --- 76
62 --- 77
62 --- 78
62 --- 79
62 --- 80
62 --- 81
62 --- 82
62 --- 104
62 --- 105
62 --- 106
62 --- 107
62 --- 108
62 --- 109
62 --- 110
62 --- 111
64 --- 73
64 --- 86
64 --- 96
64 --- 103
68 --- 91
68 --- 98
69 --- 93
69 --- 100
101 <--x 69
70 --- 95
99 <--x 70
70 --- 102
71 --- 94
71 --- 101
102 <--x 71
72 --- 92
72 --- 99
100 <--x 72
73 --- 96
73 --- 103
106 <--x 74
74 --- 107
109 <--x 75
75 --- 110
108 <--x 76
76 --- 109
77 --- 104
78 --- 104
111 <--x 78
105 <--x 79
79 --- 106
110 <--x 80
80 --- 111
107 <--x 81
81 --- 108
82 --- 105
83 --- 97
83 --- 112
96 <--x 86
92 <--x 88
93 <--x 88
94 <--x 88
95 <--x 88
91 <--x 89
97 <--x 90
99 <--x 114
100 <--x 113
101 <--x 116
102 <--x 115
```

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View File

@ -1026,71 +1026,71 @@ flowchart LR
99 --- 381 99 --- 381
99 --- 479 99 --- 479
101 --- 233 101 --- 233
101 x--> 327 101 x--> 337
101 --- 343 101 --- 343
101 --- 387 101 --- 387
102 --- 226 102 --- 226
102 x--> 327 102 x--> 337
102 --- 344 102 --- 344
102 --- 388 102 --- 388
103 --- 238 103 --- 238
103 x--> 327 103 x--> 337
103 --- 345 103 --- 345
103 --- 389 103 --- 389
104 --- 229 104 --- 229
104 x--> 327 104 x--> 337
104 --- 346 104 --- 346
104 --- 390 104 --- 390
105 --- 239 105 --- 239
105 x--> 327 105 x--> 337
105 --- 347 105 --- 347
105 --- 391 105 --- 391
106 --- 227 106 --- 227
106 x--> 327 106 x--> 337
106 --- 348 106 --- 348
106 --- 392 106 --- 392
107 --- 240 107 --- 240
107 x--> 327 107 x--> 337
107 --- 349 107 --- 349
107 --- 393 107 --- 393
108 --- 241 108 --- 241
108 x--> 327 108 x--> 337
108 --- 350 108 --- 350
108 --- 394 108 --- 394
109 --- 230 109 --- 230
109 x--> 327 109 x--> 337
109 --- 351 109 --- 351
109 --- 395 109 --- 395
110 --- 236 110 --- 236
110 x--> 327 110 x--> 337
110 --- 352 110 --- 352
110 --- 396 110 --- 396
111 --- 235 111 --- 235
111 x--> 327 111 x--> 337
111 --- 353 111 --- 353
111 --- 397 111 --- 397
112 --- 237 112 --- 237
112 x--> 327 112 x--> 337
112 --- 354 112 --- 354
112 --- 398 112 --- 398
113 --- 228 113 --- 228
113 x--> 327 113 x--> 337
113 --- 355 113 --- 355
113 --- 399 113 --- 399
114 --- 225 114 --- 225
114 x--> 327 114 x--> 337
114 --- 356 114 --- 356
114 --- 400 114 --- 400
115 --- 234 115 --- 234
115 x--> 327 115 x--> 337
115 --- 357 115 --- 357
115 --- 401 115 --- 401
116 --- 232 116 --- 232
116 x--> 327 116 x--> 337
116 --- 358 116 --- 358
116 --- 402 116 --- 402
117 --- 231 117 --- 231
117 x--> 327 117 x--> 337
117 --- 359 117 --- 359
117 --- 403 117 --- 403
222 <--x 118 222 <--x 118
@ -1425,56 +1425,56 @@ flowchart LR
224 --- 342 224 --- 342
224 --- 386 224 --- 386
225 --- 356 225 --- 356
399 <--x 225
225 --- 400 225 --- 400
401 <--x 225
226 --- 344 226 --- 344
387 <--x 226
226 --- 388 226 --- 388
389 <--x 226
227 --- 348 227 --- 348
391 <--x 227
227 --- 392 227 --- 392
393 <--x 227
228 --- 355 228 --- 355
398 <--x 228
228 --- 399 228 --- 399
400 <--x 228
229 --- 346 229 --- 346
389 <--x 229
229 --- 390 229 --- 390
391 <--x 229
230 --- 351 230 --- 351
394 <--x 230
230 --- 395 230 --- 395
396 <--x 230
231 --- 359 231 --- 359
402 <--x 231 387 <--x 231
231 --- 403 231 --- 403
232 --- 358 232 --- 358
401 <--x 232
232 --- 402 232 --- 402
403 <--x 232
233 --- 343 233 --- 343
233 --- 387 233 --- 387
403 <--x 233 388 <--x 233
234 --- 357 234 --- 357
400 <--x 234
234 --- 401 234 --- 401
402 <--x 234
235 --- 353 235 --- 353
396 <--x 235
235 --- 397 235 --- 397
398 <--x 235
236 --- 352 236 --- 352
395 <--x 236
236 --- 396 236 --- 396
397 <--x 236
237 --- 354 237 --- 354
397 <--x 237
237 --- 398 237 --- 398
399 <--x 237
238 --- 345 238 --- 345
388 <--x 238
238 --- 389 238 --- 389
390 <--x 238
239 --- 347 239 --- 347
390 <--x 239
239 --- 391 239 --- 391
392 <--x 239
240 --- 349 240 --- 349
392 <--x 240
240 --- 393 240 --- 393
394 <--x 240
241 --- 350 241 --- 350
393 <--x 241
241 --- 394 241 --- 394
395 <--x 241
406 <--x 242 406 <--x 242
242 --- 407 242 --- 407
243 --- 404 243 --- 404
@ -1657,6 +1657,23 @@ flowchart LR
369 <--x 325 369 <--x 325
370 <--x 325 370 <--x 325
371 <--x 325 371 <--x 325
343 <--x 327
344 <--x 327
345 <--x 327
346 <--x 327
347 <--x 327
348 <--x 327
349 <--x 327
350 <--x 327
351 <--x 327
352 <--x 327
353 <--x 327
354 <--x 327
355 <--x 327
356 <--x 327
357 <--x 327
358 <--x 327
359 <--x 327
363 <--x 328 363 <--x 328
364 <--x 328 364 <--x 328
365 <--x 328 365 <--x 328
@ -1675,23 +1692,6 @@ flowchart LR
377 <--x 335 377 <--x 335
378 <--x 335 378 <--x 335
379 <--x 336 379 <--x 336
343 <--x 337
344 <--x 337
345 <--x 337
346 <--x 337
347 <--x 337
348 <--x 337
349 <--x 337
350 <--x 337
351 <--x 337
352 <--x 337
353 <--x 337
354 <--x 337
355 <--x 337
356 <--x 337
357 <--x 337
358 <--x 337
359 <--x 337
367 <--x 338 367 <--x 338
385 <--x 340 385 <--x 340
373 <--x 341 373 <--x 341

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 216 KiB

View File

@ -172,7 +172,7 @@ flowchart LR
11 --- 42 11 --- 42
11 ---- 47 11 ---- 47
34 --- 48 34 --- 48
34 x--> 54 34 x--> 55
34 --- 57 34 --- 57
34 --- 62 34 --- 62
36 --- 49 36 --- 49
@ -224,7 +224,7 @@ flowchart LR
52 --- 58 52 --- 58
52 --- 63 52 --- 63
64 <--x 52 64 <--x 52
57 <--x 55 57 <--x 54
58 <--x 56 58 <--x 56
59 <--x 56 59 <--x 56
60 <--x 56 60 <--x 56

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 95 KiB

View File

@ -349,7 +349,7 @@ flowchart LR
17 --- 70 17 --- 70
17 --- 75 17 --- 75
18 --- 93 18 --- 93
18 x--> 101 18 x--> 105
18 --- 116 18 --- 116
18 --- 134 18 --- 134
19 --- 97 19 --- 97
@ -357,7 +357,7 @@ flowchart LR
19 --- 125 19 --- 125
19 --- 143 19 --- 143
20 --- 91 20 --- 91
20 x--> 101 20 x--> 105
20 --- 117 20 --- 117
20 --- 135 20 --- 135
21 --- 96 21 --- 96
@ -369,11 +369,11 @@ flowchart LR
22 --- 123 22 --- 123
22 --- 141 22 --- 141
23 --- 92 23 --- 92
23 x--> 101 23 x--> 105
23 --- 118 23 --- 118
23 --- 136 23 --- 136
24 --- 90 24 --- 90
24 x--> 101 24 x--> 105
24 --- 119 24 --- 119
24 --- 137 24 --- 137
25 --- 98 25 --- 98
@ -381,7 +381,7 @@ flowchart LR
25 --- 122 25 --- 122
25 --- 140 25 --- 140
26 --- 94 26 --- 94
26 x--> 101 26 x--> 105
26 --- 120 26 --- 120
26 --- 138 26 --- 138
27 --- 99 27 --- 99
@ -540,15 +540,15 @@ flowchart LR
113 <--x 100 113 <--x 100
114 <--x 100 114 <--x 100
115 <--x 100 115 <--x 100
116 <--x 101
117 <--x 101
118 <--x 101
119 <--x 101
120 <--x 101
108 <--x 102 108 <--x 102
109 <--x 102 109 <--x 102
110 <--x 102 110 <--x 102
111 <--x 102 111 <--x 102
116 <--x 105
117 <--x 105
118 <--x 105
119 <--x 105
120 <--x 105
121 <--x 107 121 <--x 107
122 <--x 107 122 <--x 107
123 <--x 107 123 <--x 107

View File

@ -109,7 +109,7 @@ flowchart LR
6 --- 18 6 --- 18
6 ---- 19 6 ---- 19
7 --- 24 7 --- 24
7 x--> 31 7 x--> 33
7 --- 35 7 --- 35
7 --- 45 7 --- 45
8 --- 28 8 --- 28
@ -117,7 +117,7 @@ flowchart LR
8 --- 44 8 --- 44
8 --- 54 8 --- 54
9 --- 22 9 --- 22
9 x--> 31 9 x--> 33
9 --- 36 9 --- 36
9 --- 46 9 --- 46
10 --- 27 10 --- 27
@ -129,11 +129,11 @@ flowchart LR
11 --- 42 11 --- 42
11 --- 52 11 --- 52
12 --- 23 12 --- 23
12 x--> 31 12 x--> 33
12 --- 37 12 --- 37
12 --- 47 12 --- 47
13 --- 21 13 --- 21
13 x--> 31 13 x--> 33
13 --- 38 13 --- 38
13 --- 48 13 --- 48
14 --- 29 14 --- 29
@ -141,7 +141,7 @@ flowchart LR
14 --- 41 14 --- 41
14 --- 51 14 --- 51
15 --- 25 15 --- 25
15 x--> 31 15 x--> 33
15 --- 39 15 --- 39
15 --- 49 15 --- 49
16 --- 30 16 --- 30
@ -212,11 +212,11 @@ flowchart LR
30 --- 40 30 --- 40
30 --- 50 30 --- 50
51 <--x 30 51 <--x 30
35 <--x 33 35 <--x 31
36 <--x 33 36 <--x 31
37 <--x 33 37 <--x 31
38 <--x 33 38 <--x 31
39 <--x 33 39 <--x 31
40 <--x 34 40 <--x 34
41 <--x 34 41 <--x 34
42 <--x 34 42 <--x 34

View File

@ -538,7 +538,7 @@ flowchart LR
21 --- 81 21 --- 81
21 ---- 95 21 ---- 95
22 --- 127 22 --- 127
22 x--> 153 22 x--> 162
22 --- 193 22 --- 193
22 --- 244 22 --- 244
23 --- 114 23 --- 114
@ -550,11 +550,11 @@ flowchart LR
24 --- 181 24 --- 181
24 --- 232 24 --- 232
25 --- 128 25 --- 128
25 x--> 153 25 x--> 162
25 --- 194 25 --- 194
25 --- 245 25 --- 245
26 --- 125 26 --- 125
26 x--> 153 26 x--> 162
26 --- 195 26 --- 195
26 --- 246 26 --- 246
27 --- 112 27 --- 112
@ -562,7 +562,7 @@ flowchart LR
27 --- 182 27 --- 182
27 --- 233 27 --- 233
28 --- 129 28 --- 129
28 x--> 153 28 x--> 162
28 --- 196 28 --- 196
28 --- 247 28 --- 247
29 --- 115 29 --- 115
@ -570,7 +570,7 @@ flowchart LR
29 --- 183 29 --- 183
29 --- 234 29 --- 234
30 --- 126 30 --- 126
30 x--> 153 30 x--> 162
30 --- 197 30 --- 197
30 --- 248 30 --- 248
31 --- 116 31 --- 116
@ -618,11 +618,11 @@ flowchart LR
43 --- 214 43 --- 214
43 --- 265 43 --- 265
44 --- 101 44 --- 101
44 x--> 148 44 x--> 157
44 --- 164 44 --- 164
44 --- 215 44 --- 215
45 --- 110 45 --- 110
45 x--> 161 45 x--> 152
45 --- 172 45 --- 172
45 --- 223 45 --- 223
46 --- 123 46 --- 123
@ -630,7 +630,7 @@ flowchart LR
46 --- 192 46 --- 192
46 --- 243 46 --- 243
47 --- 96 47 --- 96
47 x--> 148 47 x--> 157
47 --- 165 47 --- 165
47 --- 216 47 --- 216
48 --- 119 48 --- 119
@ -638,7 +638,7 @@ flowchart LR
48 --- 191 48 --- 191
48 --- 242 48 --- 242
49 --- 108 49 --- 108
49 x--> 161 49 x--> 152
49 --- 173 49 --- 173
49 --- 224 49 --- 224
50 --- 145 50 --- 145
@ -646,11 +646,11 @@ flowchart LR
50 --- 213 50 --- 213
50 --- 264 50 --- 264
51 --- 106 51 --- 106
51 x--> 161 51 x--> 152
51 --- 174 51 --- 174
51 --- 225 51 --- 225
52 --- 98 52 --- 98
52 x--> 148 52 x--> 157
52 --- 166 52 --- 166
52 --- 217 52 --- 217
53 --- 143 53 --- 143
@ -662,7 +662,7 @@ flowchart LR
54 --- 190 54 --- 190
54 --- 241 54 --- 241
55 --- 97 55 --- 97
55 x--> 148 55 x--> 157
55 --- 167 55 --- 167
55 --- 218 55 --- 218
56 --- 124 56 --- 124
@ -674,7 +674,7 @@ flowchart LR
57 --- 211 57 --- 211
57 --- 262 57 --- 262
58 --- 111 58 --- 111
58 x--> 161 58 x--> 152
58 --- 175 58 --- 175
58 --- 226 58 --- 226
59 --- 118 59 --- 118
@ -686,11 +686,11 @@ flowchart LR
60 --- 210 60 --- 210
60 --- 261 60 --- 261
61 --- 109 61 --- 109
61 x--> 161 61 x--> 152
61 --- 176 61 --- 176
61 --- 227 61 --- 227
62 --- 103 62 --- 103
62 x--> 148 62 x--> 157
62 --- 168 62 --- 168
62 --- 219 62 --- 219
63 --- 139 63 --- 139
@ -698,11 +698,11 @@ flowchart LR
63 --- 209 63 --- 209
63 --- 260 63 --- 260
64 --- 107 64 --- 107
64 x--> 161 64 x--> 152
64 --- 177 64 --- 177
64 --- 228 64 --- 228
65 --- 100 65 --- 100
65 x--> 148 65 x--> 157
65 --- 169 65 --- 169
65 --- 220 65 --- 220
66 --- 121 66 --- 121
@ -710,7 +710,7 @@ flowchart LR
66 --- 187 66 --- 187
66 --- 238 66 --- 238
67 --- 104 67 --- 104
67 x--> 161 67 x--> 152
67 --- 178 67 --- 178
67 --- 229 67 --- 229
68 --- 117 68 --- 117
@ -722,7 +722,7 @@ flowchart LR
69 --- 208 69 --- 208
69 --- 259 69 --- 259
70 --- 102 70 --- 102
70 x--> 148 70 x--> 157
70 --- 170 70 --- 170
70 --- 221 70 --- 221
71 --- 140 71 --- 140
@ -730,11 +730,11 @@ flowchart LR
71 --- 207 71 --- 207
71 --- 258 71 --- 258
72 --- 105 72 --- 105
72 x--> 161 72 x--> 152
72 --- 179 72 --- 179
72 --- 230 72 --- 230
73 --- 99 73 --- 99
73 x--> 148 73 x--> 157
73 --- 171 73 --- 171
73 --- 222 73 --- 222
74 --- 120 74 --- 120
@ -1063,14 +1063,19 @@ flowchart LR
146 --- 211 146 --- 211
146 --- 262 146 --- 262
263 <--x 146 263 <--x 146
172 <--x 152 164 <--x 148
173 <--x 152 165 <--x 148
174 <--x 152 166 <--x 148
175 <--x 152 167 <--x 148
176 <--x 152 168 <--x 148
177 <--x 152 169 <--x 148
178 <--x 152 170 <--x 148
179 <--x 152 171 <--x 148
193 <--x 153
194 <--x 153
195 <--x 153
196 <--x 153
197 <--x 153
202 <--x 154 202 <--x 154
185 <--x 156 185 <--x 156
186 <--x 156 186 <--x 156
@ -1080,14 +1085,6 @@ flowchart LR
190 <--x 156 190 <--x 156
191 <--x 156 191 <--x 156
192 <--x 156 192 <--x 156
164 <--x 157
165 <--x 157
166 <--x 157
167 <--x 157
168 <--x 157
169 <--x 157
170 <--x 157
171 <--x 157
203 <--x 158 203 <--x 158
204 <--x 158 204 <--x 158
205 <--x 158 205 <--x 158
@ -1101,11 +1098,14 @@ flowchart LR
182 <--x 160 182 <--x 160
183 <--x 160 183 <--x 160
184 <--x 160 184 <--x 160
193 <--x 162 172 <--x 161
194 <--x 162 173 <--x 161
195 <--x 162 174 <--x 161
196 <--x 162 175 <--x 161
197 <--x 162 176 <--x 161
177 <--x 161
178 <--x 161
179 <--x 161
207 <--x 163 207 <--x 163
208 <--x 163 208 <--x 163
209 <--x 163 209 <--x 163

View File

@ -250,7 +250,7 @@ flowchart LR
13 --- 35 13 --- 35
13 ---- 47 13 ---- 47
14 --- 55 14 --- 55
14 x--> 70 14 x--> 75
14 --- 81 14 --- 81
14 --- 100 14 --- 100
15 --- 50 15 --- 50
@ -262,11 +262,11 @@ flowchart LR
16 --- 77 16 --- 77
16 --- 96 16 --- 96
17 --- 56 17 --- 56
17 x--> 70 17 x--> 75
17 --- 82 17 --- 82
17 --- 101 17 --- 101
18 --- 53 18 --- 53
18 x--> 70 18 x--> 75
18 --- 83 18 --- 83
18 --- 102 18 --- 102
19 --- 48 19 --- 48
@ -274,7 +274,7 @@ flowchart LR
19 --- 78 19 --- 78
19 --- 97 19 --- 97
20 --- 57 20 --- 57
20 x--> 70 20 x--> 75
20 --- 84 20 --- 84
20 --- 103 20 --- 103
21 --- 51 21 --- 51
@ -282,7 +282,7 @@ flowchart LR
21 --- 79 21 --- 79
21 --- 98 21 --- 98
22 --- 54 22 --- 54
22 x--> 70 22 x--> 75
22 --- 85 22 --- 85
22 --- 104 22 --- 104
23 --- 52 23 --- 52
@ -447,6 +447,11 @@ flowchart LR
66 --- 92 66 --- 92
110 <--x 66 110 <--x 66
66 --- 111 66 --- 111
81 <--x 70
82 <--x 70
83 <--x 70
84 <--x 70
85 <--x 70
90 <--x 71 90 <--x 71
91 <--x 72 91 <--x 72
92 <--x 72 92 <--x 72
@ -461,11 +466,6 @@ flowchart LR
78 <--x 74 78 <--x 74
79 <--x 74 79 <--x 74
80 <--x 74 80 <--x 74
81 <--x 75
82 <--x 75
83 <--x 75
84 <--x 75
85 <--x 75
105 <--x 115 105 <--x 115
106 <--x 117 106 <--x 117
107 <--x 114 107 <--x 114

View File

@ -203,7 +203,7 @@ description: Artifact commands helium-tank.kcl
"path": "[uuid]", "path": "[uuid]",
"segment": { "segment": {
"type": "tangential_arc", "type": "tangential_arc",
"radius": 95.88500000000016, "radius": 95.8850000000001,
"offset": { "offset": {
"unit": "degrees", "unit": "degrees",
"value": -90.0 "value": -90.0

View File

@ -387,75 +387,75 @@ flowchart LR
25 --- 66 25 --- 66
25 --- 69 25 --- 69
26 --- 104 26 --- 104
26 x--> 119 26 x--> 113
26 --- 126 26 --- 126
26 --- 149 26 --- 149
27 --- 99 27 --- 99
27 x--> 119 27 x--> 113
27 --- 127 27 --- 127
27 --- 150 27 --- 150
28 --- 98 28 --- 98
28 x--> 119 28 x--> 113
28 --- 128 28 --- 128
28 --- 151 28 --- 151
29 --- 100 29 --- 100
29 x--> 119 29 x--> 113
29 --- 129 29 --- 129
29 --- 152 29 --- 152
30 --- 96 30 --- 96
30 x--> 119 30 x--> 113
30 --- 130 30 --- 130
30 --- 153 30 --- 153
31 --- 95 31 --- 95
31 x--> 119 31 x--> 113
31 --- 131 31 --- 131
31 --- 154 31 --- 154
32 --- 103 32 --- 103
32 x--> 119 32 x--> 113
32 --- 132 32 --- 132
32 --- 155 32 --- 155
33 --- 107 33 --- 107
33 x--> 119 33 x--> 113
33 --- 133 33 --- 133
33 --- 156 33 --- 156
34 --- 106 34 --- 106
34 x--> 119 34 x--> 113
34 --- 134 34 --- 134
34 --- 157 34 --- 157
35 --- 93 35 --- 93
35 x--> 119 35 x--> 113
35 --- 135 35 --- 135
35 --- 158 35 --- 158
36 --- 94 36 --- 94
36 x--> 119 36 x--> 113
36 --- 136 36 --- 136
36 --- 159 36 --- 159
37 --- 108 37 --- 108
37 x--> 119 37 x--> 113
37 --- 137 37 --- 137
37 --- 160 37 --- 160
38 --- 101 38 --- 101
38 x--> 119 38 x--> 113
38 --- 138 38 --- 138
38 --- 161 38 --- 161
39 --- 97 39 --- 97
39 x--> 119 39 x--> 113
39 --- 139 39 --- 139
39 --- 162 39 --- 162
40 --- 91 40 --- 91
40 x--> 119 40 x--> 113
40 --- 140 40 --- 140
40 --- 163 40 --- 163
41 --- 102 41 --- 102
41 x--> 119 41 x--> 113
41 --- 141 41 --- 141
41 --- 164 41 --- 164
42 --- 92 42 --- 92
42 x--> 119 42 x--> 113
42 --- 142 42 --- 142
42 --- 165 42 --- 165
43 --- 105 43 --- 105
43 x--> 119 43 x--> 113
43 --- 143 43 --- 143
43 --- 166 43 --- 166
45 --- 109 45 --- 109
@ -625,26 +625,26 @@ flowchart LR
110 --- 168 110 --- 168
111 --- 146 111 --- 146
111 --- 169 111 --- 169
126 <--x 113
127 <--x 113
128 <--x 113
129 <--x 113
130 <--x 113
131 <--x 113
132 <--x 113
133 <--x 113
134 <--x 113
135 <--x 113
136 <--x 113
137 <--x 113
138 <--x 113
139 <--x 113
140 <--x 113
141 <--x 113
142 <--x 113
143 <--x 113
145 <--x 117 145 <--x 117
125 <--x 118 125 <--x 118
126 <--x 119
127 <--x 119
128 <--x 119
129 <--x 119
130 <--x 119
131 <--x 119
132 <--x 119
133 <--x 119
134 <--x 119
135 <--x 119
136 <--x 119
137 <--x 119
138 <--x 119
139 <--x 119
140 <--x 119
141 <--x 119
142 <--x 119
143 <--x 119
144 <--x 120 144 <--x 120
146 <--x 121 146 <--x 121
124 <--x 122 124 <--x 122

View File

@ -3271,7 +3271,7 @@ description: Result of parsing pdu-faceplate.kcl
"id": { "id": {
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": "swtichWidth", "name": "switchWidth",
"start": 0, "start": 0,
"type": "Identifier" "type": "Identifier"
}, },
@ -3385,7 +3385,7 @@ description: Result of parsing pdu-faceplate.kcl
"id": { "id": {
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": "swtichPlateWidth", "name": "switchPlateWidth",
"start": 0, "start": 0,
"type": "Identifier" "type": "Identifier"
}, },
@ -3586,7 +3586,7 @@ description: Result of parsing pdu-faceplate.kcl
"name": { "name": {
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": "swtichPlateWidth", "name": "switchPlateWidth",
"start": 0, "start": 0,
"type": "Identifier" "type": "Identifier"
}, },
@ -3768,7 +3768,7 @@ description: Result of parsing pdu-faceplate.kcl
"name": { "name": {
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": "swtichPlateWidth", "name": "switchPlateWidth",
"start": 0, "start": 0,
"type": "Identifier" "type": "Identifier"
}, },
@ -4257,7 +4257,7 @@ description: Result of parsing pdu-faceplate.kcl
"id": { "id": {
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": "swtichButtonWidth", "name": "switchButtonWidth",
"start": 0, "start": 0,
"type": "Identifier" "type": "Identifier"
}, },
@ -4339,7 +4339,7 @@ description: Result of parsing pdu-faceplate.kcl
"name": { "name": {
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": "swtichButtonWidth", "name": "switchButtonWidth",
"start": 0, "start": 0,
"type": "Identifier" "type": "Identifier"
}, },
@ -4994,7 +4994,7 @@ description: Result of parsing pdu-faceplate.kcl
"name": { "name": {
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": "swtichButtonWidth", "name": "switchButtonWidth",
"start": 0, "start": 0,
"type": "Identifier" "type": "Identifier"
}, },
@ -5329,7 +5329,7 @@ description: Result of parsing pdu-faceplate.kcl
"name": { "name": {
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": "swtichWidth", "name": "switchWidth",
"start": 0, "start": 0,
"type": "Identifier" "type": "Identifier"
}, },

Some files were not shown because too many files have changed in this diff Show More