Compare commits

..

28 Commits

Author SHA1 Message Date
e8a82ea85e Clean up for PR 2025-04-11 09:47:45 -04:00
c1894edaed Remove sha1, add rfc3161TimeStampServer 2025-04-11 08:40:24 -04:00
8c28f34238 Back to publisherName = certificateSubjectName = KittyCAD Inc 2025-04-11 07:40:38 -04:00
d2340628a8 WIP 2025-04-11 07:05:22 -04:00
a1f5cdd690 Update signingHashAlgorithms to include sha1 2025-04-11 05:18:56 -04:00
d1d8d0a82c Use smctl windows certsync to update the windows store in build-apps (attempt) 2025-04-11 05:03:33 -04:00
f76b328136 WIP messing with Get-ChildItem -Recurse Cert 2025-04-11 04:59:45 -04:00
a13548da17 WIP messing with Get-ChildItem -Recurse Cert 2025-04-11 04:55:18 -04:00
65f4b0f239 WIP messing with Get-ChildItem -Recurse Cert 2025-04-11 04:54:05 -04:00
dbcc0bd3b4 WIP messing with Get-ChildItem -Recurse Cert 2025-04-11 04:43:35 -04:00
472b3618ac shell: pwsh 2025-04-11 04:00:47 -04:00
43e89e8bae Add back smksp_registrar.exe list and smctl.exe keypair ls 2025-04-10 19:39:56 -04:00
94a9e01301 Add certificateSha1 2025-04-10 19:02:01 -04:00
3980a1caf8 WIP 2025-04-10 18:58:46 -04:00
d4f23f8469 Trying to follow https://github.com/electron-userland/electron-builder/issues/7605#issuecomment-2257861622 2025-04-10 18:55:46 -04:00
9143c6f08a Test from https://github.com/electron-userland/electron-builder/issues/7605#issuecomment-2257861622 2025-04-10 18:49:23 -04:00
1d4456c458 Loosing my mind 2025-04-10 18:39:13 -04:00
c6fbb4fc63 Fix typo 2025-04-10 18:36:17 -04:00
b7c8d6c185 Copied line by line from docs in dummy script 2025-04-10 17:58:48 -04:00
f23aa5e642 Add dummy pierre-test-windows-code-sign, plus console log on normal script 2025-04-10 17:36:34 -04:00
8bb26c9b89 Remove stdio param for hopefully more logs 2025-04-10 16:56:54 -04:00
0d7aebdee9 DEBUG=electron-builder to get more logs (maybe) 2025-04-10 16:40:11 -04:00
ad333c2055 Try certutil.exe -csp "DigiCert Software Trust Manager KSP", forget yarn cache for now 2025-04-10 16:25:12 -04:00
3559df0c5e Add smksp_registrar.exe register which is somewhere else in their docs. Plus comment 2025-04-10 16:00:53 -04:00
e2dda07829 Update from docs, back to inherit 2025-04-10 15:33:19 -04:00
ea585cb5d6 Trying to get more logs 2025-04-10 15:04:18 -04:00
8af9af2aa7 Another try with the right things commented out 2025-04-10 14:22:30 -04:00
f0ba35c0b2 WIP: Updater on Nightly on Windows failed
Fixes #6256
2025-04-10 14:09:55 -04:00
97 changed files with 2007 additions and 7642 deletions

View File

@ -10,7 +10,8 @@ on:
- 'nightly-v[0-9]+.[0-9]+.[0-9]+' - 'nightly-v[0-9]+.[0-9]+.[0-9]+'
env: env:
IS_RELEASE: ${{ github.ref_type == 'tag' && startsWith(github.ref_name, 'v') }} # IS_RELEASE: ${{ github.ref_type == 'tag' && startsWith(github.ref_name, 'v') }}
IS_RELEASE: true
IS_NIGHTLY: ${{ github.ref_type == 'tag' && startsWith(github.ref_name, 'nightly-v') }} IS_NIGHTLY: ${{ github.ref_type == 'tag' && startsWith(github.ref_name, 'nightly-v') }}
concurrency: concurrency:
@ -99,11 +100,11 @@ jobs:
yarn files:set-version yarn files:set-version
yarn files:flip-to-nightly yarn files:flip-to-nightly
- name: Set release version # - name: Set release version
if: ${{ env.IS_RELEASE == 'true' }} # if: ${{ env.IS_RELEASE == 'true' }}
run: | # run: |
export VERSION=${GITHUB_REF_NAME#v} # export VERSION=${GITHUB_REF_NAME#v}
yarn files:set-version # yarn files:set-version
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
@ -183,30 +184,32 @@ jobs:
max_attempts: 3 max_attempts: 3
command: yarn install command: yarn install
# Next steps are from Digicert docs at
# https://docs.digicert.com/en/digicert-keylocker/ci-cd-integrations/scripts/github/scripts-for-signing-using-ksp-library-on-github.html#ksp-signing-using-github-action-488726
- name: Prepare certificate and variables (Windows only) - name: Prepare certificate and variables (Windows only)
if: ${{ (env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true') && matrix.os == 'windows-2022' }} if: ${{ (env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true') && matrix.os == 'windows-2022' }}
run: | run: |
echo "${{secrets.SM_CLIENT_CERT_FILE_B64 }}" | base64 --decode > /d/Certificate_pkcs12.p12 CERTIFICATE_PATH=$RUNNER_TEMP/certificate.p12
cat /d/Certificate_pkcs12.p12 echo "$SM_CLIENT_CERT_FILE_B64" | base64 --decode > $CERTIFICATE_PATH
echo "::set-output name=version::${GITHUB_REF#refs/tags/v}" echo "SM_CLIENT_CERT_FILE=$CERTIFICATE_PATH" >> "$GITHUB_ENV"
echo "SM_HOST=${{ secrets.SM_HOST }}" >> "$GITHUB_ENV" echo "SM_HOST=${{ secrets.SM_HOST }}" >> "$GITHUB_ENV"
echo "SM_API_KEY=${{ secrets.SM_API_KEY }}" >> "$GITHUB_ENV" echo "SM_API_KEY=${{ secrets.SM_API_KEY }}" >> "$GITHUB_ENV"
echo "SM_CLIENT_CERT_FILE=D:\\Certificate_pkcs12.p12" >> "$GITHUB_ENV"
echo "SM_CLIENT_CERT_PASSWORD=${{ secrets.SM_CLIENT_CERT_PASSWORD }}" >> "$GITHUB_ENV" echo "SM_CLIENT_CERT_PASSWORD=${{ secrets.SM_CLIENT_CERT_PASSWORD }}" >> "$GITHUB_ENV"
echo "C:\Program Files (x86)\Windows Kits\10\App Certification Kit" >> $GITHUB_PATH echo "C:\Program Files (x86)\Windows Kits\10\App Certification Kit" >> $GITHUB_PATH
echo "C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools" >> $GITHUB_PATH echo "C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools" >> $GITHUB_PATH
echo "C:\Program Files\DigiCert\DigiCert One Signing Manager Tools" >> $GITHUB_PATH echo "C:\Program Files\DigiCert\DigiCert Keylocker Tools" >> $GITHUB_PATH
shell: bash shell: bash
- name: Setup certicate with SSM KSP (Windows only) - name: Setup certicate with SSM KSP (Windows only)
if: ${{ (env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true') && matrix.os == 'windows-2022' }} if: ${{ (env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true') && matrix.os == 'windows-2022' }}
run: | run: |
curl -X GET https://one.digicert.com/signingmanager/api-ui/v1/releases/smtools-windows-x64.msi/download -H "x-api-key:%SM_API_KEY%" -o smtools-windows-x64.msi curl -X GET https://one.digicert.com/signingmanager/api-ui/v1/releases/Keylockertools-windows-x64.msi/download -H "x-api-key:%SM_API_KEY%" -o Keylockertools-windows-x64.msi
msiexec /i smtools-windows-x64.msi /quiet /qn msiexec /i Keylockertools-windows-x64.msi /quiet /qn
smksp_registrar.exe list smksp_registrar.exe list
smctl.exe keypair ls smctl.exe keypair ls
C:\Windows\System32\certutil.exe -csp "DigiCert Signing Manager KSP" -key -user C:\Windows\System32\certutil.exe -csp "DigiCert Signing Manager KSP" -key -user
smksp_cert_sync.exe smksp_cert_sync.exe
smctl windows certsync
shell: cmd shell: cmd
- name: Build the app (debug) - name: Build the app (debug)
@ -225,8 +228,8 @@ jobs:
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }} CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }} CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }} # DEBUG: "electron-notarize*"
DEBUG: "electron-notarize*" DEBUG: electron-builder
# TODO: Fix electron-notarize flakes. The logs above should help gather more data on failures # TODO: Fix electron-notarize flakes. The logs above should help gather more data on failures
uses: nick-fields/retry@v3.0.2 uses: nick-fields/retry@v3.0.2
with: with:
@ -378,7 +381,7 @@ jobs:
NOTES: ${{ needs.prepare-files.outputs.notes }} NOTES: ${{ needs.prepare-files.outputs.notes }}
PUB_DATE: ${{ github.event.repository.updated_at }} PUB_DATE: ${{ github.event.repository.updated_at }}
WEBSITE_DIR: ${{ env.IS_NIGHTLY == 'true' && 'dl.zoo.dev/releases/modeling-app/nightly' || 'dl.zoo.dev/releases/modeling-app' }} WEBSITE_DIR: ${{ env.IS_NIGHTLY == 'true' && 'dl.zoo.dev/releases/modeling-app/nightly' || 'dl.zoo.dev/releases/modeling-app' }}
URL_CODED_NAME: ${{ env.IS_NIGHTLY == 'true' && 'Zoo%20Design%20Studio%20%28Nightly%29' || 'Zoo%20Design%20Studio' }} URL_CODED_NAME: ${{ env.IS_NIGHTLY == 'true' && 'Zoo%20Modeling%20App%20%28Nightly%29' || 'Zoo%20Modeling%20App' }}
run: | run: |
RELEASE_DIR=https://${WEBSITE_DIR} RELEASE_DIR=https://${WEBSITE_DIR}
jq --null-input \ jq --null-input \

View File

@ -1,55 +0,0 @@
name: Test Nix Flake
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
nix-flake-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: cachix/install-nix-action@v31
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: nix flake check for all platforms
run: |
nix flake check --all-systems
nix-build-linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: cachix/install-nix-action@v31
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: nix build . for x86_64-linux
run: nix build .
nix-build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: cachix/install-nix-action@v31
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: nix build . for x86_64-darwin
run: nix build .

1
.gitignore vendored
View File

@ -26,7 +26,6 @@ yarn-error.log*
.idea .idea
.vscode .vscode
.helix .helix
result
# rust # rust
rust/target rust/target

80
docs/kcl/import.md Normal file

File diff suppressed because one or more lines are too long

View File

@ -77,7 +77,6 @@ layout: manual
* [`helix`](kcl/std-helix) * [`helix`](kcl/std-helix)
* [`hole`](kcl/hole) * [`hole`](kcl/hole)
* [`hollow`](kcl/hollow) * [`hollow`](kcl/hollow)
* [`intersect`](kcl/intersect)
* [`lastSegX`](kcl/lastSegX) * [`lastSegX`](kcl/lastSegX)
* [`lastSegY`](kcl/lastSegY) * [`lastSegY`](kcl/lastSegY)
* [`legAngX`](kcl/legAngX) * [`legAngX`](kcl/legAngX)
@ -124,7 +123,6 @@ layout: manual
* [`sqrt`](kcl/sqrt) * [`sqrt`](kcl/sqrt)
* [`startProfileAt`](kcl/startProfileAt) * [`startProfileAt`](kcl/startProfileAt)
* [`startSketchOn`](kcl/startSketchOn) * [`startSketchOn`](kcl/startSketchOn)
* [`subtract`](kcl/subtract)
* [`sweep`](kcl/sweep) * [`sweep`](kcl/sweep)
* [`tangentToEnd`](kcl/tangentToEnd) * [`tangentToEnd`](kcl/tangentToEnd)
* [`tangentialArc`](kcl/tangentialArc) * [`tangentialArc`](kcl/tangentialArc)
@ -133,7 +131,6 @@ layout: manual
* [`toDegrees`](kcl/toDegrees) * [`toDegrees`](kcl/toDegrees)
* [`toRadians`](kcl/toRadians) * [`toRadians`](kcl/toRadians)
* [`translate`](kcl/translate) * [`translate`](kcl/translate)
* [`union`](kcl/union)
* [`xLine`](kcl/xLine) * [`xLine`](kcl/xLine)
* [`yLine`](kcl/yLine) * [`yLine`](kcl/yLine)
* **std::math** * **std::math**

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -252,7 +252,7 @@ Data for an imported geometry.
| Property | Type | Description | Required | | Property | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `type` |enum: `ImportedGeometry`| | No | | `type` |enum: [`ImportedGeometry`](/docs/kcl/types/ImportedGeometry)| | No |
| `id` |[`string`](/docs/kcl/types/string)| The ID of the imported geometry. | No | | `id` |[`string`](/docs/kcl/types/string)| The ID of the imported geometry. | No |
| `value` |`[` [`string`](/docs/kcl/types/string) `]`| The original file paths. | No | | `value` |`[` [`string`](/docs/kcl/types/string) `]`| The original file paths. | No |

File diff suppressed because one or more lines are too long

View File

