Compare commits
10 Commits
zoom-tweak
...
v0.22.2
Author | SHA1 | Date | |
---|---|---|---|
0add26cf61 | |||
b54fc534c2 | |||
c66f851a3f | |||
13b8ab71d8 | |||
bdeab4f87d | |||
05ccf5e2f4 | |||
7ab015d783 | |||
3d6cfa980f | |||
9f5f1eb8c3 | |||
50fcdff879 |
5
.github/workflows/cargo-clippy.yml
vendored
5
.github/workflows/cargo-clippy.yml
vendored
@ -54,3 +54,8 @@ jobs:
|
||||
run: |
|
||||
cd "${{ matrix.dir }}"
|
||||
cargo clippy --all --tests --benches -- -D warnings
|
||||
# If this fails, run "cargo check" to update Cargo.lock,
|
||||
# then add Cargo.lock to the PR.
|
||||
- name: Check Cargo.lock doesn't need updating
|
||||
run: |
|
||||
cargo check --locked || echo "Pls run cargo check and commit the changed Cargo.lock"
|
||||
|
323
docs/kcl/chamfer.md
Normal file
323
docs/kcl/chamfer.md
Normal file
File diff suppressed because one or more lines are too long
@ -23,6 +23,7 @@ layout: manual
|
||||
* [`atan`](kcl/atan)
|
||||
* [`bezierCurve`](kcl/bezierCurve)
|
||||
* [`ceil`](kcl/ceil)
|
||||
* [`chamfer`](kcl/chamfer)
|
||||
* [`circle`](kcl/circle)
|
||||
* [`close`](kcl/close)
|
||||
* [`cos`](kcl/cos)
|
||||
@ -64,6 +65,7 @@ layout: manual
|
||||
* [`segEndX`](kcl/segEndX)
|
||||
* [`segEndY`](kcl/segEndY)
|
||||
* [`segLen`](kcl/segLen)
|
||||
* [`shell`](kcl/shell)
|
||||
* [`sin`](kcl/sin)
|
||||
* [`sqrt`](kcl/sqrt)
|
||||
* [`startProfileAt`](kcl/startProfileAt)
|
||||
|
@ -9,7 +9,7 @@ A circular pattern on a 2D sketch.
|
||||
|
||||
|
||||
```js
|
||||
patternCircular2d(data: CircularPattern2dData, sketch_group: SketchGroup) -> [SketchGroup]
|
||||
patternCircular2d(data: CircularPattern2dData, sketch_group_set: SketchGroupSet) -> [SketchGroup]
|
||||
```
|
||||
|
||||
### Examples
|
||||
@ -48,7 +48,7 @@ const example = extrude(1, exampleSketch)
|
||||
rotateDuplicates: string,
|
||||
}
|
||||
```
|
||||
* `sketch_group`: `SketchGroup` - A sketch group is a collection of paths. (REQUIRED)
|
||||
* `sketch_group_set`: `SketchGroupSet` - A sketch group or a group of sketch groups. (REQUIRED)
|
||||
```js
|
||||
{
|
||||
// The plane id or face id of the sketch group.
|
||||
@ -129,6 +129,7 @@ const example = extrude(1, exampleSketch)
|
||||
// The to point.
|
||||
to: [number, number],
|
||||
},
|
||||
type: "sketchGroup",
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
// The from point.
|
||||
@ -212,6 +213,9 @@ const example = extrude(1, exampleSketch)
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
} |
|
||||
{
|
||||
type: "sketchGroups",
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -9,7 +9,7 @@ A circular pattern on a 3D model.
|
||||
|
||||
|
||||
```js
|
||||
patternCircular3d(data: CircularPattern3dData, extrude_group: ExtrudeGroup) -> [ExtrudeGroup]
|
||||
patternCircular3d(data: CircularPattern3dData, extrude_group_set: ExtrudeGroupSet) -> [ExtrudeGroup]
|
||||
```
|
||||
|
||||
### Examples
|
||||
@ -47,7 +47,7 @@ const example = extrude(-5, exampleSketch)
|
||||
rotateDuplicates: string,
|
||||
}
|
||||
```
|
||||
* `extrude_group`: `ExtrudeGroup` - An extrude group is a collection of extrude surfaces. (REQUIRED)
|
||||
* `extrude_group_set`: `ExtrudeGroupSet` - A extrude group or a group of extrude groups. (REQUIRED)
|
||||
```js
|
||||
{
|
||||
// The id of the extrusion end cap
|
||||
@ -127,6 +127,7 @@ const example = extrude(-5, exampleSketch)
|
||||
}],
|
||||
// The id of the extrusion start cap
|
||||
startCapId: uuid,
|
||||
type: "extrudeGroup",
|
||||
// The extrude surfaces.
|
||||
value: [{
|
||||
// The face id for the extrude plane.
|
||||
@ -176,6 +177,9 @@ const example = extrude(-5, exampleSketch)
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
} |
|
||||
{
|
||||
type: "extrudeGroups",
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -9,7 +9,7 @@ A linear pattern on a 3D model.
|
||||
|
||||
|
||||
```js
|
||||
patternLinear3d(data: LinearPattern3dData, extrude_group: ExtrudeGroup) -> [ExtrudeGroup]
|
||||
patternLinear3d(data: LinearPattern3dData, extrude_group_set: ExtrudeGroupSet) -> [ExtrudeGroup]
|
||||
```
|
||||
|
||||
### Examples
|
||||
@ -45,7 +45,7 @@ const example = extrude(1, exampleSketch)
|
||||
repetitions: number,
|
||||
}
|
||||
```
|
||||
* `extrude_group`: `ExtrudeGroup` - An extrude group is a collection of extrude surfaces. (REQUIRED)
|
||||
* `extrude_group_set`: `ExtrudeGroupSet` - A extrude group or a group of extrude groups. (REQUIRED)
|
||||
```js
|
||||
{
|
||||
// The id of the extrusion end cap
|
||||
@ -125,6 +125,7 @@ const example = extrude(1, exampleSketch)
|
||||
}],
|
||||
// The id of the extrusion start cap
|
||||
startCapId: uuid,
|
||||
type: "extrudeGroup",
|
||||
// The extrude surfaces.
|
||||
value: [{
|
||||
// The face id for the extrude plane.
|
||||
@ -174,6 +175,9 @@ const example = extrude(1, exampleSketch)
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
} |
|
||||
{
|
||||
type: "extrudeGroups",
|
||||
}
|
||||
```
|
||||
|
||||
|
311
docs/kcl/shell.md
Normal file
311
docs/kcl/shell.md
Normal file
File diff suppressed because one or more lines are too long
10081
docs/kcl/std.json
10081
docs/kcl/std.json
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "untitled-app",
|
||||
"version": "0.22.1",
|
||||
"version": "0.22.2",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.16.0",
|
||||
@ -10,7 +10,7 @@
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"@headlessui/react": "^1.7.19",
|
||||
"@headlessui/tailwindcss": "^0.2.0",
|
||||
"@kittycad/lib": "^0.0.64",
|
||||
"@kittycad/lib": "^0.0.67",
|
||||
"@lezer/javascript": "^1.4.9",
|
||||
"@open-rpc/client-js": "^1.8.1",
|
||||
"@react-hook/resize-observer": "^2.0.1",
|
||||
|
1658
src-tauri/Cargo.lock
generated
1658
src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -16,11 +16,11 @@ tauri-build = { version = "2.0.0-beta.13", features = [] }
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
kcl-lib = { version = "0.1.53", path = "../src/wasm-lib/kcl" }
|
||||
kittycad = "0.3.0"
|
||||
kittycad = "0.3.5"
|
||||
log = "0.4.21"
|
||||
oauth2 = "4.4.2"
|
||||
serde_json = "1.0"
|
||||
tauri = { version = "2.0.0-beta.15", features = [ "devtools", "unstable"] }
|
||||
tauri = { version = "2.0.0-beta.22", features = [ "devtools", "unstable"] }
|
||||
tauri-plugin-cli = { version = "2.0.0-beta.3" }
|
||||
tauri-plugin-deep-link = { version = "2.0.0-beta.3" }
|
||||
tauri-plugin-dialog = { version = "2.0.0-beta.6" }
|
||||
|
@ -63,16 +63,17 @@
|
||||
"subcommands": {}
|
||||
},
|
||||
"deep-link": {
|
||||
"domains": [
|
||||
{
|
||||
"host": "app.zoo.dev"
|
||||
}
|
||||
]
|
||||
"mobile": [],
|
||||
"desktop": {
|
||||
"schemes": [
|
||||
"app.zoo.dev"
|
||||
]
|
||||
}
|
||||
},
|
||||
"shell": {
|
||||
"open": true
|
||||
}
|
||||
},
|
||||
"productName": "Zoo Modeling App",
|
||||
"version": "0.22.1"
|
||||
"version": "0.22.2"
|
||||
}
|
||||
|
@ -171,7 +171,9 @@ export const SettingsAuthProviderBase = ({
|
||||
})
|
||||
},
|
||||
'Execute AST': () => kclManager.executeCode(true, true),
|
||||
persistSettings: (context) =>
|
||||
},
|
||||
services: {
|
||||
'Persist settings': (context) =>
|
||||
saveSettings(context, loadedProject?.project?.path),
|
||||
},
|
||||
}
|
||||
|
@ -17,15 +17,17 @@ const prependRoutes =
|
||||
)
|
||||
}
|
||||
|
||||
type OnboardingPaths = {
|
||||
[K in keyof typeof onboardingPaths]: `/onboarding${(typeof onboardingPaths)[K]}`
|
||||
}
|
||||
|
||||
export const paths = {
|
||||
INDEX: '/',
|
||||
HOME: '/home',
|
||||
FILE: '/file',
|
||||
SETTINGS: '/settings',
|
||||
SIGN_IN: '/signin',
|
||||
ONBOARDING: prependRoutes(onboardingPaths)(
|
||||
'/onboarding'
|
||||
) as typeof onboardingPaths,
|
||||
ONBOARDING: prependRoutes(onboardingPaths)('/onboarding') as OnboardingPaths,
|
||||
} as const
|
||||
export const BROWSER_PATH = `%2F${BROWSER_PROJECT_NAME}%2F${BROWSER_FILE_NAME}${FILE_EXT}`
|
||||
|
||||
|
@ -11,98 +11,98 @@ import {
|
||||
|
||||
export const settingsMachine = createMachine(
|
||||
{
|
||||
/** @xstate-layout N4IgpgJg5mDOIC5QGUwBc0EsB2VYDpMIAbMAYlnXwEMAHW-Ae2wCNHqAnCHKZNatAFdYAbQAMAXUShajWJizNpIAB6IAbAFZN+AOwAWAIwAOYwE4AzGYBM+-ZosAaEAE9Eh62LP51ls+v0LMWt1awMAX3DnVAweAiJSCio6BjQACzAAWzAAYUZiRg5xKSQQWXlFbGU1BA9vQ0N1CwCxdVbdY1DnNwQzPp8zTTFje1D1QwtjSOj0LFx4knJKNHxMxggwYh58DYAzakFiNABVbAVi5XKFTCVSmotNY3w7YysHRuNDTXV1bvdG7w-IKTcbaazWCzTEAxOZ4QiLJIrFL4dJZMAXUpXSrVDTGazPMQPR4GXRBAx-XoGfDWIadMx4n6EqEwuLwxLLVbrTbbNKYKBpLb8tAAUWgcAxMjk11uoHumn0+DEw2sJkMulCgWsFL6YnwfX0ELsYg61jMumZs1ZCXIACU4OgAATLWGiSSXKXYu4aLz4UwWBr6DqBYYUzSePXqUlBLxmyZmC2xeZs8gxB3UYjEJ2W+YSsoem5VL0IKy6z6EsJifQ2Czq0MTHzq8Yjfz6MQmBMu5NkABUuaxBZxCDD+DD5lJgUjxssFP0xl0I+0pm06uMmg8kSiIGwXPgpRZ83dFQHRYAtA0LCO2tZjGIHJNdB5fq5EK3dc1OsNGkrA+oO1bFoe0qFrKiAnlol7BDed5zo+FK+Doc7qhCNaVv4UwbkAA */
|
||||
/** @xstate-layout N4IgpgJg5mDOIC5QGUwBc0EsB2VYDpMIAbMAYlnXwEMAHW-Ae2wCNHqAnCHKZNatAFdYAbQAMAXUShajWJizNpIAB6IALAFYAnPgBMARgDsBsQDY969QGYjmzQBoQAT0SnrADnwePY61r0PAwNtMyMAX3CnVAweAiJSCio6BjQACzAAWzAAYUZiRg5xKSQQWXlFbGU1BD1PfFtfE3UzTUNNaydXBCD1b209PTEPTTMtdQNNSOj0LFx4knJKNHxMxggwYh58DYAzakFiNABVbAVi5XKFTCVSmsGxfCMPM08PQaDNU0cXRG1tLwedTaKxif7+UJTKIgGJzPCERZJFYpfDpLJgC6lK6VaqIEx6fBmCw2Do2IJ6MxdRDvTT4MRDdRGEzWbQ6ELTGGzOIIxLLVbrTbbNKYKBpLaitAAUWgcExMjk11uoBqVgM3jMYhsAIMrVs6ipPWChOeYhC9KMFhGHNh3IS5AASnB0AACZZw0SSS4KnF3PFafADTV1YZ2IxiH7dNpGfCaIzAgE+IzWMzBa1c+Y88gxZ3UYjEV3pvBysrem5VX0IFq0y3aTXWOp6JmU34IKMxuz0joGEYWsxp2IZu1kABUxexZdxtRG+EmQMZmne3dNBs0jKewLBsbCwI81n77vwtDAHHksDhBYHeDIEGYYEI2AAbowANZ3o8nzBnm3zMelpWqRAAFp62sJ4jEsZ4AT0UJGwjPFzH6cwNW0AwWXpbRImhbABXgUpvzwL0KgnCtgJMMCII8KCYLsA11EGOkXneDxmXMCk92hfCFlIQjFXLZUgLjddWhaFkgRCaxOhbEYzBnXwXkmOjAjjfduXfU9zzdOIeJ9fiEEA6ckwMClQ2BFpmJXMF9DjYI6hZfxmMw8IgA */
|
||||
id: 'Settings',
|
||||
predictableActionArguments: true,
|
||||
context: {} as ReturnType<typeof createSettings>,
|
||||
initial: 'idle',
|
||||
states: {
|
||||
idle: {
|
||||
entry: ['setThemeClass', 'setClientSideSceneUnits', 'persistSettings'],
|
||||
entry: ['setThemeClass', 'setClientSideSceneUnits'],
|
||||
|
||||
on: {
|
||||
'*': {
|
||||
target: 'idle',
|
||||
internal: true,
|
||||
actions: ['setSettingAtLevel', 'toastSuccess', 'persistSettings'],
|
||||
target: 'persisting settings',
|
||||
actions: ['setSettingAtLevel', 'toastSuccess'],
|
||||
},
|
||||
|
||||
'set.app.onboardingStatus': {
|
||||
target: 'idle',
|
||||
internal: true,
|
||||
actions: ['setSettingAtLevel', 'persistSettings'], // No toast
|
||||
target: 'persisting settings',
|
||||
|
||||
// No toast
|
||||
actions: ['setSettingAtLevel'],
|
||||
},
|
||||
'set.app.themeColor': {
|
||||
target: 'idle',
|
||||
internal: true,
|
||||
actions: ['setSettingAtLevel', 'persistSettings'], // No toast
|
||||
target: 'persisting settings',
|
||||
|
||||
// No toast
|
||||
actions: ['setSettingAtLevel'],
|
||||
},
|
||||
|
||||
'set.modeling.defaultUnit': {
|
||||
target: 'idle',
|
||||
internal: true,
|
||||
target: 'persisting settings',
|
||||
|
||||
actions: [
|
||||
'setSettingAtLevel',
|
||||
'toastSuccess',
|
||||
'setClientSideSceneUnits',
|
||||
'Execute AST',
|
||||
'persistSettings',
|
||||
],
|
||||
},
|
||||
|
||||
'set.app.theme': {
|
||||
target: 'idle',
|
||||
internal: true,
|
||||
target: 'persisting settings',
|
||||
|
||||
actions: [
|
||||
'setSettingAtLevel',
|
||||
'toastSuccess',
|
||||
'setThemeClass',
|
||||
'setEngineTheme',
|
||||
'persistSettings',
|
||||
'setClientTheme',
|
||||
],
|
||||
},
|
||||
|
||||
'set.modeling.highlightEdges': {
|
||||
target: 'idle',
|
||||
internal: true,
|
||||
actions: [
|
||||
'setSettingAtLevel',
|
||||
'toastSuccess',
|
||||
'setEngineEdges',
|
||||
'persistSettings',
|
||||
],
|
||||
target: 'persisting settings',
|
||||
|
||||
actions: ['setSettingAtLevel', 'toastSuccess', 'setEngineEdges'],
|
||||
},
|
||||
|
||||
'Reset settings': {
|
||||
target: 'idle',
|
||||
internal: true,
|
||||
target: 'persisting settings',
|
||||
|
||||
actions: [
|
||||
'resetSettings',
|
||||
'setThemeClass',
|
||||
'setEngineTheme',
|
||||
'setClientSideSceneUnits',
|
||||
'Execute AST',
|
||||
'persistSettings',
|
||||
'setClientTheme',
|
||||
],
|
||||
},
|
||||
|
||||
'Set all settings': {
|
||||
target: 'idle',
|
||||
internal: true,
|
||||
target: 'persisting settings',
|
||||
|
||||
actions: [
|
||||
'setAllSettings',
|
||||
'setThemeClass',
|
||||
'setEngineTheme',
|
||||
'setClientSideSceneUnits',
|
||||
'Execute AST',
|
||||
'persistSettings',
|
||||
'setClientTheme',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
'persisting settings': {
|
||||
invoke: {
|
||||
src: 'Persist settings',
|
||||
id: 'persistSettings',
|
||||
onDone: 'idle',
|
||||
},
|
||||
},
|
||||
},
|
||||
tsTypes: {} as import('./settingsMachine.typegen').Typegen0,
|
||||
schema: {
|
||||
|
@ -3,7 +3,7 @@ import { Outlet, useNavigate } from 'react-router-dom'
|
||||
import Introduction from './Introduction'
|
||||
import Camera from './Camera'
|
||||
import Sketching from './Sketching'
|
||||
import { useCallback } from 'react'
|
||||
import { useCallback, useEffect } from 'react'
|
||||
import makeUrlPathRelative from '../../lib/makeUrlPathRelative'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import Streaming from './Streaming'
|
||||
@ -94,17 +94,31 @@ export function useNextClick(newStatus: string) {
|
||||
export function useDismiss() {
|
||||
const filePath = useAbsoluteFilePath()
|
||||
const {
|
||||
settings: { send },
|
||||
settings: { state, send },
|
||||
} = useSettingsAuthContext()
|
||||
const navigate = useNavigate()
|
||||
|
||||
return useCallback(() => {
|
||||
const settingsCallback = useCallback(() => {
|
||||
send({
|
||||
type: 'set.app.onboardingStatus',
|
||||
data: { level: 'user', value: 'dismissed' },
|
||||
})
|
||||
navigate(filePath)
|
||||
}, [send, navigate, filePath])
|
||||
}, [send])
|
||||
|
||||
/**
|
||||
* A "listener" for the XState to return to "idle" state
|
||||
* when the user dismisses the onboarding, using the callback above
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (
|
||||
state.context.app.onboardingStatus.user === 'dismissed' &&
|
||||
state.matches('idle')
|
||||
) {
|
||||
navigate(filePath)
|
||||
}
|
||||
}, [filePath, navigate, state])
|
||||
|
||||
return settingsCallback
|
||||
}
|
||||
|
||||
// Get the 1-indexed step number of the current onboarding step
|
||||
|
32
src/wasm-lib/Cargo.lock
generated
32
src/wasm-lib/Cargo.lock
generated
@ -406,9 +406,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.4"
|
||||
version = "4.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
|
||||
checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@ -416,9 +416,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.2"
|
||||
version = "4.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
|
||||
checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@ -430,9 +430,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.4"
|
||||
version = "4.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
|
||||
checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
@ -1369,7 +1369,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-lib"
|
||||
version = "0.1.58"
|
||||
version = "0.1.60"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"approx",
|
||||
@ -1436,9 +1436,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kittycad"
|
||||
version = "0.3.3"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0cbef813153197e60c0e96f59eea0b75f8418380f414b20250ee81b60e522c3"
|
||||
checksum = "df75feef10313fa1cb15b7cecd0f579877312ba3d42bb5b8b4c1d4b1d0fcabf0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -1451,7 +1451,7 @@ dependencies = [
|
||||
"format_serde_error",
|
||||
"futures",
|
||||
"http 0.2.12",
|
||||
"itertools 0.12.1",
|
||||
"itertools 0.13.0",
|
||||
"log",
|
||||
"mime_guess",
|
||||
"parse-display",
|
||||
@ -2378,9 +2378,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars"
|
||||
version = "0.8.20"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0218ceea14babe24a4a5836f86ade86c1effbc198164e619194cb5069187e29"
|
||||
checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92"
|
||||
dependencies = [
|
||||
"bigdecimal",
|
||||
"bytes",
|
||||
@ -2395,9 +2395,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "schemars_derive"
|
||||
version = "0.8.20"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ed5a1ccce8ff962e31a165d41f6e2a2dd1245099dc4d594f5574a86cd90f4d3"
|
||||
checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -2945,9 +2945,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tokio-tungstenite"
|
||||
version = "0.23.0"
|
||||
version = "0.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "becd34a233e7e31a3dbf7c7241b38320f57393dcae8e7324b0167d21b8e320b0"
|
||||
checksum = "c6989540ced10490aaf14e6bad2e3d33728a2813310a0c71d1574304c49631cd"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
|
@ -11,10 +11,10 @@ crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
bson = { version = "2.11.0", features = ["uuid-1", "chrono"] }
|
||||
clap = "4.5.4"
|
||||
clap = "4.5.7"
|
||||
gloo-utils = "0.2.0"
|
||||
kcl-lib = { path = "kcl" }
|
||||
kittycad = { workspace = true }
|
||||
kittycad.workspace = true
|
||||
serde_json = "1.0.116"
|
||||
tokio = { version = "1.38.0", features = ["sync"] }
|
||||
toml = "0.8.14"
|
||||
@ -68,7 +68,7 @@ members = [
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
kittycad = { version = "0.3.3", default-features = false, features = ["js", "requests"] }
|
||||
kittycad = { version = "0.3.5", default-features = false, features = ["js", "requests"] }
|
||||
kittycad-modeling-session = "0.1.4"
|
||||
|
||||
[[test]]
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-lib"
|
||||
description = "KittyCAD Language implementation and tools"
|
||||
version = "0.1.58"
|
||||
version = "0.1.60"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
@ -16,7 +16,7 @@ async-recursion = "1.1.1"
|
||||
async-trait = "0.1.80"
|
||||
base64 = "0.22.1"
|
||||
chrono = "0.4.38"
|
||||
clap = { version = "4.5.4", default-features = false, optional = true }
|
||||
clap = { version = "4.5.7", default-features = false, optional = true }
|
||||
dashmap = "5.5.3"
|
||||
databake = { version = "0.1.8", features = ["derive"] }
|
||||
derive-docs = { version = "0.1.18", path = "../derive-docs" }
|
||||
@ -56,7 +56,7 @@ web-sys = { version = "0.3.69", features = ["console"] }
|
||||
approx = "0.5"
|
||||
bson = { version = "2.11.0", features = ["uuid-1", "chrono"] }
|
||||
tokio = { version = "1.38.0", features = ["full"] }
|
||||
tokio-tungstenite = { version = "0.23.0", features = ["rustls-tls-native-roots"] }
|
||||
tokio-tungstenite = { version = "0.23.1", features = ["rustls-tls-native-roots"] }
|
||||
tower-lsp = { version = "0.20.0", features = ["proposed"] }
|
||||
|
||||
[features]
|
||||
|
@ -63,9 +63,10 @@ impl StdLibFnArg {
|
||||
|
||||
pub fn get_autocomplete_snippet(&self, index: usize) -> Result<Option<(usize, String)>> {
|
||||
if self.type_ == "SketchGroup"
|
||||
|| self.type_ == "ExtrudeGroup"
|
||||
|| self.type_ == "SketchSurface"
|
||||
|| self.type_ == "SketchGroupSet"
|
||||
|| self.type_ == "ExtrudeGroup"
|
||||
|| self.type_ == "ExtrudeGroupSet"
|
||||
|| self.type_ == "SketchSurface"
|
||||
{
|
||||
return Ok(Some((index, format!("${{{}:{}}}", index, "%"))));
|
||||
}
|
||||
|
126
src/wasm-lib/kcl/src/std/chamfer.rs
Normal file
126
src/wasm-lib/kcl/src/std/chamfer.rs
Normal file
@ -0,0 +1,126 @@
|
||||
//! Standard library chamfers.
|
||||
|
||||
use anyhow::Result;
|
||||
use derive_docs::stdlib;
|
||||
use kittycad::types::ModelingCmd;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::{ExtrudeGroup, MemoryItem},
|
||||
std::Args,
|
||||
};
|
||||
|
||||
pub(crate) const DEFAULT_TOLERANCE: f64 = 0.0000001;
|
||||
|
||||
/// Data for chamfers.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ChamferData {
|
||||
/// The radius of the chamfer.
|
||||
pub radius: f64,
|
||||
/// The tags of the paths you want to chamfer.
|
||||
pub tags: Vec<EdgeReference>,
|
||||
}
|
||||
|
||||
/// A string or a uuid.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Ord, PartialOrd, Eq, Hash)]
|
||||
#[ts(export)]
|
||||
#[serde(untagged)]
|
||||
pub enum EdgeReference {
|
||||
/// A uuid of an edge.
|
||||
Uuid(uuid::Uuid),
|
||||
/// A tag name of an edge.
|
||||
Tag(String),
|
||||
}
|
||||
|
||||
/// Create chamfers on tagged paths.
|
||||
pub async fn chamfer(args: Args) -> Result<MemoryItem, KclError> {
|
||||
let (data, extrude_group): (ChamferData, Box<ExtrudeGroup>) = args.get_data_and_extrude_group()?;
|
||||
|
||||
let extrude_group = inner_chamfer(data, extrude_group, args).await?;
|
||||
Ok(MemoryItem::ExtrudeGroup(extrude_group))
|
||||
}
|
||||
|
||||
/// Create chamfers on tagged paths.
|
||||
///
|
||||
/// ```no_run
|
||||
/// const width = 20
|
||||
/// const length = 10
|
||||
/// const thickness = 1
|
||||
/// const chamferRadius = 2
|
||||
///
|
||||
/// const mountingPlateSketch = startSketchOn("XY")
|
||||
/// |> startProfileAt([-width/2, -length/2], %)
|
||||
/// |> lineTo([width/2, -length/2], %, 'edge1')
|
||||
/// |> lineTo([width/2, length/2], %, 'edge2')
|
||||
/// |> lineTo([-width/2, length/2], %, 'edge3')
|
||||
/// |> close(%, 'edge4')
|
||||
///
|
||||
/// const mountingPlate = extrude(thickness, mountingPlateSketch)
|
||||
/// |> chamfer({
|
||||
/// radius: chamferRadius,
|
||||
/// tags: [
|
||||
/// getNextAdjacentEdge('edge1', %),
|
||||
/// getNextAdjacentEdge('edge2', %),
|
||||
/// getNextAdjacentEdge('edge3', %),
|
||||
/// getNextAdjacentEdge('edge4', %)
|
||||
/// ],
|
||||
/// }, %)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "chamfer",
|
||||
}]
|
||||
async fn inner_chamfer(
|
||||
data: ChamferData,
|
||||
extrude_group: Box<ExtrudeGroup>,
|
||||
args: Args,
|
||||
) -> Result<Box<ExtrudeGroup>, KclError> {
|
||||
// Check if tags contains any duplicate values.
|
||||
let mut tags = data.tags.clone();
|
||||
tags.sort();
|
||||
tags.dedup();
|
||||
if tags.len() != data.tags.len() {
|
||||
return Err(KclError::Type(KclErrorDetails {
|
||||
message: "Duplicate tags are not allowed.".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
}
|
||||
|
||||
for tag in data.tags {
|
||||
let edge_id = match tag {
|
||||
EdgeReference::Uuid(uuid) => uuid,
|
||||
EdgeReference::Tag(tag) => {
|
||||
extrude_group
|
||||
.sketch_group_values
|
||||
.iter()
|
||||
.find(|p| p.get_name() == tag)
|
||||
.ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
message: format!("No edge found with tag: `{}`", tag),
|
||||
source_ranges: vec![args.source_range],
|
||||
})
|
||||
})?
|
||||
.get_base()
|
||||
.geo_meta
|
||||
.id
|
||||
}
|
||||
};
|
||||
|
||||
args.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
ModelingCmd::Solid3DFilletEdge {
|
||||
edge_id,
|
||||
object_id: extrude_group.id,
|
||||
radius: data.radius,
|
||||
tolerance: DEFAULT_TOLERANCE, // We can let the user set this in the future.
|
||||
cut_type: Some(kittycad::types::CutType::Chamfer),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(extrude_group)
|
||||
}
|
@ -117,6 +117,7 @@ async fn inner_fillet(
|
||||
object_id: extrude_group.id,
|
||||
radius: data.radius,
|
||||
tolerance: DEFAULT_TOLERANCE, // We can let the user set this in the future.
|
||||
cut_type: Some(kittycad::types::CutType::Fillet),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
@ -1,5 +1,6 @@
|
||||
//! Functions implemented for language execution.
|
||||
|
||||
pub mod chamfer;
|
||||
pub mod extrude;
|
||||
pub mod fillet;
|
||||
pub mod helix;
|
||||
@ -10,6 +11,7 @@ pub mod patterns;
|
||||
pub mod revolve;
|
||||
pub mod segment;
|
||||
pub mod shapes;
|
||||
pub mod shell;
|
||||
pub mod sketch;
|
||||
pub mod types;
|
||||
pub mod utils;
|
||||
@ -29,7 +31,8 @@ use crate::{
|
||||
docs::StdLibFn,
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::{
|
||||
ExecutorContext, ExtrudeGroup, MemoryItem, Metadata, SketchGroup, SketchGroupSet, SketchSurface, SourceRange,
|
||||
ExecutorContext, ExtrudeGroup, ExtrudeGroupSet, MemoryItem, Metadata, SketchGroup, SketchGroupSet,
|
||||
SketchSurface, SourceRange,
|
||||
},
|
||||
std::{kcl_stdlib::KclStdLibFn, sketch::SketchOnFaceTag},
|
||||
};
|
||||
@ -81,11 +84,13 @@ lazy_static! {
|
||||
Box::new(crate::std::patterns::PatternLinear3D),
|
||||
Box::new(crate::std::patterns::PatternCircular2D),
|
||||
Box::new(crate::std::patterns::PatternCircular3D),
|
||||
Box::new(crate::std::chamfer::Chamfer),
|
||||
Box::new(crate::std::fillet::Fillet),
|
||||
Box::new(crate::std::fillet::GetOppositeEdge),
|
||||
Box::new(crate::std::fillet::GetNextAdjacentEdge),
|
||||
Box::new(crate::std::fillet::GetPreviousAdjacentEdge),
|
||||
Box::new(crate::std::helix::Helix),
|
||||
Box::new(crate::std::shell::Shell),
|
||||
Box::new(crate::std::revolve::Revolve),
|
||||
Box::new(crate::std::revolve::GetEdge),
|
||||
Box::new(crate::std::import::Import),
|
||||
@ -769,6 +774,52 @@ impl Args {
|
||||
Ok((data, sketch_surface, tag))
|
||||
}
|
||||
|
||||
fn get_data_and_extrude_group_set<T: serde::de::DeserializeOwned>(&self) -> Result<(T, ExtrudeGroupSet), KclError> {
|
||||
let first_value = self
|
||||
.args
|
||||
.first()
|
||||
.ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
message: format!("Expected a struct as the first argument, found `{:?}`", self.args),
|
||||
source_ranges: vec![self.source_range],
|
||||
})
|
||||
})?
|
||||
.get_json_value()?;
|
||||
|
||||
let data: T = serde_json::from_value(first_value).map_err(|e| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
message: format!("Failed to deserialize struct from JSON: {}", e),
|
||||
source_ranges: vec![self.source_range],
|
||||
})
|
||||
})?;
|
||||
|
||||
let second_value = self.args.get(1).ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
message: format!(
|
||||
"Expected an ExtrudeGroup as the second argument, found `{:?}`",
|
||||
self.args
|
||||
),
|
||||
source_ranges: vec![self.source_range],
|
||||
})
|
||||
})?;
|
||||
|
||||
let extrude_set = if let MemoryItem::ExtrudeGroup(eg) = second_value {
|
||||
ExtrudeGroupSet::ExtrudeGroup(eg.clone())
|
||||
} else if let MemoryItem::ExtrudeGroups { value } = second_value {
|
||||
ExtrudeGroupSet::ExtrudeGroups(value.clone())
|
||||
} else {
|
||||
return Err(KclError::Type(KclErrorDetails {
|
||||
message: format!(
|
||||
"Expected a ExtrudeGroup or Vector of ExtrudeGroups as the second argument, found `{:?}`",
|
||||
self.args
|
||||
),
|
||||
source_ranges: vec![self.source_range],
|
||||
}));
|
||||
};
|
||||
|
||||
Ok((data, extrude_set))
|
||||
}
|
||||
|
||||
fn get_data_and_extrude_group<T: serde::de::DeserializeOwned>(&self) -> Result<(T, Box<ExtrudeGroup>), KclError> {
|
||||
let first_value = self
|
||||
.args
|
||||
|
@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::{ExtrudeGroup, Geometries, Geometry, MemoryItem, SketchGroup, SketchGroupSet},
|
||||
executor::{ExtrudeGroup, ExtrudeGroupSet, Geometries, Geometry, MemoryItem, SketchGroup, SketchGroupSet},
|
||||
std::{types::Uint, Args},
|
||||
};
|
||||
|
||||
@ -141,7 +141,7 @@ async fn inner_pattern_linear_2d(
|
||||
|
||||
/// A linear pattern on a 3D model.
|
||||
pub async fn pattern_linear_3d(args: Args) -> Result<MemoryItem, KclError> {
|
||||
let (data, extrude_group): (LinearPattern3dData, Box<ExtrudeGroup>) = args.get_data_and_extrude_group()?;
|
||||
let (data, extrude_group_set): (LinearPattern3dData, ExtrudeGroupSet) = args.get_data_and_extrude_group_set()?;
|
||||
|
||||
if data.axis == [0.0, 0.0, 0.0] {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
@ -152,7 +152,7 @@ pub async fn pattern_linear_3d(args: Args) -> Result<MemoryItem, KclError> {
|
||||
}));
|
||||
}
|
||||
|
||||
let extrude_groups = inner_pattern_linear_3d(data, extrude_group, args).await?;
|
||||
let extrude_groups = inner_pattern_linear_3d(data, extrude_group_set, args).await?;
|
||||
Ok(MemoryItem::ExtrudeGroups { value: extrude_groups })
|
||||
}
|
||||
|
||||
@ -178,26 +178,36 @@ pub async fn pattern_linear_3d(args: Args) -> Result<MemoryItem, KclError> {
|
||||
}]
|
||||
async fn inner_pattern_linear_3d(
|
||||
data: LinearPattern3dData,
|
||||
extrude_group: Box<ExtrudeGroup>,
|
||||
extrude_group_set: ExtrudeGroupSet,
|
||||
args: Args,
|
||||
) -> Result<Vec<Box<ExtrudeGroup>>, KclError> {
|
||||
let starting_extrude_groups = match extrude_group_set {
|
||||
ExtrudeGroupSet::ExtrudeGroup(extrude_group) => vec![extrude_group],
|
||||
ExtrudeGroupSet::ExtrudeGroups(extrude_groups) => extrude_groups,
|
||||
};
|
||||
|
||||
if args.ctx.is_mock {
|
||||
return Ok(vec![extrude_group.clone()]);
|
||||
return Ok(starting_extrude_groups);
|
||||
}
|
||||
|
||||
let geometries = pattern_linear(
|
||||
LinearPattern::ThreeD(data),
|
||||
Geometry::ExtrudeGroup(extrude_group),
|
||||
args.clone(),
|
||||
)
|
||||
.await?;
|
||||
let mut extrude_groups = Vec::new();
|
||||
for extrude_group in starting_extrude_groups.iter() {
|
||||
let geometries = pattern_linear(
|
||||
LinearPattern::ThreeD(data.clone()),
|
||||
Geometry::ExtrudeGroup(extrude_group.clone()),
|
||||
args.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let Geometries::ExtrudeGroups(extrude_groups) = geometries else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Expected a vec of extrude groups".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
};
|
||||
let Geometries::ExtrudeGroups(new_extrude_groups) = geometries else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Expected a vec of extrude groups".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
};
|
||||
|
||||
extrude_groups.extend(new_extrude_groups);
|
||||
}
|
||||
|
||||
Ok(extrude_groups)
|
||||
}
|
||||
@ -335,9 +345,9 @@ impl CircularPattern {
|
||||
|
||||
/// A circular pattern on a 2D sketch.
|
||||
pub async fn pattern_circular_2d(args: Args) -> Result<MemoryItem, KclError> {
|
||||
let (data, sketch_group): (CircularPattern2dData, Box<SketchGroup>) = args.get_data_and_sketch_group()?;
|
||||
let (data, sketch_group_set): (CircularPattern2dData, SketchGroupSet) = args.get_data_and_sketch_group_set()?;
|
||||
|
||||
let sketch_groups = inner_pattern_circular_2d(data, sketch_group, args).await?;
|
||||
let sketch_groups = inner_pattern_circular_2d(data, sketch_group_set, args).await?;
|
||||
Ok(MemoryItem::SketchGroups { value: sketch_groups })
|
||||
}
|
||||
|
||||
@ -364,35 +374,45 @@ pub async fn pattern_circular_2d(args: Args) -> Result<MemoryItem, KclError> {
|
||||
}]
|
||||
async fn inner_pattern_circular_2d(
|
||||
data: CircularPattern2dData,
|
||||
sketch_group: Box<SketchGroup>,
|
||||
sketch_group_set: SketchGroupSet,
|
||||
args: Args,
|
||||
) -> Result<Vec<Box<SketchGroup>>, KclError> {
|
||||
let starting_sketch_groups = match sketch_group_set {
|
||||
SketchGroupSet::SketchGroup(sketch_group) => vec![sketch_group],
|
||||
SketchGroupSet::SketchGroups(sketch_groups) => sketch_groups,
|
||||
};
|
||||
|
||||
if args.ctx.is_mock {
|
||||
return Ok(vec![sketch_group]);
|
||||
return Ok(starting_sketch_groups);
|
||||
}
|
||||
|
||||
let geometries = pattern_circular(
|
||||
CircularPattern::TwoD(data),
|
||||
Geometry::SketchGroup(sketch_group),
|
||||
args.clone(),
|
||||
)
|
||||
.await?;
|
||||
let mut sketch_groups = Vec::new();
|
||||
for sketch_group in starting_sketch_groups.iter() {
|
||||
let geometries = pattern_circular(
|
||||
CircularPattern::TwoD(data.clone()),
|
||||
Geometry::SketchGroup(sketch_group.clone()),
|
||||
args.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let Geometries::SketchGroups(sketch_groups) = geometries else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Expected a vec of sketch groups".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
};
|
||||
let Geometries::SketchGroups(new_sketch_groups) = geometries else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Expected a vec of sketch groups".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
};
|
||||
|
||||
sketch_groups.extend(new_sketch_groups);
|
||||
}
|
||||
|
||||
Ok(sketch_groups)
|
||||
}
|
||||
|
||||
/// A circular pattern on a 3D model.
|
||||
pub async fn pattern_circular_3d(args: Args) -> Result<MemoryItem, KclError> {
|
||||
let (data, extrude_group): (CircularPattern3dData, Box<ExtrudeGroup>) = args.get_data_and_extrude_group()?;
|
||||
let (data, extrude_group_set): (CircularPattern3dData, ExtrudeGroupSet) = args.get_data_and_extrude_group_set()?;
|
||||
|
||||
let extrude_groups = inner_pattern_circular_3d(data, extrude_group, args).await?;
|
||||
let extrude_groups = inner_pattern_circular_3d(data, extrude_group_set, args).await?;
|
||||
Ok(MemoryItem::ExtrudeGroups { value: extrude_groups })
|
||||
}
|
||||
|
||||
@ -416,26 +436,36 @@ pub async fn pattern_circular_3d(args: Args) -> Result<MemoryItem, KclError> {
|
||||
}]
|
||||
async fn inner_pattern_circular_3d(
|
||||
data: CircularPattern3dData,
|
||||
extrude_group: Box<ExtrudeGroup>,
|
||||
extrude_group_set: ExtrudeGroupSet,
|
||||
args: Args,
|
||||
) -> Result<Vec<Box<ExtrudeGroup>>, KclError> {
|
||||
let starting_extrude_groups = match extrude_group_set {
|
||||
ExtrudeGroupSet::ExtrudeGroup(extrude_group) => vec![extrude_group],
|
||||
ExtrudeGroupSet::ExtrudeGroups(extrude_groups) => extrude_groups,
|
||||
};
|
||||
|
||||
if args.ctx.is_mock {
|
||||
return Ok(vec![extrude_group]);
|
||||
return Ok(starting_extrude_groups);
|
||||
}
|
||||
|
||||
let geometries = pattern_circular(
|
||||
CircularPattern::ThreeD(data),
|
||||
Geometry::ExtrudeGroup(extrude_group),
|
||||
args.clone(),
|
||||
)
|
||||
.await?;
|
||||
let mut extrude_groups = Vec::new();
|
||||
for extrude_group in starting_extrude_groups.iter() {
|
||||
let geometries = pattern_circular(
|
||||
CircularPattern::ThreeD(data.clone()),
|
||||
Geometry::ExtrudeGroup(extrude_group.clone()),
|
||||
args.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let Geometries::ExtrudeGroups(extrude_groups) = geometries else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Expected a vec of extrude groups".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
};
|
||||
let Geometries::ExtrudeGroups(new_extrude_groups) = geometries else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Expected a vec of extrude groups".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
};
|
||||
|
||||
extrude_groups.extend(new_extrude_groups);
|
||||
}
|
||||
|
||||
Ok(extrude_groups)
|
||||
}
|
||||
|
140
src/wasm-lib/kcl/src/std/shell.rs
Normal file
140
src/wasm-lib/kcl/src/std/shell.rs
Normal file
@ -0,0 +1,140 @@
|
||||
//! Standard library shells.
|
||||
|
||||
use anyhow::Result;
|
||||
use derive_docs::stdlib;
|
||||
use kittycad::types::ModelingCmd;
|
||||
use parse_display::{Display, FromStr};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::{ExtrudeGroup, ExtrudeSurface, MemoryItem},
|
||||
std::{sketch::StartOrEnd, Args},
|
||||
};
|
||||
|
||||
/// A tag for a face.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, FromStr, Display)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "snake_case", untagged)]
|
||||
#[display("{0}")]
|
||||
pub enum FaceTag {
|
||||
StartOrEnd(StartOrEnd),
|
||||
/// A string tag for the face you want to sketch on.
|
||||
String(String),
|
||||
}
|
||||
|
||||
/// Data for shells.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ShellData {
|
||||
/// The thickness of the shell.
|
||||
pub thickness: f64,
|
||||
/// The faces you want removed.
|
||||
pub faces: Vec<FaceTag>,
|
||||
}
|
||||
|
||||
/// Create a shell.
|
||||
pub async fn shell(args: Args) -> Result<MemoryItem, KclError> {
|
||||
let (data, extrude_group): (ShellData, Box<ExtrudeGroup>) = args.get_data_and_extrude_group()?;
|
||||
|
||||
let extrude_group = inner_shell(data, extrude_group, args).await?;
|
||||
Ok(MemoryItem::ExtrudeGroup(extrude_group))
|
||||
}
|
||||
|
||||
/// Shell a solid.
|
||||
///
|
||||
/// ```no_run
|
||||
/// const firstSketch = startSketchOn('XY')
|
||||
/// |> startProfileAt([-12, 12], %)
|
||||
/// |> line([24, 0], %)
|
||||
/// |> line([0, -24], %)
|
||||
/// |> line([-24, 0], %)
|
||||
/// |> close(%)
|
||||
/// |> extrude(6, %)
|
||||
///
|
||||
/// // Remove the end face for the extrusion.
|
||||
/// shell({
|
||||
/// faces: ['end'],
|
||||
/// thickness: 0.25,
|
||||
/// }, firstSketch)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "shell",
|
||||
}]
|
||||
async fn inner_shell(
|
||||
data: ShellData,
|
||||
extrude_group: Box<ExtrudeGroup>,
|
||||
args: Args,
|
||||
) -> Result<Box<ExtrudeGroup>, KclError> {
|
||||
if data.faces.is_empty() {
|
||||
return Err(KclError::Type(KclErrorDetails {
|
||||
message: "Expected at least one face".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
}
|
||||
|
||||
let mut face_ids = Vec::new();
|
||||
for tag in data.faces {
|
||||
let extrude_plane_id = match tag {
|
||||
FaceTag::String(ref s) => {
|
||||
if s.is_empty() {
|
||||
return Err(KclError::Type(KclErrorDetails {
|
||||
message: "Expected a non-empty tag for the face".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
}
|
||||
extrude_group
|
||||
.value
|
||||
.iter()
|
||||
.find_map(|extrude_surface| match extrude_surface {
|
||||
ExtrudeSurface::ExtrudePlane(extrude_plane) if extrude_plane.name == *s => {
|
||||
Some(extrude_plane.face_id)
|
||||
}
|
||||
ExtrudeSurface::ExtrudeArc(extrude_arc) if extrude_arc.name == *s => Some(extrude_arc.face_id),
|
||||
ExtrudeSurface::ExtrudePlane(_) | ExtrudeSurface::ExtrudeArc(_) => None,
|
||||
})
|
||||
.ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
message: format!("Expected a face with the tag `{}`", tag),
|
||||
source_ranges: vec![args.source_range],
|
||||
})
|
||||
})?
|
||||
}
|
||||
FaceTag::StartOrEnd(StartOrEnd::Start) => extrude_group.start_cap_id.ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
message: "Expected a start face".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
})
|
||||
})?,
|
||||
FaceTag::StartOrEnd(StartOrEnd::End) => extrude_group.end_cap_id.ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
message: "Expected an end face".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
})
|
||||
})?,
|
||||
};
|
||||
|
||||
face_ids.push(extrude_plane_id);
|
||||
}
|
||||
|
||||
if face_ids.is_empty() {
|
||||
return Err(KclError::Type(KclErrorDetails {
|
||||
message: "Expected at least one valid face".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
}
|
||||
|
||||
args.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
ModelingCmd::Solid3DShellFace {
|
||||
face_ids,
|
||||
object_id: extrude_group.id,
|
||||
shell_thickness: data.thickness,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(extrude_group)
|
||||
}
|
BIN
src/wasm-lib/kcl/tests/outputs/serial_test_example_chamfer0.png
Normal file
BIN
src/wasm-lib/kcl/tests/outputs/serial_test_example_chamfer0.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 98 KiB |
BIN
src/wasm-lib/kcl/tests/outputs/serial_test_example_shell0.png
Normal file
BIN
src/wasm-lib/kcl/tests/outputs/serial_test_example_shell0.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 157 KiB |
@ -1959,3 +1959,53 @@ async fn serial_test_neg_xz_plane() {
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/neg_xz_plane.png", &result, 1.0);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn serial_test_linear_pattern3d_a_pattern() {
|
||||
let code = r#"const exampleSketch = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([0, 2], %)
|
||||
|> line([3, 1], %)
|
||||
|> line([0, -4], %)
|
||||
|> close(%)
|
||||
|> extrude(1, %)
|
||||
|
||||
const pattn1 = patternLinear3d({
|
||||
axis: [1, 0, 0],
|
||||
repetitions: 6,
|
||||
distance: 6
|
||||
}, exampleSketch)
|
||||
|
||||
const pattn2 = patternLinear3d({
|
||||
axis: [0, 0, 1],
|
||||
distance: 1,
|
||||
repetitions: 6
|
||||
}, pattn1)
|
||||
"#;
|
||||
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/linear_pattern3d_a_pattern.png", &result, 1.0);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn serial_test_circular_pattern3d_a_pattern() {
|
||||
let code = r#"const exampleSketch = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([0, 2], %)
|
||||
|> line([3, 1], %)
|
||||
|> line([0, -4], %)
|
||||
|> close(%)
|
||||
|> extrude(1, %)
|
||||
|
||||
const pattn1 = patternLinear3d({
|
||||
axis: [1, 0, 0],
|
||||
repetitions: 6,
|
||||
distance: 6
|
||||
}, exampleSketch)
|
||||
|
||||
const pattn2 = patternCircular3d({axis: [0,0, 1], center: [-20, -20, -20], repetitions: 40, arcDegrees: 360, rotateDuplicates: false}, pattn1)
|
||||
"#;
|
||||
|
||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/circular_pattern3d_a_pattern.png", &result, 1.0);
|
||||
}
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 209 KiB |
Binary file not shown.
After Width: | Height: | Size: 176 KiB |
@ -1880,10 +1880,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60"
|
||||
integrity sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==
|
||||
|
||||
"@kittycad/lib@^0.0.64":
|
||||
version "0.0.64"
|
||||
resolved "https://registry.yarnpkg.com/@kittycad/lib/-/lib-0.0.64.tgz#0cea0788cd8af4a8964ddbf7152028affadcb17f"
|
||||
integrity sha512-qHyvNYKbhsfR5aXLFrdKrBQ4JI+0G0v096oROD3HatJ+AIzg5H0THmI+rMnQ9L4zx4U6n1A9gLi7ZQjSsZsleg==
|
||||
"@kittycad/lib@^0.0.67":
|
||||
version "0.0.67"
|
||||
resolved "https://registry.yarnpkg.com/@kittycad/lib/-/lib-0.0.67.tgz#b8edc66d83e41a79a7f238ba41bc27f0101935fb"
|
||||
integrity sha512-Uy2fve75bgpnlPiIgKrnKAqiko+1hlTCPSIPky6mv7Hrnwn7FhWAeeesdyc1Xws9Ae18kNyA2po8udK6PjZPkA==
|
||||
dependencies:
|
||||
node-fetch "3.3.2"
|
||||
openapi-types "^12.0.0"
|
||||
|
Reference in New Issue
Block a user