Compare commits

...

8 Commits

Author SHA1 Message Date
af482c30bd save as bw compatible (#6088)
* save as bw compatible

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

* fixes

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

* js side only for bw compatibility changes

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

* updates

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

* failure for doing wrong thing

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

* remove from ts side

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2025-04-02 03:39:55 +00:00
4e36e398ee Display the next version using native git commands (#6091) 2025-04-02 03:06:38 +00:00
b5f81b9384 Use new Point3d for named views (#6102)
* Use new Point3d for named views

* Wait a bit for the file menu to be created

---------

Co-authored-by: Jace Browning <jacebrowning@gmail.com>
Co-authored-by: Jess Frazelle <jessfraz@users.noreply.github.com>
2025-04-02 02:00:46 +00:00
879b471aed Revert "sort imports" (#6100)
Revert "sort imports (#6094)"

This reverts commit 2fc8cb5376.
2025-04-01 15:31:19 -07:00
964d81dc0e transform w optional args (#6095)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2025-04-01 14:23:36 -07:00
443f7cd5fd try to make wheels smaller (#6098)
* try to make wheels smaller

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

* target

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

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2025-04-01 14:22:33 -07:00
2fc8cb5376 sort imports (#6094)
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2025-04-01 14:20:42 -07:00
ee20a09e7e Update main.kcl (#6087)
* Update main.kcl

Finally making this bracket smarter
correcting fillet issues
single body w/ SSI extruded cuts
it even works as a sendcutsend upload now

* Update kcl-samples simulation test output

* Update main.kcl

descriptive variable names

* Update kcl-samples simulation test output

* Update main.kcl

spelling corrections

* Update kcl-samples simulation test output

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Josh Gomez <114548659+jgomez720@users.noreply.github.com>
2025-04-01 11:28:09 -07:00
30 changed files with 15642 additions and 6702 deletions

View File

@ -69,6 +69,14 @@
{ {
"selector": "CallExpression[callee.object.name='Array'][callee.property.name='isArray']", "selector": "CallExpression[callee.object.name='Array'][callee.property.name='isArray']",
"message": "Use isArray() in lib/utils.ts instead of Array.isArray()." "message": "Use isArray() in lib/utils.ts instead of Array.isArray()."
},
{
"selector": "CallExpression[callee.object.name='TOML'][callee.property.name='stringify']",
"message": "Do not use TOML.stringify directly. Use the wrappers in test-utils instead like settingsToToml."
},
{
"selector": "CallExpression[callee.object.name='TOML'][callee.property.name='parse']",
"message": "Do not use TOML.parse directly. Use the wrappers in test-utils instead like tomlToSettings."
} }
], ],
"semi": [ "semi": [

File diff suppressed because one or more lines are too long

View File

@ -267784,9 +267784,10 @@
"type": "number", "type": "number",
"schema": { "schema": {
"$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema", "$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema",
"title": "double", "title": "Nullable_double",
"type": "number", "type": "number",
"format": "double", "format": "double",
"nullable": true,
"definitions": { "definitions": {
"Solid": { "Solid": {
"type": "object", "type": "object",
@ -269371,9 +269372,9 @@
} }
} }
}, },
"required": true, "required": false,
"includeInSnippet": true, "includeInSnippet": true,
"description": "The scale factor for the x axis.", "description": "The scale factor for the x axis. Default is 1 if not provided.",
"labelRequired": true "labelRequired": true
}, },
{ {
@ -269381,9 +269382,10 @@
"type": "number", "type": "number",
"schema": { "schema": {
"$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema", "$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema",
"title": "double", "title": "Nullable_double",
"type": "number", "type": "number",
"format": "double", "format": "double",
"nullable": true,
"definitions": { "definitions": {
"Solid": { "Solid": {
"type": "object", "type": "object",
@ -270968,9 +270970,9 @@
} }
} }
}, },
"required": true, "required": false,
"includeInSnippet": true, "includeInSnippet": true,
"description": "The scale factor for the y axis.", "description": "The scale factor for the y axis. Default is 1 if not provided.",
"labelRequired": true "labelRequired": true
}, },
{ {
@ -270978,9 +270980,10 @@
"type": "number", "type": "number",
"schema": { "schema": {
"$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema", "$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema",
"title": "double", "title": "Nullable_double",
"type": "number", "type": "number",
"format": "double", "format": "double",
"nullable": true,
"definitions": { "definitions": {
"Solid": { "Solid": {
"type": "object", "type": "object",
@ -272565,9 +272568,9 @@
} }
} }
}, },
"required": true, "required": false,
"includeInSnippet": true, "includeInSnippet": true,
"description": "The scale factor for the z axis.", "description": "The scale factor for the z axis. Default is 1 if not provided.",
"labelRequired": true "labelRequired": true
}, },
{ {
@ -275836,9 +275839,9 @@
"unpublished": false, "unpublished": false,
"deprecated": false, "deprecated": false,
"examples": [ "examples": [
"// Scale a pipe.\n\n// Create a path for the sweep.\nsweepPath = startSketchOn(XZ)\n |> startProfileAt([0.05, 0.05], %)\n |> line(end = [0, 7])\n |> tangentialArc({ offset = 90, radius = 5 }, %)\n |> line(end = [-3, 0])\n |> tangentialArc({ offset = -90, radius = 5 }, %)\n |> line(end = [0, 7])\n\n// Create a hole for the pipe.\npipeHole = startSketchOn(XY)\n |> circle(center = [0, 0], radius = 1.5)\n\nsweepSketch = startSketchOn(XY)\n |> circle(center = [0, 0], radius = 2)\n |> hole(pipeHole, %)\n |> sweep(path = sweepPath)\n |> scale(x = 1.0, y = 1.0, z = 2.5)", "// Scale a pipe.\n\n// Create a path for the sweep.\nsweepPath = startSketchOn(XZ)\n |> startProfileAt([0.05, 0.05], %)\n |> line(end = [0, 7])\n |> tangentialArc({ offset = 90, radius = 5 }, %)\n |> line(end = [-3, 0])\n |> tangentialArc({ offset = -90, radius = 5 }, %)\n |> line(end = [0, 7])\n\n// Create a hole for the pipe.\npipeHole = startSketchOn(XY)\n |> circle(center = [0, 0], radius = 1.5)\n\nsweepSketch = startSketchOn(XY)\n |> circle(center = [0, 0], radius = 2)\n |> hole(pipeHole, %)\n |> sweep(path = sweepPath)\n |> scale(z = 2.5)",
"// Scale an imported model.\n\n\nimport \"tests/inputs/cube.sldprt\" as cube\n\ncube\n |> scale(x = 1.0, y = 1.0, z = 2.5)", "// Scale an imported model.\n\n\nimport \"tests/inputs/cube.sldprt\" as cube\n\ncube\n |> scale(z = 2.5)",
"// Sweep two sketches along the same path.\n\n\nsketch001 = startSketchOn(XY)\nrectangleSketch = startProfileAt([-200, 23.86], sketch001)\n |> angledLine([0, 73.47], %, $rectangleSegmentA001)\n |> angledLine([\n segAng(rectangleSegmentA001) - 90,\n 50.61\n ], %)\n |> angledLine([\n segAng(rectangleSegmentA001),\n -segLen(rectangleSegmentA001)\n ], %)\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\n\ncircleSketch = circle(sketch001, center = [200, -30.29], radius = 32.63)\n\nsketch002 = startSketchOn(YZ)\nsweepPath = startProfileAt([0, 0], sketch002)\n |> yLine(length = 231.81)\n |> tangentialArc({ radius = 80, offset = -90 }, %)\n |> xLine(length = 384.93)\n\nparts = sweep([rectangleSketch, circleSketch], path = sweepPath)\n\n// Scale the sweep.\nscale(\n parts,\n x = 1.0,\n y = 1.0,\n z = 0.5,\n)" "// Sweep two sketches along the same path.\n\n\nsketch001 = startSketchOn(XY)\nrectangleSketch = startProfileAt([-200, 23.86], sketch001)\n |> angledLine([0, 73.47], %, $rectangleSegmentA001)\n |> angledLine([\n segAng(rectangleSegmentA001) - 90,\n 50.61\n ], %)\n |> angledLine([\n segAng(rectangleSegmentA001),\n -segLen(rectangleSegmentA001)\n ], %)\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\n\ncircleSketch = circle(sketch001, center = [200, -30.29], radius = 32.63)\n\nsketch002 = startSketchOn(YZ)\nsweepPath = startProfileAt([0, 0], sketch002)\n |> yLine(length = 231.81)\n |> tangentialArc({ radius = 80, offset = -90 }, %)\n |> xLine(length = 384.93)\n\nparts = sweep([rectangleSketch, circleSketch], path = sweepPath)\n\n// Scale the sweep.\nscale(parts, z = 0.5)"
] ]
}, },
{ {
@ -326051,9 +326054,10 @@
"type": "number", "type": "number",
"schema": { "schema": {
"$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema", "$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema",
"title": "double", "title": "Nullable_double",
"type": "number", "type": "number",
"format": "double", "format": "double",
"nullable": true,
"definitions": { "definitions": {
"Solid": { "Solid": {
"type": "object", "type": "object",
@ -327638,9 +327642,9 @@
} }
} }
}, },
"required": true, "required": false,
"includeInSnippet": true, "includeInSnippet": true,
"description": "The amount to move the solid or sketch along the x axis.", "description": "The amount to move the solid or sketch along the x axis. Defaults to 0 if not provided.",
"labelRequired": true "labelRequired": true
}, },
{ {
@ -327648,9 +327652,10 @@
"type": "number", "type": "number",
"schema": { "schema": {
"$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema", "$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema",
"title": "double", "title": "Nullable_double",
"type": "number", "type": "number",
"format": "double", "format": "double",
"nullable": true,
"definitions": { "definitions": {
"Solid": { "Solid": {
"type": "object", "type": "object",
@ -329235,9 +329240,9 @@
} }
} }
}, },
"required": true, "required": false,
"includeInSnippet": true, "includeInSnippet": true,
"description": "The amount to move the solid or sketch along the y axis.", "description": "The amount to move the solid or sketch along the y axis. Defaults to 0 if not provided.",
"labelRequired": true "labelRequired": true
}, },
{ {
@ -329245,9 +329250,10 @@
"type": "number", "type": "number",
"schema": { "schema": {
"$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema", "$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema",
"title": "double", "title": "Nullable_double",
"type": "number", "type": "number",
"format": "double", "format": "double",
"nullable": true,
"definitions": { "definitions": {
"Solid": { "Solid": {
"type": "object", "type": "object",
@ -330832,9 +330838,9 @@
} }
} }
}, },
"required": true, "required": false,
"includeInSnippet": true, "includeInSnippet": true,
"description": "The amount to move the solid or sketch along the z axis.", "description": "The amount to move the solid or sketch along the z axis. Defaults to 0 if not provided.",
"labelRequired": true "labelRequired": true
}, },
{ {
@ -334106,8 +334112,8 @@
"// Move a pipe.\n\n// Create a path for the sweep.\nsweepPath = startSketchOn(XZ)\n |> startProfileAt([0.05, 0.05], %)\n |> line(end = [0, 7])\n |> tangentialArc({ offset = 90, radius = 5 }, %)\n |> line(end = [-3, 0])\n |> tangentialArc({ offset = -90, radius = 5 }, %)\n |> line(end = [0, 7])\n\n// Create a hole for the pipe.\npipeHole = startSketchOn(XY)\n |> circle(center = [0, 0], radius = 1.5)\n\nsweepSketch = startSketchOn(XY)\n |> circle(center = [0, 0], radius = 2)\n |> hole(pipeHole, %)\n |> sweep(path = sweepPath)\n |> translate(x = 1.0, y = 1.0, z = 2.5)", "// Move a pipe.\n\n// Create a path for the sweep.\nsweepPath = startSketchOn(XZ)\n |> startProfileAt([0.05, 0.05], %)\n |> line(end = [0, 7])\n |> tangentialArc({ offset = 90, radius = 5 }, %)\n |> line(end = [-3, 0])\n |> tangentialArc({ offset = -90, radius = 5 }, %)\n |> line(end = [0, 7])\n\n// Create a hole for the pipe.\npipeHole = startSketchOn(XY)\n |> circle(center = [0, 0], radius = 1.5)\n\nsweepSketch = startSketchOn(XY)\n |> circle(center = [0, 0], radius = 2)\n |> hole(pipeHole, %)\n |> sweep(path = sweepPath)\n |> translate(x = 1.0, y = 1.0, z = 2.5)",
"// Move an imported model.\n\n\nimport \"tests/inputs/cube.sldprt\" as cube\n\ncube\n |> translate(x = 1.0, y = 1.0, z = 2.5)", "// Move an imported model.\n\n\nimport \"tests/inputs/cube.sldprt\" as cube\n\ncube\n |> translate(x = 1.0, y = 1.0, z = 2.5)",
"// Sweep two sketches along the same path.\n\n\nsketch001 = startSketchOn(XY)\nrectangleSketch = startProfileAt([-200, 23.86], sketch001)\n |> angledLine([0, 73.47], %, $rectangleSegmentA001)\n |> angledLine([\n segAng(rectangleSegmentA001) - 90,\n 50.61\n ], %)\n |> angledLine([\n segAng(rectangleSegmentA001),\n -segLen(rectangleSegmentA001)\n ], %)\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\n\ncircleSketch = circle(sketch001, center = [200, -30.29], radius = 32.63)\n\nsketch002 = startSketchOn(YZ)\nsweepPath = startProfileAt([0, 0], sketch002)\n |> yLine(length = 231.81)\n |> tangentialArc({ radius = 80, offset = -90 }, %)\n |> xLine(length = 384.93)\n\nparts = sweep([rectangleSketch, circleSketch], path = sweepPath)\n\n// Move the sweeps.\ntranslate(\n parts,\n x = 1.0,\n y = 1.0,\n z = 2.5,\n)", "// Sweep two sketches along the same path.\n\n\nsketch001 = startSketchOn(XY)\nrectangleSketch = startProfileAt([-200, 23.86], sketch001)\n |> angledLine([0, 73.47], %, $rectangleSegmentA001)\n |> angledLine([\n segAng(rectangleSegmentA001) - 90,\n 50.61\n ], %)\n |> angledLine([\n segAng(rectangleSegmentA001),\n -segLen(rectangleSegmentA001)\n ], %)\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\n\ncircleSketch = circle(sketch001, center = [200, -30.29], radius = 32.63)\n\nsketch002 = startSketchOn(YZ)\nsweepPath = startProfileAt([0, 0], sketch002)\n |> yLine(length = 231.81)\n |> tangentialArc({ radius = 80, offset = -90 }, %)\n |> xLine(length = 384.93)\n\nparts = sweep([rectangleSketch, circleSketch], path = sweepPath)\n\n// Move the sweeps.\ntranslate(\n parts,\n x = 1.0,\n y = 1.0,\n z = 2.5,\n)",
"// Move a sketch.\n\n\nfn square(length) {\n l = length / 2\n p0 = [-l, -l]\n p1 = [-l, l]\n p2 = [l, l]\n p3 = [l, -l]\n\n return startSketchOn(XY)\n |> startProfileAt(p0, %)\n |> line(endAbsolute = p1)\n |> line(endAbsolute = p2)\n |> line(endAbsolute = p3)\n |> close()\n}\n\nsquare(10)\n |> translate(x = 5, y = 5, z = 0)\n |> extrude(length = 10)", "// Move a sketch.\n\n\nfn square(length) {\n l = length / 2\n p0 = [-l, -l]\n p1 = [-l, l]\n p2 = [l, l]\n p3 = [l, -l]\n\n return startSketchOn(XY)\n |> startProfileAt(p0, %)\n |> line(endAbsolute = p1)\n |> line(endAbsolute = p2)\n |> line(endAbsolute = p3)\n |> close()\n}\n\nsquare(10)\n |> translate(x = 5, y = 5)\n |> extrude(length = 10)",
"// Translate and rotate a sketch to create a loft.\nsketch001 = startSketchOn(XY)\n\nfn square() {\n return startProfileAt([-10, 10], sketch001)\n |> xLine(length = 20)\n |> yLine(length = -20)\n |> xLine(length = -20)\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\n}\n\nprofile001 = square()\n\nprofile002 = square()\n |> translate(x = 0, y = 0, z = 20)\n |> rotate(axis = [0, 0, 1.0], angle = 45)\n\nloft([profile001, profile002])" "// Translate and rotate a sketch to create a loft.\nsketch001 = startSketchOn(XY)\n\nfn square() {\n return startProfileAt([-10, 10], sketch001)\n |> xLine(length = 20)\n |> yLine(length = -20)\n |> xLine(length = -20)\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\n}\n\nprofile001 = square()\n\nprofile002 = square()\n |> translate(z = 20)\n |> rotate(axis = [0, 0, 1.0], angle = 45)\n\nloft([profile001, profile002])"
] ]
}, },
{ {

View File

@ -13,9 +13,9 @@ Translate is really useful for sketches if you want to move a sketch and then ro
```js ```js
translate( translate(
objects: SolidOrSketchOrImportedGeometry, objects: SolidOrSketchOrImportedGeometry,
x: number, x?: number,
y: number, y?: number,
z: number, z?: number,
global?: bool, global?: bool,
): SolidOrSketchOrImportedGeometry ): SolidOrSketchOrImportedGeometry
``` ```
@ -26,9 +26,9 @@ translate(
| Name | Type | Description | Required | | Name | Type | Description | Required |
|----------|------|-------------|----------| |----------|------|-------------|----------|
| `objects` | [`SolidOrSketchOrImportedGeometry`](/docs/kcl/types/SolidOrSketchOrImportedGeometry) | The solid, sketch, or set of solids or sketches to move. | Yes | | `objects` | [`SolidOrSketchOrImportedGeometry`](/docs/kcl/types/SolidOrSketchOrImportedGeometry) | The solid, sketch, or set of solids or sketches to move. | Yes |
| `x` | [`number`](/docs/kcl/types/number) | The amount to move the solid or sketch along the x axis. | Yes | | `x` | [`number`](/docs/kcl/types/number) | The amount to move the solid or sketch along the x axis. Defaults to 0 if not provided. | No |
| `y` | [`number`](/docs/kcl/types/number) | The amount to move the solid or sketch along the y axis. | Yes | | `y` | [`number`](/docs/kcl/types/number) | The amount to move the solid or sketch along the y axis. Defaults to 0 if not provided. | No |
| `z` | [`number`](/docs/kcl/types/number) | The amount to move the solid or sketch along the z axis. | Yes | | `z` | [`number`](/docs/kcl/types/number) | The amount to move the solid or sketch along the z axis. Defaults to 0 if not provided. | No |
| `global` | [`bool`](/docs/kcl/types/bool) | If true, the transform is applied in global space. The origin of the model will move. By default, the transform is applied in local sketch axis, therefore the origin will not move. | No | | `global` | [`bool`](/docs/kcl/types/bool) | If true, the transform is applied in global space. The origin of the model will move. By default, the transform is applied in local sketch axis, therefore the origin will not move. | No |
### Returns ### Returns
@ -134,7 +134,7 @@ fn square(length) {
} }
square(10) square(10)
|> translate(x = 5, y = 5, z = 0) |> translate(x = 5, y = 5)
|> extrude(length = 10) |> extrude(length = 10)
``` ```
@ -156,7 +156,7 @@ fn square() {
profile001 = square() profile001 = square()
profile002 = square() profile002 = square()
|> translate(x = 0, y = 0, z = 20) |> translate(z = 20)
|> rotate(axis = [0, 0, 1.0], angle = 45) |> rotate(axis = [0, 0, 1.0], angle = 45)
loft([profile001, profile002]) loft([profile001, profile002])

View File

@ -1,5 +1,4 @@
/* eslint-disable react-hooks/rules-of-hooks */ /* eslint-disable react-hooks/rules-of-hooks */
import type { import type {
BrowserContext, BrowserContext,
ElectronApplication, ElectronApplication,
@ -9,10 +8,9 @@ import type {
import { _electron as electron } from '@playwright/test' import { _electron as electron } from '@playwright/test'
import * as TOML from '@iarna/toml'
import { TEST_SETTINGS } from '../storageStates' import { TEST_SETTINGS } from '../storageStates'
import { SETTINGS_FILE_NAME } from 'lib/constants' import { SETTINGS_FILE_NAME } from 'lib/constants'
import { getUtils, setup } from '../test-utils' import { getUtils, setup, settingsToToml } from '../test-utils'
import fsp from 'fs/promises' import fsp from 'fs/promises'
import fs from 'node:fs' import fs from 'node:fs'
import path from 'path' import path from 'path'
@ -287,26 +285,30 @@ export class ElectronZoo {
let settingsOverridesToml = '' let settingsOverridesToml = ''
if (appSettings) { if (appSettings) {
settingsOverridesToml = TOML.stringify({ settingsOverridesToml = settingsToToml({
// @ts-expect-error
settings: { settings: {
...TEST_SETTINGS, ...TEST_SETTINGS,
...appSettings, ...appSettings,
app: { app: {
...TEST_SETTINGS.app, ...TEST_SETTINGS.app,
project_directory: this.projectDirName,
...appSettings.app, ...appSettings.app,
}, },
project: {
...TEST_SETTINGS.project,
directory: this.projectDirName,
},
}, },
}) })
} else { } else {
settingsOverridesToml = TOML.stringify({ settingsOverridesToml = settingsToToml({
// @ts-expect-error
settings: { settings: {
...TEST_SETTINGS, ...TEST_SETTINGS,
app: { app: {
...TEST_SETTINGS.app, ...TEST_SETTINGS.app,
project_directory: this.projectDirName, },
project: {
...TEST_SETTINGS.project,
directory: this.projectDirName,
}, },
}, },
}) })

View File

@ -10,6 +10,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
test('File.Create project', async ({ tronApp, cmdBar, page }) => { test('File.Create project', async ({ tronApp, cmdBar, page }) => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) fail()
const newProject = const newProject =
@ -29,6 +30,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
test('File.Open project', async ({ tronApp, cmdBar, page }) => { test('File.Open project', async ({ tronApp, cmdBar, page }) => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) fail()
const openProject = const openProject =
@ -52,6 +54,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
}) => { }) => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) fail()
const userSettings = app.applicationMenu.getMenuItemById( const userSettings = app.applicationMenu.getMenuItemById(
@ -75,6 +78,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
}) => { }) => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) fail()
const keybindings = app.applicationMenu.getMenuItemById( const keybindings = app.applicationMenu.getMenuItemById(
@ -96,6 +100,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
}) => { }) => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(
@ -112,6 +117,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
test('File.Preferences.Theme', async ({ tronApp, cmdBar, page }) => { test('File.Preferences.Theme', async ({ tronApp, cmdBar, page }) => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(
@ -136,6 +142,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
}) => { }) => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(
@ -152,6 +159,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
test('File.Preferences.Sign out', async ({ tronApp, cmdBar, page }) => { test('File.Preferences.Sign out', async ({ tronApp, cmdBar, page }) => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById('File.Sign out') const menu = app.applicationMenu.getMenuItemById('File.Sign out')
@ -170,6 +178,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
test('Edit.Rename project', async ({ tronApp, cmdBar, page }) => { test('Edit.Rename project', async ({ tronApp, cmdBar, page }) => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(
@ -188,6 +197,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
test('Edit.Delete project', async ({ tronApp, cmdBar, page }) => { test('Edit.Delete project', async ({ tronApp, cmdBar, page }) => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(
@ -210,6 +220,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
}) => { }) => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(
@ -228,6 +239,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
test('View.Command Palette...', async ({ tronApp, cmdBar, page }) => { test('View.Command Palette...', async ({ tronApp, cmdBar, page }) => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(
@ -245,6 +257,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
test('Help.Show all commands', async ({ tronApp, cmdBar, page }) => { test('Help.Show all commands', async ({ tronApp, cmdBar, page }) => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(
@ -260,6 +273,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
test('Help.KCL code samples', async ({ tronApp, cmdBar, page }) => { test('Help.KCL code samples', async ({ tronApp, cmdBar, page }) => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(
@ -275,6 +289,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
}) => { }) => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(
@ -293,6 +308,7 @@ test.describe('Native file menu', { tag: ['@electron'] }, () => {
test('Help.Reset onboarding', async ({ tronApp, cmdBar, page }) => { test('Help.Reset onboarding', async ({ tronApp, cmdBar, page }) => {
if (!tronApp) fail() if (!tronApp) fail()
// Run electron snippet to find the Menu! // Run electron snippet to find the Menu!
await page.waitForTimeout(100) // wait for createModelingPageMenu() to run
await tronApp.electron.evaluate(async ({ app }) => { await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail() if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById( const menu = app.applicationMenu.getMenuItemById(

View File

@ -345,7 +345,9 @@ const extrudeDefaultPlane = async (
app: { app: {
onboarding_status: 'dismissed', onboarding_status: 'dismissed',
show_debug_panel: true, show_debug_panel: true,
theme: 'dark', appearance: {
theme: 'dark',
},
}, },
project: { project: {
default_project_name: 'project-$nnn', default_project_name: 'project-$nnn',

View File

@ -9,9 +9,10 @@ export const IS_PLAYWRIGHT_KEY = 'playwright'
export const TEST_SETTINGS_KEY = '/settings.toml' export const TEST_SETTINGS_KEY = '/settings.toml'
export const TEST_SETTINGS: DeepPartial<Settings> = { export const TEST_SETTINGS: DeepPartial<Settings> = {
app: { app: {
theme: Themes.Dark, appearance: {
theme: Themes.Dark,
},
onboarding_status: 'dismissed', onboarding_status: 'dismissed',
project_directory: '',
show_debug_panel: true, show_debug_panel: true,
}, },
modeling: { modeling: {
@ -22,6 +23,7 @@ export const TEST_SETTINGS: DeepPartial<Settings> = {
}, },
project: { project: {
default_project_name: 'project-$nnn', default_project_name: 'project-$nnn',
directory: '',
}, },
text_editor: { text_editor: {
text_wrapping: true, text_wrapping: true,
@ -54,7 +56,7 @@ export const TEST_SETTINGS_ONBOARDING_START: DeepPartial<Settings> = {
export const TEST_SETTINGS_DEFAULT_THEME: DeepPartial<Settings> = { export const TEST_SETTINGS_DEFAULT_THEME: DeepPartial<Settings> = {
...TEST_SETTINGS, ...TEST_SETTINGS,
app: { ...TEST_SETTINGS.app, theme: Themes.System }, app: { ...TEST_SETTINGS.app, appearance: { theme: Themes.System } },
} }
export const TEST_SETTINGS_CORRUPTED = { export const TEST_SETTINGS_CORRUPTED = {

View File

@ -903,15 +903,21 @@ export async function setup(
settings: { settings: {
...TEST_SETTINGS, ...TEST_SETTINGS,
app: { app: {
appearance: {
...TEST_SETTINGS.app?.appearance,
theme: 'dark',
},
...TEST_SETTINGS.project, ...TEST_SETTINGS.project,
project_directory: TEST_SETTINGS.app?.project_directory,
onboarding_status: 'dismissed', onboarding_status: 'dismissed',
theme: 'dark', },
project: {
...TEST_SETTINGS.project,
directory: TEST_SETTINGS.project?.directory,
}, },
}, },
}), }),
IS_PLAYWRIGHT_KEY, IS_PLAYWRIGHT_KEY,
PLAYWRIGHT_TEST_DIR: TEST_SETTINGS.app?.project_directory || '', PLAYWRIGHT_TEST_DIR: TEST_SETTINGS.project?.directory || '',
PERSIST_MODELING_CONTEXT, PERSIST_MODELING_CONTEXT,
} }
) )
@ -1112,21 +1118,25 @@ export async function pollEditorLinesSelectedLength(page: Page, lines: number) {
} }
export function settingsToToml(settings: DeepPartial<Configuration>) { export function settingsToToml(settings: DeepPartial<Configuration>) {
// eslint-disable-next-line no-restricted-syntax
return TOML.stringify(settings as any) return TOML.stringify(settings as any)
} }
export function tomlToSettings(toml: string): DeepPartial<Configuration> { export function tomlToSettings(toml: string): DeepPartial<Configuration> {
// eslint-disable-next-line no-restricted-syntax
return TOML.parse(toml) return TOML.parse(toml)
} }
export function tomlToPerProjectSettings( export function tomlToPerProjectSettings(
toml: string toml: string
): DeepPartial<ProjectConfiguration> { ): DeepPartial<ProjectConfiguration> {
// eslint-disable-next-line no-restricted-syntax
return TOML.parse(toml) return TOML.parse(toml)
} }
export function perProjectsettingsToToml( export function perProjectsettingsToToml(
settings: DeepPartial<ProjectConfiguration> settings: DeepPartial<ProjectConfiguration>
) { ) {
// eslint-disable-next-line no-restricted-syntax
return TOML.stringify(settings as any) return TOML.stringify(settings as any)
} }

View File

@ -45,12 +45,12 @@ test.describe('Testing settings', () => {
) )
) )
expect(storedSettings.settings?.app?.theme).toBe('dark') expect(storedSettings.settings?.app?.appearance?.theme).toBe('dark')
// Check that the invalid settings were changed to good defaults // Check that the invalid settings were changed to good defaults
expect(storedSettings.settings?.modeling?.base_unit).toBe('in') expect(storedSettings.settings?.modeling?.base_unit).toBe('in')
expect(storedSettings.settings?.modeling?.mouse_controls).toBe('zoo') expect(storedSettings.settings?.modeling?.mouse_controls).toBe('zoo')
expect(storedSettings.settings?.app?.project_directory).toBe('') expect(storedSettings.settings?.project?.directory).toBe('')
expect(storedSettings.settings?.project?.default_project_name).toBe( expect(storedSettings.settings?.project?.default_project_name).toBe(
'project-$nnn' 'project-$nnn'
) )
@ -381,7 +381,9 @@ test.describe('Testing settings', () => {
} }
await tronApp.cleanProjectDir({ await tronApp.cleanProjectDir({
app: { app: {
theme_color: '259', appearance: {
color: 259,
},
}, },
}) })
@ -413,9 +415,12 @@ test.describe('Testing settings', () => {
await tronApp.cleanProjectDir({ await tronApp.cleanProjectDir({
app: { app: {
// Doesn't matter what you set it to. It will appearance: {
// default to 264.5 // Doesn't matter what you set it to. It will
theme_color: '0', // default to 264.5
color: 0,
},
}, },
}) })

View File

@ -26,7 +26,7 @@
"@fortawesome/react-fontawesome": "^0.2.0", "@fortawesome/react-fontawesome": "^0.2.0",
"@headlessui/react": "^1.7.19", "@headlessui/react": "^1.7.19",
"@headlessui/tailwindcss": "^0.2.2", "@headlessui/tailwindcss": "^0.2.2",
"@kittycad/lib": "2.0.23", "@kittycad/lib": "2.0.25",
"@lezer/highlight": "^1.2.1", "@lezer/highlight": "^1.2.1",
"@lezer/lr": "^1.4.1", "@lezer/lr": "^1.4.1",
"@react-hook/resize-observer": "^2.0.1", "@react-hook/resize-observer": "^2.0.1",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 57 KiB

View File

@ -4,72 +4,142 @@
// Set Units // Set Units
@settings(defaultLengthUnit = in) @settings(defaultLengthUnit = in)
// Define constants such as sheet metal thickness, bend radius, flange length, bolt diameter size, etc. // Input bolt pattern dimensions to mount the bracket
thickness = 0.090 mountingBoltDiameter = 1 / 4
bendRad = 0.08 mountingBoltPatternX = 7
outsideBendRad = bendRad + thickness mountingBoltPatternY = 4
flangeLength = 0.5
hatHeight = 3
hatWidth = 5
boltSize = 0.25
flangeWidth = 1.5
// Sketch and extrude the base shape and fillet the inside and outside edges. // Input bolt pattern dimensions to mount a component to the bracket
baseExtrusion = startSketchOn(-XZ) componentBoltDiameter = 3 / 16
|> startProfileAt([0, 0], %) componentBoltPatternX = 2
|> line(end = [0, thickness], tag = $e1) componentBoltPatternY = 3
|> line(end = [flangeLength, 0], tag = $e2)
|> line(end = [0, hatHeight], tag = $e3)
|> line(end = [hatWidth, 0], tag = $e4)
|> line(end = [0, -hatHeight], tag = $e5)
|> line(end = [flangeLength, 0], tag = $e6)
|> line(end = [0, -thickness], tag = $e7)
|> line(end = [-flangeLength - thickness, 0], tag = $e8)
|> line(end = [0, hatHeight], tag = $e9)
|> line(end = [-hatWidth + 2 * thickness, 0], tag = $e10)
|> line(end = [0, -hatHeight], tag = $e11)
|> close(tag = $e12)
|> extrude(length = hatWidth)
|> fillet(radius = bendRad, tags = [getNextAdjacentEdge(e2)])
|> fillet(radius = outsideBendRad, tags = [getNextAdjacentEdge(e3)])
|> fillet(radius = outsideBendRad, tags = [getNextAdjacentEdge(e4)])
|> fillet(radius = bendRad, tags = [getNextAdjacentEdge(e5)])
|> fillet(radius = outsideBendRad, tags = [getNextAdjacentEdge(e8)])
|> fillet(radius = bendRad, tags = [getNextAdjacentEdge(e9)])
|> fillet(radius = bendRad, tags = [getNextAdjacentEdge(e10)])
|> fillet(radius = outsideBendRad, tags = [getNextAdjacentEdge(e11)])
// Define the flanges and place the bolt holes // Define bracket constants such as sheet metal thickness, bend radius, flange length, etc.
flange1 = startSketchOn(XY) hatHeight = 2.5
|> startProfileAt([0, 0], %) bendAngle = 75
|> line(end = [0, hatWidth]) thickness = 0.125
|> line(end = [flangeWidth, 0], tag = $e13) interiorBendRadius = 0.125
|> line(end = [0, -hatWidth], tag = $e14)
// Calculate Remaining Parameters
exteriorBendRadius = interiorBendRadius + thickness
overhang = 3 * mountingBoltDiameter
flangeLength = 6 * mountingBoltDiameter
flangeExtrusion = if mountingBoltPatternY > componentBoltPatternY {
mountingBoltPatternY + overhang * 2
} else {
componentBoltPatternY + overhang * 2
}
// Draw the extrusion profile of the sheet metal bracket
bracketProfile = startSketchOn(XZ)
|> startProfileAt([
-mountingBoltPatternX / 2 - overhang,
0
], %)
|> xLine(length = flangeLength)
|> tangentialArc({
radius = exteriorBendRadius,
offset = bendAngle
}, %)
|> angledLineToY([bendAngle, hatHeight - thickness], %, $seg01)
|> tangentialArc({
radius = interiorBendRadius,
offset = -bendAngle
}, %)
|> xLine(endAbsolute = 0, tag = $seg02)
|> xLine(length = segLen(seg02))
|> tangentialArc({
radius = interiorBendRadius,
offset = -bendAngle
}, %)
|> angledLine([-bendAngle, segLen(seg01)], %)
|> tangentialArc({
radius = exteriorBendRadius,
offset = bendAngle
}, %)
|> xLine(length = flangeLength)
|> yLine(length = thickness, tag = $seg03)
|> xLine(length = -flangeLength, tag = $seg04)
|> tangentialArc({
radius = interiorBendRadius,
offset = -bendAngle
}, %)
|> angledLine([180 - bendAngle, segLen(seg01)], %)
|> tangentialArc({
radius = exteriorBendRadius,
offset = bendAngle
}, %)
|> xLine(endAbsolute = 0, tag = $seg05)
|> xLine(length = -segLen(seg05))
|> tangentialArc({
radius = exteriorBendRadius,
offset = bendAngle
}, %)
|> angledLine([bendAngle - 180, segLen(seg01)], %)
|> tangentialArc({
radius = interiorBendRadius,
offset = -bendAngle
}, %)
|> xLine(length = -flangeLength, tag = $seg06)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg07)
|> close() |> close()
|> hole(circle(center = [0.75, 1], radius = boltSize), %) |> extrude(length = flangeExtrusion)
|> hole(circle(center = [0.75, 4], radius = boltSize), %)
|> extrude(length = thickness)
|> fillet( |> fillet(
radius = 0.5, radius = flangeLength / 3,
tags = [ tags = [
getNextAdjacentEdge(e13), seg03,
getNextAdjacentEdge(e14) getOppositeEdge(seg03),
seg07,
getOppositeEdge(seg07)
], ],
) )
flange2 = startSketchOn(XY) // Cut the bolt pattern in the left base flange
|> startProfileAt([-6, 0], %) leftFlangeBoltPattern = startSketchOn(bracketProfile, seg04)
|> line(end = [0, hatWidth]) |> circle(center = [-mountingBoltPatternX / 2, overhang], radius = mountingBoltDiameter / 2)
|> line(end = [-flangeWidth, 0], tag = $e15) |> patternLinear2d(
|> line(end = [0, -hatWidth], tag = $e16) %,
|> close() instances = 2,
|> hole(circle(center = [-6.75, 1], radius = boltSize), %) distance = mountingBoltPatternY,
|> hole(circle(center = [-6.75, 4], radius = boltSize), %) axis = [0, 1],
|> extrude(length = thickness)
|> fillet(
radius = 0.25,
tags = [
getNextAdjacentEdge(e15),
getNextAdjacentEdge(e16)
],
) )
|> extrude(length = -thickness)
// Cut the bolt pattern in the right base flange
rightFlangeBoltPattern = startSketchOn(bracketProfile, seg06)
|> circle(center = [mountingBoltPatternX / 2, overhang], radius = mountingBoltDiameter / 2)
|> patternLinear2d(
%,
instances = 2,
distance = mountingBoltPatternY,
axis = [0, 1],
)
|> extrude(length = -thickness)
// Provision the top flange with holes to mount an object
topFlangeBoltPattern = startSketchOn(bracketProfile, seg05)
|> circle(
center = [
-componentBoltPatternX / 2,
-componentBoltPatternY / 2 + flangeExtrusion / 2
],
radius = componentBoltDiameter / 2,
)
|> patternLinear2d(
%,
instances = 2,
distance = componentBoltPatternX,
axis = [1, 0],
)
|> patternLinear2d(
%,
instances = 2,
distance = componentBoltPatternY,
axis = [0, 1],
)
|> extrude(length = -thickness)
// Place a hole at the center of the component bolt pattern to reduce mass
centeredHole = startSketchOn(bracketProfile, seg05)
|> circle(center = [0, flangeExtrusion / 2], radius = 0.75)
|> extrude(length = -thickness)

View File

@ -113,15 +113,19 @@ pub struct AppSettings {
pub onboarding_status: OnboardingStatus, pub onboarding_status: OnboardingStatus,
/// Backwards compatible project directory setting. /// Backwards compatible project directory setting.
#[serde(default, alias = "projectDirectory", skip_serializing_if = "Option::is_none")] #[serde(default, alias = "projectDirectory", skip_serializing_if = "Option::is_none")]
#[ts(skip)]
pub project_directory: Option<std::path::PathBuf>, pub project_directory: Option<std::path::PathBuf>,
/// Backwards compatible theme setting. /// Backwards compatible theme setting.
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
#[ts(skip)]
pub theme: Option<AppTheme>, pub theme: Option<AppTheme>,
/// The hue of the primary theme color for the app. /// The hue of the primary theme color for the app.
#[serde(default, skip_serializing_if = "Option::is_none", alias = "themeColor")] #[serde(default, skip_serializing_if = "Option::is_none", alias = "themeColor")]
#[ts(skip)]
pub theme_color: Option<FloatOrInt>, pub theme_color: Option<FloatOrInt>,
/// Whether or not Screen Space Ambient Occlusion (SSAO) is enabled. /// Whether or not Screen Space Ambient Occlusion (SSAO) is enabled.
#[serde(default, alias = "enableSSAO", skip_serializing_if = "Option::is_none")] #[serde(default, alias = "enableSSAO", skip_serializing_if = "Option::is_none")]
#[ts(skip)]
pub enable_ssao: Option<bool>, pub enable_ssao: Option<bool>,
/// Permanently dismiss the banner warning to download the desktop app. /// Permanently dismiss the banner warning to download the desktop app.
/// This setting only applies to the web app. And is temporary until we have Linux support. /// This setting only applies to the web app. And is temporary until we have Linux support.
@ -285,6 +289,7 @@ pub struct ModelingSettings {
/// of the app to aid in development. /// of the app to aid in development.
/// Remove this when we remove backwards compatibility with the old settings file. /// Remove this when we remove backwards compatibility with the old settings file.
#[serde(default, alias = "showDebugPanel", skip_serializing_if = "is_default")] #[serde(default, alias = "showDebugPanel", skip_serializing_if = "is_default")]
#[ts(skip)]
pub show_debug_panel: bool, pub show_debug_panel: bool,
/// Whether or not Screen Space Ambient Occlusion (SSAO) is enabled. /// Whether or not Screen Space Ambient Occlusion (SSAO) is enabled.
#[serde(default, skip_serializing_if = "is_default")] #[serde(default, skip_serializing_if = "is_default")]

View File

@ -94,9 +94,11 @@ pub struct ProjectAppSettings {
pub onboarding_status: OnboardingStatus, pub onboarding_status: OnboardingStatus,
/// The hue of the primary theme color for the app. /// The hue of the primary theme color for the app.
#[serde(default, skip_serializing_if = "Option::is_none", alias = "themeColor")] #[serde(default, skip_serializing_if = "Option::is_none", alias = "themeColor")]
#[ts(skip)]
pub theme_color: Option<FloatOrInt>, pub theme_color: Option<FloatOrInt>,
/// Whether or not Screen Space Ambient Occlusion (SSAO) is enabled. /// Whether or not Screen Space Ambient Occlusion (SSAO) is enabled.
#[serde(default, alias = "enableSSAO", skip_serializing_if = "Option::is_none")] #[serde(default, alias = "enableSSAO", skip_serializing_if = "Option::is_none")]
#[ts(skip)]
pub enable_ssao: Option<bool>, pub enable_ssao: Option<bool>,
/// Permanently dismiss the banner warning to download the desktop app. /// Permanently dismiss the banner warning to download the desktop app.
/// This setting only applies to the web app. And is temporary until we have Linux support. /// This setting only applies to the web app. And is temporary until we have Linux support.
@ -143,6 +145,7 @@ pub struct ProjectModelingSettings {
/// of the app to aid in development. /// of the app to aid in development.
/// Remove this when we remove backwards compatibility with the old settings file. /// Remove this when we remove backwards compatibility with the old settings file.
#[serde(default, alias = "showDebugPanel", skip_serializing_if = "is_default")] #[serde(default, alias = "showDebugPanel", skip_serializing_if = "is_default")]
#[ts(skip)]
pub show_debug_panel: bool, pub show_debug_panel: bool,
/// Whether or not Screen Space Ambient Occlusion (SSAO) is enabled. /// Whether or not Screen Space Ambient Occlusion (SSAO) is enabled.
#[serde(default, skip_serializing_if = "is_default")] #[serde(default, skip_serializing_if = "is_default")]

View File

@ -28,11 +28,19 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
]), ]),
exec_state, exec_state,
)?; )?;
let scale_x = args.get_kw_arg("x")?; let scale_x = args.get_kw_arg_opt("x")?;
let scale_y = args.get_kw_arg("y")?; let scale_y = args.get_kw_arg_opt("y")?;
let scale_z = args.get_kw_arg("z")?; let scale_z = args.get_kw_arg_opt("z")?;
let global = args.get_kw_arg_opt("global")?; let global = args.get_kw_arg_opt("global")?;
// Ensure at least one scale value is provided.
if scale_x.is_none() && scale_y.is_none() && scale_z.is_none() {
return Err(KclError::Semantic(KclErrorDetails {
message: "Expected `x`, `y`, or `z` to be provided.".to_string(),
source_ranges: vec![args.source_range],
}));
}
let objects = inner_scale(objects, scale_x, scale_y, scale_z, global, exec_state, args).await?; let objects = inner_scale(objects, scale_x, scale_y, scale_z, global, exec_state, args).await?;
Ok(objects.into()) Ok(objects.into())
} }
@ -85,8 +93,6 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
/// |> hole(pipeHole, %) /// |> hole(pipeHole, %)
/// |> sweep(path = sweepPath) /// |> sweep(path = sweepPath)
/// |> scale( /// |> scale(
/// x = 1.0,
/// y = 1.0,
/// z = 2.5, /// z = 2.5,
/// ) /// )
/// ``` /// ```
@ -98,8 +104,6 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
/// ///
/// cube /// cube
/// |> scale( /// |> scale(
/// x = 1.0,
/// y = 1.0,
/// z = 2.5, /// z = 2.5,
/// ) /// )
/// ``` /// ```
@ -135,7 +139,7 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
/// parts = sweep([rectangleSketch, circleSketch], path = sweepPath) /// parts = sweep([rectangleSketch, circleSketch], path = sweepPath)
/// ///
/// // Scale the sweep. /// // Scale the sweep.
/// scale(parts, x = 1.0, y = 1.0, z = 0.5) /// scale(parts, z = 0.5)
/// ``` /// ```
#[stdlib { #[stdlib {
name = "scale", name = "scale",
@ -144,17 +148,17 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
unlabeled_first = true, unlabeled_first = true,
args = { args = {
objects = {docs = "The solid, sketch, or set of solids or sketches to scale."}, objects = {docs = "The solid, sketch, or set of solids or sketches to scale."},
x = {docs = "The scale factor for the x axis."}, x = {docs = "The scale factor for the x axis. Default is 1 if not provided.", include_in_snippet = true},
y = {docs = "The scale factor for the y axis."}, y = {docs = "The scale factor for the y axis. Default is 1 if not provided.", include_in_snippet = true},
z = {docs = "The scale factor for the z axis."}, z = {docs = "The scale factor for the z axis. Default is 1 if not provided.", include_in_snippet = true},
global = {docs = "If true, the transform is applied in global space. The origin of the model will move. By default, the transform is applied in local sketch axis, therefore the origin will not move."} global = {docs = "If true, the transform is applied in global space. The origin of the model will move. By default, the transform is applied in local sketch axis, therefore the origin will not move."}
} }
}] }]
async fn inner_scale( async fn inner_scale(
objects: SolidOrSketchOrImportedGeometry, objects: SolidOrSketchOrImportedGeometry,
x: f64, x: Option<f64>,
y: f64, y: Option<f64>,
z: f64, z: Option<f64>,
global: Option<bool>, global: Option<bool>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
@ -174,7 +178,11 @@ async fn inner_scale(
object_id, object_id,
transforms: vec![shared::ComponentTransform { transforms: vec![shared::ComponentTransform {
scale: Some(shared::TransformBy::<Point3d<f64>> { scale: Some(shared::TransformBy::<Point3d<f64>> {
property: Point3d { x, y, z }, property: Point3d {
x: x.unwrap_or(1.0),
y: y.unwrap_or(1.0),
z: z.unwrap_or(1.0),
},
set: false, set: false,
is_local: !global.unwrap_or(false), is_local: !global.unwrap_or(false),
}), }),
@ -201,11 +209,19 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
]), ]),
exec_state, exec_state,
)?; )?;
let translate_x = args.get_kw_arg("x")?; let translate_x = args.get_kw_arg_opt("x")?;
let translate_y = args.get_kw_arg("y")?; let translate_y = args.get_kw_arg_opt("y")?;
let translate_z = args.get_kw_arg("z")?; let translate_z = args.get_kw_arg_opt("z")?;
let global = args.get_kw_arg_opt("global")?; let global = args.get_kw_arg_opt("global")?;
// Ensure at least one translation value is provided.
if translate_x.is_none() && translate_y.is_none() && translate_z.is_none() {
return Err(KclError::Semantic(KclErrorDetails {
message: "Expected `x`, `y`, or `z` to be provided.".to_string(),
source_ranges: vec![args.source_range],
}));
}
let objects = inner_translate(objects, translate_x, translate_y, translate_z, global, exec_state, args).await?; let objects = inner_translate(objects, translate_x, translate_y, translate_z, global, exec_state, args).await?;
Ok(objects.into()) Ok(objects.into())
} }
@ -326,7 +342,6 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
/// |> translate( /// |> translate(
/// x = 5, /// x = 5,
/// y = 5, /// y = 5,
/// z = 0,
/// ) /// )
/// |> extrude( /// |> extrude(
/// length = 10, /// length = 10,
@ -349,7 +364,7 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
/// profile001 = square() /// profile001 = square()
/// ///
/// profile002 = square() /// profile002 = square()
/// |> translate(x = 0, y = 0, z = 20) /// |> translate(z = 20)
/// |> rotate(axis = [0, 0, 1.0], angle = 45) /// |> rotate(axis = [0, 0, 1.0], angle = 45)
/// ///
/// loft([profile001, profile002]) /// loft([profile001, profile002])
@ -361,17 +376,17 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
unlabeled_first = true, unlabeled_first = true,
args = { args = {
objects = {docs = "The solid, sketch, or set of solids or sketches to move."}, objects = {docs = "The solid, sketch, or set of solids or sketches to move."},
x = {docs = "The amount to move the solid or sketch along the x axis."}, x = {docs = "The amount to move the solid or sketch along the x axis. Defaults to 0 if not provided.", include_in_snippet = true},
y = {docs = "The amount to move the solid or sketch along the y axis."}, y = {docs = "The amount to move the solid or sketch along the y axis. Defaults to 0 if not provided.", include_in_snippet = true},
z = {docs = "The amount to move the solid or sketch along the z axis."}, z = {docs = "The amount to move the solid or sketch along the z axis. Defaults to 0 if not provided.", include_in_snippet = true},
global = {docs = "If true, the transform is applied in global space. The origin of the model will move. By default, the transform is applied in local sketch axis, therefore the origin will not move."} global = {docs = "If true, the transform is applied in global space. The origin of the model will move. By default, the transform is applied in local sketch axis, therefore the origin will not move."}
} }
}] }]
async fn inner_translate( async fn inner_translate(
objects: SolidOrSketchOrImportedGeometry, objects: SolidOrSketchOrImportedGeometry,
x: f64, x: Option<f64>,
y: f64, y: Option<f64>,
z: f64, z: Option<f64>,
global: Option<bool>, global: Option<bool>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: Args, args: Args,
@ -392,9 +407,9 @@ async fn inner_translate(
transforms: vec![shared::ComponentTransform { transforms: vec![shared::ComponentTransform {
translate: Some(shared::TransformBy::<Point3d<LengthUnit>> { translate: Some(shared::TransformBy::<Point3d<LengthUnit>> {
property: shared::Point3d { property: shared::Point3d {
x: LengthUnit(x), x: LengthUnit(x.unwrap_or_default()),
y: LengthUnit(y), y: LengthUnit(y.unwrap_or_default()),
z: LengthUnit(z), z: LengthUnit(z.unwrap_or_default()),
}, },
set: false, set: false,
is_local: !global.unwrap_or(false), is_local: !global.unwrap_or(false),
@ -1001,4 +1016,34 @@ sweepSketch = startSketchOn('XY')
.to_string() .to_string()
); );
} }
#[tokio::test(flavor = "multi_thread")]
async fn test_translate_no_args() {
let ast = PIPE.to_string()
+ r#"
|> translate(
)
"#;
let result = parse_execute(&ast).await;
assert!(result.is_err());
assert_eq!(
result.unwrap_err().message(),
r#"Expected `x`, `y`, or `z` to be provided."#.to_string()
);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_scale_no_args() {
let ast = PIPE.to_string()
+ r#"
|> scale(
)
"#;
let result = parse_execute(&ast).await;
assert!(result.is_err());
assert_eq!(
result.unwrap_err().message(),
r#"Expected `x`, `y`, or `z` to be provided."#.to_string()
);
}
} }

View File

@ -1,141 +1,145 @@
```mermaid ```mermaid
flowchart LR flowchart LR
subgraph path2 [Path] subgraph path2 [Path]
2["Path<br>[728, 753, 0]"] 2["Path<br>[1227, 1308, 0]"]
3["Segment<br>[759, 796, 0]"] 3["Segment<br>[1314, 1342, 0]"]
4["Segment<br>[802, 842, 0]"] 4["Segment<br>[1348, 1436, 0]"]
5["Segment<br>[848, 885, 0]"] 5["Segment<br>[1442, 1502, 0]"]
6["Segment<br>[891, 927, 0]"] 6["Segment<br>[1508, 1597, 0]"]
7["Segment<br>[933, 971, 0]"] 7["Segment<br>[1603, 1639, 0]"]
8["Segment<br>[977, 1017, 0]"] 8["Segment<br>[1645, 1674, 0]"]
9["Segment<br>[1023, 1061, 0]"] 9["Segment<br>[1680, 1769, 0]"]
10["Segment<br>[1067, 1120, 0]"] 10["Segment<br>[1775, 1817, 0]"]
11["Segment<br>[1126, 1163, 0]"] 11["Segment<br>[1823, 1911, 0]"]
12["Segment<br>[1169, 1223, 0]"] 12["Segment<br>[1917, 1945, 0]"]
13["Segment<br>[1229, 1268, 0]"] 13["Segment<br>[1951, 1990, 0]"]
14["Segment<br>[1274, 1291, 0]"] 14["Segment<br>[1996, 2039, 0]"]
15[Solid2d] 15["Segment<br>[2045, 2134, 0]"]
16["Segment<br>[2140, 2187, 0]"]
17["Segment<br>[2193, 2281, 0]"]
18["Segment<br>[2287, 2323, 0]"]
19["Segment<br>[2329, 2359, 0]"]
20["Segment<br>[2365, 2453, 0]"]
21["Segment<br>[2459, 2506, 0]"]
22["Segment<br>[2512, 2601, 0]"]
23["Segment<br>[2607, 2650, 0]"]
24["Segment<br>[2656, 2726, 0]"]
25["Segment<br>[2732, 2739, 0]"]
26[Solid2d]
end end
subgraph path56 [Path] subgraph path94 [Path]
56["Path<br>[1947, 1972, 0]"] 94["Path<br>[3071, 3160, 0]"]
57["Segment<br>[1978, 2003, 0]"] 95["Segment<br>[3071, 3160, 0]"]
58["Segment<br>[2009, 2049, 0]"] 96[Solid2d]
59["Segment<br>[2055, 2093, 0]"]
60["Segment<br>[2099, 2106, 0]"]
61[Solid2d]
end end
subgraph path62 [Path] subgraph path102 [Path]
62["Path<br>[2117, 2162, 0]"] 102["Path<br>[3435, 3523, 0]"]
63["Segment<br>[2117, 2162, 0]"] 103["Segment<br>[3435, 3523, 0]"]
64[Solid2d] 104[Solid2d]
end end
subgraph path65 [Path] subgraph path110 [Path]
65["Path<br>[2177, 2222, 0]"] 110["Path<br>[3805, 3985, 0]"]
66["Segment<br>[2177, 2222, 0]"] 111["Segment<br>[3805, 3985, 0]"]
67[Solid2d] 112[Solid2d]
end end
subgraph path84 [Path] subgraph path120 [Path]
84["Path<br>[2430, 2456, 0]"] 120["Path<br>[4401, 4457, 0]"]
85["Segment<br>[2462, 2487, 0]"] 121["Segment<br>[4401, 4457, 0]"]
86["Segment<br>[2493, 2534, 0]"] 122[Solid2d]
87["Segment<br>[2540, 2578, 0]"]
88["Segment<br>[2584, 2591, 0]"]
89[Solid2d]
end end
subgraph path90 [Path] 1["Plane<br>[1204, 1221, 0]"]
90["Path<br>[2602, 2648, 0]"] 27["Sweep Extrusion<br>[2745, 2778, 0]"]
91["Segment<br>[2602, 2648, 0]"]
92[Solid2d]
end
subgraph path93 [Path]
93["Path<br>[2663, 2709, 0]"]
94["Segment<br>[2663, 2709, 0]"]
95[Solid2d]
end
1["Plane<br>[704, 722, 0]"]
16["Sweep Extrusion<br>[1297, 1323, 0]"]
17[Wall]
18[Wall]
19[Wall]
20[Wall]
21[Wall]
22[Wall]
23[Wall]
24[Wall]
25[Wall]
26[Wall]
27[Wall]
28[Wall] 28[Wall]
29["Cap Start"] 29[Wall]
30["Cap End"] 30[Wall]
31["SweepEdge Opposite"] 31[Wall]
32["SweepEdge Adjacent"] 32[Wall]
33["SweepEdge Opposite"] 33[Wall]
34["SweepEdge Adjacent"] 34[Wall]
35["SweepEdge Opposite"] 35[Wall]
36["SweepEdge Adjacent"] 36[Wall]
37["SweepEdge Opposite"] 37[Wall]
38["SweepEdge Adjacent"] 38[Wall]
39["SweepEdge Opposite"] 39[Wall]
40["SweepEdge Adjacent"] 40[Wall]
41["SweepEdge Opposite"] 41[Wall]
42["SweepEdge Adjacent"] 42[Wall]
43["SweepEdge Opposite"] 43[Wall]
44["SweepEdge Adjacent"] 44[Wall]
45["SweepEdge Opposite"] 45[Wall]
46["SweepEdge Adjacent"] 46[Wall]
47["SweepEdge Opposite"] 47[Wall]
48["SweepEdge Adjacent"] 48["Cap Start"]
49["SweepEdge Opposite"] 49["Cap End"]
50["SweepEdge Adjacent"] 50["SweepEdge Opposite"]
51["SweepEdge Opposite"] 51["SweepEdge Adjacent"]
52["SweepEdge Adjacent"] 52["SweepEdge Opposite"]
53["SweepEdge Opposite"] 53["SweepEdge Adjacent"]
54["SweepEdge Adjacent"] 54["SweepEdge Opposite"]
55["Plane<br>[1924, 1941, 0]"] 55["SweepEdge Adjacent"]
68["Sweep Extrusion<br>[2232, 2259, 0]"] 56["SweepEdge Opposite"]
69[Wall] 57["SweepEdge Adjacent"]
70[Wall] 58["SweepEdge Opposite"]
71[Wall] 59["SweepEdge Adjacent"]
72[Wall] 60["SweepEdge Opposite"]
73["Cap Start"] 61["SweepEdge Adjacent"]
74["Cap End"] 62["SweepEdge Opposite"]
75["SweepEdge Opposite"] 63["SweepEdge Adjacent"]
76["SweepEdge Adjacent"] 64["SweepEdge Opposite"]
77["SweepEdge Opposite"] 65["SweepEdge Adjacent"]
78["SweepEdge Adjacent"] 66["SweepEdge Opposite"]
79["SweepEdge Opposite"] 67["SweepEdge Adjacent"]
80["SweepEdge Adjacent"] 68["SweepEdge Opposite"]
81["SweepEdge Opposite"] 69["SweepEdge Adjacent"]
82["SweepEdge Adjacent"] 70["SweepEdge Opposite"]
83["Plane<br>[2407, 2424, 0]"] 71["SweepEdge Adjacent"]
96["Sweep Extrusion<br>[2719, 2746, 0]"] 72["SweepEdge Opposite"]
97[Wall] 73["SweepEdge Adjacent"]
74["SweepEdge Opposite"]
75["SweepEdge Adjacent"]
76["SweepEdge Opposite"]
77["SweepEdge Adjacent"]
78["SweepEdge Opposite"]
79["SweepEdge Adjacent"]
80["SweepEdge Opposite"]
81["SweepEdge Adjacent"]
82["SweepEdge Opposite"]
83["SweepEdge Adjacent"]
84["SweepEdge Opposite"]
85["SweepEdge Adjacent"]
86["SweepEdge Opposite"]
87["SweepEdge Adjacent"]
88["SweepEdge Opposite"]
89["SweepEdge Adjacent"]
90["EdgeCut Fillet<br>[2784, 2955, 0]"]
91["EdgeCut Fillet<br>[2784, 2955, 0]"]
92["EdgeCut Fillet<br>[2784, 2955, 0]"]
93["EdgeCut Fillet<br>[2784, 2955, 0]"]
97["Sweep Extrusion<br>[3289, 3317, 0]"]
98[Wall] 98[Wall]
99[Wall] 99["SweepEdge Opposite"]
100[Wall] 100["SweepEdge Adjacent"]
101["Cap Start"] 101["Sweep Extrusion<br>[3289, 3317, 0]"]
102["Cap End"] 105["Sweep Extrusion<br>[3652, 3680, 0]"]
103["SweepEdge Opposite"] 106[Wall]
104["SweepEdge Adjacent"]
105["SweepEdge Opposite"]
106["SweepEdge Adjacent"]
107["SweepEdge Opposite"] 107["SweepEdge Opposite"]
108["SweepEdge Adjacent"] 108["SweepEdge Adjacent"]
109["SweepEdge Opposite"] 109["Sweep Extrusion<br>[3652, 3680, 0]"]
110["SweepEdge Adjacent"] 113["Sweep Extrusion<br>[4239, 4267, 0]"]
111["EdgeCut Fillet<br>[1329, 1387, 0]"] 114[Wall]
112["EdgeCut Fillet<br>[1393, 1458, 0]"] 115["SweepEdge Opposite"]
113["EdgeCut Fillet<br>[1464, 1529, 0]"] 116["SweepEdge Adjacent"]
114["EdgeCut Fillet<br>[1535, 1593, 0]"] 117["Sweep Extrusion<br>[4239, 4267, 0]"]
115["EdgeCut Fillet<br>[1599, 1664, 0]"] 118["Sweep Extrusion<br>[4239, 4267, 0]"]
116["EdgeCut Fillet<br>[1670, 1728, 0]"] 119["Sweep Extrusion<br>[4239, 4267, 0]"]
117["EdgeCut Fillet<br>[1734, 1793, 0]"] 123["Sweep Extrusion<br>[4463, 4491, 0]"]
118["EdgeCut Fillet<br>[1799, 1865, 0]"] 124[Wall]
119["EdgeCut Fillet<br>[2265, 2395, 0]"] 125["SweepEdge Opposite"]
120["EdgeCut Fillet<br>[2265, 2395, 0]"] 126["SweepEdge Adjacent"]
121["EdgeCut Fillet<br>[2752, 2883, 0]"] 127["StartSketchOnFace<br>[3029, 3065, 0]"]
122["EdgeCut Fillet<br>[2752, 2883, 0]"] 128["StartSketchOnFace<br>[3393, 3429, 0]"]
129["StartSketchOnFace<br>[3763, 3799, 0]"]
130["StartSketchOnFace<br>[4359, 4395, 0]"]
1 --- 2 1 --- 2
2 --- 3 2 --- 3
2 --- 4 2 --- 4
@ -149,170 +153,187 @@ flowchart LR
2 --- 12 2 --- 12
2 --- 13 2 --- 13
2 --- 14 2 --- 14
2 ---- 16
2 --- 15 2 --- 15
2 --- 16
2 --- 17
2 --- 18
2 --- 19
2 --- 20
2 --- 21
2 --- 22
2 --- 23
2 --- 24
2 --- 25
2 ---- 27
2 --- 26
3 --- 28 3 --- 28
3 --- 53 3 --- 50
3 --- 54 3 --- 51
4 --- 27 4 --- 29
4 --- 51
4 --- 52 4 --- 52
5 --- 26 4 --- 53
5 --- 49 5 --- 30
5 --- 50 5 --- 54
6 --- 25 5 --- 55
6 --- 47 6 --- 31
6 --- 48 6 --- 56
7 --- 24 6 --- 57
7 --- 45 7 --- 32
7 --- 46 7 --- 58
8 --- 23 7 --- 59
8 --- 43 9 --- 33
8 --- 44 9 --- 60
9 --- 22 9 --- 61
9 --- 41 10 --- 34
9 --- 42 10 --- 62
10 --- 21 10 --- 63
10 --- 39 11 --- 35
10 --- 40 11 --- 64
11 --- 20 11 --- 65
11 --- 37
11 --- 38
12 --- 19
12 --- 35
12 --- 36 12 --- 36
13 --- 18 12 --- 66
13 --- 33 12 --- 67
13 --- 34 13 --- 37
14 --- 17 13 --- 68
14 --- 31 13 --- 69
14 --- 32 13 --- 90
16 --- 17 14 --- 38
16 --- 18 14 --- 70
16 --- 19 14 --- 71
16 --- 20 15 --- 39
16 --- 21 15 --- 72
16 --- 22 15 --- 73
16 --- 23
16 --- 24
16 --- 25
16 --- 26
16 --- 27
16 --- 28
16 --- 29
16 --- 30
16 --- 31
16 --- 32
16 --- 33
16 --- 34
16 --- 35
16 --- 36
16 --- 37
16 --- 38
16 --- 39
16 --- 40 16 --- 40
16 --- 41 16 --- 74
16 --- 42 16 --- 75
16 --- 43 17 --- 41
16 --- 44 17 --- 76
16 --- 45 17 --- 77
16 --- 46 18 --- 42
16 --- 47 18 --- 78
16 --- 48 18 --- 79
16 --- 49 20 --- 43
16 --- 50 20 --- 80
16 --- 51 20 --- 81
16 --- 52 21 --- 44
16 --- 53 21 --- 82
16 --- 54 21 --- 83
55 --- 56 22 --- 45
55 --- 62 22 --- 84
55 --- 65 22 --- 85
56 --- 57 23 --- 46
56 --- 58 23 --- 86
56 --- 59 23 --- 87
56 --- 60 24 --- 47
56 ---- 68 24 --- 88
56 --- 61 24 --- 89
57 --- 72 24 --- 92
57 --- 81 27 --- 28
57 --- 82 27 --- 29
58 --- 71 27 --- 30
58 --- 79 27 --- 31
58 --- 80 27 --- 32
59 --- 70 27 --- 33
59 --- 77 27 --- 34
59 --- 78 27 --- 35
60 --- 69 27 --- 36
60 --- 75 27 --- 37
60 --- 76 27 --- 38
62 --- 63 27 --- 39
62 --- 64 27 --- 40
65 --- 66 27 --- 41
65 --- 67 27 --- 42
68 --- 69 27 --- 43
68 --- 70 27 --- 44
68 --- 71 27 --- 45
68 --- 72 27 --- 46
68 --- 73 27 --- 47
68 --- 74 27 --- 48
68 --- 75 27 --- 49
68 --- 76 27 --- 50
68 --- 77 27 --- 51
68 --- 78 27 --- 52
68 --- 79 27 --- 53
68 --- 80 27 --- 54
68 --- 81 27 --- 55
68 --- 82 27 --- 56
83 --- 84 27 --- 57
83 --- 90 27 --- 58
83 --- 93 27 --- 59
84 --- 85 27 --- 60
84 --- 86 27 --- 61
84 --- 87 27 --- 62
84 --- 88 27 --- 63
84 ---- 96 27 --- 64
84 --- 89 27 --- 65
85 --- 97 27 --- 66
85 --- 103 27 --- 67
85 --- 104 27 --- 68
86 --- 98 27 --- 69
86 --- 105 27 --- 70
86 --- 106 27 --- 71
87 --- 99 27 --- 72
87 --- 107 27 --- 73
87 --- 108 27 --- 74
88 --- 100 27 --- 75
88 --- 109 27 --- 76
88 --- 110 27 --- 77
90 --- 91 27 --- 78
90 --- 92 27 --- 79
93 --- 94 27 --- 80
93 --- 95 27 --- 81
96 --- 97 27 --- 82
96 --- 98 27 --- 83
96 --- 99 27 --- 84
96 --- 100 27 --- 85
96 --- 101 27 --- 86
96 --- 102 27 --- 87
96 --- 103 27 --- 88
96 --- 104 27 --- 89
96 --- 105 38 --- 94
96 --- 106 42 --- 110
96 --- 107 42 --- 120
96 --- 108 46 --- 102
96 --- 109 68 <--x 91
96 --- 110 88 <--x 93
52 <--x 111 94 --- 95
50 <--x 112 94 ---- 97
48 <--x 113 94 --- 96
46 <--x 114 95 --- 98
40 <--x 115 95 --- 99
38 <--x 116 95 --- 100
36 <--x 117 97 --- 98
34 <--x 118 97 --- 99
80 <--x 119 97 --- 100
78 <--x 120 102 --- 103
106 <--x 121 102 ---- 105
108 <--x 122 102 --- 104
103 --- 106
103 --- 107
103 --- 108
105 --- 106
105 --- 107
105 --- 108
110 --- 111
110 ---- 113
110 --- 112
111 --- 114
111 --- 115
111 --- 116
113 --- 114
113 --- 115
113 --- 116
120 --- 121
120 ---- 123
120 --- 122
121 --- 124
121 --- 125
121 --- 126
123 --- 124
123 --- 125
123 --- 126
38 <--x 127
46 <--x 128
42 <--x 129
42 <--x 130
``` ```

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 57 KiB

View File

@ -3,6 +3,7 @@ name = "kcl-python-bindings"
version = "0.3.57" version = "0.3.57"
edition = "2021" edition = "2021"
repository = "https://github.com/kittycad/modeling-app" repository = "https://github.com/kittycad/modeling-app"
exclude = ["tests/*", "files/*", "venv/*"]
# 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
[lib] [lib]

View File

@ -20,3 +20,10 @@ test = [
[tool.maturin] [tool.maturin]
features = ["pyo3/extension-module"] features = ["pyo3/extension-module"]
[tool.setuptools]
include-package-data = false
[tool.setuptools.packages.find]
include = ["src*"]
exclude = ["files*", "tests*", "venv*", "target*"]

View File

@ -176,6 +176,8 @@ pub fn serialize_configuration(val: JsValue) -> Result<JsValue, String> {
let config: kcl_lib::Configuration = val.into_serde().map_err(|e| e.to_string())?; let config: kcl_lib::Configuration = val.into_serde().map_err(|e| e.to_string())?;
let toml_str = toml::to_string_pretty(&config).map_err(|e| e.to_string())?; let toml_str = toml::to_string_pretty(&config).map_err(|e| e.to_string())?;
let settings = kcl_lib::Configuration::backwards_compatible_toml_parse(&toml_str).map_err(|e| e.to_string())?;
let toml_str = toml::to_string_pretty(&settings).map_err(|e| e.to_string())?;
// The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the // The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
// gloo-serialize crate instead. // gloo-serialize crate instead.
@ -190,6 +192,9 @@ pub fn serialize_project_configuration(val: JsValue) -> Result<JsValue, String>
let config: kcl_lib::ProjectConfiguration = val.into_serde().map_err(|e| e.to_string())?; let config: kcl_lib::ProjectConfiguration = val.into_serde().map_err(|e| e.to_string())?;
let toml_str = toml::to_string_pretty(&config).map_err(|e| e.to_string())?; let toml_str = toml::to_string_pretty(&config).map_err(|e| e.to_string())?;
let settings =
kcl_lib::ProjectConfiguration::backwards_compatible_toml_parse(&toml_str).map_err(|e| e.to_string())?;
let toml_str = toml::to_string_pretty(&settings).map_err(|e| e.to_string())?;
// The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the // The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
// gloo-serialize crate instead. // gloo-serialize crate instead.

View File

@ -1,9 +1,9 @@
# Requires access to an environment variable GH_TOKEN #!/bin/bash
# If you are in the path of the git repository the gh release list will automatically point to that git repo set -euo pipefail
# aka cd /some/path/modeling-app
# $ gh release list # Fetch the latest release tag
# Get the latest semver tag from git git fetch --all --tags
latest_tag=$(gh release list --json name,isLatest --jq '.[] | select(.isLatest)|.name') latest_tag=$(git tag --sort=-v:refname | grep "^v[0-9]" | head -n 1)
# Function to bump version numbers # Function to bump version numbers
bump_version() { bump_version() {

View File

@ -39,7 +39,6 @@ export function mouseControlsToCameraSystem(
// TODO: understand why the values come back without underscores and fix the root cause // TODO: understand why the values come back without underscores and fix the root cause
// @ts-ignore: TS2678 // @ts-ignore: TS2678
case 'onshape': case 'onshape':
case 'on_shape':
return 'OnShape' return 'OnShape'
case 'trackpad_friendly': case 'trackpad_friendly':
return 'Trackpad Friendly' return 'Trackpad Friendly'
@ -52,7 +51,6 @@ export function mouseControlsToCameraSystem(
// TODO: understand why the values come back without underscores and fix the root cause // TODO: understand why the values come back without underscores and fix the root cause
// @ts-ignore: TS2678 // @ts-ignore: TS2678
case 'autocad': case 'autocad':
case 'auto_cad':
return 'AutoCAD' return 'AutoCAD'
default: default:
return undefined return undefined

View File

@ -7,6 +7,8 @@ import { settingsActor, getSettings } from 'machines/appMachine'
import { err, reportRejection } from 'lib/trap' import { err, reportRejection } from 'lib/trap'
import { import {
CameraViewState_type, CameraViewState_type,
Point3d_type,
Point4d_type,
WorldCoordinateSystem_type, WorldCoordinateSystem_type,
} from '@kittycad/lib/dist/types/src/models' } from '@kittycad/lib/dist/types/src/models'
@ -16,6 +18,31 @@ function isWorldCoordinateSystemType(
return x === 'right_handed_up_z' || x === 'right_handed_up_y' return x === 'right_handed_up_z' || x === 'right_handed_up_y'
} }
type Tuple3 = [number, number, number]
type Tuple4 = [number, number, number, number]
function point3DToNumberArray(value: Point3d_type): Tuple3 {
return [value.x, value.y, value.z]
}
function numberArrayToPoint3D(value: Tuple3): Point3d_type {
return {
x: value[0],
y: value[1],
z: value[2],
}
}
function point4DToNumberArray(value: Point4d_type): Tuple4 {
return [value.x, value.y, value.z, value.w]
}
function numberArrayToPoint4D(value: Tuple4): Point4d_type {
return {
x: value[0],
y: value[1],
z: value[2],
w: value[3],
}
}
function namedViewToCameraViewState( function namedViewToCameraViewState(
namedView: NamedView namedView: NamedView
): CameraViewState_type | Error { ): CameraViewState_type | Error {
@ -32,8 +59,8 @@ function namedViewToCameraViewState(
ortho_scale_factor: namedView.ortho_scale_factor, ortho_scale_factor: namedView.ortho_scale_factor,
world_coord_system: worldCoordinateSystem, world_coord_system: worldCoordinateSystem,
is_ortho: namedView.is_ortho, is_ortho: namedView.is_ortho,
pivot_position: namedView.pivot_position, pivot_position: numberArrayToPoint3D(namedView.pivot_position),
pivot_rotation: namedView.pivot_rotation, pivot_rotation: numberArrayToPoint4D(namedView.pivot_rotation),
} }
return cameraViewState return cameraViewState
@ -43,29 +70,11 @@ function cameraViewStateToNamedView(
name: string, name: string,
cameraViewState: CameraViewState_type cameraViewState: CameraViewState_type
): NamedView | Error { ): NamedView | Error {
let pivot_position: [number, number, number] | null = null let pivot_position: Tuple3 | null = null
let pivot_rotation: [number, number, number, number] | null = null let pivot_rotation: Tuple4 | null = null
if (cameraViewState.pivot_position.length === 3) { pivot_position = point3DToNumberArray(cameraViewState.pivot_position)
pivot_position = [ pivot_rotation = point4DToNumberArray(cameraViewState.pivot_rotation)
cameraViewState.pivot_position[0],
cameraViewState.pivot_position[1],
cameraViewState.pivot_position[2],
]
} else {
return new Error(`invalid pivot position ${cameraViewState.pivot_position}`)
}
if (cameraViewState.pivot_rotation.length === 4) {
pivot_rotation = [
cameraViewState.pivot_rotation[0],
cameraViewState.pivot_rotation[1],
cameraViewState.pivot_rotation[2],
cameraViewState.pivot_rotation[3],
]
} else {
return new Error(`invalid pivot rotation ${cameraViewState.pivot_rotation}`)
}
// Create a new named view // Create a new named view
const requestedView: NamedView = { const requestedView: NamedView = {

View File

@ -67,9 +67,7 @@ export async function renameProjectDirectory(
export async function ensureProjectDirectoryExists( export async function ensureProjectDirectoryExists(
config: DeepPartial<Configuration> config: DeepPartial<Configuration>
): Promise<string | undefined> { ): Promise<string | undefined> {
const projectDir = const projectDir = config.settings?.project?.directory
config.settings?.app?.project_directory ||
config.settings?.project?.directory
if (!projectDir) { if (!projectDir) {
console.error('projectDir is falsey', config) console.error('projectDir is falsey', config)
return Promise.reject(new Error('projectDir is falsey')) return Promise.reject(new Error('projectDir is falsey'))
@ -588,8 +586,7 @@ export const readAppSettingsFile = async () => {
} }
const hasProjectDirectorySetting = const hasProjectDirectorySetting =
parsedAppConfig.settings?.project?.directory || parsedAppConfig.settings?.project?.directory
parsedAppConfig.settings?.app?.project_directory
if (hasProjectDirectorySetting) { if (hasProjectDirectorySetting) {
return parsedAppConfig return parsedAppConfig

View File

@ -1866,10 +1866,10 @@
"@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14" "@jridgewell/sourcemap-codec" "^1.4.14"
"@kittycad/lib@2.0.23": "@kittycad/lib@2.0.25":
version "2.0.23" version "2.0.25"
resolved "https://registry.yarnpkg.com/@kittycad/lib/-/lib-2.0.23.tgz#0d215d458b35f6d207eeb90443889fa77b21b913" resolved "https://registry.yarnpkg.com/@kittycad/lib/-/lib-2.0.25.tgz#ad3e9a548752440b18102ca9b83e10811c2c08ab"
integrity sha512-5T7+gHB21RX5bE7ILp3TnLzp0rA7CP1BucNctHynANG/sXV44tD7U8YEcQsi+/ZmMkvrxmZ/3r/UQjgzhQUh7w== integrity sha512-Qw5veBEX37lOfdg93OiSKFcTC+3y5q3hcfjML53BbRwE7bzwE/PlPFAouqqnts4a9PEETHxeO1CsKe3YUW+ysA==
dependencies: dependencies:
openapi-types "^12.0.0" openapi-types "^12.0.0"
ts-node "^10.9.1" ts-node "^10.9.1"