@ -1144,7 +1144,7 @@ sketch001 = startSketchOn(XZ)
) )
test( test(
`Can import a local OBJ file`, `Can use the import stdlib function on a local OBJ file`,
{ tag: '@electron' }, { tag: '@electron' },
async ({ page, context }, testInfo) => { async ({ page, context }, testInfo) => {
test.fixme(orRunWhenFullSuiteEnabled()) test.fixme(orRunWhenFullSuiteEnabled())
@ -1194,7 +1194,7 @@ sketch001 = startSketchOn(XZ)
.toBeLessThan(15) .toBeLessThan(15)
}) })
await test.step(`Write the import function line`, async () => { await test.step(`Write the import function line`, async () => {
await u.codeLocator.fill(`import 'cube.obj'\ncube`) await u.codeLocator.fill(`import('cube.obj')`)
await page.waitForTimeout(800) await page.waitForTimeout(800)
}) })
await test.step(`Reset the camera before checking`, async () => { await test.step(`Reset the camera before checking`, async () => {

View File

@ -99,6 +99,7 @@ export class HomePageFixture {
createAndGoToProject = async (projectTitle = 'untitled') => { createAndGoToProject = async (projectTitle = 'untitled') => {
await this.projectsLoaded() await this.projectsLoaded()
await this.projectButtonNew.click() await this.projectButtonNew.click()
await this.projectTextName.click()
await this.projectTextName.fill(projectTitle) await this.projectTextName.fill(projectTitle)
await this.projectButtonContinue.click() await this.projectButtonContinue.click()
} }

View File

@ -233,7 +233,7 @@ export class SceneFixture {
settled = async (cmdBar: CmdBarFixture) => { settled = async (cmdBar: CmdBarFixture) => {
const u = await getUtils(this.page) const u = await getUtils(this.page)
await expect(this.startEditSketchBtn).not.toBeDisabled({ timeout: 15_000 }) await expect(this.startEditSketchBtn).not.toBeDisabled()
await expect(this.startEditSketchBtn).toBeVisible() await expect(this.startEditSketchBtn).toBeVisible()
await cmdBar.openCmdBar() await cmdBar.openCmdBar()

View File

@ -68,10 +68,12 @@ test.describe('edit with AI example snapshots', () => {
body1CapCoords.x, body1CapCoords.x,
body1CapCoords.y body1CapCoords.y
) )
const yellow: [number, number, number] = [179, 179, 131]
const submittingToast = page.getByText('Submitting to Text-to-CAD API...') const submittingToast = page.getByText('Submitting to Text-to-CAD API...')
await test.step('wait for scene to load select body and check selection came through', async () => { await test.step('wait for scene to load select body and check selection came through', async () => {
await clickBody1Cap() await clickBody1Cap()
await scene.expectPixelColor(yellow, body1CapCoords, 20)
await editor.expectState({ await editor.expectState({
highlightedCode: '', highlightedCode: '',
activeLines: ['|>startProfileAt([-73.64,-42.89],%)'], activeLines: ['|>startProfileAt([-73.64,-42.89],%)'],

View File

@ -588,7 +588,6 @@ test(
'Draft circle should look right', 'Draft circle should look right',
{ tag: '@snapshot' }, { tag: '@snapshot' },
async ({ page, context, cmdBar, scene }) => { async ({ page, context, cmdBar, scene }) => {
test.fixme(orRunWhenFullSuiteEnabled())
const u = await getUtils(page) const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio const PUR = 400 / 37.5 //pixeltoUnitRatio

View File

@ -1,15 +0,0 @@
import { createProject } from '@e2e/playwright/test-utils'
import { test } from '@e2e/playwright/zoo-test'
test.describe('Stress test', () => {
test('Create project and load stress test', async ({
cmdBar,
scene,
page,
}, testInfo) => {
const projectName = 'stress-test-project'
// Create and load project
await createProject({ name: projectName, page })
await scene.settled(cmdBar)
})
})

View File

@ -33,10 +33,12 @@ win:
- x64 - x64
- arm64 - arm64
signtoolOptions: signtoolOptions:
sign: "./scripts/sign-win.js" certificateSha1: F4C9A52FF7BC26EE5E054946F6B11DEEA94C748D
signingHashAlgorithms: signingHashAlgorithms:
- sha256 - sha256
publisherName: "KittyCAD Inc" # needs to be exactly like on Digicert publisherName: "KittyCAD Inc"
certificateSubjectName: "KittyCAD Inc"
rfc3161TimeStampServer: http://timestamp.digicert.com
icon: "assets/icon.ico" icon: "assets/icon.ico"
fileAssociations: fileAssociations:
- ext: kcl - ext: kcl

77
flake.lock generated
View File

@ -1,56 +1,6 @@
{ {
"nodes": { "nodes": {
"naersk": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1743800763,
"narHash": "sha256-YFKV+fxEpMgP5VsUcM6Il28lI0NlpM7+oB1XxbBAYCw=",
"owner": "nix-community",
"repo": "naersk",
"rev": "ed0232117731a4c19d3ee93aa0c382a8fe754b01",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "naersk",
"type": "github"
}
},
"nixpkgs": { "nixpkgs": {
"locked": {
"lastModified": 1744157173,
"narHash": "sha256-bWSjxDwq7iVePrhmA7tY2dyMWHuNJo8knkO4y+q4ZkY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "6a39c6e495eefabc935d8ddf66aa45d85b85fa3f",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1744157173,
"narHash": "sha256-bWSjxDwq7iVePrhmA7tY2dyMWHuNJo8knkO4y+q4ZkY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "6a39c6e495eefabc935d8ddf66aa45d85b85fa3f",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": { "locked": {
"lastModified": 1736320768, "lastModified": 1736320768,
"narHash": "sha256-nIYdTAiKIGnFNugbomgBJR+Xv5F1ZQU+HfaBqJKroC0=", "narHash": "sha256-nIYdTAiKIGnFNugbomgBJR+Xv5F1ZQU+HfaBqJKroC0=",
@ -66,23 +16,38 @@
"type": "github" "type": "github"
} }
}, },
"nixpkgs_2": {
"locked": {
"lastModified": 1728538411,
"narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": { "root": {
"inputs": { "inputs": {
"naersk": "naersk", "nixpkgs": "nixpkgs",
"nixpkgs": "nixpkgs_2",
"rust-overlay": "rust-overlay" "rust-overlay": "rust-overlay"
} }
}, },
"rust-overlay": { "rust-overlay": {
"inputs": { "inputs": {
"nixpkgs": "nixpkgs_3" "nixpkgs": "nixpkgs_2"
}, },
"locked": { "locked": {
"lastModified": 1744338850, "lastModified": 1736476219,
"narHash": "sha256-pwMIVmsb8fjjT92n5XFDqCsplcX70qVMMT7NulumPXs=", "narHash": "sha256-+qyv3QqdZCdZ3cSO/cbpEY6tntyYjfe1bB12mdpNFaY=",
"owner": "oxalica", "owner": "oxalica",
"repo": "rust-overlay", "repo": "rust-overlay",
"rev": "5e64aecc018e6f775572609e7d7485fdba6985a7", "rev": "de30cc5963da22e9742bbbbb9a3344570ed237b9",
"type": "github" "type": "github"
}, },
"original": { "original": {

136
flake.nix
View File

@ -1,96 +1,84 @@
{ {
description = "zoo.dev modeling-app"; description = "modeling-app development environment";
# Flake inputs
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
rust-overlay.url = "github:oxalica/rust-overlay"; rust-overlay.url = "github:oxalica/rust-overlay"; # A helper for Rust + Nix
naersk.url = "github:nix-community/naersk";
}; };
outputs = { # Flake outputs
self, outputs = { self, nixpkgs, rust-overlay }:
nixpkgs, let
rust-overlay, # Overlays enable you to customize the Nixpkgs attribute set
naersk, overlays = [
}: let # Makes a `rust-bin` attribute available in Nixpkgs
overlays = [ (import rust-overlay)
(import rust-overlay) # Provides a `rustToolchain` attribute for Nixpkgs that we can use to
(self: super: { # create a Rust environment
rustToolchain = super.rust-bin.stable.latest.default.override { (self: super: {
targets = ["wasm32-unknown-unknown"]; rustToolchain = super. rust-bin.stable.latest.default.override {
extensions = ["rustfmt" "llvm-tools-preview" "rust-src"]; targets = [ "wasm32-unknown-unknown" ];
}; extensions = [ "rustfmt" "llvm-tools-preview" "rust-src" ];
})
];
allSystems = [
"x86_64-linux"
"aarch64-linux"
"x86_64-darwin"
"aarch64-darwin"
];
forAllSystems = f:
nixpkgs.lib.genAttrs allSystems (system:
f {
pkgs = import nixpkgs {
inherit overlays system;
}; };
system = system; })
}); (self: super: {
in { cargo-llvm-cov = super.cargo-llvm-cov.overrideAttrs(oa: {
devShells = forAllSystems ({pkgs, ...}: { doCheck = false; doInstallCheck = false;
default = pkgs.mkShell { });
packages = })
(with pkgs; [ ];
# Systems supported
allSystems = [
"x86_64-linux" # 64-bit Intel/AMD Linux
"aarch64-linux" # 64-bit ARM Linux
"x86_64-darwin" # 64-bit Intel macOS
"aarch64-darwin" # 64-bit ARM macOS
];
# Helper to provide system-specific attributes
forAllSystems = f: nixpkgs.lib.genAttrs allSystems (system: f {
pkgs = import nixpkgs { inherit overlays system; config.allowBroken = true; };
});
in
{
# Development environment output
devShells = forAllSystems ({ pkgs }: {
default = pkgs.mkShell {
# The Nix packages provided in the environment
packages = (with pkgs; [
# The package provided by our custom overlay. Includes cargo, Clippy, cargo-fmt,
# rustdoc, rustfmt, and other tools.
rustToolchain rustToolchain
cargo-llvm-cov
cargo-nextest cargo-nextest
just just
postgresql.lib postgresql.lib
openssl openssl
pkg-config pkg-config
nodejs_22 nodejs_22
yarn yarn
electron electron
playwright-driver.browsers playwright-driver.browsers
wasm-pack ]) ++ pkgs.lib.optionals pkgs.stdenv.isDarwin (with pkgs; [
python3Full
])
++ pkgs.lib.optionals pkgs.stdenv.isDarwin (with pkgs; [
libiconv libiconv
darwin.apple_sdk.frameworks.Security darwin.apple_sdk.frameworks.Security
]); ]);
TARGET_CC = "${pkgs.stdenv.cc}/bin/${pkgs.stdenv.cc.targetPrefix}cc"; TARGET_CC = "${pkgs.stdenv.cc}/bin/${pkgs.stdenv.cc.targetPrefix}cc";
LIBCLANG_PATH = "${pkgs.libclang.lib}/lib"; LIBCLANG_PATH = "${pkgs.libclang.lib}/lib";
ELECTRON_OVERRIDE_DIST_PATH = "${pkgs.electron}/bin/"; ELECTRON_OVERRIDE_DIST_PATH = "${pkgs.electron}/bin/";
PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS = true; PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS = true;
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH = "${pkgs.playwright-driver.browsers}/chromium-1091/chrome-linux/chrome"; PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH = "${pkgs.playwright-driver.browsers}/chromium-1091/chrome-linux/chrome";
PLAYWRIGHT_BROWSERS_PATH = "${pkgs.playwright-driver.browsers}"; PLAYWRIGHT_BROWSERS_PATH = "${pkgs.playwright-driver.browsers}";
NODE_ENV = "development"; NODE_ENV = "development";
}; };
}); });
};
packages = forAllSystems ({
pkgs,
system,
}: let
naersk-lib = pkgs.callPackage naersk {
cargo = pkgs.rustToolchain;
rustc = pkgs.rustToolchain;
};
in {
kcl-language-server = naersk-lib.buildPackage {
pname = "kcl-language-server";
version = "0.1.0";
release = true;
src = ./rust;
cargoBuildOptions = opt: opt ++ ["-p" "kcl-language-server"];
buildInputs = [pkgs.openssl pkgs.pkg-config];
};
default = self.packages.${system}.kcl-language-server;
});
};
} }

View File

@ -389,13 +389,6 @@ export class LanguageServerPlugin implements PluginValue {
} }
if (insertText && insertTextFormat === 2) { if (insertText && insertTextFormat === 2) {
// We end with ${} so you can jump to the end of the snippet.
// After the last argument.
// This is not standard from the lsp so we add it here.
if (insertText.endsWith(')')) {
// We have a function its safe to insert the ${} at the end.
insertText = insertText + '${}'
}
return snippetCompletion(insertText, completion) return snippetCompletion(insertText, completion)
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 58 KiB

24
rust/Cargo.lock generated
View File

@ -1780,7 +1780,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-bumper" name = "kcl-bumper"
version = "0.1.60" version = "0.1.58"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@ -1791,7 +1791,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-derive-docs" name = "kcl-derive-docs"
version = "0.1.60" version = "0.1.58"
dependencies = [ dependencies = [
"Inflector", "Inflector",
"anyhow", "anyhow",
@ -1810,7 +1810,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-directory-test-macro" name = "kcl-directory-test-macro"
version = "0.1.60" version = "0.1.58"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1819,7 +1819,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-language-server" name = "kcl-language-server"
version = "0.2.60" version = "0.2.58"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@ -1840,7 +1840,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-language-server-release" name = "kcl-language-server-release"
version = "0.1.60" version = "0.1.58"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@ -1860,7 +1860,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-lib" name = "kcl-lib"
version = "0.2.60" version = "0.2.58"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"approx 0.5.1", "approx 0.5.1",
@ -1928,7 +1928,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-python-bindings" name = "kcl-python-bindings"
version = "0.3.60" version = "0.3.58"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"kcl-lib", "kcl-lib",
@ -1943,7 +1943,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-test-server" name = "kcl-test-server"
version = "0.1.60" version = "0.1.58"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"hyper 0.14.32", "hyper 0.14.32",
@ -1956,7 +1956,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-to-core" name = "kcl-to-core"
version = "0.1.60" version = "0.1.58"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -1970,7 +1970,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-wasm-lib" name = "kcl-wasm-lib"
version = "0.1.60" version = "0.1.58"
dependencies = [ dependencies = [
"bson", "bson",
"console_error_panic_hook", "console_error_panic_hook",
@ -2033,9 +2033,9 @@ dependencies = [
[[package]] [[package]]
name = "kittycad-modeling-cmds" name = "kittycad-modeling-cmds"
version = "0.2.113" version = "0.2.110"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa1c927569925425a1b03711617c384a30cb7554394e8a6a01266910b22421de" checksum = "bdfd16800a12a2eaefff53958bd871875c246e669274269f7caefc25d19641ad"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",

View File

@ -36,7 +36,7 @@ dashmap = { version = "6.1.0" }
http = "1" http = "1"
indexmap = "2.7.0" indexmap = "2.7.0"
kittycad = { version = "0.3.36", default-features = false, features = ["js", "requests"] } kittycad = { version = "0.3.36", default-features = false, features = ["js", "requests"] }
kittycad-modeling-cmds = { version = "0.2.113", features = ["ts-rs", "websocket"] } kittycad-modeling-cmds = { version = "0.2.110", features = ["ts-rs", "websocket"] }
lazy_static = "1.5.0" lazy_static = "1.5.0"
miette = "7.5.0" miette = "7.5.0"
pyo3 = { version = "0.24.0" } pyo3 = { version = "0.24.0" }

View File

@ -1,7 +1,7 @@
[package] [package]
name = "kcl-bumper" name = "kcl-bumper"
version = "0.1.60" version = "0.1.58"
edition = "2021" edition = "2021"
repository = "https://github.com/KittyCAD/modeling-api" repository = "https://github.com/KittyCAD/modeling-api"
rust-version = "1.76" rust-version = "1.76"

View File

@ -1,7 +1,7 @@
[package] [package]
name = "kcl-derive-docs" name = "kcl-derive-docs"
description = "A tool for generating documentation from Rust derive macros" description = "A tool for generating documentation from Rust derive macros"
version = "0.1.60" version = "0.1.58"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app" repository = "https://github.com/KittyCAD/modeling-app"

View File

@ -1,7 +1,7 @@
[package] [package]
name = "kcl-directory-test-macro" name = "kcl-directory-test-macro"
description = "A tool for generating tests from a directory of kcl files" description = "A tool for generating tests from a directory of kcl files"
version = "0.1.60" version = "0.1.58"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app" repository = "https://github.com/KittyCAD/modeling-app"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "kcl-language-server-release" name = "kcl-language-server-release"
version = "0.1.60" version = "0.1.58"
edition = "2021" edition = "2021"
authors = ["KittyCAD Inc <kcl@kittycad.io>"] authors = ["KittyCAD Inc <kcl@kittycad.io>"]
publish = false publish = false

View File

@ -2,7 +2,7 @@
name = "kcl-language-server" name = "kcl-language-server"
description = "A language server for KCL." description = "A language server for KCL."
authors = ["KittyCAD Inc <kcl@kittycad.io>"] authors = ["KittyCAD Inc <kcl@kittycad.io>"]
version = "0.2.60" version = "0.2.58"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -9,10 +9,7 @@ export async function createClient(
serverOptions: lc.ServerOptions serverOptions: lc.ServerOptions
): Promise<lc.LanguageClient> { ): Promise<lc.LanguageClient> {
const clientOptions: lc.LanguageClientOptions = { const clientOptions: lc.LanguageClientOptions = {
documentSelector: [ documentSelector: [{ scheme: 'file', language: 'kcl' }],
{ scheme: 'file', language: 'kcl' },
{ scheme: 'untitled', language: 'kcl' },
],
initializationOptions, initializationOptions,
traceOutputChannel, traceOutputChannel,
outputChannel, outputChannel,

View File

@ -1,7 +1,7 @@
[package] [package]
name = "kcl-lib" name = "kcl-lib"
description = "KittyCAD Language implementation and tools" description = "KittyCAD Language implementation and tools"
version = "0.2.60" version = "0.2.58"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app" repository = "https://github.com/KittyCAD/modeling-app"
@ -103,7 +103,7 @@ tokio-tungstenite = { version = "0.24.0", features = [
tower-lsp = { workspace = true, features = ["proposed", "default"] } tower-lsp = { workspace = true, features = ["proposed", "default"] }
[features] [features]
default = ["cli", "engine"] default = ["engine"]
cli = ["dep:clap", "kittycad/clap"] cli = ["dep:clap", "kittycad/clap"]
dhat-heap = ["dep:dhat"] dhat-heap = ["dep:dhat"]
# For the lsp server, when run with stdout for rpc we want to disable println. # For the lsp server, when run with stdout for rpc we want to disable println.

View File

@ -444,11 +444,12 @@ impl FnData {
} }
} }
#[allow(clippy::literal_string_with_formatting_args)]
pub(super) fn to_autocomplete_snippet(&self) -> String { pub(super) fn to_autocomplete_snippet(&self) -> String {
if self.name == "loft" { if self.name == "loft" {
return "loft([${0:sketch000}, ${1:sketch001}])".to_owned(); return "loft([${0:sketch000}, ${1:sketch001}])${}".to_owned();
} else if self.name == "hole" { } else if self.name == "hole" {
return "hole(${0:holeSketch}, ${1:%})".to_owned(); return "hole(${0:holeSketch}, ${1:%})${}".to_owned();
} }
let mut args = Vec::new(); let mut args = Vec::new();
let mut index = 0; let mut index = 0;
@ -458,7 +459,9 @@ impl FnData {
args.push(arg_str); args.push(arg_str);
} }
} }
format!("{}({})", self.preferred_name, args.join(", ")) // We end with ${} so you can jump to the end of the snippet.
// After the last argument.
format!("{}({})${{}}", self.preferred_name, args.join(", "))
} }
fn to_signature_help(&self) -> SignatureHelp { fn to_signature_help(&self) -> SignatureHelp {

View File

@ -498,17 +498,12 @@ pub trait StdLibFn: std::fmt::Debug + Send + Sync {
}) })
} }
#[allow(clippy::literal_string_with_formatting_args)]
fn to_autocomplete_snippet(&self) -> Result<String> { fn to_autocomplete_snippet(&self) -> Result<String> {
if self.name() == "loft" { if self.name() == "loft" {
return Ok("loft([${0:sketch000}, ${1:sketch001}])".to_string()); return Ok("loft([${0:sketch000}, ${1:sketch001}])${}".to_string());
} else if self.name() == "union" {
return Ok("union([${0:extrude001}, ${1:extrude002}])".to_string());
} else if self.name() == "subtract" {
return Ok("subtract([${0:extrude001}], tools = [${1:extrude002}])".to_string());
} else if self.name() == "intersect" {
return Ok("intersect([${0:extrude001}, ${1:extrude002}])".to_string());
} else if self.name() == "hole" { } else if self.name() == "hole" {
return Ok("hole(${0:holeSketch}, ${1:%})".to_string()); return Ok("hole(${0:holeSketch}, ${1:%})${}".to_string());
} }
let in_keyword_fn = self.keyword_arguments(); let in_keyword_fn = self.keyword_arguments();
let mut args = Vec::new(); let mut args = Vec::new();
@ -519,7 +514,9 @@ pub trait StdLibFn: std::fmt::Debug + Send + Sync {
args.push(arg_str); args.push(arg_str);
} }
} }
Ok(format!("{}({})", self.name(), args.join(", "))) // We end with ${} so you can jump to the end of the snippet.
// After the last argument.
Ok(format!("{}({})${{}}", self.name(), args.join(", ")))
} }
fn to_signature_help(&self) -> SignatureHelp { fn to_signature_help(&self) -> SignatureHelp {
@ -893,26 +890,29 @@ mod tests {
} }
#[test] #[test]
#[allow(clippy::literal_string_with_formatting_args)]
fn get_autocomplete_snippet_line() { fn get_autocomplete_snippet_line() {
let line_fn: Box<dyn StdLibFn> = Box::new(crate::std::sketch::Line); let line_fn: Box<dyn StdLibFn> = Box::new(crate::std::sketch::Line);
let snippet = line_fn.to_autocomplete_snippet().unwrap(); let snippet = line_fn.to_autocomplete_snippet().unwrap();
assert_eq!(snippet, r#"line(${0:%}, end = [${1:3.14}, ${2:3.14}])"#); assert_eq!(snippet, r#"line(${0:%}, end = [${1:3.14}, ${2:3.14}])${}"#);
} }
#[test] #[test]
#[allow(clippy::literal_string_with_formatting_args)]
fn get_autocomplete_snippet_extrude() { fn get_autocomplete_snippet_extrude() {
let extrude_fn: Box<dyn StdLibFn> = Box::new(crate::std::extrude::Extrude); let extrude_fn: Box<dyn StdLibFn> = Box::new(crate::std::extrude::Extrude);
let snippet = extrude_fn.to_autocomplete_snippet().unwrap(); let snippet = extrude_fn.to_autocomplete_snippet().unwrap();
assert_eq!(snippet, r#"extrude(${0:%}, length = ${1:3.14})"#); assert_eq!(snippet, r#"extrude(${0:%}, length = ${1:3.14})${}"#);
} }
#[test] #[test]
#[allow(clippy::literal_string_with_formatting_args)]
fn get_autocomplete_snippet_fillet() { fn get_autocomplete_snippet_fillet() {
let fillet_fn: Box<dyn StdLibFn> = Box::new(crate::std::fillet::Fillet); let fillet_fn: Box<dyn StdLibFn> = Box::new(crate::std::fillet::Fillet);
let snippet = fillet_fn.to_autocomplete_snippet().unwrap(); let snippet = fillet_fn.to_autocomplete_snippet().unwrap();
assert_eq!( assert_eq!(
snippet, snippet,
r#"fillet(${0:%}, radius = ${1:3.14}, tags = [${2:"tag_or_edge_fn"}])"# r#"fillet(${0:%}, radius = ${1:3.14}, tags = [${2:"tag_or_edge_fn"}])${}"#
); );
} }
@ -920,17 +920,18 @@ mod tests {
fn get_autocomplete_snippet_start_sketch_on() { fn get_autocomplete_snippet_start_sketch_on() {
let start_sketch_on_fn: Box<dyn StdLibFn> = Box::new(crate::std::sketch::StartSketchOn); let start_sketch_on_fn: Box<dyn StdLibFn> = Box::new(crate::std::sketch::StartSketchOn);
let snippet = start_sketch_on_fn.to_autocomplete_snippet().unwrap(); let snippet = start_sketch_on_fn.to_autocomplete_snippet().unwrap();
assert_eq!(snippet, r#"startSketchOn(${0:"XY"})"#); assert_eq!(snippet, r#"startSketchOn(${0:"XY"})${}"#);
} }
#[test] #[test]
#[allow(clippy::literal_string_with_formatting_args)]
fn get_autocomplete_snippet_pattern_circular_3d() { fn get_autocomplete_snippet_pattern_circular_3d() {
// We test this one specifically because it has ints and floats and strings. // We test this one specifically because it has ints and floats and strings.
let pattern_fn: Box<dyn StdLibFn> = Box::new(crate::std::patterns::PatternCircular3D); let pattern_fn: Box<dyn StdLibFn> = Box::new(crate::std::patterns::PatternCircular3D);
let snippet = pattern_fn.to_autocomplete_snippet().unwrap(); let snippet = pattern_fn.to_autocomplete_snippet().unwrap();
assert_eq!( assert_eq!(
snippet, snippet,
r#"patternCircular3d(${0:%}, instances = ${1:10}, axis = [${2:3.14}, ${3:3.14}, ${4:3.14}], center = [${5:3.14}, ${6:3.14}, ${7:3.14}], arcDegrees = ${8:3.14}, rotateDuplicates = ${9:false})"# r#"patternCircular3d(${0:%}, instances = ${1:10}, axis = [${2:3.14}, ${3:3.14}, ${4:3.14}], center = [${5:3.14}, ${6:3.14}, ${7:3.14}], arcDegrees = ${8:3.14}, rotateDuplicates = ${9:false})${}"#
); );
} }
@ -941,10 +942,11 @@ mod tests {
panic!(); panic!();
}; };
let snippet = revolve_fn.to_autocomplete_snippet(); let snippet = revolve_fn.to_autocomplete_snippet();
assert_eq!(snippet, r#"revolve(axis = ${0:X})"#); assert_eq!(snippet, r#"revolve(axis = ${0:X})${}"#);
} }
#[test] #[test]
#[allow(clippy::literal_string_with_formatting_args)]
fn get_autocomplete_snippet_circle() { fn get_autocomplete_snippet_circle() {
let data = kcl_doc::walk_prelude(); let data = kcl_doc::walk_prelude();
let DocData::Fn(circle_fn) = data.into_iter().find(|d| d.name() == "circle").unwrap() else { let DocData::Fn(circle_fn) = data.into_iter().find(|d| d.name() == "circle").unwrap() else {
@ -953,11 +955,12 @@ mod tests {
let snippet = circle_fn.to_autocomplete_snippet(); let snippet = circle_fn.to_autocomplete_snippet();
assert_eq!( assert_eq!(
snippet, snippet,
r#"circle(center = [${0:3.14}, ${1:3.14}], radius = ${2:3.14})"# r#"circle(center = [${0:3.14}, ${1:3.14}], radius = ${2:3.14})${}"#
); );
} }
#[test] #[test]
#[allow(clippy::literal_string_with_formatting_args)]
fn get_autocomplete_snippet_arc() { fn get_autocomplete_snippet_arc() {
let arc_fn: Box<dyn StdLibFn> = Box::new(crate::std::sketch::Arc); let arc_fn: Box<dyn StdLibFn> = Box::new(crate::std::sketch::Arc);
let snippet = arc_fn.to_autocomplete_snippet().unwrap(); let snippet = arc_fn.to_autocomplete_snippet().unwrap();
@ -967,7 +970,7 @@ mod tests {
angleStart = ${0:3.14}, angleStart = ${0:3.14},
angleEnd = ${1:3.14}, angleEnd = ${1:3.14},
radius = ${2:3.14}, radius = ${2:3.14},
}, ${3:%})"# }, ${3:%})${}"#
); );
} }
@ -975,16 +978,17 @@ mod tests {
fn get_autocomplete_snippet_map() { fn get_autocomplete_snippet_map() {
let map_fn: Box<dyn StdLibFn> = Box::new(crate::std::array::Map); let map_fn: Box<dyn StdLibFn> = Box::new(crate::std::array::Map);
let snippet = map_fn.to_autocomplete_snippet().unwrap(); let snippet = map_fn.to_autocomplete_snippet().unwrap();
assert_eq!(snippet, r#"map(${0:[0..9]})"#); assert_eq!(snippet, r#"map(${0:[0..9]})${}"#);
} }
#[test] #[test]
#[allow(clippy::literal_string_with_formatting_args)]
fn get_autocomplete_snippet_pattern_linear_2d() { fn get_autocomplete_snippet_pattern_linear_2d() {
let pattern_fn: Box<dyn StdLibFn> = Box::new(crate::std::patterns::PatternLinear2D); let pattern_fn: Box<dyn StdLibFn> = Box::new(crate::std::patterns::PatternLinear2D);
let snippet = pattern_fn.to_autocomplete_snippet().unwrap(); let snippet = pattern_fn.to_autocomplete_snippet().unwrap();
assert_eq!( assert_eq!(
snippet, snippet,
r#"patternLinear2d(${0:%}, instances = ${1:10}, distance = ${2:3.14}, axis = [${3:3.14}, ${4:3.14}])"# r#"patternLinear2d(${0:%}, instances = ${1:10}, distance = ${2:3.14}, axis = [${3:3.14}, ${4:3.14}])${}"#
); );
} }
@ -994,32 +998,36 @@ mod tests {
let snippet = appearance_fn.to_autocomplete_snippet().unwrap(); let snippet = appearance_fn.to_autocomplete_snippet().unwrap();
assert_eq!( assert_eq!(
snippet, snippet,
r#"appearance(${0:%}, color = ${1:"#.to_owned() + "\"#" + r#"ff0000"})"# r#"appearance(${0:%}, color = ${1:"#.to_owned() + "\"#" + r#"ff0000"})${}"#
); );
} }
#[test] #[test]
#[allow(clippy::literal_string_with_formatting_args)]
fn get_autocomplete_snippet_loft() { fn get_autocomplete_snippet_loft() {
let loft_fn: Box<dyn StdLibFn> = Box::new(crate::std::loft::Loft); let loft_fn: Box<dyn StdLibFn> = Box::new(crate::std::loft::Loft);
let snippet = loft_fn.to_autocomplete_snippet().unwrap(); let snippet = loft_fn.to_autocomplete_snippet().unwrap();
assert_eq!(snippet, r#"loft([${0:sketch000}, ${1:sketch001}])"#); assert_eq!(snippet, r#"loft([${0:sketch000}, ${1:sketch001}])${}"#);
} }
#[test] #[test]
#[allow(clippy::literal_string_with_formatting_args)]
fn get_autocomplete_snippet_sweep() { fn get_autocomplete_snippet_sweep() {
let sweep_fn: Box<dyn StdLibFn> = Box::new(crate::std::sweep::Sweep); let sweep_fn: Box<dyn StdLibFn> = Box::new(crate::std::sweep::Sweep);
let snippet = sweep_fn.to_autocomplete_snippet().unwrap(); let snippet = sweep_fn.to_autocomplete_snippet().unwrap();
assert_eq!(snippet, r#"sweep(${0:%}, path = ${1:sketch000})"#); assert_eq!(snippet, r#"sweep(${0:%}, path = ${1:sketch000})${}"#);
} }
#[test] #[test]
#[allow(clippy::literal_string_with_formatting_args)]
fn get_autocomplete_snippet_hole() { fn get_autocomplete_snippet_hole() {
let hole_fn: Box<dyn StdLibFn> = Box::new(crate::std::sketch::Hole); let hole_fn: Box<dyn StdLibFn> = Box::new(crate::std::sketch::Hole);
let snippet = hole_fn.to_autocomplete_snippet().unwrap(); let snippet = hole_fn.to_autocomplete_snippet().unwrap();
assert_eq!(snippet, r#"hole(${0:holeSketch}, ${1:%})"#); assert_eq!(snippet, r#"hole(${0:holeSketch}, ${1:%})${}"#);
} }
#[test] #[test]
#[allow(clippy::literal_string_with_formatting_args)]
fn get_autocomplete_snippet_helix() { fn get_autocomplete_snippet_helix() {
let data = kcl_doc::walk_prelude(); let data = kcl_doc::walk_prelude();
let DocData::Fn(helix_fn) = data.into_iter().find(|d| d.name() == "helix").unwrap() else { let DocData::Fn(helix_fn) = data.into_iter().find(|d| d.name() == "helix").unwrap() else {
@ -1028,32 +1036,36 @@ mod tests {
let snippet = helix_fn.to_autocomplete_snippet(); let snippet = helix_fn.to_autocomplete_snippet();
assert_eq!( assert_eq!(
snippet, snippet,
r#"helix(revolutions = ${0:3.14}, angleStart = ${1:3.14}, radius = ${2:3.14}, axis = ${3:X}, length = ${4:3.14})"# r#"helix(revolutions = ${0:3.14}, angleStart = ${1:3.14}, radius = ${2:3.14}, axis = ${3:X}, length = ${4:3.14})${}"#
); );
} }
#[test] #[test]
#[allow(clippy::literal_string_with_formatting_args)]
fn get_autocomplete_snippet_union() { fn get_autocomplete_snippet_union() {
let union_fn: Box<dyn StdLibFn> = Box::new(crate::std::csg::Union); let union_fn: Box<dyn StdLibFn> = Box::new(crate::std::csg::Union);
let snippet = union_fn.to_autocomplete_snippet().unwrap(); let snippet = union_fn.to_autocomplete_snippet().unwrap();
assert_eq!(snippet, r#"union([${0:extrude001}, ${1:extrude002}])"#); assert_eq!(snippet, r#"union(${0:%})${}"#);
} }
#[test] #[test]
#[allow(clippy::literal_string_with_formatting_args)]
fn get_autocomplete_snippet_subtract() { fn get_autocomplete_snippet_subtract() {
let subtract_fn: Box<dyn StdLibFn> = Box::new(crate::std::csg::Subtract); let subtract_fn: Box<dyn StdLibFn> = Box::new(crate::std::csg::Subtract);
let snippet = subtract_fn.to_autocomplete_snippet().unwrap(); let snippet = subtract_fn.to_autocomplete_snippet().unwrap();
assert_eq!(snippet, r#"subtract([${0:extrude001}], tools = [${1:extrude002}])"#); assert_eq!(snippet, r#"subtract(${0:%}, tools = ${1:%})${}"#);
} }
#[test] #[test]
#[allow(clippy::literal_string_with_formatting_args)]
fn get_autocomplete_snippet_intersect() { fn get_autocomplete_snippet_intersect() {
let intersect_fn: Box<dyn StdLibFn> = Box::new(crate::std::csg::Intersect); let intersect_fn: Box<dyn StdLibFn> = Box::new(crate::std::csg::Intersect);
let snippet = intersect_fn.to_autocomplete_snippet().unwrap(); let snippet = intersect_fn.to_autocomplete_snippet().unwrap();
assert_eq!(snippet, r#"intersect([${0:extrude001}, ${1:extrude002}])"#); assert_eq!(snippet, r#"intersect(${0:%})${}"#);
} }
#[test] #[test]
#[allow(clippy::literal_string_with_formatting_args)]
fn get_autocomplete_snippet_get_common_edge() { fn get_autocomplete_snippet_get_common_edge() {
let get_common_edge_fn: Box<dyn StdLibFn> = Box::new(crate::std::edge::GetCommonEdge); let get_common_edge_fn: Box<dyn StdLibFn> = Box::new(crate::std::edge::GetCommonEdge);
let snippet = get_common_edge_fn.to_autocomplete_snippet().unwrap(); let snippet = get_common_edge_fn.to_autocomplete_snippet().unwrap();
@ -1061,34 +1073,40 @@ mod tests {
snippet, snippet,
r#"getCommonEdge(faces = [{ r#"getCommonEdge(faces = [{
value = ${0:"string"}, value = ${0:"string"},
}])"# }])${}"#
); );
} }
#[test] #[test]
#[allow(clippy::literal_string_with_formatting_args)]
fn get_autocomplete_snippet_scale() { fn get_autocomplete_snippet_scale() {
let scale_fn: Box<dyn StdLibFn> = Box::new(crate::std::transform::Scale); let scale_fn: Box<dyn StdLibFn> = Box::new(crate::std::transform::Scale);
let snippet = scale_fn.to_autocomplete_snippet().unwrap(); let snippet = scale_fn.to_autocomplete_snippet().unwrap();
assert_eq!(snippet, r#"scale(${0:%}, x = ${1:3.14}, y = ${2:3.14}, z = ${3:3.14})"#); assert_eq!(
snippet,
r#"scale(${0:%}, x = ${1:3.14}, y = ${2:3.14}, z = ${3:3.14})${}"#
);
} }
#[test] #[test]
#[allow(clippy::literal_string_with_formatting_args)]
fn get_autocomplete_snippet_translate() { fn get_autocomplete_snippet_translate() {
let translate_fn: Box<dyn StdLibFn> = Box::new(crate::std::transform::Translate); let translate_fn: Box<dyn StdLibFn> = Box::new(crate::std::transform::Translate);
let snippet = translate_fn.to_autocomplete_snippet().unwrap(); let snippet = translate_fn.to_autocomplete_snippet().unwrap();
assert_eq!( assert_eq!(
snippet, snippet,
r#"translate(${0:%}, x = ${1:3.14}, y = ${2:3.14}, z = ${3:3.14})"# r#"translate(${0:%}, x = ${1:3.14}, y = ${2:3.14}, z = ${3:3.14})${}"#
); );
} }
#[test] #[test]
#[allow(clippy::literal_string_with_formatting_args)]
fn get_autocomplete_snippet_rotate() { fn get_autocomplete_snippet_rotate() {
let rotate_fn: Box<dyn StdLibFn> = Box::new(crate::std::transform::Rotate); let rotate_fn: Box<dyn StdLibFn> = Box::new(crate::std::transform::Rotate);
let snippet = rotate_fn.to_autocomplete_snippet().unwrap(); let snippet = rotate_fn.to_autocomplete_snippet().unwrap();
assert_eq!( assert_eq!(
snippet, snippet,
r#"rotate(${0:%}, roll = ${1:3.14}, pitch = ${2:3.14}, yaw = ${3:3.14})"# r#"rotate(${0:%}, roll = ${1:3.14}, pitch = ${2:3.14}, yaw = ${3:3.14})${}"#
); );
} }

View File

@ -20,6 +20,7 @@ pub(crate) const SETTINGS_UNIT_ANGLE: &str = "defaultAngleUnit";
pub(super) const NO_PRELUDE: &str = "no_std"; pub(super) const NO_PRELUDE: &str = "no_std";
pub(super) const IMPORT_FORMAT: &str = "format"; pub(super) const IMPORT_FORMAT: &str = "format";
pub(super) const IMPORT_FORMAT_VALUES: [&str; 9] = ["fbx", "gltf", "glb", "obj", "ply", "sldprt", "stp", "step", "stl"];
pub(super) const IMPORT_COORDS: &str = "coords"; pub(super) const IMPORT_COORDS: &str = "coords";
pub(super) const IMPORT_COORDS_VALUES: [(&str, &System); 3] = pub(super) const IMPORT_COORDS_VALUES: [(&str, &System); 3] =
[("zoo", KITTYCAD), ("opengl", OPENGL), ("vulkan", VULKAN)]; [("zoo", KITTYCAD), ("opengl", OPENGL), ("vulkan", VULKAN)];

View File

@ -115,30 +115,6 @@ impl CodeRef {
} }
} }
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
#[ts(export_to = "Artifact.ts")]
#[serde(rename_all = "camelCase")]
pub struct CompositeSolid {
pub id: ArtifactId,
pub sub_type: CompositeSolidSubType,
/// Constituent solids of the composite solid.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub solid_ids: Vec<ArtifactId>,
/// Tool solids used for asymmetric operations like subtract.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tool_ids: Vec<ArtifactId>,
pub code_ref: CodeRef,
}
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, ts_rs::TS)]
#[ts(export_to = "Artifact.ts")]
#[serde(rename_all = "camelCase")]
pub enum CompositeSolidSubType {
Intersect,
Subtract,
Union,
}
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
#[ts(export_to = "Artifact.ts")] #[ts(export_to = "Artifact.ts")]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -342,7 +318,6 @@ pub struct Helix {
#[ts(export_to = "Artifact.ts")] #[ts(export_to = "Artifact.ts")]
#[serde(tag = "type", rename_all = "camelCase")] #[serde(tag = "type", rename_all = "camelCase")]
pub enum Artifact { pub enum Artifact {
CompositeSolid(CompositeSolid),
Plane(Plane), Plane(Plane),
Path(Path), Path(Path),
Segment(Segment), Segment(Segment),
@ -361,7 +336,6 @@ pub enum Artifact {
impl Artifact { impl Artifact {
pub(crate) fn id(&self) -> ArtifactId { pub(crate) fn id(&self) -> ArtifactId {
match self { match self {
Artifact::CompositeSolid(a) => a.id,
Artifact::Plane(a) => a.id, Artifact::Plane(a) => a.id,
Artifact::Path(a) => a.id, Artifact::Path(a) => a.id,
Artifact::Segment(a) => a.id, Artifact::Segment(a) => a.id,
@ -381,7 +355,6 @@ impl Artifact {
#[expect(dead_code)] #[expect(dead_code)]
pub(crate) fn code_ref(&self) -> Option<&CodeRef> { pub(crate) fn code_ref(&self) -> Option<&CodeRef> {
match self { match self {
Artifact::CompositeSolid(a) => Some(&a.code_ref),
Artifact::Plane(a) => Some(&a.code_ref), Artifact::Plane(a) => Some(&a.code_ref),
Artifact::Path(a) => Some(&a.code_ref), Artifact::Path(a) => Some(&a.code_ref),
Artifact::Segment(a) => Some(&a.code_ref), Artifact::Segment(a) => Some(&a.code_ref),
@ -402,7 +375,6 @@ impl Artifact {
/// type, return the new artifact which should be used as a replacement. /// type, return the new artifact which should be used as a replacement.
fn merge(&mut self, new: Artifact) -> Option<Artifact> { fn merge(&mut self, new: Artifact) -> Option<Artifact> {
match self { match self {
Artifact::CompositeSolid(a) => a.merge(new),
Artifact::Plane(a) => a.merge(new), Artifact::Plane(a) => a.merge(new),
Artifact::Path(a) => a.merge(new), Artifact::Path(a) => a.merge(new),
Artifact::Segment(a) => a.merge(new), Artifact::Segment(a) => a.merge(new),
@ -420,18 +392,6 @@ impl Artifact {
} }
} }
impl CompositeSolid {
fn merge(&mut self, new: Artifact) -> Option<Artifact> {
let Artifact::CompositeSolid(new) = new else {
return Some(new);
};
merge_ids(&mut self.solid_ids, new.solid_ids);
merge_ids(&mut self.tool_ids, new.tool_ids);
None
}
}
impl Plane { impl Plane {
fn merge(&mut self, new: Artifact) -> Option<Artifact> { fn merge(&mut self, new: Artifact) -> Option<Artifact> {
let Artifact::Plane(new) = new else { let Artifact::Plane(new) = new else {
@ -1087,85 +1047,6 @@ fn artifacts_to_update(
// the helix here, but it's not useful right now. // the helix here, but it's not useful right now.
return Ok(return_arr); return Ok(return_arr);
} }
ModelingCmd::BooleanIntersection(_) | ModelingCmd::BooleanSubtract(_) | ModelingCmd::BooleanUnion(_) => {
let (sub_type, solid_ids, tool_ids) = match cmd {
ModelingCmd::BooleanIntersection(intersection) => {
let solid_ids = intersection
.solid_ids
.iter()
.copied()
.map(ArtifactId::new)
.collect::<Vec<_>>();
(CompositeSolidSubType::Intersect, solid_ids, Vec::new())
}
ModelingCmd::BooleanSubtract(subtract) => {
let solid_ids = subtract
.target_ids
.iter()
.copied()
.map(ArtifactId::new)
.collect::<Vec<_>>();
let tool_ids = subtract
.tool_ids
.iter()
.copied()
.map(ArtifactId::new)
.collect::<Vec<_>>();
(CompositeSolidSubType::Subtract, solid_ids, tool_ids)
}
ModelingCmd::BooleanUnion(union) => {
let solid_ids = union.solid_ids.iter().copied().map(ArtifactId::new).collect::<Vec<_>>();
(CompositeSolidSubType::Union, solid_ids, Vec::new())
}
_ => unreachable!(),
};
let mut new_solid_ids = vec![id];
match response {
OkModelingCmdResponse::BooleanIntersection(intersection) => intersection
.extra_solid_ids
.iter()
.copied()
.map(ArtifactId::new)
.for_each(|id| new_solid_ids.push(id)),
OkModelingCmdResponse::BooleanSubtract(subtract) => subtract
.extra_solid_ids
.iter()
.copied()
.map(ArtifactId::new)
.for_each(|id| new_solid_ids.push(id)),
OkModelingCmdResponse::BooleanUnion(union) => union
.extra_solid_ids
.iter()
.copied()
.map(ArtifactId::new)
.for_each(|id| new_solid_ids.push(id)),
_ => {}
}
let return_arr = new_solid_ids
.into_iter()
// Extra solid IDs may include the command's ID. Make sure we
// don't create a duplicate.
.filter(|solid_id| *solid_id != id)
.map(|solid_id| {
Artifact::CompositeSolid(CompositeSolid {
id: solid_id,
sub_type,
solid_ids: solid_ids.clone(),
tool_ids: tool_ids.clone(),
code_ref: CodeRef {
range,
path_to_node: path_to_node.clone(),
},
})
})
.collect::<Vec<_>>();
// TODO: Should we add the reverse graph edges?
return Ok(return_arr);
}
_ => {} _ => {}
} }

View File

@ -67,11 +67,6 @@ impl Artifact {
/// the graph. This should be disjoint with `child_ids`. /// the graph. This should be disjoint with `child_ids`.
pub(crate) fn back_edges(&self) -> Vec<ArtifactId> { pub(crate) fn back_edges(&self) -> Vec<ArtifactId> {
match self { match self {
Artifact::CompositeSolid(a) => {
let mut ids = a.solid_ids.clone();
ids.extend(a.tool_ids.iter());
ids
}
Artifact::Plane(_) => Vec::new(), Artifact::Plane(_) => Vec::new(),
Artifact::Path(a) => vec![a.plane_id], Artifact::Path(a) => vec![a.plane_id],
Artifact::Segment(a) => vec![a.path_id], Artifact::Segment(a) => vec![a.path_id],
@ -92,11 +87,6 @@ impl Artifact {
/// the graph. /// the graph.
pub(crate) fn child_ids(&self) -> Vec<ArtifactId> { pub(crate) fn child_ids(&self) -> Vec<ArtifactId> {
match self { match self {
Artifact::CompositeSolid(_) => {
// Note: Don't include these since they're parents: solid_ids,
// tool_ids.
Vec::new()
}
Artifact::Plane(a) => a.path_ids.clone(), Artifact::Plane(a) => a.path_ids.clone(),
Artifact::Path(a) => { Artifact::Path(a) => {
// Note: Don't include these since they're parents: plane_id. // Note: Don't include these since they're parents: plane_id.
@ -223,7 +213,6 @@ impl ArtifactGraph {
let id = artifact.id(); let id = artifact.id();
let grouped = match artifact { let grouped = match artifact {
Artifact::CompositeSolid(_) => false,
Artifact::Plane(_) => false, Artifact::Plane(_) => false,
Artifact::Path(_) => { Artifact::Path(_) => {
groups.entry(id).or_insert_with(Vec::new).push(id); groups.entry(id).or_insert_with(Vec::new).push(id);
@ -289,15 +278,6 @@ impl ArtifactGraph {
} }
match artifact { match artifact {
Artifact::CompositeSolid(composite_solid) => {
writeln!(
output,
"{prefix}{}[\"CompositeSolid {:?}<br>{:?}\"]",
id,
composite_solid.sub_type,
code_ref_display(&composite_solid.code_ref)
)?;
}
Artifact::Plane(plane) => { Artifact::Plane(plane) => {
writeln!( writeln!(
output, output,

View File

@ -939,40 +939,25 @@ impl Node<BinaryExpression> {
if self.operator == BinaryOperator::Add || self.operator == BinaryOperator::Or { if self.operator == BinaryOperator::Add || self.operator == BinaryOperator::Or {
if let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value) { if let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value) {
let args = crate::std::Args::new(Default::default(), self.into(), ctx.clone(), None); let args = crate::std::Args::new(Default::default(), self.into(), ctx.clone(), None);
let result = crate::std::csg::inner_union( let result =
vec![*left.clone(), *right.clone()], crate::std::csg::inner_union(vec![*left.clone(), *right.clone()], exec_state, args).await?;
Default::default(),
exec_state,
args,
)
.await?;
return Ok(result.into()); return Ok(result.into());
} }
} else if self.operator == BinaryOperator::Sub { } else if self.operator == BinaryOperator::Sub {
// Check if we have solids. // Check if we have solids.
if let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value) { if let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value) {
let args = crate::std::Args::new(Default::default(), self.into(), ctx.clone(), None); let args = crate::std::Args::new(Default::default(), self.into(), ctx.clone(), None);
let result = crate::std::csg::inner_subtract( let result =
vec![*left.clone()], crate::std::csg::inner_subtract(vec![*left.clone()], vec![*right.clone()], exec_state, args)
vec![*right.clone()], .await?;
Default::default(),
exec_state,
args,
)
.await?;
return Ok(result.into()); return Ok(result.into());
} }
} else if self.operator == BinaryOperator::And { } else if self.operator == BinaryOperator::And {
// Check if we have solids. // Check if we have solids.
if let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value) { if let (KclValue::Solid { value: left }, KclValue::Solid { value: right }) = (&left_value, &right_value) {
let args = crate::std::Args::new(Default::default(), self.into(), ctx.clone(), None); let args = crate::std::Args::new(Default::default(), self.into(), ctx.clone(), None);
let result = crate::std::csg::inner_intersect( let result =
vec![*left.clone(), *right.clone()], crate::std::csg::inner_intersect(vec![*left.clone(), *right.clone()], exec_state, args).await?;
Default::default(),
exec_state,
args,
)
.await?;
return Ok(result.into()); return Ok(result.into());
} }
} }

View File

@ -173,7 +173,7 @@ pub(super) fn format_from_annotations(
KclError::Semantic(KclErrorDetails { KclError::Semantic(KclErrorDetails {
message: format!( message: format!(
"Unknown format for import, expected one of: {}", "Unknown format for import, expected one of: {}",
crate::IMPORT_FILE_EXTENSIONS.join(", ") annotations::IMPORT_FORMAT_VALUES.join(", ")
), ),
source_ranges: vec![p.as_source_range()], source_ranges: vec![p.as_source_range()],
}) })

View File

@ -11,7 +11,9 @@ pub use cache::{bust_cache, clear_mem_cache};
pub use cad_op::Operation; pub use cad_op::Operation;
pub use geometry::*; pub use geometry::*;
pub use id_generator::IdGenerator; pub use id_generator::IdGenerator;
pub(crate) use import::PreImportedGeometry; pub(crate) use import::{
import_foreign, send_to_engine as send_import_to_engine, PreImportedGeometry, ZOO_COORD_SYSTEM,
};
use indexmap::IndexMap; use indexmap::IndexMap;
pub use kcl_value::{KclObjectFields, KclValue}; pub use kcl_value::{KclObjectFields, KclValue};
use kcmc::{ use kcmc::{

View File

@ -131,36 +131,11 @@ pub mod pretty {
pub use crate::{parsing::token::NumericSuffix, unparser::format_number}; pub use crate::{parsing::token::NumericSuffix, unparser::format_number};
} }
#[cfg(feature = "cli")]
use clap::ValueEnum;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[allow(unused_imports)] #[allow(unused_imports)]
use crate::log::{log, logln}; use crate::log::{log, logln};
lazy_static::lazy_static! {
pub static ref IMPORT_FILE_EXTENSIONS: Vec<String> = {
let mut import_file_extensions = vec!["stp".to_string(), "glb".to_string(), "fbxb".to_string()];
#[cfg(feature = "cli")]
let named_extensions = kittycad::types::FileImportFormat::value_variants()
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<String>>();
#[cfg(not(feature = "cli"))]
let named_extensions = vec![]; // We don't really need this outside of the CLI.
// Add all the default import formats.
import_file_extensions.extend_from_slice(&named_extensions);
import_file_extensions
};
pub static ref RELEVANT_FILE_EXTENSIONS: Vec<String> = {
let mut relevant_extensions = IMPORT_FILE_EXTENSIONS.clone();
relevant_extensions.push("kcl".to_string());
relevant_extensions
};
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Program { pub struct Program {
#[serde(flatten)] #[serde(flatten)]

View File

@ -3418,148 +3418,3 @@ async fn kcl_test_kcl_lsp_multi_file_error() {
server.executor_ctx().await.clone().unwrap().close().await; server.executor_ctx().await.clone().unwrap().close().await;
} }
#[tokio::test(flavor = "multi_thread")]
async fn test_kcl_lsp_on_hover_untitled_file_scheme() {
let server = kcl_lsp_server(true).await.unwrap();
// Send open file.
server
.did_open(tower_lsp::lsp_types::DidOpenTextDocumentParams {
text_document: tower_lsp::lsp_types::TextDocumentItem {
uri: "untitled:Untitled-1".try_into().unwrap(),
language_id: "kcl".to_string(),
version: 1,
text: r#"startSketchOn(XY)
foo = 42
foo
fn bar(x: string): string {
return x
}
bar("an arg")
startSketchOn(XY)
|> startProfileAt([0, 0], %)
|> line(end = [10, 0])
|> line(end = [0, 10])
"#
.to_string(),
},
})
.await;
// Std lib call
let hover = server
.hover(tower_lsp::lsp_types::HoverParams {
text_document_position_params: tower_lsp::lsp_types::TextDocumentPositionParams {
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
uri: "untitled:Untitled-1".try_into().unwrap(),
},
position: tower_lsp::lsp_types::Position { line: 0, character: 2 },
},
work_done_progress_params: Default::default(),
})
.await
.unwrap();
match hover.unwrap().contents {
tower_lsp::lsp_types::HoverContents::Markup(tower_lsp::lsp_types::MarkupContent { value, .. }) => {
assert!(value.contains("startSketchOn"));
assert!(value.contains(": SketchSurface"));
assert!(value.contains("Start a new 2-dimensional sketch on a specific"));
}
_ => unreachable!(),
}
// Variable use
let hover = server
.hover(tower_lsp::lsp_types::HoverParams {
text_document_position_params: tower_lsp::lsp_types::TextDocumentPositionParams {
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
uri: "untitled:Untitled-1".try_into().unwrap(),
},
position: tower_lsp::lsp_types::Position { line: 2, character: 1 },
},
work_done_progress_params: Default::default(),
})
.await
.unwrap();
match hover.unwrap().contents {
tower_lsp::lsp_types::HoverContents::Markup(tower_lsp::lsp_types::MarkupContent { value, .. }) => {
assert!(value.contains("foo: number = 42"));
}
_ => unreachable!(),
}
// User-defined function call.
let hover = server
.hover(tower_lsp::lsp_types::HoverParams {
text_document_position_params: tower_lsp::lsp_types::TextDocumentPositionParams {
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
uri: "untitled:Untitled-1".try_into().unwrap(),
},
position: tower_lsp::lsp_types::Position { line: 8, character: 1 },
},
work_done_progress_params: Default::default(),
})
.await
.unwrap();
match hover.unwrap().contents {
tower_lsp::lsp_types::HoverContents::Markup(tower_lsp::lsp_types::MarkupContent { value, .. }) => {
assert!(value.contains("bar(x: string): string"));
}
_ => unreachable!(),
}
// Variable inside a function
let hover = server
.hover(tower_lsp::lsp_types::HoverParams {
text_document_position_params: tower_lsp::lsp_types::TextDocumentPositionParams {
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
uri: "untitled:Untitled-1".try_into().unwrap(),
},
position: tower_lsp::lsp_types::Position { line: 5, character: 9 },
},
work_done_progress_params: Default::default(),
})
.await
.unwrap();
match hover.unwrap().contents {
tower_lsp::lsp_types::HoverContents::Markup(tower_lsp::lsp_types::MarkupContent { value, .. }) => {
assert!(value.contains("x: string"));
}
_ => unreachable!(),
}
// std function KwArg
let hover = server
.hover(tower_lsp::lsp_types::HoverParams {
text_document_position_params: tower_lsp::lsp_types::TextDocumentPositionParams {
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
uri: "untitled:Untitled-1".try_into().unwrap(),
},
position: tower_lsp::lsp_types::Position {
line: 12,
character: 11,
},
},
work_done_progress_params: Default::default(),
})
.await
.unwrap();
match hover.unwrap().contents {
tower_lsp::lsp_types::HoverContents::Markup(tower_lsp::lsp_types::MarkupContent { value, .. }) => {
assert!(value.contains("end?: [number]"));
assert!(value.contains("How far away (along the X and Y axes) should this line go?"));
}
_ => unreachable!(),
}
server.executor_ctx().await.clone().unwrap().close().await;
}

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,
}; };
thread_local! { thread_local! {
@ -1803,6 +1803,11 @@ fn import_stmt(i: &mut TokenSlice) -> PResult<BoxNode<ImportStatement>> {
end = alias.end; end = alias.end;
*selector_alias = Some(alias); *selector_alias = Some(alias);
} }
ParseContext::warn(CompilationError::err(
SourceRange::new(start, path.end, path.module_id),
"Importing a whole module is experimental, likely to be buggy, and likely to change",
));
} }
let path_string = match path.inner.value { let path_string = match path.inner.value {
@ -1838,6 +1843,8 @@ fn import_stmt(i: &mut TokenSlice) -> PResult<BoxNode<ImportStatement>> {
)) ))
} }
const FOREIGN_IMPORT_EXTENSIONS: [&str; 8] = ["fbx", "gltf", "glb", "obj", "ply", "sldprt", "step", "stl"];
/// Validates the path string in an `import` statement. /// Validates the path string in an `import` statement.
/// ///
/// `var_name` is `true` if the path will be used as a variable name. /// `var_name` is `true` if the path will be used as a variable name.
@ -1902,11 +1909,12 @@ fn validate_path_string(path_string: String, var_name: bool, path_range: SourceR
ImportPath::Std { path: segments } ImportPath::Std { path: segments }
} else if path_string.contains('.') { } else if path_string.contains('.') {
let extn = std::path::Path::new(&path_string).extension().unwrap_or_default(); // TODO should allow other extensions if there is a format attribute.
if !IMPORT_FILE_EXTENSIONS.contains(&extn.to_string_lossy().to_string()) { let extn = &path_string[path_string.rfind('.').unwrap() + 1..];
if !FOREIGN_IMPORT_EXTENSIONS.contains(&extn) {
ParseContext::warn(CompilationError::err( ParseContext::warn(CompilationError::err(
path_range, path_range,
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: {}", FOREIGN_IMPORT_EXTENSIONS.join(", ")),
)) ))
} }
ImportPath::Foreign { path: path_string } ImportPath::Foreign { path: path_string }
@ -1914,7 +1922,7 @@ fn validate_path_string(path_string: String, var_name: bool, path_range: SourceR
return Err(ErrMode::Cut( return Err(ErrMode::Cut(
CompilationError::fatal( CompilationError::fatal(
path_range, path_range,
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: {}", FOREIGN_IMPORT_EXTENSIONS.join(", ")),
) )
.into(), .into(),
)); ));
@ -4490,9 +4498,21 @@ export fn cos(num: number(rad)): number(_) {}"#;
#[test] #[test]
fn warn_import() { fn warn_import() {
let some_program_string = r#"import "foo.bad""#; let some_program_string = r#"import "foo.kcl""#;
let (_, errs) = assert_no_err(some_program_string); let (_, errs) = assert_no_err(some_program_string);
assert_eq!(errs.len(), 1, "{errs:#?}"); assert_eq!(errs.len(), 1, "{errs:#?}");
let some_program_string = r#"import "foo.obj""#;
let (_, errs) = assert_no_err(some_program_string);
assert_eq!(errs.len(), 1, "{errs:#?}");
let some_program_string = r#"import "foo.sldprt""#;
let (_, errs) = assert_no_err(some_program_string);
assert_eq!(errs.len(), 1, "{errs:#?}");
let some_program_string = r#"import "foo.bad""#;
let (_, errs) = assert_no_err(some_program_string);
assert_eq!(errs.len(), 2, "{errs:#?}");
} }
#[test] #[test]

View File

@ -664,6 +664,10 @@ impl Args {
FromArgs::from_args(self, 0) FromArgs::from_args(self, 0)
} }
pub(crate) fn get_import_data(&self) -> Result<(String, Option<crate::std::import::ImportFormat>), KclError> {
FromArgs::from_args(self, 0)
}
pub(crate) fn get_sketch_data_and_optional_tag( pub(crate) fn get_sketch_data_and_optional_tag(
&self, &self,
) -> Result<(super::sketch::SketchData, Option<FaceTag>), KclError> { ) -> Result<(super::sketch::SketchData, Option<FaceTag>), KclError> {
@ -1073,6 +1077,35 @@ macro_rules! let_field_of {
}; };
} }
impl<'a> FromKclValue<'a> for crate::std::import::ImportFormat {
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
let obj = arg.as_object()?;
let_field_of!(obj, typ "format");
match typ {
"fbx" => Some(Self::Fbx {}),
"gltf" => Some(Self::Gltf {}),
"sldprt" => Some(Self::Sldprt {}),
"step" => Some(Self::Step {}),
"stl" => {
let_field_of!(obj, coords?);
let_field_of!(obj, units);
Some(Self::Stl { coords, units })
}
"obj" => {
let_field_of!(obj, coords?);
let_field_of!(obj, units);
Some(Self::Obj { coords, units })
}
"ply" => {
let_field_of!(obj, coords?);
let_field_of!(obj, units);
Some(Self::Ply { coords, units })
}
_ => None,
}
}
}
impl<'a> FromKclValue<'a> for super::sketch::AngledLineThatIntersectsData { impl<'a> FromKclValue<'a> for super::sketch::AngledLineThatIntersectsData {
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> { fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
let obj = arg.as_object()?; let obj = arg.as_object()?;

View File

@ -2,13 +2,6 @@
use anyhow::Result; use anyhow::Result;
use kcl_derive_docs::stdlib; use kcl_derive_docs::stdlib;
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, ModelingCmd};
use kittycad_modeling_cmds::{
self as kcmc,
ok_response::OkModelingCmdResponse,
output::{BooleanIntersection, BooleanSubtract, BooleanUnion},
websocket::OkWebSocketResponseData,
};
use crate::{ use crate::{
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
@ -16,13 +9,10 @@ use crate::{
std::Args, std::Args,
}; };
use super::DEFAULT_TOLERANCE;
/// Union two or more solids into a single solid. /// Union two or more solids into a single solid.
pub async fn union(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn union(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let solids: Vec<Solid> = let solids: Vec<Solid> =
args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::Union(vec![RuntimeType::solids()]), exec_state)?; args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::Union(vec![RuntimeType::solids()]), exec_state)?;
let tolerance = args.get_kw_arg_opt("tolerance")?;
if solids.len() < 2 { if solids.len() < 2 {
return Err(KclError::UndefinedValue(KclErrorDetails { return Err(KclError::UndefinedValue(KclErrorDetails {
@ -31,7 +21,7 @@ pub async fn union(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
})); }));
} }
let solids = inner_union(solids, tolerance, exec_state, args).await?; let solids = inner_union(solids, exec_state, args).await?;
Ok(solids.into()) Ok(solids.into())
} }
@ -40,19 +30,18 @@ pub async fn union(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
/// ```no_run /// ```no_run
/// // Union two cubes using the stdlib functions. /// // Union two cubes using the stdlib functions.
/// ///
/// fn cube(center, size) { /// fn cube(center) {
/// return startSketchOn('XY') /// return startSketchOn('XY')
/// |> startProfileAt([center[0] - size, center[1] - size], %) /// |> startProfileAt([center[0] - 10, center[1] - 10], %)
/// |> line(endAbsolute = [center[0] + size, center[1] - size]) /// |> line(endAbsolute = [center[0] + 10, center[1] - 10])
/// |> line(endAbsolute = [center[0] + size, center[1] + size]) /// |> line(endAbsolute = [center[0] + 10, center[1] + 10])
/// |> line(endAbsolute = [center[0] - size, center[1] + size]) /// |> line(endAbsolute = [center[0] - 10, center[1] + 10])
/// |> close() /// |> close()
/// |> extrude(length = 10) /// |> extrude(length = 10)
/// } /// }
/// ///
/// part001 = cube([0, 0], 10) /// part001 = cube([0, 0])
/// part002 = cube([7, 3], 5) /// part002 = cube([20, 10])
/// |> translate(z = 1)
/// ///
/// unionedPart = union([part001, part002]) /// unionedPart = union([part001, part002])
/// ``` /// ```
@ -62,19 +51,18 @@ pub async fn union(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
/// // NOTE: This will not work when using codemods through the UI. /// // NOTE: This will not work when using codemods through the UI.
/// // Codemods will generate the stdlib function call instead. /// // Codemods will generate the stdlib function call instead.
/// ///
/// fn cube(center, size) { /// fn cube(center) {
/// return startSketchOn('XY') /// return startSketchOn('XY')
/// |> startProfileAt([center[0] - size, center[1] - size], %) /// |> startProfileAt([center[0] - 10, center[1] - 10], %)
/// |> line(endAbsolute = [center[0] + size, center[1] - size]) /// |> line(endAbsolute = [center[0] + 10, center[1] - 10])
/// |> line(endAbsolute = [center[0] + size, center[1] + size]) /// |> line(endAbsolute = [center[0] + 10, center[1] + 10])
/// |> line(endAbsolute = [center[0] - size, center[1] + size]) /// |> line(endAbsolute = [center[0] - 10, center[1] + 10])
/// |> close() /// |> close()
/// |> extrude(length = 10) /// |> extrude(length = 10)
/// } /// }
/// ///
/// part001 = cube([0, 0], 10) /// part001 = cube([0, 0])
/// part002 = cube([7, 3], 5) /// part002 = cube([20, 10])
/// |> translate(z = 1)
/// ///
/// // This is the equivalent of: union([part001, part002]) /// // This is the equivalent of: union([part001, part002])
/// unionedPart = part001 + part002 /// unionedPart = part001 + part002
@ -85,19 +73,18 @@ pub async fn union(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
/// // NOTE: This will not work when using codemods through the UI. /// // NOTE: This will not work when using codemods through the UI.
/// // Codemods will generate the stdlib function call instead. /// // Codemods will generate the stdlib function call instead.
/// ///
/// fn cube(center, size) { /// fn cube(center) {
/// return startSketchOn('XY') /// return startSketchOn('XY')
/// |> startProfileAt([center[0] - size, center[1] - size], %) /// |> startProfileAt([center[0] - 10, center[1] - 10], %)
/// |> line(endAbsolute = [center[0] + size, center[1] - size]) /// |> line(endAbsolute = [center[0] + 10, center[1] - 10])
/// |> line(endAbsolute = [center[0] + size, center[1] + size]) /// |> line(endAbsolute = [center[0] + 10, center[1] + 10])
/// |> line(endAbsolute = [center[0] - size, center[1] + size]) /// |> line(endAbsolute = [center[0] - 10, center[1] + 10])
/// |> close() /// |> close()
/// |> extrude(length = 10) /// |> extrude(length = 10)
/// } /// }
/// ///
/// part001 = cube([0, 0], 10) /// part001 = cube([0, 0])
/// part002 = cube([7, 3], 5) /// part002 = cube([20, 10])
/// |> translate(z = 1)
/// ///
/// // This is the equivalent of: union([part001, part002]) /// // This is the equivalent of: union([part001, part002])
/// // Programmers will understand `|` as a union operation, but mechanical engineers /// // Programmers will understand `|` as a union operation, but mechanical engineers
@ -109,64 +96,31 @@ pub async fn union(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
feature_tree_operation = true, feature_tree_operation = true,
keywords = true, keywords = true,
unlabeled_first = true, unlabeled_first = true,
deprecated = true,
args = { args = {
solids = {docs = "The solids to union."}, solids = {docs = "The solids to union."},
tolerance = {docs = "The tolerance to use for the union operation."},
} }
}] }]
pub(crate) async fn inner_union( pub(crate) async fn inner_union(
solids: Vec<Solid>, solids: Vec<Solid>,
tolerance: Option<f64>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
) -> Result<Vec<Solid>, KclError> { ) -> Result<Vec<Solid>, KclError> {
let solid_out_id = exec_state.next_uuid();
let mut solid = solids[0].clone();
solid.id = solid_out_id;
let mut new_solids = vec![solid.clone()];
if args.ctx.no_engine_commands().await {
return Ok(new_solids);
}
// Flush the fillets for the solids. // Flush the fillets for the solids.
args.flush_batch_for_solids(exec_state, &solids).await?; args.flush_batch_for_solids(exec_state, &solids).await?;
let result = args // TODO: call the engine union operation.
.send_modeling_cmd( // TODO: figure out all the shit after for the faces etc.
solid_out_id,
ModelingCmd::from(mcmd::BooleanUnion {
solid_ids: solids.iter().map(|s| s.id).collect(),
tolerance: LengthUnit(tolerance.unwrap_or(DEFAULT_TOLERANCE)),
}),
)
.await?;
let OkWebSocketResponseData::Modeling { // For now just return the first solid.
modeling_response: OkModelingCmdResponse::BooleanUnion(BooleanUnion { extra_solid_ids }), // Til we have a proper implementation.
} = result Ok(vec![solids[0].clone()])
else {
return Err(KclError::Internal(KclErrorDetails {
message: "Failed to get the result of the union operation.".to_string(),
source_ranges: vec![args.source_range],
}));
};
// If we have more solids, set those as well.
if !extra_solid_ids.is_empty() {
solid.id = extra_solid_ids[0];
new_solids.push(solid.clone());
}
Ok(new_solids)
} }
/// Intersect returns the shared volume between multiple solids, preserving only /// Intersect returns the shared volume between multiple solids, preserving only
/// overlapping regions. /// overlapping regions.
pub async fn intersect(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { pub async fn intersect(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let solids: Vec<Solid> = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?; let solids: Vec<Solid> = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?;
let tolerance = args.get_kw_arg_opt("tolerance")?;
if solids.len() < 2 { if solids.len() < 2 {
return Err(KclError::UndefinedValue(KclErrorDetails { return Err(KclError::UndefinedValue(KclErrorDetails {
@ -175,7 +129,7 @@ pub async fn intersect(exec_state: &mut ExecState, args: Args) -> Result<KclValu
})); }));
} }
let solids = inner_intersect(solids, tolerance, exec_state, args).await?; let solids = inner_intersect(solids, exec_state, args).await?;
Ok(solids.into()) Ok(solids.into())
} }
@ -190,19 +144,18 @@ pub async fn intersect(exec_state: &mut ExecState, args: Args) -> Result<KclValu
/// ```no_run /// ```no_run
/// // Intersect two cubes using the stdlib functions. /// // Intersect two cubes using the stdlib functions.
/// ///
/// fn cube(center, size) { /// fn cube(center) {
/// return startSketchOn('XY') /// return startSketchOn('XY')
/// |> startProfileAt([center[0] - size, center[1] - size], %) /// |> startProfileAt([center[0] - 10, center[1] - 10], %)
/// |> line(endAbsolute = [center[0] + size, center[1] - size]) /// |> line(endAbsolute = [center[0] + 10, center[1] - 10])
/// |> line(endAbsolute = [center[0] + size, center[1] + size]) /// |> line(endAbsolute = [center[0] + 10, center[1] + 10])
/// |> line(endAbsolute = [center[0] - size, center[1] + size]) /// |> line(endAbsolute = [center[0] - 10, center[1] + 10])
/// |> close() /// |> close()
/// |> extrude(length = 10) /// |> extrude(length = 10)
/// } /// }
/// ///
/// part001 = cube([0, 0], 10) /// part001 = cube([0, 0])
/// part002 = cube([7, 3], 5) /// part002 = cube([8, 8])
/// |> translate(z = 1)
/// ///
/// intersectedPart = intersect([part001, part002]) /// intersectedPart = intersect([part001, part002])
/// ``` /// ```
@ -212,19 +165,18 @@ pub async fn intersect(exec_state: &mut ExecState, args: Args) -> Result<KclValu
/// // NOTE: This will not work when using codemods through the UI. /// // NOTE: This will not work when using codemods through the UI.
/// // Codemods will generate the stdlib function call instead. /// // Codemods will generate the stdlib function call instead.
/// ///
/// fn cube(center, size) { /// fn cube(center) {
/// return startSketchOn('XY') /// return startSketchOn('XY')
/// |> startProfileAt([center[0] - size, center[1] - size], %) /// |> startProfileAt([center[0] - 10, center[1] - 10], %)
/// |> line(endAbsolute = [center[0] + size, center[1] - size]) /// |> line(endAbsolute = [center[0] + 10, center[1] - 10])
/// |> line(endAbsolute = [center[0] + size, center[1] + size]) /// |> line(endAbsolute = [center[0] + 10, center[1] + 10])
/// |> line(endAbsolute = [center[0] - size, center[1] + size]) /// |> line(endAbsolute = [center[0] - 10, center[1] + 10])
/// |> close() /// |> close()
/// |> extrude(length = 10) /// |> extrude(length = 10)
/// } /// }
/// ///
/// part001 = cube([0, 0], 10) /// part001 = cube([0, 0])
/// part002 = cube([7, 3], 5) /// part002 = cube([8, 8])
/// |> translate(z = 1)
/// ///
/// // This is the equivalent of: intersect([part001, part002]) /// // This is the equivalent of: intersect([part001, part002])
/// intersectedPart = part001 & part002 /// intersectedPart = part001 & part002
@ -234,57 +186,25 @@ pub async fn intersect(exec_state: &mut ExecState, args: Args) -> Result<KclValu
feature_tree_operation = true, feature_tree_operation = true,
keywords = true, keywords = true,
unlabeled_first = true, unlabeled_first = true,
deprecated = true,
args = { args = {
solids = {docs = "The solids to intersect."}, solids = {docs = "The solids to intersect."},
tolerance = {docs = "The tolerance to use for the intersection operation."},
} }
}] }]
pub(crate) async fn inner_intersect( pub(crate) async fn inner_intersect(
solids: Vec<Solid>, solids: Vec<Solid>,
tolerance: Option<f64>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
) -> Result<Vec<Solid>, KclError> { ) -> Result<Vec<Solid>, KclError> {
let solid_out_id = exec_state.next_uuid();
let mut solid = solids[0].clone();
solid.id = solid_out_id;
let mut new_solids = vec![solid.clone()];
if args.ctx.no_engine_commands().await {
return Ok(new_solids);
}
// Flush the fillets for the solids. // Flush the fillets for the solids.
args.flush_batch_for_solids(exec_state, &solids).await?; args.flush_batch_for_solids(exec_state, &solids).await?;
let result = args // TODO: call the engine union operation.
.send_modeling_cmd( // TODO: figure out all the shit after for the faces etc.
solid_out_id,
ModelingCmd::from(mcmd::BooleanIntersection {
solid_ids: solids.iter().map(|s| s.id).collect(),
tolerance: LengthUnit(tolerance.unwrap_or(DEFAULT_TOLERANCE)),
}),
)
.await?;
let OkWebSocketResponseData::Modeling { // For now just return the first solid.
modeling_response: OkModelingCmdResponse::BooleanIntersection(BooleanIntersection { extra_solid_ids }), // Til we have a proper implementation.
} = result Ok(vec![solids[0].clone()])
else {
return Err(KclError::Internal(KclErrorDetails {
message: "Failed to get the result of the intersection operation.".to_string(),
source_ranges: vec![args.source_range],
}));
};
// If we have more solids, set those as well.
if !extra_solid_ids.is_empty() {
solid.id = extra_solid_ids[0];
new_solids.push(solid.clone());
}
Ok(new_solids)
} }
/// Subtract removes tool solids from base solids, leaving the remaining material. /// Subtract removes tool solids from base solids, leaving the remaining material.
@ -292,23 +212,7 @@ pub async fn subtract(exec_state: &mut ExecState, args: Args) -> Result<KclValue
let solids: Vec<Solid> = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?; let solids: Vec<Solid> = args.get_unlabeled_kw_arg_typed("solids", &RuntimeType::solids(), exec_state)?;
let tools: Vec<Solid> = args.get_kw_arg_typed("tools", &RuntimeType::solids(), exec_state)?; let tools: Vec<Solid> = args.get_kw_arg_typed("tools", &RuntimeType::solids(), exec_state)?;
if solids.len() > 1 { let solids = inner_subtract(solids, tools, exec_state, args).await?;
return Err(KclError::UndefinedValue(KclErrorDetails {
message: "Only one solid is allowed for a subtract operation, currently.".to_string(),
source_ranges: vec![args.source_range],
}));
}
if tools.len() > 1 {
return Err(KclError::UndefinedValue(KclErrorDetails {
message: "Only one tool is allowed for a subtract operation, currently.".to_string(),
source_ranges: vec![args.source_range],
}));
}
let tolerance = args.get_kw_arg_opt("tolerance")?;
let solids = inner_subtract(solids, tools, tolerance, exec_state, args).await?;
Ok(solids.into()) Ok(solids.into())
} }
@ -323,19 +227,20 @@ pub async fn subtract(exec_state: &mut ExecState, args: Args) -> Result<KclValue
/// ```no_run /// ```no_run
/// // Subtract a cylinder from a cube using the stdlib functions. /// // Subtract a cylinder from a cube using the stdlib functions.
/// ///
/// fn cube(center, size) { /// fn cube(center) {
/// return startSketchOn('XY') /// return startSketchOn('XY')
/// |> startProfileAt([center[0] - size, center[1] - size], %) /// |> startProfileAt([center[0] - 10, center[1] - 10], %)
/// |> line(endAbsolute = [center[0] + size, center[1] - size]) /// |> line(endAbsolute = [center[0] + 10, center[1] - 10])
/// |> line(endAbsolute = [center[0] + size, center[1] + size]) /// |> line(endAbsolute = [center[0] + 10, center[1] + 10])
/// |> line(endAbsolute = [center[0] - size, center[1] + size]) /// |> line(endAbsolute = [center[0] - 10, center[1] + 10])
/// |> close() /// |> close()
/// |> extrude(length = 10) /// |> extrude(length = 10)
/// } /// }
/// ///
/// part001 = cube([0, 0], 10) /// part001 = cube([0, 0])
/// part002 = cube([7, 3], 5) /// part002 = startSketchOn('XY')
/// |> translate(z = 1) /// |> circle(center = [0, 0], radius = 2)
/// |> extrude(length = 10)
/// ///
/// subtractedPart = subtract([part001], tools=[part002]) /// subtractedPart = subtract([part001], tools=[part002])
/// ``` /// ```
@ -345,19 +250,20 @@ pub async fn subtract(exec_state: &mut ExecState, args: Args) -> Result<KclValue
/// // NOTE: This will not work when using codemods through the UI. /// // NOTE: This will not work when using codemods through the UI.
/// // Codemods will generate the stdlib function call instead. /// // Codemods will generate the stdlib function call instead.
/// ///
/// fn cube(center, size) { /// fn cube(center) {
/// return startSketchOn('XY') /// return startSketchOn('XY')
/// |> startProfileAt([center[0] - size, center[1] - size], %) /// |> startProfileAt([center[0] - 10, center[1] - 10], %)
/// |> line(endAbsolute = [center[0] + size, center[1] - size]) /// |> line(endAbsolute = [center[0] + 10, center[1] - 10])
/// |> line(endAbsolute = [center[0] + size, center[1] + size]) /// |> line(endAbsolute = [center[0] + 10, center[1] + 10])
/// |> line(endAbsolute = [center[0] - size, center[1] + size]) /// |> line(endAbsolute = [center[0] - 10, center[1] + 10])
/// |> close() /// |> close()
/// |> extrude(length = 10) /// |> extrude(length = 10)
/// } /// }
/// ///
/// part001 = cube([0, 0], 10) /// part001 = cube([0, 0])
/// part002 = cube([7, 3], 5) /// part002 = startSketchOn('XY')
/// |> translate(z = 1) /// |> circle(center = [0, 0], radius = 2)
/// |> extrude(length = 10)
/// ///
/// // This is the equivalent of: subtract([part001], tools=[part002]) /// // This is the equivalent of: subtract([part001], tools=[part002])
/// subtractedPart = part001 - part002 /// subtractedPart = part001 - part002
@ -367,59 +273,26 @@ pub async fn subtract(exec_state: &mut ExecState, args: Args) -> Result<KclValue
feature_tree_operation = true, feature_tree_operation = true,
keywords = true, keywords = true,
unlabeled_first = true, unlabeled_first = true,
deprecated = true,
args = { args = {
solids = {docs = "The solids to use as the base to subtract from."}, solids = {docs = "The solids to use as the base to subtract from."},
tools = {docs = "The solids to subtract."}, tools = {docs = "The solids to subtract."},
tolerance = {docs = "The tolerance to use for the subtraction operation."},
} }
}] }]
pub(crate) async fn inner_subtract( pub(crate) async fn inner_subtract(
solids: Vec<Solid>, solids: Vec<Solid>,
tools: Vec<Solid>, tools: Vec<Solid>,
tolerance: Option<f64>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
) -> Result<Vec<Solid>, KclError> { ) -> Result<Vec<Solid>, KclError> {
let solid_out_id = exec_state.next_uuid();
let mut solid = solids[0].clone();
solid.id = solid_out_id;
let mut new_solids = vec![solid.clone()];
if args.ctx.no_engine_commands().await {
return Ok(new_solids);
}
// Flush the fillets for the solids and the tools. // Flush the fillets for the solids and the tools.
let combined_solids = solids.iter().chain(tools.iter()).cloned().collect::<Vec<Solid>>(); let combined_solids = solids.iter().chain(tools.iter()).cloned().collect::<Vec<Solid>>();
args.flush_batch_for_solids(exec_state, &combined_solids).await?; args.flush_batch_for_solids(exec_state, &combined_solids).await?;
let result = args // TODO: call the engine union operation.
.send_modeling_cmd( // TODO: figure out all the shit after for the faces etc.
solid_out_id,
ModelingCmd::from(mcmd::BooleanSubtract {
target_ids: solids.iter().map(|s| s.id).collect(),
tool_ids: tools.iter().map(|s| s.id).collect(),
tolerance: LengthUnit(tolerance.unwrap_or(DEFAULT_TOLERANCE)),
}),
)
.await?;
let OkWebSocketResponseData::Modeling { // For now just return the first solid.
modeling_response: OkModelingCmdResponse::BooleanSubtract(BooleanSubtract { extra_solid_ids }), // Til we have a proper implementation.
} = result Ok(vec![solids[0].clone()])
else {
return Err(KclError::Internal(KclErrorDetails {
message: "Failed to get the result of the subtract operation.".to_string(),
source_ranges: vec![args.source_range],
}));
};
// If we have more solids, set those as well.
if !extra_solid_ids.is_empty() {
solid.id = extra_solid_ids[0];
new_solids.push(solid.clone());
}
Ok(new_solids)
} }

View File

@ -0,0 +1,181 @@
//! Standard library functions involved in importing files.
use anyhow::Result;
use kcl_derive_docs::stdlib;
use kcmc::{coord::System, format::InputFormat3d, units::UnitLength};
use kittycad_modeling_cmds as kcmc;
use crate::{
errors::{KclError, KclErrorDetails},
execution::{import_foreign, send_import_to_engine, ExecState, ImportedGeometry, KclValue, ZOO_COORD_SYSTEM},
std::Args,
};
/// Import format specifier
#[derive(serde :: Serialize, serde :: Deserialize, PartialEq, Debug, Clone, schemars :: JsonSchema)]
#[cfg_attr(feature = "tabled", derive(tabled::Tabled))]
#[serde(tag = "format")]
pub enum ImportFormat {
/// Autodesk Filmbox (FBX) format
#[serde(rename = "fbx")]
Fbx {},
/// Binary glTF 2.0. We refer to this as glTF since that is how our customers refer to
/// it, but this can also import binary glTF (glb).
#[serde(rename = "gltf")]
Gltf {},
/// Wavefront OBJ format.
#[serde(rename = "obj")]
Obj {
/// Co-ordinate system of input data.
/// Defaults to the [KittyCAD co-ordinate system.
coords: Option<System>,
/// The units of the input data. This is very important for correct scaling and when
/// calculating physics properties like mass, etc.
/// Defaults to millimeters.
units: UnitLength,
},
/// The PLY Polygon File Format.
#[serde(rename = "ply")]
Ply {
/// Co-ordinate system of input data.
/// Defaults to the [KittyCAD co-ordinate system.
coords: Option<System>,
/// The units of the input data. This is very important for correct scaling and when
/// calculating physics properties like mass, etc.
/// Defaults to millimeters.
units: UnitLength,
},
/// SolidWorks part (SLDPRT) format.
#[serde(rename = "sldprt")]
Sldprt {},
/// ISO 10303-21 (STEP) format.
#[serde(rename = "step")]
Step {},
/// *ST**ereo**L**ithography format.
#[serde(rename = "stl")]
Stl {
/// Co-ordinate system of input data.
/// Defaults to the [KittyCAD co-ordinate system.
coords: Option<System>,
/// The units of the input data. This is very important for correct scaling and when
/// calculating physics properties like mass, etc.
/// Defaults to millimeters.
units: UnitLength,
},
}
impl From<ImportFormat> for InputFormat3d {
fn from(format: ImportFormat) -> Self {
match format {
ImportFormat::Fbx {} => InputFormat3d::Fbx(Default::default()),
ImportFormat::Gltf {} => InputFormat3d::Gltf(Default::default()),
ImportFormat::Obj { coords, units } => InputFormat3d::Obj(kcmc::format::obj::import::Options {
coords: coords.unwrap_or(ZOO_COORD_SYSTEM),
units,
}),
ImportFormat::Ply { coords, units } => InputFormat3d::Ply(kcmc::format::ply::import::Options {
coords: coords.unwrap_or(ZOO_COORD_SYSTEM),
units,
}),
ImportFormat::Sldprt {} => InputFormat3d::Sldprt(kcmc::format::sldprt::import::Options {
split_closed_faces: false,
}),
ImportFormat::Step {} => InputFormat3d::Step(kcmc::format::step::import::Options {
split_closed_faces: false,
}),
ImportFormat::Stl { coords, units } => InputFormat3d::Stl(kcmc::format::stl::import::Options {
coords: coords.unwrap_or(ZOO_COORD_SYSTEM),
units,
}),
}
}
}
/// Import a CAD file.
/// For formats lacking unit data (STL, OBJ, PLY), the default import unit is millimeters.
/// Otherwise you can specify the unit by passing in the options parameter.
/// If you import a gltf file, we will try to find the bin file and import it as well.
///
/// Import paths are relative to the current project directory. This only works in the desktop app
/// not in browser.
pub async fn import(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let (file_path, options): (String, Option<ImportFormat>) = args.get_import_data()?;
let imported_geometry = inner_import(file_path, options, exec_state, args).await?;
Ok(KclValue::ImportedGeometry(imported_geometry))
}
/// Import a CAD file.
///
/// **DEPRECATED** Prefer to use import statements.
///
/// For formats lacking unit data (such as STL, OBJ, or PLY files), the default
/// unit of measurement is millimeters. Alternatively you may specify the unit
/// by passing your desired measurement unit in the options parameter. When
/// importing a GLTF file, the bin file will be imported as well. Import paths
/// are relative to the current project directory.
///
/// Note: The import command currently only works when using the native
/// Design Studio.
///
/// ```no_run
/// model = import("tests/inputs/cube.obj")
/// ```
///
/// ```no_run
/// model = import("tests/inputs/cube.obj", {format: "obj", units: "m"})
/// ```
///
/// ```no_run
/// model = import("tests/inputs/cube.gltf")
/// ```
///
/// ```no_run
/// model = import("tests/inputs/cube.sldprt")
/// ```
///
/// ```no_run
/// model = import("tests/inputs/cube.step")
/// ```
///
/// ```no_run
/// import height, buildSketch from 'common.kcl'
///
/// plane = 'XZ'
/// margin = 2
/// s1 = buildSketch(plane, [0, 0])
/// s2 = buildSketch(plane, [0, height() + margin])
/// ```
#[stdlib {
name = "import",
feature_tree_operation = true,
deprecated = true,
tags = [],
}]
async fn inner_import(
file_path: String,
options: Option<ImportFormat>,
exec_state: &mut ExecState,
args: Args,
) -> Result<ImportedGeometry, KclError> {
if file_path.is_empty() {
return Err(KclError::Semantic(KclErrorDetails {
message: "No file path was provided.".to_string(),
source_ranges: vec![args.source_range],
}));
}
let format = options.map(InputFormat3d::from);
send_import_to_engine(
import_foreign(
std::path::Path::new(&file_path),
format,
exec_state,
&args.ctx,
args.source_range,
)
.await?,
&args.ctx,
)
.await
}

View File

@ -12,6 +12,7 @@ pub mod edge;
pub mod extrude; pub mod extrude;
pub mod fillet; pub mod fillet;
pub mod helix; pub mod helix;
pub mod import;
pub mod loft; pub mod loft;
pub mod math; pub mod math;
pub mod mirror; pub mod mirror;
@ -110,6 +111,7 @@ lazy_static! {
Box::new(crate::std::sweep::Sweep), Box::new(crate::std::sweep::Sweep),
Box::new(crate::std::loft::Loft), Box::new(crate::std::loft::Loft),
Box::new(crate::std::planes::OffsetPlane), Box::new(crate::std::planes::OffsetPlane),
Box::new(crate::std::import::Import),
Box::new(crate::std::math::Acos), Box::new(crate::std::math::Acos),
Box::new(crate::std::math::Asin), Box::new(crate::std::math::Asin),
Box::new(crate::std::math::Atan), Box::new(crate::std::math::Atan),

View File

@ -1,5 +1,8 @@
use std::fmt::Write; use std::fmt::Write;
#[cfg(feature = "cli")]
use clap::ValueEnum;
use crate::parsing::{ use crate::parsing::{
ast::types::{ ast::types::{
Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem, Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
@ -192,7 +195,7 @@ impl Node<Annotation> {
result.push_str(&indentation); result.push_str(&indentation);
result.push_str(comment); result.push_str(comment);
} }
if !result.ends_with("\n\n") && result != "\n" { if !comment.ends_with("*/") && !result.ends_with("\n\n") && result != "\n" {
result.push('\n'); result.push('\n');
} }
} }
@ -864,6 +867,29 @@ impl Parameter {
} }
} }
lazy_static::lazy_static! {
pub static ref IMPORT_FILE_EXTENSIONS: Vec<String> = {
let mut import_file_extensions = vec!["stp".to_string(), "glb".to_string(), "fbxb".to_string()];
#[cfg(feature = "cli")]
let named_extensions = kittycad::types::FileImportFormat::value_variants()
.iter()
.map(|x| format!("{}", x))
.collect::<Vec<String>>();
#[cfg(not(feature = "cli"))]
let named_extensions = vec![]; // We don't really need this outside of the CLI.
// Add all the default import formats.
import_file_extensions.extend_from_slice(&named_extensions);
import_file_extensions
};
pub static ref RELEVANT_EXTENSIONS: Vec<String> = {
let mut relevant_extensions = IMPORT_FILE_EXTENSIONS.clone();
relevant_extensions.push("kcl".to_string());
relevant_extensions
};
}
/// Collect all the kcl (and other relevant) files in a directory, recursively. /// Collect all the kcl (and other relevant) files in a directory, recursively.
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
#[async_recursion::async_recursion] #[async_recursion::async_recursion]
@ -883,7 +909,7 @@ pub async fn walk_dir(dir: &std::path::PathBuf) -> Result<Vec<std::path::PathBuf
files.extend(walk_dir(&path).await?); files.extend(walk_dir(&path).await?);
} else if path } else if path
.extension() .extension()
.is_some_and(|ext| crate::RELEVANT_FILE_EXTENSIONS.contains(&ext.to_string_lossy().to_string())) .is_some_and(|ext| RELEVANT_EXTENSIONS.contains(&ext.to_string_lossy().to_string()))
{ {
files.push(path); files.push(path);
} }
@ -1022,20 +1048,6 @@ bar = 0
assert_eq!(output, input); assert_eq!(output, input);
} }
#[test]
fn recast_annotations_with_block_comment() {
let input = r#"/* Start comment
sdfsdfsdfs */
@settings(defaultLengthUnit = in)
foo = 42
"#;
let program = crate::parsing::top_level_parse(input).unwrap();
let output = program.recast(&Default::default(), 0);
assert_eq!(output, input);
}
#[test] #[test]
fn test_recast_if_else_if_same() { fn test_recast_if_else_if_same() {
let input = r#"b = if false { let input = r#"b = if false {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -178,7 +178,7 @@ description: Artifact commands intersect_cubes.kcl
"command": { "command": {
"type": "extrude", "type": "extrude",
"target": "[uuid]", "target": "[uuid]",
"distance": 20.0, "distance": 10.0,
"faces": null, "faces": null,
"opposite": "None" "opposite": "None"
} }
@ -342,7 +342,7 @@ description: Artifact commands intersect_cubes.kcl
"type": "move_path_pen", "type": "move_path_pen",
"path": "[uuid]", "path": "[uuid]",
"to": { "to": {
"x": 2.0, "x": -2.0,
"y": -2.0, "y": -2.0,
"z": 0.0 "z": 0.0
} }
@ -364,7 +364,7 @@ description: Artifact commands intersect_cubes.kcl
"segment": { "segment": {
"type": "line", "type": "line",
"end": { "end": {
"x": 12.0, "x": 18.0,
"y": -2.0, "y": -2.0,
"z": 0.0 "z": 0.0
}, },
@ -381,8 +381,8 @@ description: Artifact commands intersect_cubes.kcl
"segment": { "segment": {
"type": "line", "type": "line",
"end": { "end": {
"x": 12.0, "x": 18.0,
"y": 8.0, "y": 18.0,
"z": 0.0 "z": 0.0
}, },
"relative": false "relative": false
@ -398,8 +398,8 @@ description: Artifact commands intersect_cubes.kcl
"segment": { "segment": {
"type": "line", "type": "line",
"end": { "end": {
"x": 2.0, "x": -2.0,
"y": 8.0, "y": 18.0,
"z": 0.0 "z": 0.0
}, },
"relative": false "relative": false
@ -544,41 +544,5 @@ description: Artifact commands intersect_cubes.kcl
"edge_id": "[uuid]", "edge_id": "[uuid]",
"face_id": "[uuid]" "face_id": "[uuid]"
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_object_transform",
"object_id": "[uuid]",
"transforms": [
{
"translate": {
"property": {
"x": 0.0,
"y": 0.0,
"z": 1.0
},
"set": false,
"is_local": true
},
"rotate_rpy": null,
"rotate_angle_axis": null,
"scale": null
}
]
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "boolean_intersection",
"solid_ids": [
"[uuid]",
"[uuid]"
],
"tolerance": 0.0000001
}
} }
] ]

View File

@ -1,23 +1,23 @@
```mermaid ```mermaid
flowchart LR flowchart LR
subgraph path2 [Path] subgraph path2 [Path]
2["Path<br>[58, 113, 0]"] 2["Path<br>[52, 103, 0]"]
3["Segment<br>[121, 177, 0]"] 3["Segment<br>[111, 163, 0]"]
4["Segment<br>[185, 241, 0]"] 4["Segment<br>[171, 223, 0]"]
5["Segment<br>[249, 305, 0]"] 5["Segment<br>[231, 283, 0]"]
6["Segment<br>[313, 320, 0]"] 6["Segment<br>[291, 298, 0]"]
7[Solid2d] 7[Solid2d]
end end
subgraph path24 [Path] subgraph path24 [Path]
24["Path<br>[58, 113, 0]"] 24["Path<br>[52, 103, 0]"]
25["Segment<br>[121, 177, 0]"] 25["Segment<br>[111, 163, 0]"]
26["Segment<br>[185, 241, 0]"] 26["Segment<br>[171, 223, 0]"]
27["Segment<br>[249, 305, 0]"] 27["Segment<br>[231, 283, 0]"]
28["Segment<br>[313, 320, 0]"] 28["Segment<br>[291, 298, 0]"]
29[Solid2d] 29[Solid2d]
end end
1["Plane<br>[33, 50, 0]"] 1["Plane<br>[27, 44, 0]"]
8["Sweep Extrusion<br>[328, 354, 0]"] 8["Sweep Extrusion<br>[306, 326, 0]"]
9[Wall] 9[Wall]
10[Wall] 10[Wall]
11[Wall] 11[Wall]
@ -32,8 +32,8 @@ flowchart LR
20["SweepEdge Adjacent"] 20["SweepEdge Adjacent"]
21["SweepEdge Opposite"] 21["SweepEdge Opposite"]
22["SweepEdge Adjacent"] 22["SweepEdge Adjacent"]
23["Plane<br>[33, 50, 0]"] 23["Plane<br>[27, 44, 0]"]
30["Sweep Extrusion<br>[328, 354, 0]"] 30["Sweep Extrusion<br>[306, 326, 0]"]
31[Wall] 31[Wall]
32[Wall] 32[Wall]
33[Wall] 33[Wall]
@ -48,7 +48,6 @@ flowchart LR
42["SweepEdge Adjacent"] 42["SweepEdge Adjacent"]
43["SweepEdge Opposite"] 43["SweepEdge Opposite"]
44["SweepEdge Adjacent"] 44["SweepEdge Adjacent"]
45["CompositeSolid Intersect<br>[448, 477, 0]"]
1 --- 2 1 --- 2
2 --- 3 2 --- 3
2 --- 4 2 --- 4
@ -115,6 +114,4 @@ flowchart LR
30 --- 42 30 --- 42
30 --- 43 30 --- 43
30 --- 44 30 --- 44
2 <--x 45
24 <--x 45
``` ```

View File

@ -101,20 +101,16 @@ description: Result of parsing intersect_cubes.kcl
}, },
"operator": "-", "operator": "-",
"right": { "right": {
"abs_path": false,
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": { "raw": "10",
"commentStart": 0,
"end": 0,
"name": "size",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0, "start": 0,
"type": "Name", "type": "Literal",
"type": "Name" "type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
}, },
"start": 0, "start": 0,
"type": "BinaryExpression", "type": "BinaryExpression",
@ -153,20 +149,16 @@ description: Result of parsing intersect_cubes.kcl
}, },
"operator": "-", "operator": "-",
"right": { "right": {
"abs_path": false,
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": { "raw": "10",
"commentStart": 0,
"end": 0,
"name": "size",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0, "start": 0,
"type": "Name", "type": "Literal",
"type": "Name" "type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
}, },
"start": 0, "start": 0,
"type": "BinaryExpression", "type": "BinaryExpression",
@ -254,20 +246,16 @@ description: Result of parsing intersect_cubes.kcl
}, },
"operator": "+", "operator": "+",
"right": { "right": {
"abs_path": false,
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": { "raw": "10",
"commentStart": 0,
"end": 0,
"name": "size",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0, "start": 0,
"type": "Name", "type": "Literal",
"type": "Name" "type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
}, },
"start": 0, "start": 0,
"type": "BinaryExpression", "type": "BinaryExpression",
@ -306,20 +294,16 @@ description: Result of parsing intersect_cubes.kcl
}, },
"operator": "-", "operator": "-",
"right": { "right": {
"abs_path": false,
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": { "raw": "10",
"commentStart": 0,
"end": 0,
"name": "size",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0, "start": 0,
"type": "Name", "type": "Literal",
"type": "Name" "type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
}, },
"start": 0, "start": 0,
"type": "BinaryExpression", "type": "BinaryExpression",
@ -402,20 +386,16 @@ description: Result of parsing intersect_cubes.kcl
}, },
"operator": "+", "operator": "+",
"right": { "right": {
"abs_path": false,
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": { "raw": "10",
"commentStart": 0,
"end": 0,
"name": "size",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0, "start": 0,
"type": "Name", "type": "Literal",
"type": "Name" "type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
}, },
"start": 0, "start": 0,
"type": "BinaryExpression", "type": "BinaryExpression",
@ -454,20 +434,16 @@ description: Result of parsing intersect_cubes.kcl
}, },
"operator": "+", "operator": "+",
"right": { "right": {
"abs_path": false,
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": { "raw": "10",
"commentStart": 0,
"end": 0,
"name": "size",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0, "start": 0,
"type": "Name", "type": "Literal",
"type": "Name" "type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
}, },
"start": 0, "start": 0,
"type": "BinaryExpression", "type": "BinaryExpression",
@ -550,20 +526,16 @@ description: Result of parsing intersect_cubes.kcl
}, },
"operator": "-", "operator": "-",
"right": { "right": {
"abs_path": false,
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": { "raw": "10",
"commentStart": 0,
"end": 0,
"name": "size",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0, "start": 0,
"type": "Name", "type": "Literal",
"type": "Name" "type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
}, },
"start": 0, "start": 0,
"type": "BinaryExpression", "type": "BinaryExpression",
@ -602,20 +574,16 @@ description: Result of parsing intersect_cubes.kcl
}, },
"operator": "+", "operator": "+",
"right": { "right": {
"abs_path": false,
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": { "raw": "10",
"commentStart": 0,
"end": 0,
"name": "size",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0, "start": 0,
"type": "Name", "type": "Literal",
"type": "Name" "type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
}, },
"start": 0, "start": 0,
"type": "BinaryExpression", "type": "BinaryExpression",
@ -688,38 +656,14 @@ description: Result of parsing intersect_cubes.kcl
"arg": { "arg": {
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"left": { "raw": "10",
"commentStart": 0,
"end": 0,
"raw": "2",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 2.0,
"suffix": "None"
}
},
"operator": "*",
"right": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "size",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
},
"start": 0, "start": 0,
"type": "BinaryExpression", "type": "Literal",
"type": "BinaryExpression" "type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
} }
} }
], ],
@ -775,16 +719,6 @@ description: Result of parsing intersect_cubes.kcl
"start": 0, "start": 0,
"type": "Identifier" "type": "Identifier"
} }
},
{
"type": "Parameter",
"identifier": {
"commentStart": 0,
"end": 0,
"name": "size",
"start": 0,
"type": "Identifier"
}
} }
], ],
"start": 0, "start": 0,
@ -846,18 +780,6 @@ description: Result of parsing intersect_cubes.kcl
"start": 0, "start": 0,
"type": "ArrayExpression", "type": "ArrayExpression",
"type": "ArrayExpression" "type": "ArrayExpression"
},
{
"commentStart": 0,
"end": 0,
"raw": "10",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
} }
], ],
"callee": { "callee": {
@ -903,129 +825,61 @@ description: Result of parsing intersect_cubes.kcl
"type": "Identifier" "type": "Identifier"
}, },
"init": { "init": {
"body": [ "arguments": [
{ {
"arguments": [ "commentStart": 0,
{ "elements": [
"commentStart": 0,
"elements": [
{
"commentStart": 0,
"end": 0,
"raw": "7",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 7.0,
"suffix": "None"
}
},
{
"commentStart": 0,
"end": 0,
"raw": "3",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 3.0,
"suffix": "None"
}
}
],
"end": 0,
"start": 0,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
{ {
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"raw": "5", "raw": "8",
"start": 0, "start": 0,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": { "value": {
"value": 5.0, "value": 8.0,
"suffix": "None"
}
},
{
"commentStart": 0,
"end": 0,
"raw": "8",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 8.0,
"suffix": "None" "suffix": "None"
} }
} }
], ],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "cube",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0, "end": 0,
"start": 0, "start": 0,
"type": "CallExpression", "type": "ArrayExpression",
"type": "CallExpression" "type": "ArrayExpression"
},
{
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "z",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "1",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 1.0,
"suffix": "None"
}
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "translate",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
} }
], ],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "cube",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"start": 0, "start": 0,
"type": "PipeExpression", "type": "CallExpression",
"type": "PipeExpression" "type": "CallExpression"
}, },
"start": 0, "start": 0,
"type": "VariableDeclarator" "type": "VariableDeclarator"

View File

@ -1,15 +1,14 @@
fn cube(center, size) { fn cube(center) {
return startSketchOn(XY) return startSketchOn(XY)
|> startProfileAt([center[0] - size, center[1] - size], %) |> startProfileAt([center[0] - 10, center[1] - 10], %)
|> line(endAbsolute = [center[0] + size, center[1] - size]) |> line(endAbsolute = [center[0] + 10, center[1] - 10])
|> line(endAbsolute = [center[0] + size, center[1] + size]) |> line(endAbsolute = [center[0] + 10, center[1] + 10])
|> line(endAbsolute = [center[0] - size, center[1] + size]) |> line(endAbsolute = [center[0] - 10, center[1] + 10])
|> close() |> close()
|> extrude(length = 2 * size) |> extrude(length = 10)
} }
part001 = cube([0, 0], 10) part001 = cube([0, 0])
part002 = cube([7, 3], 5) part002 = cube([8, 8])
|> translate(z = 1)
fullPart = intersect([part001, part002]) fullPart = intersect([part001, part002])

View File

@ -10,7 +10,7 @@ description: Operations executed intersect_cubes.kcl
"name": "cube", "name": "cube",
"functionSourceRange": [ "functionSourceRange": [
7, 7,
356, 328,
0 0
], ],
"unlabeledArg": null, "unlabeledArg": null,
@ -38,7 +38,7 @@ description: Operations executed intersect_cubes.kcl
"length": { "length": {
"value": { "value": {
"type": "Number", "type": "Number",
"value": 20.0, "value": 10.0,
"ty": { "ty": {
"type": "Default", "type": "Default",
"len": { "len": {
@ -75,7 +75,7 @@ description: Operations executed intersect_cubes.kcl
"name": "cube", "name": "cube",
"functionSourceRange": [ "functionSourceRange": [
7, 7,
356, 328,
0 0
], ],
"unlabeledArg": null, "unlabeledArg": null,

View File

@ -7,365 +7,182 @@ description: Variables in memory after executing intersect_cubes.kcl
"type": "Function" "type": "Function"
}, },
"fullPart": { "fullPart": {
"type": "HomArray", "type": "Solid",
"value": [ "value": {
{ "type": "Solid",
"type": "Solid", "id": "[uuid]",
"value": { "artifactId": "[uuid]",
"type": "Solid", "value": [
{
"faceId": "[uuid]",
"id": "[uuid]", "id": "[uuid]",
"artifactId": "[uuid]", "sourceRange": [],
"value": [ "tag": null,
{ "type": "extrudePlane"
"faceId": "[uuid]", },
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
}
],
"sketch": {
"type": "Sketch",
"id": "[uuid]",
"paths": [
{
"__geoMeta": {
"id": "[uuid]", "id": "[uuid]",
"sourceRange": [], "sourceRange": []
"tag": null,
"type": "extrudePlane"
}, },
{ "from": [
"faceId": "[uuid]", -10.0,
"id": "[uuid]", -10.0
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
}
],
"sketch": {
"type": "Sketch",
"id": "[uuid]",
"paths": [
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
-10.0,
-10.0
],
"tag": null,
"to": [
10.0,
-10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
10.0,
-10.0
],
"tag": null,
"to": [
10.0,
10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
10.0,
10.0
],
"tag": null,
"to": [
-10.0,
10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
-10.0,
10.0
],
"tag": null,
"to": [
-10.0,
-10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
}
], ],
"on": { "tag": null,
"type": "plane", "to": [
"id": "[uuid]", 10.0,
"artifactId": "[uuid]", -10.0
"value": "XY", ],
"origin": { "type": "ToPoint",
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"xAxis": {
"x": 1.0,
"y": 0.0,
"z": 0.0
},
"yAxis": {
"x": 0.0,
"y": 1.0,
"z": 0.0
},
"zAxis": {
"x": 0.0,
"y": 0.0,
"z": 1.0
},
"units": {
"type": "Mm"
}
},
"start": {
"from": [
-10.0,
-10.0
],
"to": [
-10.0,
-10.0
],
"units": {
"type": "Mm"
},
"tag": null,
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
}
},
"artifactId": "[uuid]",
"originalId": "[uuid]",
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
}, },
"height": 20.0, {
"startCapId": "[uuid]", "__geoMeta": {
"endCapId": "[uuid]", "id": "[uuid]",
"sourceRange": []
},
"from": [
10.0,
-10.0
],
"tag": null,
"to": [
10.0,
10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
10.0,
10.0
],
"tag": null,
"to": [
-10.0,
10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
-10.0,
10.0
],
"tag": null,
"to": [
-10.0,
-10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
}
],
"on": {
"type": "plane",
"id": "[uuid]",
"artifactId": "[uuid]",
"value": "XY",
"origin": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"xAxis": {
"x": 1.0,
"y": 0.0,
"z": 0.0
},
"yAxis": {
"x": 0.0,
"y": 1.0,
"z": 0.0
},
"zAxis": {
"x": 0.0,
"y": 0.0,
"z": 1.0
},
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"start": {
"from": [
-10.0,
-10.0
],
"to": [
-10.0,
-10.0
],
"units": {
"type": "Mm"
},
"tag": null,
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
}
},
"artifactId": "[uuid]",
"originalId": "[uuid]",
"units": {
"type": "Mm"
} }
}, },
{ "height": 10.0,
"type": "Solid", "startCapId": "[uuid]",
"value": { "endCapId": "[uuid]",
"type": "Solid", "units": {
"id": "[uuid]", "type": "Mm"
"artifactId": "[uuid]",
"value": [
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
}
],
"sketch": {
"type": "Sketch",
"id": "[uuid]",
"paths": [
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
-10.0,
-10.0
],
"tag": null,
"to": [
10.0,
-10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
10.0,
-10.0
],
"tag": null,
"to": [
10.0,
10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
10.0,
10.0
],
"tag": null,
"to": [
-10.0,
10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
-10.0,
10.0
],
"tag": null,
"to": [
-10.0,
-10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
}
],
"on": {
"type": "plane",
"id": "[uuid]",
"artifactId": "[uuid]",
"value": "XY",
"origin": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"xAxis": {
"x": 1.0,
"y": 0.0,
"z": 0.0
},
"yAxis": {
"x": 0.0,
"y": 1.0,
"z": 0.0
},
"zAxis": {
"x": 0.0,
"y": 0.0,
"z": 1.0
},
"units": {
"type": "Mm"
}
},
"start": {
"from": [
-10.0,
-10.0
],
"to": [
-10.0,
-10.0
],
"units": {
"type": "Mm"
},
"tag": null,
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
}
},
"artifactId": "[uuid]",
"originalId": "[uuid]",
"units": {
"type": "Mm"
}
},
"height": 20.0,
"startCapId": "[uuid]",
"endCapId": "[uuid]",
"units": {
"type": "Mm"
}
}
} }
] }
}, },
"part001": { "part001": {
"type": "Solid", "type": "Solid",
@ -537,7 +354,7 @@ description: Variables in memory after executing intersect_cubes.kcl
"type": "Mm" "type": "Mm"
} }
}, },
"height": 20.0, "height": 10.0,
"startCapId": "[uuid]", "startCapId": "[uuid]",
"endCapId": "[uuid]", "endCapId": "[uuid]",
"units": { "units": {
@ -591,12 +408,12 @@ description: Variables in memory after executing intersect_cubes.kcl
"sourceRange": [] "sourceRange": []
}, },
"from": [ "from": [
2.0, -2.0,
-2.0 -2.0
], ],
"tag": null, "tag": null,
"to": [ "to": [
12.0, 18.0,
-2.0 -2.0
], ],
"type": "ToPoint", "type": "ToPoint",
@ -610,13 +427,13 @@ description: Variables in memory after executing intersect_cubes.kcl
"sourceRange": [] "sourceRange": []
}, },
"from": [ "from": [
12.0, 18.0,
-2.0 -2.0
], ],
"tag": null, "tag": null,
"to": [ "to": [
12.0, 18.0,
8.0 18.0
], ],
"type": "ToPoint", "type": "ToPoint",
"units": { "units": {
@ -629,13 +446,13 @@ description: Variables in memory after executing intersect_cubes.kcl
"sourceRange": [] "sourceRange": []
}, },
"from": [ "from": [
12.0, 18.0,
8.0 18.0
], ],
"tag": null, "tag": null,
"to": [ "to": [
2.0, -2.0,
8.0 18.0
], ],
"type": "ToPoint", "type": "ToPoint",
"units": { "units": {
@ -648,12 +465,12 @@ description: Variables in memory after executing intersect_cubes.kcl
"sourceRange": [] "sourceRange": []
}, },
"from": [ "from": [
2.0, -2.0,
8.0 18.0
], ],
"tag": null, "tag": null,
"to": [ "to": [
2.0, -2.0,
-2.0 -2.0
], ],
"type": "ToPoint", "type": "ToPoint",
@ -693,11 +510,11 @@ description: Variables in memory after executing intersect_cubes.kcl
}, },
"start": { "start": {
"from": [ "from": [
2.0, -2.0,
-2.0 -2.0
], ],
"to": [ "to": [
2.0, -2.0,
-2.0 -2.0
], ],
"units": { "units": {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 67 KiB

View File

@ -2,18 +2,17 @@
source: kcl-lib/src/simulation_tests.rs source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing intersect_cubes.kcl description: Result of unparsing intersect_cubes.kcl
--- ---
fn cube(center, size) { fn cube(center) {
return startSketchOn(XY) return startSketchOn(XY)
|> startProfileAt([center[0] - size, center[1] - size], %) |> startProfileAt([center[0] - 10, center[1] - 10], %)
|> line(endAbsolute = [center[0] + size, center[1] - size]) |> line(endAbsolute = [center[0] + 10, center[1] - 10])
|> line(endAbsolute = [center[0] + size, center[1] + size]) |> line(endAbsolute = [center[0] + 10, center[1] + 10])
|> line(endAbsolute = [center[0] - size, center[1] + size]) |> line(endAbsolute = [center[0] - 10, center[1] + 10])
|> close() |> close()
|> extrude(length = 2 * size) |> extrude(length = 10)
} }
part001 = cube([0, 0], 10) part001 = cube([0, 0])
part002 = cube([7, 3], 5) part002 = cube([8, 8])
|> translate(z = 1)
fullPart = intersect([part001, part002]) fullPart = intersect([part001, part002])

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View File

@ -342,8 +342,8 @@ description: Artifact commands subtract_cylinder_from_cube.kcl
"type": "move_path_pen", "type": "move_path_pen",
"path": "[uuid]", "path": "[uuid]",
"to": { "to": {
"x": 4.0, "x": 2.0,
"y": 2.0, "y": 0.0,
"z": 0.0 "z": 0.0
} }
} }
@ -364,8 +364,8 @@ description: Artifact commands subtract_cylinder_from_cube.kcl
"segment": { "segment": {
"type": "arc", "type": "arc",
"center": { "center": {
"x": 2.0, "x": 0.0,
"y": 2.0 "y": 0.0
}, },
"radius": 2.0, "radius": 2.0,
"start": { "start": {
@ -410,7 +410,7 @@ description: Artifact commands subtract_cylinder_from_cube.kcl
"command": { "command": {
"type": "extrude", "type": "extrude",
"target": "[uuid]", "target": "[uuid]",
"distance": 5.0, "distance": 10.0,
"faces": null, "faces": null,
"opposite": "None" "opposite": "None"
} }
@ -458,19 +458,5 @@ description: Artifact commands subtract_cylinder_from_cube.kcl
"edge_id": "[uuid]", "edge_id": "[uuid]",
"face_id": "[uuid]" "face_id": "[uuid]"
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "boolean_subtract",
"target_ids": [
"[uuid]"
],
"tool_ids": [
"[uuid]"
],
"tolerance": 0.0000001
}
} }
] ]

View File

@ -30,13 +30,12 @@ flowchart LR
21["SweepEdge Opposite"] 21["SweepEdge Opposite"]
22["SweepEdge Adjacent"] 22["SweepEdge Adjacent"]
23["Plane<br>[363, 382, 0]"] 23["Plane<br>[363, 382, 0]"]
27["Sweep Extrusion<br>[429, 448, 0]"] 27["Sweep Extrusion<br>[429, 449, 0]"]
28[Wall] 28[Wall]
29["Cap Start"] 29["Cap Start"]
30["Cap End"] 30["Cap End"]
31["SweepEdge Opposite"] 31["SweepEdge Opposite"]
32["SweepEdge Adjacent"] 32["SweepEdge Adjacent"]
33["CompositeSolid Subtract<br>[461, 497, 0]"]
1 --- 2 1 --- 2
2 --- 3 2 --- 3
2 --- 4 2 --- 4
@ -82,6 +81,4 @@ flowchart LR
27 --- 30 27 --- 30
27 --- 31 27 --- 31
27 --- 32 27 --- 32
2 <--x 33
24 <--x 33
``` ```

View File

@ -876,24 +876,24 @@ description: Result of parsing subtract_cylinder_from_cube.kcl
{ {
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"raw": "2", "raw": "0",
"start": 0, "start": 0,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": { "value": {
"value": 2.0, "value": 0.0,
"suffix": "None" "suffix": "None"
} }
}, },
{ {
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"raw": "2", "raw": "0",
"start": 0, "start": 0,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": { "value": {
"value": 2.0, "value": 0.0,
"suffix": "None" "suffix": "None"
} }
} }
@ -963,12 +963,12 @@ description: Result of parsing subtract_cylinder_from_cube.kcl
"arg": { "arg": {
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"raw": "5", "raw": "10",
"start": 0, "start": 0,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": { "value": {
"value": 5.0, "value": 10.0,
"suffix": "None" "suffix": "None"
} }
} }

View File

@ -10,7 +10,7 @@ fn cube(center) {
part001 = cube([0, 0]) part001 = cube([0, 0])
part002 = startSketchOn('XY') part002 = startSketchOn('XY')
|> circle(center = [2, 2], radius = 2) |> circle(center = [0, 0], radius = 2)
|> extrude(length = 5) |> extrude(length = 10)
fullPart = subtract([part001], tools=[part002]) fullPart = subtract([part001], tools=[part002])

View File

@ -88,7 +88,7 @@ description: Operations executed subtract_cylinder_from_cube.kcl
"length": { "length": {
"value": { "value": {
"type": "Number", "type": "Number",
"value": 5.0, "value": 10.0,
"ty": { "ty": {
"type": "Default", "type": "Default",
"len": { "len": {

View File

@ -7,365 +7,182 @@ description: Variables in memory after executing subtract_cylinder_from_cube.kcl
"type": "Function" "type": "Function"
}, },
"fullPart": { "fullPart": {
"type": "HomArray", "type": "Solid",
"value": [ "value": {
{ "type": "Solid",
"type": "Solid", "id": "[uuid]",
"value": { "artifactId": "[uuid]",
"type": "Solid", "value": [
{
"faceId": "[uuid]",
"id": "[uuid]", "id": "[uuid]",
"artifactId": "[uuid]", "sourceRange": [],
"value": [ "tag": null,
{ "type": "extrudePlane"
"faceId": "[uuid]", },
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
}
],
"sketch": {
"type": "Sketch",
"id": "[uuid]",
"paths": [
{
"__geoMeta": {
"id": "[uuid]", "id": "[uuid]",
"sourceRange": [], "sourceRange": []
"tag": null,
"type": "extrudePlane"
}, },
{ "from": [
"faceId": "[uuid]", -10.0,
"id": "[uuid]", -10.0
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
}
],
"sketch": {
"type": "Sketch",
"id": "[uuid]",
"paths": [
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
-10.0,
-10.0
],
"tag": null,
"to": [
10.0,
-10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
10.0,
-10.0
],
"tag": null,
"to": [
10.0,
10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
10.0,
10.0
],
"tag": null,
"to": [
-10.0,
10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
-10.0,
10.0
],
"tag": null,
"to": [
-10.0,
-10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
}
], ],
"on": { "tag": null,
"type": "plane", "to": [
"id": "[uuid]", 10.0,
"artifactId": "[uuid]", -10.0
"value": "XY", ],
"origin": { "type": "ToPoint",
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"xAxis": {
"x": 1.0,
"y": 0.0,
"z": 0.0
},
"yAxis": {
"x": 0.0,
"y": 1.0,
"z": 0.0
},
"zAxis": {
"x": 0.0,
"y": 0.0,
"z": 1.0
},
"units": {
"type": "Mm"
}
},
"start": {
"from": [
-10.0,
-10.0
],
"to": [
-10.0,
-10.0
],
"units": {
"type": "Mm"
},
"tag": null,
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
}
},
"artifactId": "[uuid]",
"originalId": "[uuid]",
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
}, },
"height": 10.0, {
"startCapId": "[uuid]", "__geoMeta": {
"endCapId": "[uuid]", "id": "[uuid]",
"sourceRange": []
},
"from": [
10.0,
-10.0
],
"tag": null,
"to": [
10.0,
10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
10.0,
10.0
],
"tag": null,
"to": [
-10.0,
10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
-10.0,
10.0
],
"tag": null,
"to": [
-10.0,
-10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
}
],
"on": {
"type": "plane",
"id": "[uuid]",
"artifactId": "[uuid]",
"value": "XY",
"origin": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"xAxis": {
"x": 1.0,
"y": 0.0,
"z": 0.0
},
"yAxis": {
"x": 0.0,
"y": 1.0,
"z": 0.0
},
"zAxis": {
"x": 0.0,
"y": 0.0,
"z": 1.0
},
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"start": {
"from": [
-10.0,
-10.0
],
"to": [
-10.0,
-10.0
],
"units": {
"type": "Mm"
},
"tag": null,
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
}
},
"artifactId": "[uuid]",
"originalId": "[uuid]",
"units": {
"type": "Mm"
} }
}, },
{ "height": 10.0,
"type": "Solid", "startCapId": "[uuid]",
"value": { "endCapId": "[uuid]",
"type": "Solid", "units": {
"id": "[uuid]", "type": "Mm"
"artifactId": "[uuid]",
"value": [
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
}
],
"sketch": {
"type": "Sketch",
"id": "[uuid]",
"paths": [
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
-10.0,
-10.0
],
"tag": null,
"to": [
10.0,
-10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
10.0,
-10.0
],
"tag": null,
"to": [
10.0,
10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
10.0,
10.0
],
"tag": null,
"to": [
-10.0,
10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
-10.0,
10.0
],
"tag": null,
"to": [
-10.0,
-10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
}
],
"on": {
"type": "plane",
"id": "[uuid]",
"artifactId": "[uuid]",
"value": "XY",
"origin": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"xAxis": {
"x": 1.0,
"y": 0.0,
"z": 0.0
},
"yAxis": {
"x": 0.0,
"y": 1.0,
"z": 0.0
},
"zAxis": {
"x": 0.0,
"y": 0.0,
"z": 1.0
},
"units": {
"type": "Mm"
}
},
"start": {
"from": [
-10.0,
-10.0
],
"to": [
-10.0,
-10.0
],
"units": {
"type": "Mm"
},
"tag": null,
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
}
},
"artifactId": "[uuid]",
"originalId": "[uuid]",
"units": {
"type": "Mm"
}
},
"height": 10.0,
"startCapId": "[uuid]",
"endCapId": "[uuid]",
"units": {
"type": "Mm"
}
}
} }
] }
}, },
"part001": { "part001": {
"type": "Solid", "type": "Solid",
@ -571,18 +388,18 @@ description: Variables in memory after executing subtract_cylinder_from_cube.kcl
}, },
"ccw": true, "ccw": true,
"center": [ "center": [
2.0, 0.0,
2.0 0.0
], ],
"from": [ "from": [
4.0, 2.0,
2.0 0.0
], ],
"radius": 2.0, "radius": 2.0,
"tag": null, "tag": null,
"to": [ "to": [
4.0, 2.0,
2.0 0.0
], ],
"type": "Circle", "type": "Circle",
"units": { "units": {
@ -621,12 +438,12 @@ description: Variables in memory after executing subtract_cylinder_from_cube.kcl
}, },
"start": { "start": {
"from": [ "from": [
4.0, 2.0,
2.0 0.0
], ],
"to": [ "to": [
4.0, 2.0,
2.0 0.0
], ],
"units": { "units": {
"type": "Mm" "type": "Mm"
@ -643,7 +460,7 @@ description: Variables in memory after executing subtract_cylinder_from_cube.kcl
"type": "Mm" "type": "Mm"
} }
}, },
"height": 5.0, "height": 10.0,
"startCapId": "[uuid]", "startCapId": "[uuid]",
"endCapId": "[uuid]", "endCapId": "[uuid]",
"units": { "units": {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -14,7 +14,7 @@ fn cube(center) {
part001 = cube([0, 0]) part001 = cube([0, 0])
part002 = startSketchOn(XY) part002 = startSketchOn(XY)
|> circle(center = [2, 2], radius = 2) |> circle(center = [0, 0], radius = 2)
|> extrude(length = 5) |> extrude(length = 10)
fullPart = subtract([part001], tools = [part002]) fullPart = subtract([part001], tools = [part002])

View File

@ -178,7 +178,7 @@ description: Artifact commands union_cubes.kcl
"command": { "command": {
"type": "extrude", "type": "extrude",
"target": "[uuid]", "target": "[uuid]",
"distance": 20.0, "distance": 10.0,
"faces": null, "faces": null,
"opposite": "None" "opposite": "None"
} }
@ -342,8 +342,8 @@ description: Artifact commands union_cubes.kcl
"type": "move_path_pen", "type": "move_path_pen",
"path": "[uuid]", "path": "[uuid]",
"to": { "to": {
"x": 2.0, "x": 10.0,
"y": -2.0, "y": 0.0,
"z": 0.0 "z": 0.0
} }
} }
@ -364,8 +364,8 @@ description: Artifact commands union_cubes.kcl
"segment": { "segment": {
"type": "line", "type": "line",
"end": { "end": {
"x": 12.0, "x": 30.0,
"y": -2.0, "y": 0.0,
"z": 0.0 "z": 0.0
}, },
"relative": false "relative": false
@ -381,8 +381,8 @@ description: Artifact commands union_cubes.kcl
"segment": { "segment": {
"type": "line", "type": "line",
"end": { "end": {
"x": 12.0, "x": 30.0,
"y": 8.0, "y": 20.0,
"z": 0.0 "z": 0.0
}, },
"relative": false "relative": false
@ -398,8 +398,8 @@ description: Artifact commands union_cubes.kcl
"segment": { "segment": {
"type": "line", "type": "line",
"end": { "end": {
"x": 2.0, "x": 10.0,
"y": 8.0, "y": 20.0,
"z": 0.0 "z": 0.0
}, },
"relative": false "relative": false
@ -544,41 +544,5 @@ description: Artifact commands union_cubes.kcl
"edge_id": "[uuid]", "edge_id": "[uuid]",
"face_id": "[uuid]" "face_id": "[uuid]"
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_object_transform",
"object_id": "[uuid]",
"transforms": [
{
"translate": {
"property": {
"x": 0.0,
"y": 0.0,
"z": 1.0
},
"set": false,
"is_local": true
},
"rotate_rpy": null,
"rotate_angle_axis": null,
"scale": null
}
]
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "boolean_union",
"solid_ids": [
"[uuid]",
"[uuid]"
],
"tolerance": 0.0000001
}
} }
] ]

View File

@ -1,23 +1,23 @@
```mermaid ```mermaid
flowchart LR flowchart LR
subgraph path2 [Path] subgraph path2 [Path]
2["Path<br>[58, 113, 0]"] 2["Path<br>[52, 103, 0]"]
3["Segment<br>[121, 177, 0]"] 3["Segment<br>[111, 163, 0]"]
4["Segment<br>[185, 241, 0]"] 4["Segment<br>[171, 223, 0]"]
5["Segment<br>[249, 305, 0]"] 5["Segment<br>[231, 283, 0]"]
6["Segment<br>[313, 320, 0]"] 6["Segment<br>[291, 298, 0]"]
7[Solid2d] 7[Solid2d]
end end
subgraph path24 [Path] subgraph path24 [Path]
24["Path<br>[58, 113, 0]"] 24["Path<br>[52, 103, 0]"]
25["Segment<br>[121, 177, 0]"] 25["Segment<br>[111, 163, 0]"]
26["Segment<br>[185, 241, 0]"] 26["Segment<br>[171, 223, 0]"]
27["Segment<br>[249, 305, 0]"] 27["Segment<br>[231, 283, 0]"]
28["Segment<br>[313, 320, 0]"] 28["Segment<br>[291, 298, 0]"]
29[Solid2d] 29[Solid2d]
end end
1["Plane<br>[33, 50, 0]"] 1["Plane<br>[27, 44, 0]"]
8["Sweep Extrusion<br>[328, 354, 0]"] 8["Sweep Extrusion<br>[306, 326, 0]"]
9[Wall] 9[Wall]
10[Wall] 10[Wall]
11[Wall] 11[Wall]
@ -32,8 +32,8 @@ flowchart LR
20["SweepEdge Adjacent"] 20["SweepEdge Adjacent"]
21["SweepEdge Opposite"] 21["SweepEdge Opposite"]
22["SweepEdge Adjacent"] 22["SweepEdge Adjacent"]
23["Plane<br>[33, 50, 0]"] 23["Plane<br>[27, 44, 0]"]
30["Sweep Extrusion<br>[328, 354, 0]"] 30["Sweep Extrusion<br>[306, 326, 0]"]
31[Wall] 31[Wall]
32[Wall] 32[Wall]
33[Wall] 33[Wall]
@ -48,7 +48,6 @@ flowchart LR
42["SweepEdge Adjacent"] 42["SweepEdge Adjacent"]
43["SweepEdge Opposite"] 43["SweepEdge Opposite"]
44["SweepEdge Adjacent"] 44["SweepEdge Adjacent"]
45["CompositeSolid Union<br>[448, 473, 0]"]
1 --- 2 1 --- 2
2 --- 3 2 --- 3
2 --- 4 2 --- 4
@ -115,6 +114,4 @@ flowchart LR
30 --- 42 30 --- 42
30 --- 43 30 --- 43
30 --- 44 30 --- 44
2 <--x 45
24 <--x 45
``` ```

View File

@ -101,20 +101,16 @@ description: Result of parsing union_cubes.kcl
}, },
"operator": "-", "operator": "-",
"right": { "right": {
"abs_path": false,
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": { "raw": "10",
"commentStart": 0,
"end": 0,
"name": "size",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0, "start": 0,
"type": "Name", "type": "Literal",
"type": "Name" "type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
}, },
"start": 0, "start": 0,
"type": "BinaryExpression", "type": "BinaryExpression",
@ -153,20 +149,16 @@ description: Result of parsing union_cubes.kcl
}, },
"operator": "-", "operator": "-",
"right": { "right": {
"abs_path": false,
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": { "raw": "10",
"commentStart": 0,
"end": 0,
"name": "size",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0, "start": 0,
"type": "Name", "type": "Literal",
"type": "Name" "type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
}, },
"start": 0, "start": 0,
"type": "BinaryExpression", "type": "BinaryExpression",
@ -254,20 +246,16 @@ description: Result of parsing union_cubes.kcl
}, },
"operator": "+", "operator": "+",
"right": { "right": {
"abs_path": false,
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": { "raw": "10",
"commentStart": 0,
"end": 0,
"name": "size",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0, "start": 0,
"type": "Name", "type": "Literal",
"type": "Name" "type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
}, },
"start": 0, "start": 0,
"type": "BinaryExpression", "type": "BinaryExpression",
@ -306,20 +294,16 @@ description: Result of parsing union_cubes.kcl
}, },
"operator": "-", "operator": "-",
"right": { "right": {
"abs_path": false,
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": { "raw": "10",
"commentStart": 0,
"end": 0,
"name": "size",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0, "start": 0,
"type": "Name", "type": "Literal",
"type": "Name" "type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
}, },
"start": 0, "start": 0,
"type": "BinaryExpression", "type": "BinaryExpression",
@ -402,20 +386,16 @@ description: Result of parsing union_cubes.kcl
}, },
"operator": "+", "operator": "+",
"right": { "right": {
"abs_path": false,
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": { "raw": "10",
"commentStart": 0,
"end": 0,
"name": "size",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0, "start": 0,
"type": "Name", "type": "Literal",
"type": "Name" "type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
}, },
"start": 0, "start": 0,
"type": "BinaryExpression", "type": "BinaryExpression",
@ -454,20 +434,16 @@ description: Result of parsing union_cubes.kcl
}, },
"operator": "+", "operator": "+",
"right": { "right": {
"abs_path": false,
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": { "raw": "10",
"commentStart": 0,
"end": 0,
"name": "size",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0, "start": 0,
"type": "Name", "type": "Literal",
"type": "Name" "type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
}, },
"start": 0, "start": 0,
"type": "BinaryExpression", "type": "BinaryExpression",
@ -550,20 +526,16 @@ description: Result of parsing union_cubes.kcl
}, },
"operator": "-", "operator": "-",
"right": { "right": {
"abs_path": false,
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": { "raw": "10",
"commentStart": 0,
"end": 0,
"name": "size",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0, "start": 0,
"type": "Name", "type": "Literal",
"type": "Name" "type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
}, },
"start": 0, "start": 0,
"type": "BinaryExpression", "type": "BinaryExpression",
@ -602,20 +574,16 @@ description: Result of parsing union_cubes.kcl
}, },
"operator": "+", "operator": "+",
"right": { "right": {
"abs_path": false,
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": { "raw": "10",
"commentStart": 0,
"end": 0,
"name": "size",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0, "start": 0,
"type": "Name", "type": "Literal",
"type": "Name" "type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
}, },
"start": 0, "start": 0,
"type": "BinaryExpression", "type": "BinaryExpression",
@ -688,38 +656,14 @@ description: Result of parsing union_cubes.kcl
"arg": { "arg": {
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"left": { "raw": "10",
"commentStart": 0,
"end": 0,
"raw": "2",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 2.0,
"suffix": "None"
}
},
"operator": "*",
"right": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "size",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
},
"start": 0, "start": 0,
"type": "BinaryExpression", "type": "Literal",
"type": "BinaryExpression" "type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
} }
} }
], ],
@ -775,16 +719,6 @@ description: Result of parsing union_cubes.kcl
"start": 0, "start": 0,
"type": "Identifier" "type": "Identifier"
} }
},
{
"type": "Parameter",
"identifier": {
"commentStart": 0,
"end": 0,
"name": "size",
"start": 0,
"type": "Identifier"
}
} }
], ],
"start": 0, "start": 0,
@ -846,18 +780,6 @@ description: Result of parsing union_cubes.kcl
"start": 0, "start": 0,
"type": "ArrayExpression", "type": "ArrayExpression",
"type": "ArrayExpression" "type": "ArrayExpression"
},
{
"commentStart": 0,
"end": 0,
"raw": "10",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
} }
], ],
"callee": { "callee": {
@ -903,129 +825,61 @@ description: Result of parsing union_cubes.kcl
"type": "Identifier" "type": "Identifier"
}, },
"init": { "init": {
"body": [ "arguments": [
{ {
"arguments": [ "commentStart": 0,
{ "elements": [
"commentStart": 0,
"elements": [
{
"commentStart": 0,
"end": 0,
"raw": "7",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 7.0,
"suffix": "None"
}
},
{
"commentStart": 0,
"end": 0,
"raw": "3",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 3.0,
"suffix": "None"
}
}
],
"end": 0,
"start": 0,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
{ {
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"raw": "5", "raw": "20",
"start": 0, "start": 0,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": { "value": {
"value": 5.0, "value": 20.0,
"suffix": "None"
}
},
{
"commentStart": 0,
"end": 0,
"raw": "10",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 10.0,
"suffix": "None" "suffix": "None"
} }
} }
], ],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "cube",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0, "end": 0,
"start": 0, "start": 0,
"type": "CallExpression", "type": "ArrayExpression",
"type": "CallExpression" "type": "ArrayExpression"
},
{
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "z",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "1",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 1.0,
"suffix": "None"
}
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "translate",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
} }
], ],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "cube",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"start": 0, "start": 0,
"type": "PipeExpression", "type": "CallExpression",
"type": "PipeExpression" "type": "CallExpression"
}, },
"start": 0, "start": 0,
"type": "VariableDeclarator" "type": "VariableDeclarator"

View File

@ -1,15 +1,14 @@
fn cube(center, size) { fn cube(center) {
return startSketchOn(XY) return startSketchOn(XY)
|> startProfileAt([center[0] - size, center[1] - size], %) |> startProfileAt([center[0] - 10, center[1] - 10], %)
|> line(endAbsolute = [center[0] + size, center[1] - size]) |> line(endAbsolute = [center[0] + 10, center[1] - 10])
|> line(endAbsolute = [center[0] + size, center[1] + size]) |> line(endAbsolute = [center[0] + 10, center[1] + 10])
|> line(endAbsolute = [center[0] - size, center[1] + size]) |> line(endAbsolute = [center[0] - 10, center[1] + 10])
|> close() |> close()
|> extrude(length = 2 * size) |> extrude(length = 10)
} }
part001 = cube([0, 0], 10) part001 = cube([0, 0])
part002 = cube([7, 3], 5) part002 = cube([20, 10])
|> translate(z = 1)
fullPart = union([part001, part002]) fullPart = union([part001, part002])

View File

@ -10,7 +10,7 @@ description: Operations executed union_cubes.kcl
"name": "cube", "name": "cube",
"functionSourceRange": [ "functionSourceRange": [
7, 7,
356, 328,
0 0
], ],
"unlabeledArg": null, "unlabeledArg": null,
@ -38,7 +38,7 @@ description: Operations executed union_cubes.kcl
"length": { "length": {
"value": { "value": {
"type": "Number", "type": "Number",
"value": 20.0, "value": 10.0,
"ty": { "ty": {
"type": "Default", "type": "Default",
"len": { "len": {
@ -75,7 +75,7 @@ description: Operations executed union_cubes.kcl
"name": "cube", "name": "cube",
"functionSourceRange": [ "functionSourceRange": [
7, 7,
356, 328,
0 0
], ],
"unlabeledArg": null, "unlabeledArg": null,

View File

@ -7,365 +7,182 @@ description: Variables in memory after executing union_cubes.kcl
"type": "Function" "type": "Function"
}, },
"fullPart": { "fullPart": {
"type": "HomArray", "type": "Solid",
"value": [ "value": {
{ "type": "Solid",
"type": "Solid", "id": "[uuid]",
"value": { "artifactId": "[uuid]",
"type": "Solid", "value": [
{
"faceId": "[uuid]",
"id": "[uuid]", "id": "[uuid]",
"artifactId": "[uuid]", "sourceRange": [],
"value": [ "tag": null,
{ "type": "extrudePlane"
"faceId": "[uuid]", },
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
}
],
"sketch": {
"type": "Sketch",
"id": "[uuid]",
"paths": [
{
"__geoMeta": {
"id": "[uuid]", "id": "[uuid]",
"sourceRange": [], "sourceRange": []
"tag": null,
"type": "extrudePlane"
}, },
{ "from": [
"faceId": "[uuid]", -10.0,
"id": "[uuid]", -10.0
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
}
],
"sketch": {
"type": "Sketch",
"id": "[uuid]",
"paths": [
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
-10.0,
-10.0
],
"tag": null,
"to": [
10.0,
-10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
10.0,
-10.0
],
"tag": null,
"to": [
10.0,
10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
10.0,
10.0
],
"tag": null,
"to": [
-10.0,
10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
-10.0,
10.0
],
"tag": null,
"to": [
-10.0,
-10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
}
], ],
"on": { "tag": null,
"type": "plane", "to": [
"id": "[uuid]", 10.0,
"artifactId": "[uuid]", -10.0
"value": "XY", ],
"origin": { "type": "ToPoint",
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"xAxis": {
"x": 1.0,
"y": 0.0,
"z": 0.0
},
"yAxis": {
"x": 0.0,
"y": 1.0,
"z": 0.0
},
"zAxis": {
"x": 0.0,
"y": 0.0,
"z": 1.0
},
"units": {
"type": "Mm"
}
},
"start": {
"from": [
-10.0,
-10.0
],
"to": [
-10.0,
-10.0
],
"units": {
"type": "Mm"
},
"tag": null,
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
}
},
"artifactId": "[uuid]",
"originalId": "[uuid]",
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
}, },
"height": 20.0, {
"startCapId": "[uuid]", "__geoMeta": {
"endCapId": "[uuid]", "id": "[uuid]",
"sourceRange": []
},
"from": [
10.0,
-10.0
],
"tag": null,
"to": [
10.0,
10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
10.0,
10.0
],
"tag": null,
"to": [
-10.0,
10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
-10.0,
10.0
],
"tag": null,
"to": [
-10.0,
-10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
}
],
"on": {
"type": "plane",
"id": "[uuid]",
"artifactId": "[uuid]",
"value": "XY",
"origin": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"xAxis": {
"x": 1.0,
"y": 0.0,
"z": 0.0
},
"yAxis": {
"x": 0.0,
"y": 1.0,
"z": 0.0
},
"zAxis": {
"x": 0.0,
"y": 0.0,
"z": 1.0
},
"units": { "units": {
"type": "Mm" "type": "Mm"
} }
},
"start": {
"from": [
-10.0,
-10.0
],
"to": [
-10.0,
-10.0
],
"units": {
"type": "Mm"
},
"tag": null,
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
}
},
"artifactId": "[uuid]",
"originalId": "[uuid]",
"units": {
"type": "Mm"
} }
}, },
{ "height": 10.0,
"type": "Solid", "startCapId": "[uuid]",
"value": { "endCapId": "[uuid]",
"type": "Solid", "units": {
"id": "[uuid]", "type": "Mm"
"artifactId": "[uuid]",
"value": [
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
},
{
"faceId": "[uuid]",
"id": "[uuid]",
"sourceRange": [],
"tag": null,
"type": "extrudePlane"
}
],
"sketch": {
"type": "Sketch",
"id": "[uuid]",
"paths": [
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
-10.0,
-10.0
],
"tag": null,
"to": [
10.0,
-10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
10.0,
-10.0
],
"tag": null,
"to": [
10.0,
10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
10.0,
10.0
],
"tag": null,
"to": [
-10.0,
10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
},
{
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
},
"from": [
-10.0,
10.0
],
"tag": null,
"to": [
-10.0,
-10.0
],
"type": "ToPoint",
"units": {
"type": "Mm"
}
}
],
"on": {
"type": "plane",
"id": "[uuid]",
"artifactId": "[uuid]",
"value": "XY",
"origin": {
"x": 0.0,
"y": 0.0,
"z": 0.0
},
"xAxis": {
"x": 1.0,
"y": 0.0,
"z": 0.0
},
"yAxis": {
"x": 0.0,
"y": 1.0,
"z": 0.0
},
"zAxis": {
"x": 0.0,
"y": 0.0,
"z": 1.0
},
"units": {
"type": "Mm"
}
},
"start": {
"from": [
-10.0,
-10.0
],
"to": [
-10.0,
-10.0
],
"units": {
"type": "Mm"
},
"tag": null,
"__geoMeta": {
"id": "[uuid]",
"sourceRange": []
}
},
"artifactId": "[uuid]",
"originalId": "[uuid]",
"units": {
"type": "Mm"
}
},
"height": 20.0,
"startCapId": "[uuid]",
"endCapId": "[uuid]",
"units": {
"type": "Mm"
}
}
} }
] }
}, },
"part001": { "part001": {
"type": "Solid", "type": "Solid",
@ -537,7 +354,7 @@ description: Variables in memory after executing union_cubes.kcl
"type": "Mm" "type": "Mm"
} }
}, },
"height": 20.0, "height": 10.0,
"startCapId": "[uuid]", "startCapId": "[uuid]",
"endCapId": "[uuid]", "endCapId": "[uuid]",
"units": { "units": {
@ -591,13 +408,13 @@ description: Variables in memory after executing union_cubes.kcl
"sourceRange": [] "sourceRange": []
}, },
"from": [ "from": [
2.0, 10.0,
-2.0 0.0
], ],
"tag": null, "tag": null,
"to": [ "to": [
12.0, 30.0,
-2.0 0.0
], ],
"type": "ToPoint", "type": "ToPoint",
"units": { "units": {
@ -610,13 +427,13 @@ description: Variables in memory after executing union_cubes.kcl
"sourceRange": [] "sourceRange": []
}, },
"from": [ "from": [
12.0, 30.0,
-2.0 0.0
], ],
"tag": null, "tag": null,
"to": [ "to": [
12.0, 30.0,
8.0 20.0
], ],
"type": "ToPoint", "type": "ToPoint",
"units": { "units": {
@ -629,13 +446,13 @@ description: Variables in memory after executing union_cubes.kcl
"sourceRange": [] "sourceRange": []
}, },
"from": [ "from": [
12.0, 30.0,
8.0 20.0
], ],
"tag": null, "tag": null,
"to": [ "to": [
2.0, 10.0,
8.0 20.0
], ],
"type": "ToPoint", "type": "ToPoint",
"units": { "units": {
@ -648,13 +465,13 @@ description: Variables in memory after executing union_cubes.kcl
"sourceRange": [] "sourceRange": []
}, },
"from": [ "from": [
2.0, 10.0,
8.0 20.0
], ],
"tag": null, "tag": null,
"to": [ "to": [
2.0, 10.0,
-2.0 0.0
], ],
"type": "ToPoint", "type": "ToPoint",
"units": { "units": {
@ -693,12 +510,12 @@ description: Variables in memory after executing union_cubes.kcl
}, },
"start": { "start": {
"from": [ "from": [
2.0, 10.0,
-2.0 0.0
], ],
"to": [ "to": [
2.0, 10.0,
-2.0 0.0
], ],
"units": { "units": {
"type": "Mm" "type": "Mm"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View File

@ -2,18 +2,17 @@
source: kcl-lib/src/simulation_tests.rs source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing union_cubes.kcl description: Result of unparsing union_cubes.kcl
--- ---
fn cube(center, size) { fn cube(center) {
return startSketchOn(XY) return startSketchOn(XY)
|> startProfileAt([center[0] - size, center[1] - size], %) |> startProfileAt([center[0] - 10, center[1] - 10], %)
|> line(endAbsolute = [center[0] + size, center[1] - size]) |> line(endAbsolute = [center[0] + 10, center[1] - 10])
|> line(endAbsolute = [center[0] + size, center[1] + size]) |> line(endAbsolute = [center[0] + 10, center[1] + 10])
|> line(endAbsolute = [center[0] - size, center[1] + size]) |> line(endAbsolute = [center[0] - 10, center[1] + 10])
|> close() |> close()
|> extrude(length = 2 * size) |> extrude(length = 10)
} }
part001 = cube([0, 0], 10) part001 = cube([0, 0])
part002 = cube([7, 3], 5) part002 = cube([20, 10])
|> translate(z = 1)
fullPart = union([part001, part002]) fullPart = union([part001, part002])

View File

@ -1,6 +1,6 @@
[package] [package]
name = "kcl-python-bindings" name = "kcl-python-bindings"
version = "0.3.60" version = "0.3.58"
edition = "2021" edition = "2021"
repository = "https://github.com/kittycad/modeling-app" repository = "https://github.com/kittycad/modeling-app"
exclude = ["tests/*", "files/*", "venv/*"] exclude = ["tests/*", "files/*", "venv/*"]

View File

@ -1,7 +1,7 @@
[package] [package]
name = "kcl-test-server" name = "kcl-test-server"
description = "A test server for KCL" description = "A test server for KCL"
version = "0.1.60" version = "0.1.58"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"

View File

@ -1,7 +1,7 @@
[package] [package]
name = "kcl-to-core" name = "kcl-to-core"
description = "Utility methods to convert kcl to engine core executable tests" description = "Utility methods to convert kcl to engine core executable tests"
version = "0.1.60" version = "0.1.58"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app" repository = "https://github.com/KittyCAD/modeling-app"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "kcl-wasm-lib" name = "kcl-wasm-lib"
version = "0.1.60" version = "0.1.58"
edition = "2021" edition = "2021"
repository = "https://github.com/KittyCAD/modeling-app" repository = "https://github.com/KittyCAD/modeling-app"
rust-version = "1.83" rust-version = "1.83"

View File

@ -292,22 +292,3 @@ pub fn get_kcl_version() -> String {
kcl_lib::version().to_string() kcl_lib::version().to_string()
} }
/// Get the allowed import file extensions.
#[wasm_bindgen]
pub fn import_file_extensions() -> Result<Vec<String>, String> {
console_error_panic_hook::set_once();
Ok(kcl_lib::IMPORT_FILE_EXTENSIONS.iter().map(|s| s.to_string()).collect())
}
/// Get the allowed relevant file extensions (imports + kcl).
#[wasm_bindgen]
pub fn relevant_file_extensions() -> Result<Vec<String>, String> {
console_error_panic_hook::set_once();
Ok(kcl_lib::RELEVANT_FILE_EXTENSIONS
.iter()
.map(|s| s.to_string())
.collect::<Vec<String>>())
}

View File

@ -1,37 +0,0 @@
// From https://github.com/OpenBuilds/OpenBuilds-CONTROL/blob/4800540ffaa517925fc2cff26670809efa341ffe/signWin.js
const { execSync } = require('node:child_process')
exports.default = async (configuration) => {
if (!process.env.SM_API_KEY) {
console.error(
'Signing using signWin.js script: failed: SM_API_KEY ENV VAR NOT FOUND'
)
return
}
if (!process.env.WINDOWS_CERTIFICATE_THUMBPRINT) {
console.error(
'Signing using signWin.js script: failed: FINGERPRINT ENV VAR NOT FOUND'
)
return
}
if (!configuration.path) {
throw new Error(
`Signing using signWin.js script: failed: TARGET PATH NOT FOUND`
)
}
try {
execSync(
`smctl sign --fingerprint="${process.env.WINDOWS_CERTIFICATE_THUMBPRINT
}" --input "${String(configuration.path)}"`,
{
stdio: 'inherit',
}
)
console.log('Signing using signWin.js script: successful')
} catch (error) {
throw new Error('Signing using signWin.js script: failed:', error)
}
}

View File

@ -14,7 +14,6 @@ import {
commandBarActor, commandBarActor,
useCommandBarState, useCommandBarState,
} from '@src/machines/commandBarMachine' } from '@src/machines/commandBarMachine'
import toast from 'react-hot-toast'
export const COMMAND_PALETTE_HOTKEY = 'mod+k' export const COMMAND_PALETTE_HOTKEY = 'mod+k'
@ -36,23 +35,13 @@ export const CommandBar = () => {
commandBarActor.send({ type: 'Close' }) commandBarActor.send({ type: 'Close' })
}, [pathname]) }, [pathname])
/**
* if the engine connection is about to end, we don't want users
* to be able to perform commands that might require that connection,
* so we just close the command palette.
* TODO: instead, let each command control whether it is disabled, and
* don't just bail out
*/
useEffect(() => { useEffect(() => {
if ( if (
!commandBarActor.getSnapshot().matches('Closed') && immediateState.type !== EngineConnectionStateType.ConnectionEstablished
(immediateState.type === EngineConnectionStateType.Disconnecting ||
immediateState.type === EngineConnectionStateType.Disconnected)
) { ) {
commandBarActor.send({ type: 'Close' }) commandBarActor.send({ type: 'Close' })
toast.error('Exiting command flow because engine disconnected')
} }
}, [immediateState, commandBarActor]) }, [immediateState])
// Hook up keyboard shortcuts // Hook up keyboard shortcuts
useHotkeyWrapper([COMMAND_PALETTE_HOTKEY], () => { useHotkeyWrapper([COMMAND_PALETTE_HOTKEY], () => {

View File

@ -4,7 +4,6 @@ import type { MouseEventHandler } from 'react'
import { useCallback, useContext, useEffect, useMemo } from 'react' import { useCallback, useContext, useEffect, useMemo } from 'react'
import { useHotkeys } from 'react-hotkeys-hook' import { useHotkeys } from 'react-hotkeys-hook'
import { useAppState } from '@src/AppState'
import { ActionIcon } from '@src/components/ActionIcon' import { ActionIcon } from '@src/components/ActionIcon'
import type { CustomIconName } from '@src/components/CustomIcon' import type { CustomIconName } from '@src/components/CustomIcon'
import { MachineManagerContext } from '@src/components/MachineManagerProvider' import { MachineManagerContext } from '@src/components/MachineManagerProvider'
@ -17,10 +16,7 @@ import { sidebarPanes } from '@src/components/ModelingSidebar/ModelingPanes'
import Tooltip from '@src/components/Tooltip' import Tooltip from '@src/components/Tooltip'
import { DEV } from '@src/env' import { DEV } from '@src/env'
import { useModelingContext } from '@src/hooks/useModelingContext' import { useModelingContext } from '@src/hooks/useModelingContext'
import { useNetworkContext } from '@src/hooks/useNetworkContext'
import { NetworkHealthState } from '@src/hooks/useNetworkStatus'
import { useKclContext } from '@src/lang/KclProvider' import { useKclContext } from '@src/lang/KclProvider'
import { EngineConnectionStateType } from '@src/lang/std/engineConnection'
import { SIDEBAR_BUTTON_SUFFIX } from '@src/lib/constants' import { SIDEBAR_BUTTON_SUFFIX } from '@src/lib/constants'
import { isDesktop } from '@src/lib/isDesktop' import { isDesktop } from '@src/lib/isDesktop'
import { useSettings } from '@src/machines/appMachine' import { useSettings } from '@src/machines/appMachine'
@ -56,16 +52,6 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
: 'pointer-events-auto ' : 'pointer-events-auto '
const showDebugPanel = settings.app.showDebugPanel const showDebugPanel = settings.app.showDebugPanel
const { overallState, immediateState } = useNetworkContext()
const { isExecuting } = useKclContext()
const { isStreamReady } = useAppState()
const reliesOnEngine =
(overallState !== NetworkHealthState.Ok &&
overallState !== NetworkHealthState.Weak) ||
isExecuting ||
immediateState.type !== EngineConnectionStateType.ConnectionEstablished ||
!isStreamReady
const paneCallbackProps = useMemo( const paneCallbackProps = useMemo(
() => ({ () => ({
kclContext, kclContext,
@ -107,8 +93,6 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
sidebarName: 'Export part', sidebarName: 'Export part',
icon: 'floppyDiskArrow', icon: 'floppyDiskArrow',
keybinding: 'Ctrl + Shift + E', keybinding: 'Ctrl + Shift + E',
disable: () =>
reliesOnEngine ? 'Need engine connection to export' : undefined,
action: () => action: () =>
commandBarActor.send({ commandBarActor.send({
type: 'Find and select command', type: 'Find and select command',

View File

@ -12,7 +12,6 @@ import {
getSweepFromSuspectedSweepSurface, getSweepFromSuspectedSweepSurface,
getWallCodeRef, getWallCodeRef,
} from '@src/lang/std/artifactGraph' } from '@src/lang/std/artifactGraph'
import { isTopLevelModule } from '@src/lang/util'
import type { CallExpression, CallExpressionKw } from '@src/lang/wasm' import type { CallExpression, CallExpressionKw } from '@src/lang/wasm'
import { defaultSourceRange } from '@src/lang/wasm' import { defaultSourceRange } from '@src/lang/wasm'
import type { DefaultPlaneStr } from '@src/lib/planes' import type { DefaultPlaneStr } from '@src/lib/planes'
@ -191,8 +190,8 @@ export function useEngineConnectionSubscriptions() {
kclManager.artifactGraph kclManager.artifactGraph
) )
if (!err(extrusion)) { if (!err(extrusion)) {
if (!isTopLevelModule(extrusion.codeRef.range)) { const fileIndex = getModuleId(extrusion.codeRef.range)
const fileIndex = getModuleId(extrusion.codeRef.range) if (fileIndex !== 0) {
const importDetails = const importDetails =
kclManager.execState.filenames[fileIndex] kclManager.execState.filenames[fileIndex]
if (!importDetails) { if (!importDetails) {

View File

@ -514,9 +514,7 @@ export function getCodeRefsByArtifactId(
artifactGraph: ArtifactGraph artifactGraph: ArtifactGraph
): Array<CodeRef> | null { ): Array<CodeRef> | null {
const artifact = artifactGraph.get(id) const artifact = artifactGraph.get(id)
if (artifact?.type === 'compositeSolid') { if (artifact?.type === 'solid2d') {
return [artifact.codeRef]
} else if (artifact?.type === 'solid2d') {
const codeRef = getSolid2dCodeRef(artifact, artifactGraph) const codeRef = getSolid2dCodeRef(artifact, artifactGraph)
if (err(codeRef)) return null if (err(codeRef)) return null
return [codeRef] return [codeRef]

View File

@ -71,7 +71,6 @@ export type {
ArtifactId, ArtifactId,
Cap as CapArtifact, Cap as CapArtifact,
CodeRef, CodeRef,
CompositeSolid as CompositeSolidArtifact,
EdgeCut, EdgeCut,
Path as PathArtifact, Path as PathArtifact,
Plane as PlaneArtifact, Plane as PlaneArtifact,

View File

@ -1,12 +0,0 @@
import {
import_file_extensions,
relevant_file_extensions,
} from '@rust/kcl-wasm-lib/pkg/kcl_wasm_lib'
export function importFileExtensions(): string[] {
return import_file_extensions()
}
export function relevantFileExtensions(): string[] {
return relevant_file_extensions()
}

View File

@ -1,4 +1,5 @@
import type { Models } from '@kittycad/lib/dist/types/src' import type { Models } from '@kittycad/lib/dist/types/src'
import type { FileImportFormat_type } from '@kittycad/lib/dist/types/src/models'
import type { UnitAngle, UnitLength } from '@rust/kcl-lib/bindings/ModelingCmd' import type { UnitAngle, UnitLength } from '@rust/kcl-lib/bindings/ModelingCmd'
@ -35,6 +36,29 @@ export const PROJECT_IMAGE_NAME = `thumbnail.png`
export const FILE_PERSIST_KEY = `${PROJECT_FOLDER}-last-opened` as const export const FILE_PERSIST_KEY = `${PROJECT_FOLDER}-last-opened` as const
/** The default name given to new kcl files in a project */ /** The default name given to new kcl files in a project */
export const DEFAULT_FILE_NAME = 'Untitled' export const DEFAULT_FILE_NAME = 'Untitled'
/** The file endings that will appear in
* the file explorer if found in a project directory */
// TODO: make stp part of this enum as an alias to step
// TODO: make glb part of this enum as it is in fact supported
export type NativeFileType = 'kcl'
export type RelevantFileType =
| FileImportFormat_type
| NativeFileType
| 'stp'
| 'glb'
export const NATIVE_FILE_TYPE: NativeFileType = 'kcl'
export const RELEVANT_FILE_TYPES: RelevantFileType[] = [
'kcl',
'fbx',
'gltf',
'glb',
'obj',
'ply',
'sldprt',
'stp',
'step',
'stl',
] as const
/** The default name for a tutorial project */ /** The default name for a tutorial project */
export const ONBOARDING_PROJECT_NAME = 'Tutorial Project $nn' export const ONBOARDING_PROJECT_NAME = 'Tutorial Project $nn'
/** /**

View File

@ -2,14 +2,9 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { Configuration } from '@rust/kcl-lib/bindings/Configuration' import type { Configuration } from '@rust/kcl-lib/bindings/Configuration'
import { initPromise } from '@src/lang/wasm' import { isRelevantFile, listProjects } from '@src/lib/desktop'
import { listProjects } from '@src/lib/desktop'
import type { DeepPartial } from '@src/lib/types' import type { DeepPartial } from '@src/lib/types'
beforeAll(async () => {
await initPromise
})
// Mock the electron window global // Mock the electron window global
const mockElectron = { const mockElectron = {
readdir: vi.fn(), readdir: vi.fn(),
@ -117,6 +112,41 @@ describe('desktop utilities', () => {
mockElectron.kittycad.mockResolvedValue({}) mockElectron.kittycad.mockResolvedValue({})
}) })
describe('isRelevantFile', () => {
it('finds supported extension files relevant', () => {
expect(isRelevantFile('part.kcl')).toEqual(true)
expect(isRelevantFile('part.fbx')).toEqual(true)
expect(isRelevantFile('part.gltf')).toEqual(true)
expect(isRelevantFile('part.glb')).toEqual(true)
expect(isRelevantFile('part.obj')).toEqual(true)
expect(isRelevantFile('part.ply')).toEqual(true)
expect(isRelevantFile('part.sldprt')).toEqual(true)
expect(isRelevantFile('part.stp')).toEqual(true)
expect(isRelevantFile('part.step')).toEqual(true)
expect(isRelevantFile('part.stl')).toEqual(true)
})
// TODO: we should be lowercasing the extension here to check. .sldprt or .SLDPRT should be supported
// But the api doesn't allow it today, so revisit this and the tests once this is done
it('finds (now) supported uppercase extension files *not* relevant', () => {
expect(isRelevantFile('part.KCL')).toEqual(false)
expect(isRelevantFile('part.FBX')).toEqual(false)
expect(isRelevantFile('part.GLTF')).toEqual(false)
expect(isRelevantFile('part.GLB')).toEqual(false)
expect(isRelevantFile('part.OBJ')).toEqual(false)
expect(isRelevantFile('part.PLY')).toEqual(false)
expect(isRelevantFile('part.SLDPRT')).toEqual(false)
expect(isRelevantFile('part.STP')).toEqual(false)
expect(isRelevantFile('part.STEP')).toEqual(false)
expect(isRelevantFile('part.STL')).toEqual(false)
})
it("doesn't find .docx or .SLDASM relevant", () => {
expect(isRelevantFile('paper.docx')).toEqual(false)
expect(isRelevantFile('assembly.SLDASM')).toEqual(false)
})
})
describe('listProjects', () => { describe('listProjects', () => {
it('does not list .git directories', async () => { it('does not list .git directories', async () => {
const projects = await listProjects(mockConfig) const projects = await listProjects(mockConfig)

View File

@ -10,13 +10,13 @@ import {
parseAppSettings, parseAppSettings,
parseProjectSettings, parseProjectSettings,
} from '@src/lang/wasm' } from '@src/lang/wasm'
import { relevantFileExtensions } from '@src/lang/wasmUtils'
import { import {
DEFAULT_DEFAULT_LENGTH_UNIT, DEFAULT_DEFAULT_LENGTH_UNIT,
PROJECT_ENTRYPOINT, PROJECT_ENTRYPOINT,
PROJECT_FOLDER, PROJECT_FOLDER,
PROJECT_IMAGE_NAME, PROJECT_IMAGE_NAME,
PROJECT_SETTINGS_FILE_NAME, PROJECT_SETTINGS_FILE_NAME,
RELEVANT_FILE_TYPES,
SETTINGS_FILE_NAME, SETTINGS_FILE_NAME,
TELEMETRY_FILE_NAME, TELEMETRY_FILE_NAME,
TELEMETRY_RAW_FILE_NAME, TELEMETRY_RAW_FILE_NAME,
@ -201,13 +201,15 @@ export async function listProjects(
return projects return projects
} }
// TODO: we should be lowercasing the extension here to check. .sldprt or .SLDPRT should be supported
// But the api doesn't allow it today, so revisit this and the tests once this is done
export const isRelevantFile = (filename: string): boolean =>
RELEVANT_FILE_TYPES.some((ext) => filename.endsWith('.' + ext))
const collectAllFilesRecursiveFrom = async ( const collectAllFilesRecursiveFrom = async (
path: string, path: string,
canReadWritePath: boolean canReadWritePath: boolean
) => { ) => {
const RELEVANT_FILE_EXTENSIONS = relevantFileExtensions()
const isRelevantFile = (filename: string): boolean =>
RELEVANT_FILE_EXTENSIONS.some((ext) => filename.endsWith('.' + ext))
// Make sure the filesystem object exists. // Make sure the filesystem object exists.
try { try {
await window.electron.stat(path) await window.electron.stat(path)

View File

@ -3,13 +3,8 @@ import os from 'os'
import path from 'path' import path from 'path'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { initPromise } from '@src/lang/wasm'
import getCurrentProjectFile from '@src/lib/getCurrentProjectFile' import getCurrentProjectFile from '@src/lib/getCurrentProjectFile'
beforeAll(async () => {
await initPromise
})
describe('getCurrentProjectFile', () => { describe('getCurrentProjectFile', () => {
test('with explicit open file with space (URL encoded)', async () => { test('with explicit open file with space (URL encoded)', async () => {
const name = `kittycad-modeling-projects-${uuidv4()}` const name = `kittycad-modeling-projects-${uuidv4()}`

View File

@ -1,12 +1,17 @@
import {
importFileExtensions,
relevantFileExtensions,
} from '@src/lang/wasmUtils'
import type { Stats } from 'fs' import type { Stats } from 'fs'
import * as fs from 'fs/promises' import * as fs from 'fs/promises'
import * as path from 'path' import * as path from 'path'
import { PROJECT_ENTRYPOINT } from '@src/lib/constants' import {
NATIVE_FILE_TYPE,
PROJECT_ENTRYPOINT,
RELEVANT_FILE_TYPES,
type RelevantFileType,
} from '@src/lib/constants'
const shouldWrapExtension = (extension: string) =>
RELEVANT_FILE_TYPES.includes(extension as RelevantFileType) &&
extension !== NATIVE_FILE_TYPE
/// Get the current project file from the path. /// Get the current project file from the path.
/// This is used for double-clicking on a file in the file explorer, /// This is used for double-clicking on a file in the file explorer,
@ -14,12 +19,6 @@ import { PROJECT_ENTRYPOINT } from '@src/lib/constants'
export default async function getCurrentProjectFile( export default async function getCurrentProjectFile(
pathString: string pathString: string
): Promise<string | Error> { ): Promise<string | Error> {
// Extract the values into an array
const allFileImportFormats: string[] = importFileExtensions()
const relevantExtensions: string[] = relevantFileExtensions()
const shouldWrapExtension = (extension: string) =>
allFileImportFormats.includes(extension)
// Fix for "." path, which is the current directory. // Fix for "." path, which is the current directory.
let sourcePath = pathString === '.' ? process.cwd() : pathString let sourcePath = pathString === '.' ? process.cwd() : pathString
@ -72,9 +71,12 @@ export default async function getCurrentProjectFile(
// Check if the extension on what we are trying to open is a relevant file type. // Check if the extension on what we are trying to open is a relevant file type.
const extension = path.extname(sourcePath).slice(1).toLowerCase() const extension = path.extname(sourcePath).slice(1).toLowerCase()
if (!relevantExtensions.includes(extension) && extension !== 'toml') { if (
!RELEVANT_FILE_TYPES.includes(extension as RelevantFileType) &&
extension !== 'toml'
) {
return new Error( return new Error(
`File type (${extension}) cannot be opened with this app: '${sourcePath}', try opening one of the following file types: ${relevantExtensions.join( `File type (${extension}) cannot be opened with this app: '${sourcePath}', try opening one of the following file types: ${RELEVANT_FILE_TYPES.join(
', ' ', '
)}` )}`
) )

View File

@ -15,7 +15,6 @@ import type {
format_number as FormatNumber, format_number as FormatNumber,
get_kcl_version as GetKclVersion, get_kcl_version as GetKclVersion,
get_tangential_arc_to_info as GetTangentialArcToInfo, get_tangential_arc_to_info as GetTangentialArcToInfo,
import_file_extensions as ImportFileExtensions,
is_kcl_empty_or_only_settings as IsKclEmptyOrOnlySettings, is_kcl_empty_or_only_settings as IsKclEmptyOrOnlySettings,
is_points_ccw as IsPointsCcw, is_points_ccw as IsPointsCcw,
kcl_lint as KclLint, kcl_lint as KclLint,
@ -24,7 +23,6 @@ import type {
parse_project_settings as ParseProjectSettings, parse_project_settings as ParseProjectSettings,
parse_wasm as ParseWasm, parse_wasm as ParseWasm,
recast_wasm as RecastWasm, recast_wasm as RecastWasm,
relevant_file_extensions as RelevantFileExtensions,
serialize_configuration as SerializeConfiguration, serialize_configuration as SerializeConfiguration,
serialize_project_configuration as SerializeProjectConfiguration, serialize_project_configuration as SerializeProjectConfiguration,
} from '@rust/kcl-wasm-lib/pkg/kcl_wasm_lib' } from '@rust/kcl-wasm-lib/pkg/kcl_wasm_lib'
@ -113,9 +111,3 @@ export const serialize_project_configuration: typeof SerializeProjectConfigurati
(...args) => { (...args) => {
return getModule().serialize_project_configuration(...args) return getModule().serialize_project_configuration(...args)
} }
export const import_file_extensions: typeof ImportFileExtensions = () => {
return getModule().import_file_extensions()
}
export const relevant_file_extensions: typeof RelevantFileExtensions = () => {
return getModule().relevant_file_extensions()
}

View File

@ -45,8 +45,7 @@ export function useDemoCode() {
} }
// Don't run if the network isn't healthy or the connection isn't established // Don't run if the network isn't healthy or the connection isn't established
if ( if (
overallState === NetworkHealthState.Disconnected || overallState !== NetworkHealthState.Ok ||
overallState === NetworkHealthState.Issue ||
immediateState.type !== EngineConnectionStateType.ConnectionEstablished immediateState.type !== EngineConnectionStateType.ConnectionEstablished
) { ) {
return return