Merge branch 'main' into pierremtb/issue3528-Add-electron-updater

This commit is contained in:
Pierre Jacquier
2024-08-22 06:01:51 -04:00
committed by GitHub
149 changed files with 2061 additions and 622 deletions

BIN
assets/icon.icns Normal file

Binary file not shown.

BIN
assets/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

View File

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

BIN
assets/icon@2x.icns Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

38
docs/kcl/cm.md Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

38
docs/kcl/ft.md Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

38
docs/kcl/inch.md Normal file

File diff suppressed because one or more lines are too long

View File

@ -32,17 +32,20 @@ layout: manual
* [`chamfer`](kcl/chamfer)
* [`circle`](kcl/circle)
* [`close`](kcl/close)
* [`cm`](kcl/cm)
* [`cos`](kcl/cos)
* [`e`](kcl/e)
* [`extrude`](kcl/extrude)
* [`fillet`](kcl/fillet)
* [`floor`](kcl/floor)
* [`ft`](kcl/ft)
* [`getNextAdjacentEdge`](kcl/getNextAdjacentEdge)
* [`getOppositeEdge`](kcl/getOppositeEdge)
* [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge)
* [`helix`](kcl/helix)
* [`hole`](kcl/hole)
* [`import`](kcl/import)
* [`inch`](kcl/inch)
* [`int`](kcl/int)
* [`lastSegX`](kcl/lastSegX)
* [`lastSegY`](kcl/lastSegY)
@ -55,8 +58,10 @@ layout: manual
* [`log`](kcl/log)
* [`log10`](kcl/log10)
* [`log2`](kcl/log2)
* [`m`](kcl/m)
* [`max`](kcl/max)
* [`min`](kcl/min)
* [`mm`](kcl/mm)
* [`patternCircular2d`](kcl/patternCircular2d)
* [`patternCircular3d`](kcl/patternCircular3d)
* [`patternLinear2d`](kcl/patternLinear2d)
@ -89,3 +94,4 @@ layout: manual
* [`xLineTo`](kcl/xLineTo)
* [`yLine`](kcl/yLine)
* [`yLineTo`](kcl/yLineTo)
* [`yd`](kcl/yd)

File diff suppressed because one or more lines are too long

38
docs/kcl/m.md Normal file

File diff suppressed because one or more lines are too long

38
docs/kcl/mm.md Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -55356,7 +55356,7 @@
"unpublished": false,
"deprecated": false,
"examples": [
"let n = 1.0285\nlet m = 1.0286\nassertEqual(n, m, 0.01, \"n is within the given tolerance for m\")"
"let n = 1.0285\nlet o = 1.0286\nassertEqual(n, o, 0.01, \"n is within the given tolerance for o\")"
]
},
{
@ -82255,6 +82255,29 @@
"const exampleSketch = startSketchOn('-XZ')\n |> startProfileAt([0, 0], %)\n |> line([10, 0], %)\n |> line([0, 10], %)\n |> close(%)\n\nconst example = extrude(10, exampleSketch)"
]
},
{
"name": "cm",
"summary": "Centimeters conversion factor for current projects units.",
"description": "No matter what units the current project uses, this function will always return the conversion factor to centimeters.\nFor example, if the current project uses inches, this function will return `0.393701`. If the current project uses millimeters, this function will return `10`. If the current project uses centimeters, this function will return `1`.\n**Caution**: This function is only intended to be used when you absolutely MUST have different units in your code than the project settings. Otherwise, it is a bad pattern to use this function.\nWe merely provide these functions for convenience and readability, as `10 * cm()` is more readable that your intent is \"I want 10 centimeters\" than `10 * 10`, if the project settings are in millimeters.",
"tags": [
"units"
],
"args": [],
"returnValue": {
"name": "",
"type": "number",
"schema": {
"type": "number",
"format": "double"
},
"required": true
},
"unpublished": false,
"deprecated": false,
"examples": [
"const totalWidth = 10 * cm()"
]
},
{
"name": "cos",
"summary": "Compute the cosine of a number (in radians).",
@ -98138,6 +98161,29 @@
"const sketch001 = startSketchOn('XZ')\n |> startProfileAt([0, 0], %)\n |> lineTo([12, 10], %)\n |> line([floor(7.02986), 0], %)\n |> yLineTo(0, %)\n |> close(%)\n\nconst extrude001 = extrude(5, sketch001)"
]
},
{
"name": "ft",
"summary": "Feet conversion factor for current projects units.",
"description": "No matter what units the current project uses, this function will always return the conversion factor to feet.\nFor example, if the current project uses inches, this function will return `12`. If the current project uses millimeters, this function will return `304.8`. If the current project uses feet, this function will return `1`.\n**Caution**: This function is only intended to be used when you absolutely MUST have different units in your code than the project settings. Otherwise, it is a bad pattern to use this function.\nWe merely provide these functions for convenience and readability, as `10 * ft()` is more readable that your intent is \"I want 10 feet\" than `10 * 304.8`, if the project settings are in millimeters.",
"tags": [
"units"
],
"args": [],
"returnValue": {
"name": "",
"type": "number",
"schema": {
"type": "number",
"format": "double"
},
"required": true
},
"unpublished": false,
"deprecated": false,
"examples": [
"const totalWidth = 10 * ft()"
]
},
{
"name": "getNextAdjacentEdge",
"summary": "Get the next adjacent edge to the edge given.",
@ -117499,6 +117545,29 @@
"const model = import(\"tests/inputs/cube.step\")"
]
},
{
"name": "inch",
"summary": "Inches conversion factor for current projects units.",
"description": "No matter what units the current project uses, this function will always return the conversion factor to inches.\nFor example, if the current project uses inches, this function will return `1`. If the current project uses millimeters, this function will return `25.4`.\n**Caution**: This function is only intended to be used when you absolutely MUST have different units in your code than the project settings. Otherwise, it is a bad pattern to use this function.\nWe merely provide these functions for convenience and readability, as `10 * inch()` is more readable that your intent is \"I want 10 inches\" than `10 * 25.4`, if the project settings are in millimeters.",
"tags": [
"units"
],
"args": [],
"returnValue": {
"name": "",
"type": "number",
"schema": {
"type": "number",
"format": "double"
},
"required": true
},
"unpublished": false,
"deprecated": false,
"examples": [
"const totalWidth = 10 * inch()"
]
},
{
"name": "int",
"summary": "Convert a number to an integer.",
@ -137886,6 +137955,29 @@
"const exampleSketch = startSketchOn(\"XZ\")\n |> startProfileAt([0, 0], %)\n |> line([log2(100), 0], %)\n |> line([5, 8], %)\n |> line([-10, 0], %)\n |> close(%)\n\nconst example = extrude(5, exampleSketch)"
]
},
{
"name": "m",
"summary": "Meters conversion factor for current projects units.",
"description": "No matter what units the current project uses, this function will always return the conversion factor to meters.\nFor example, if the current project uses inches, this function will return `39.3701`. If the current project uses millimeters, this function will return `1000`. If the current project uses meters, this function will return `1`.\n**Caution**: This function is only intended to be used when you absolutely MUST have different units in your code than the project settings. Otherwise, it is a bad pattern to use this function.\nWe merely provide these functions for convenience and readability, as `10 * m()` is more readable that your intent is \"I want 10 meters\" than `10 * 1000`, if the project settings are in millimeters.",
"tags": [
"units"
],
"args": [],
"returnValue": {
"name": "",
"type": "number",
"schema": {
"type": "number",
"format": "double"
},
"required": true
},
"unpublished": false,
"deprecated": false,
"examples": [
"const totalWidth = 10 * m()"
]
},
{
"name": "max",
"summary": "Compute the maximum of the given arguments.",
@ -137958,6 +138050,29 @@
"const exampleSketch = startSketchOn(\"XZ\")\n |> startProfileAt([0, 0], %)\n |> angledLine({\n angle: 70,\n length: min(15, 31, 4, 13, 22)\n }, %)\n |> line([20, 0], %)\n |> close(%)\n\nconst example = extrude(5, exampleSketch)"
]
},
{
"name": "mm",
"summary": "Millimeters conversion factor for current projects units.",
"description": "No matter what units the current project uses, this function will always return the conversion factor to millimeters.\nFor example, if the current project uses inches, this function will return `(1/25.4)`. If the current project uses millimeters, this function will return `1`.\n**Caution**: This function is only intended to be used when you absolutely MUST have different units in your code than the project settings. Otherwise, it is a bad pattern to use this function.\nWe merely provide these functions for convenience and readability, as `10 * mm()` is more readable that your intent is \"I want 10 millimeters\" than `10 * (1/25.4)`, if the project settings are in inches.",
"tags": [
"units"
],
"args": [],
"returnValue": {
"name": "",
"type": "number",
"schema": {
"type": "number",
"format": "double"
},
"required": true
},
"unpublished": false,
"deprecated": false,
"examples": [
"const totalWidth = 10 * mm()"
]
},
{
"name": "patternCircular2d",
"summary": "Repeat a 2-dimensional sketch some number of times along a partial or",
@ -252170,5 +252285,28 @@
"examples": [
"const exampleSketch = startSketchOn(\"XZ\")\n |> startProfileAt([0, 0], %)\n |> angledLine({ angle: 50, length: 45 }, %)\n |> yLineTo(0, %)\n |> close(%)\n\nconst example = extrude(5, exampleSketch)"
]
},
{
"name": "yd",
"summary": "Yards conversion factor for current projects units.",
"description": "No matter what units the current project uses, this function will always return the conversion factor to yards.\nFor example, if the current project uses inches, this function will return `36`. If the current project uses millimeters, this function will return `914.4`. If the current project uses yards, this function will return `1`.\n**Caution**: This function is only intended to be used when you absolutely MUST have different units in your code than the project settings. Otherwise, it is a bad pattern to use this function.\nWe merely provide these functions for convenience and readability, as `10 * yd()` is more readable that your intent is \"I want 10 yards\" than `10 * 914.4`, if the project settings are in millimeters.",
"tags": [
"units"
],
"args": [],
"returnValue": {
"name": "",
"type": "number",
"schema": {
"type": "number",
"format": "double"
},
"required": true
},
"unpublished": false,
"deprecated": false,
"examples": [
"const totalWidth = 10 * yd()"
]
}
]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

38
docs/kcl/yd.md Normal file

File diff suppressed because one or more lines are too long

View File

@ -12,50 +12,47 @@ test.afterEach(async ({ page }, testInfo) => {
})
test.describe('Command bar tests', () => {
// TODO fixme: enter is not working in the command bar
test.fixme(
'Extrude from command bar selects extrude line after',
async ({ page }) => {
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch001 = startSketchOn('XY')
test('Extrude from command bar selects extrude line after', async ({
page,
}) => {
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`const sketch001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> xLine(-20, %)
|> close(%)
`
)
})
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
// Click the line of code for xLine.
await page.getByText(`close(%)`).click() // TODO remove this and reinstate // await topHorzSegmentClick()
await page.waitForTimeout(100)
await page.getByRole('button', { name: 'Extrude' }).click()
await page.waitForTimeout(200)
await page.keyboard.press('Enter')
await page.waitForTimeout(200)
await page.keyboard.press('Enter')
await page.waitForTimeout(200)
await expect(page.locator('.cm-activeLine')).toHaveText(
`const extrude001 = extrude(${KCL_DEFAULT_LENGTH}, sketch001)`
)
}
)
})
// TODO fixme: enter is not working in the command bar
test.fixme('Fillet from command bar', async ({ page }) => {
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]')
await u.closeDebugPanel()
// Click the line of code for xLine.
await page.getByText(`close(%)`).click() // TODO remove this and reinstate // await topHorzSegmentClick()
await page.waitForTimeout(100)
await page.getByRole('button', { name: 'Extrude' }).click()
await page.waitForTimeout(200)
await page.keyboard.press('Enter')
await page.waitForTimeout(200)
await page.keyboard.press('Enter')
await page.waitForTimeout(200)
await expect(page.locator('.cm-activeLine')).toHaveText(
`const extrude001 = extrude(${KCL_DEFAULT_LENGTH}, sketch001)`
)
})
test('Fillet from command bar', async ({ page }) => {
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -6,6 +6,9 @@ import { MakerRpm } from '@electron-forge/maker-rpm'
import { VitePlugin } from '@electron-forge/plugin-vite'
import { FusesPlugin } from '@electron-forge/plugin-fuses'
import { FuseV1Options, FuseVersion } from '@electron/fuses'
import path from 'path'
const rootDir = process.cwd()
const config: ForgeConfig = {
packagerConfig: {
@ -19,12 +22,14 @@ const config: ForgeConfig = {
}) ||
undefined,
executableName: 'zoo-modeling-app',
icon: path.resolve(rootDir, 'assets', 'icon'),
},
rebuildConfig: {},
makers: [
new MakerSquirrel((arch) => ({
// TODO: reactive once we have releases live
// remoteReleases: `https://${process.env.WEBSITE_DIR}/win32/${arch}`,
setupIcon: path.resolve(rootDir, 'assets', 'icon.ico'),
})),
new MakerZIP(
(arch) => ({
@ -32,8 +37,16 @@ const config: ForgeConfig = {
}),
['darwin']
),
new MakerRpm({}),
new MakerDeb({}),
new MakerRpm({
options: {
icon: path.resolve(rootDir, 'assets', 'icon.png'),
},
}),
new MakerDeb({
options: {
icon: path.resolve(rootDir, 'assets', 'icon.png'),
},
}),
],
publishers: [
{

View File

@ -50,6 +50,7 @@ import {
ExtrudeGroup,
VariableDeclaration,
VariableDeclarator,
sketchGroupFromKclValue,
} from 'lang/wasm'
import {
engineCommandManager,
@ -588,10 +589,12 @@ export class SceneEntities {
const variableDeclarationName =
_node1.node?.declarations?.[0]?.id?.name || ''
const sg = kclManager.programMemory.get(
const sg = sketchGroupFromKclValue(
kclManager.programMemory.get(variableDeclarationName),
variableDeclarationName
) as SketchGroup
const lastSeg = sg?.value?.slice(-1)[0] || sg.start
)
if (err(sg)) return sg
const lastSeg = sg.value?.slice(-1)[0] || sg.start
const index = sg.value.length // because we've added a new segment that's not in the memory yet, no need for `-1`
@ -786,9 +789,11 @@ export class SceneEntities {
programMemoryOverride,
})
this.sceneProgramMemory = programMemory
const sketchGroup = programMemory.get(
const sketchGroup = sketchGroupFromKclValue(
programMemory.get(variableDeclarationName),
variableDeclarationName
) as SketchGroup
)
if (err(sketchGroup)) return sketchGroup
const sgPaths = sketchGroup.value
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
@ -840,9 +845,11 @@ export class SceneEntities {
// Prepare to update the THREEjs scene
this.sceneProgramMemory = programMemory
const sketchGroup = programMemory.get(
const sketchGroup = sketchGroupFromKclValue(
programMemory.get(variableDeclarationName),
variableDeclarationName
) as SketchGroup
)
if (err(sketchGroup)) return sketchGroup
const sgPaths = sketchGroup.value
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
@ -1111,8 +1118,12 @@ export class SceneEntities {
const maybeSketchGroup = programMemory.get(variableDeclarationName)
let sketchGroup = undefined
if (maybeSketchGroup?.type === 'SketchGroup') {
sketchGroup = maybeSketchGroup
const sg = sketchGroupFromKclValue(
maybeSketchGroup,
variableDeclarationName
)
if (!err(sg)) {
sketchGroup = sg
} else if ((maybeSketchGroup as ExtrudeGroup).sketchGroup) {
sketchGroup = (maybeSketchGroup as ExtrudeGroup).sketchGroup
}
@ -1656,9 +1667,12 @@ function prepareTruncatedMemoryAndAst(
)
if (err(_node)) return _node
const variableDeclarationName = _node.node?.declarations?.[0]?.id?.name || ''
const lastSeg = (
programMemory.get(variableDeclarationName) as SketchGroup
).value.slice(-1)[0]
const sg = sketchGroupFromKclValue(
programMemory.get(variableDeclarationName),
variableDeclarationName
)
if (err(sg)) return sg
const lastSeg = sg?.value.slice(-1)[0]
if (draftSegment) {
// truncatedAst needs to setup with another segment at the end
let newSegment
@ -1782,10 +1796,11 @@ export function sketchGroupFromPathToNode({
if (result?.type === 'ExtrudeGroup') {
return result.sketchGroup
}
if (result?.type === 'SketchGroup') {
return result
const sg = sketchGroupFromKclValue(result, varDec?.id?.name)
if (err(sg)) {
return null
}
return null
return sg
}
function colorSegment(object: any, color: number) {

View File

@ -1,6 +1,5 @@
import { Completion } from '@codemirror/autocomplete'
import { EditorView, ViewUpdate } from '@codemirror/view'
import { EditorState } from '@codemirror/state'
import { CustomIcon } from 'components/CustomIcon'
import { useCommandsContext } from 'hooks/useCommandsContext'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
@ -84,17 +83,13 @@ function CommandBarKclInput({
if (event.key === 'Backspace' && value === '') {
event.preventDefault()
stepBack()
} else if (event.key === 'Enter') {
event.preventDefault()
handleSubmit()
}
},
}),
varMentions(varMentionData),
EditorState.transactionFilter.of((tr) => {
if (tr.newDoc.lines > 1) {
handleSubmit()
return []
}
return tr
}),
EditorView.updateListener.of((vu: ViewUpdate) => {
if (vu.docChanged) {
setValue(vu.state.doc.toString())

View File

@ -1,11 +1,16 @@
import toast from 'react-hot-toast'
import ReactJson from 'react-json-view'
import { useMemo } from 'react'
import { ProgramMemory, Path, ExtrudeSurface } from 'lang/wasm'
import {
ProgramMemory,
Path,
ExtrudeSurface,
sketchGroupFromKclValue,
} from 'lang/wasm'
import { useKclContext } from 'lang/KclProvider'
import { useResolvedTheme } from 'hooks/useResolvedTheme'
import { ActionButton } from 'components/ActionButton'
import { trap } from 'lib/trap'
import { err, trap } from 'lib/trap'
import Tooltip from 'components/Tooltip'
import { useModelingContext } from 'hooks/useModelingContext'
@ -84,8 +89,9 @@ export const processMemory = (programMemory: ProgramMemory) => {
const processedMemory: any = {}
for (const [key, val] of programMemory?.visibleEntries()) {
if (typeof val.value !== 'function') {
if (val.type === 'SketchGroup') {
processedMemory[key] = val.value.map(({ __geoMeta, ...rest }: Path) => {
const sg = sketchGroupFromKclValue(val, null)
if (!err(sg)) {
processedMemory[key] = sg.value.map(({ __geoMeta, ...rest }: Path) => {
return rest
})
} else if (val.type === 'ExtrudeGroup') {

View File

@ -18,41 +18,45 @@ const mySketch001 = startSketchOn('XY')
// @ts-ignore
const sketch001 = programMemory?.get('mySketch001')
expect(sketch001).toEqual({
type: 'SketchGroup',
on: expect.any(Object),
start: {
to: [0, 0],
from: [0, 0],
tag: null,
__geoMeta: {
id: expect.any(String),
sourceRange: [46, 71],
},
},
value: [
{
type: 'ToPoint',
tag: null,
to: [-1.59, -1.54],
from: [0, 0],
__geoMeta: {
sourceRange: [77, 102],
id: expect.any(String),
},
},
{
type: 'ToPoint',
to: [0.46, -5.82],
from: [-1.59, -1.54],
tag: null,
__geoMeta: {
sourceRange: [108, 132],
id: expect.any(String),
},
},
],
id: expect.any(String),
type: 'UserVal',
__meta: [{ sourceRange: [46, 71] }],
value: {
type: 'SketchGroup',
on: expect.any(Object),
start: {
to: [0, 0],
from: [0, 0],
tag: null,
__geoMeta: {
id: expect.any(String),
sourceRange: [46, 71],
},
},
value: [
{
type: 'ToPoint',
tag: null,
to: [-1.59, -1.54],
from: [0, 0],
__geoMeta: {
sourceRange: [77, 102],
id: expect.any(String),
},
},
{
type: 'ToPoint',
to: [0.46, -5.82],
from: [-1.59, -1.54],
tag: null,
__geoMeta: {
sourceRange: [108, 132],
id: expect.any(String),
},
},
],
id: expect.any(String),
__meta: [{ sourceRange: [46, 71] }],
},
})
})
test('extrude artifacts', async () => {

View File

@ -1,6 +1,12 @@
import fs from 'node:fs'
import { parse, ProgramMemory, SketchGroup, initPromise } from './wasm'
import {
parse,
ProgramMemory,
SketchGroup,
initPromise,
sketchGroupFromKclValue,
} from './wasm'
import { enginelessExecutor } from '../lib/testHelpers'
import { KCLError } from './errors'
@ -52,7 +58,7 @@ const newVar = myVar + 1`
`
const mem = await exe(code)
// geo is three js buffer geometry and is very bloated to have in tests
const minusGeo = mem.get('mySketch')?.value
const minusGeo = mem.get('mySketch')?.value?.value
expect(minusGeo).toEqual([
{
type: 'ToPoint',
@ -146,68 +152,72 @@ const newVar = myVar + 1`
].join('\n')
const mem = await exe(code)
expect(mem.get('mySk1')).toEqual({
type: 'SketchGroup',
on: expect.any(Object),
start: {
to: [0, 0],
from: [0, 0],
tag: null,
__geoMeta: {
id: expect.any(String),
sourceRange: [39, 63],
},
},
tags: {
myPath: {
__meta: [
{
sourceRange: [109, 116],
},
],
type: 'TagIdentifier',
value: 'myPath',
info: expect.any(Object),
},
},
value: [
{
type: 'ToPoint',
to: [1, 1],
type: 'UserVal',
value: {
type: 'SketchGroup',
on: expect.any(Object),
start: {
to: [0, 0],
from: [0, 0],
tag: null,
__geoMeta: {
sourceRange: [69, 85],
id: expect.any(String),
sourceRange: [39, 63],
},
},
{
type: 'ToPoint',
to: [0, 1],
from: [1, 1],
__geoMeta: {
sourceRange: [91, 117],
id: expect.any(String),
},
tag: {
end: 116,
start: 109,
type: 'TagDeclarator',
tags: {
myPath: {
__meta: [
{
sourceRange: [109, 116],
},
],
type: 'TagIdentifier',
value: 'myPath',
digest: null,
info: expect.any(Object),
},
},
{
type: 'ToPoint',
to: [1, 1],
from: [0, 1],
tag: null,
__geoMeta: {
sourceRange: [123, 139],
id: expect.any(String),
value: [
{
type: 'ToPoint',
to: [1, 1],
from: [0, 0],
tag: null,
__geoMeta: {
sourceRange: [69, 85],
id: expect.any(String),
},
},
},
],
id: expect.any(String),
{
type: 'ToPoint',
to: [0, 1],
from: [1, 1],
__geoMeta: {
sourceRange: [91, 117],
id: expect.any(String),
},
tag: {
end: 116,
start: 109,
type: 'TagDeclarator',
value: 'myPath',
digest: null,
},
},
{
type: 'ToPoint',
to: [1, 1],
from: [0, 1],
tag: null,
__geoMeta: {
sourceRange: [123, 139],
id: expect.any(String),
},
},
],
id: expect.any(String),
__meta: [{ sourceRange: [39, 63] }],
},
__meta: [{ sourceRange: [39, 63] }],
})
})
@ -358,7 +368,7 @@ describe('testing math operators', () => {
'|> line([-2.21, -legLen(5, min(3, 999))], %)',
].join('\n')
const mem = await exe(code)
const sketch = mem.get('part001')
const sketch = sketchGroupFromKclValue(mem.get('part001'), 'part001')
// result of `-legLen(5, min(3, 999))` should be -4
const yVal = (sketch as SketchGroup).value?.[0]?.to?.[1]
expect(yVal).toBe(-4)
@ -376,7 +386,7 @@ describe('testing math operators', () => {
``,
].join('\n')
const mem = await exe(code)
const sketch = mem.get('part001')
const sketch = sketchGroupFromKclValue(mem.get('part001'), 'part001')
// expect -legLen(segLen('seg01'), myVar) to equal -4 setting the y value back to 0
expect((sketch as SketchGroup).value?.[1]?.from).toEqual([3, 4])
expect((sketch as SketchGroup).value?.[1]?.to).toEqual([6, 0])
@ -385,7 +395,10 @@ describe('testing math operators', () => {
`legLen(segLen(seg01), myVar)`
)
const removedUnaryExpMem = await exe(removedUnaryExp)
const removedUnaryExpMemSketch = removedUnaryExpMem.get('part001')
const removedUnaryExpMemSketch = sketchGroupFromKclValue(
removedUnaryExpMem.get('part001'),
'part001'
)
// without the minus sign, the y value should be 8
expect((removedUnaryExpMemSketch as SketchGroup).value?.[1]?.to).toEqual([

View File

@ -17,7 +17,7 @@ import {
PathToNode,
ProgramMemory,
SourceRange,
SketchGroup,
sketchGroupFromKclValue,
} from './wasm'
import {
isNodeSafeToReplacePath,
@ -563,6 +563,7 @@ export function createArrayExpression(
start: 0,
end: 0,
digest: null,
nonCodeMeta: { nonCodeNodes: {}, start: [], digest: null },
elements,
}
}
@ -980,7 +981,11 @@ export async function deleteFromSelection(
if (err(parent)) {
return
}
const sketchToPreserve = programMemory.get(sketchName) as SketchGroup
const sketchToPreserve = sketchGroupFromKclValue(
programMemory.get(sketchName),
sketchName
)
if (err(sketchToPreserve)) return sketchToPreserve
console.log('sketchName', sketchName)
// Can't kick off multiple requests at once as getFaceDetails
// is three engine calls in one and they conflict

View File

@ -10,12 +10,12 @@ import {
Program,
ProgramMemory,
ReturnStatement,
SketchGroup,
SourceRange,
SyntaxType,
Expr,
VariableDeclaration,
VariableDeclarator,
sketchGroupFromKclValue,
} from './wasm'
import { createIdentifier, splitPathAtLastIndex } from './modifyAst'
import { getSketchSegmentFromSourceRange } from './std/sketchConstraints'
@ -661,15 +661,16 @@ export function isLinesParallelAndConstrained(
if (err(_varDec)) return _varDec
const varDec = _varDec.node
const varName = (varDec as VariableDeclaration)?.declarations[0]?.id?.name
const path = programMemory?.get(varName) as SketchGroup
const sg = sketchGroupFromKclValue(programMemory?.get(varName), varName)
if (err(sg)) return sg
const _primarySegment = getSketchSegmentFromSourceRange(
path,
sg,
primaryLine.range
)
if (err(_primarySegment)) return _primarySegment
const primarySegment = _primarySegment.segment
const _segment = getSketchSegmentFromSourceRange(path, secondaryLine.range)
const _segment = getSketchSegmentFromSourceRange(sg, secondaryLine.range)
if (err(_segment)) return _segment
const { segment: secondarySegment, index: secondaryIndex } = _segment
const primaryAngle = getAngle(primarySegment.from, primarySegment.to)
@ -708,9 +709,7 @@ export function isLinesParallelAndConstrained(
constraintType === 'angle' || constraintLevel === 'full'
// get the previous segment
const prevSegment = (programMemory.get(varName) as SketchGroup).value[
secondaryIndex - 1
]
const prevSegment = sg.value[secondaryIndex - 1]
const prevSourceRange = prevSegment.__geoMeta.sourceRange
const isParallelAndConstrained =
@ -779,7 +778,10 @@ export function hasExtrudeSketchGroup({
if (varDec.type !== 'VariableDeclaration') return false
const varName = varDec.declarations[0].id.name
const varValue = programMemory?.get(varName)
return varValue?.type === 'ExtrudeGroup' || varValue?.type === 'SketchGroup'
return (
varValue?.type === 'ExtrudeGroup' ||
!err(sketchGroupFromKclValue(varValue, varName))
)
}
export function isSingleCursorInPipe(

View File

@ -12,6 +12,7 @@ import {
Literal,
VariableDeclaration,
Identifier,
sketchGroupFromKclValue,
} from 'lang/wasm'
import {
getNodeFromPath,
@ -1009,9 +1010,12 @@ export const angledLineOfXLength: SketchLineHelper = {
const { node: varDec } = nodeMeta2
const variableName = varDec.id.name
const sketch = previousProgramMemory?.get(variableName)
if (!sketch || sketch.type !== 'SketchGroup') {
return new Error('not a SketchGroup')
const sketch = sketchGroupFromKclValue(
previousProgramMemory?.get(variableName),
variableName
)
if (err(sketch)) {
return sketch
}
const angle = createLiteral(roundOff(getAngle(from, to), 0))
const xLength = createLiteral(roundOff(Math.abs(from[0] - to[0]), 2) || 0.1)
@ -1105,10 +1109,11 @@ export const angledLineOfYLength: SketchLineHelper = {
if (err(nodeMeta2)) return nodeMeta2
const { node: varDec } = nodeMeta2
const variableName = varDec.id.name
const sketch = previousProgramMemory?.get(variableName)
if (!sketch || sketch.type !== 'SketchGroup') {
return new Error('not a SketchGroup')
}
const sketch = sketchGroupFromKclValue(
previousProgramMemory?.get(variableName),
variableName
)
if (err(sketch)) return sketch
const angle = createLiteral(roundOff(getAngle(from, to), 0))
const yLength = createLiteral(roundOff(Math.abs(from[1] - to[1]), 2) || 0.1)
@ -1443,7 +1448,11 @@ export const angledLineThatIntersects: SketchLineHelper = {
const { node: varDec } = nodeMeta2
const varName = varDec.declarations[0].id.name
const sketchGroup = previousProgramMemory.get(varName) as SketchGroup
const sketchGroup = sketchGroupFromKclValue(
previousProgramMemory.get(varName),
varName
)
if (err(sketchGroup)) return sketchGroup
const intersectPath = sketchGroup.value.find(
({ tag }: Path) => tag && tag.value === intersectTagName
)

View File

@ -1,4 +1,10 @@
import { parse, SketchGroup, recast, initPromise } from '../wasm'
import {
parse,
SketchGroup,
recast,
initPromise,
sketchGroupFromKclValue,
} from '../wasm'
import {
ConstraintType,
getTransformInfos,
@ -362,10 +368,11 @@ const part001 = startSketchOn('XY')
it('normal case works', async () => {
const programMemory = await enginelessExecutor(parse(code))
const index = code.indexOf('// normal-segment') - 7
const _segment = getSketchSegmentFromSourceRange(
programMemory.get('part001') as SketchGroup,
[index, index]
)
const sg = sketchGroupFromKclValue(
programMemory.get('part001'),
'part001'
) as SketchGroup
const _segment = getSketchSegmentFromSourceRange(sg, [index, index])
if (err(_segment)) throw _segment
const { __geoMeta, ...segment } = _segment.segment
expect(segment).toEqual({
@ -379,7 +386,10 @@ const part001 = startSketchOn('XY')
const programMemory = await enginelessExecutor(parse(code))
const index = code.indexOf('// segment-in-start') - 7
const _segment = getSketchSegmentFromSourceRange(
programMemory.get('part001') as SketchGroup,
sketchGroupFromKclValue(
programMemory.get('part001'),
'part001'
) as SketchGroup,
[index, index]
)
if (err(_segment)) throw _segment

View File

@ -10,6 +10,7 @@ import {
VariableDeclarator,
PathToNode,
ProgramMemory,
sketchGroupFromKclValue,
} from '../wasm'
import {
getNodeFromPath,
@ -1636,12 +1637,16 @@ export function transformAstSketchLines({
})
const varName = varDec.node.id.name
let sketchGroup = programMemory.get(varName)
if (sketchGroup?.type === 'ExtrudeGroup') {
sketchGroup = sketchGroup.sketchGroup
let kclVal = programMemory.get(varName)
let sketchGroup
if (kclVal?.type === 'ExtrudeGroup') {
sketchGroup = kclVal.sketchGroup
} else {
sketchGroup = sketchGroupFromKclValue(kclVal, varName)
if (err(sketchGroup)) {
return
}
}
if (!sketchGroup || sketchGroup.type !== 'SketchGroup')
return new Error('not a sketch group')
const segMeta = getSketchSegmentFromPathToNode(
sketchGroup,
ast,

View File

@ -38,6 +38,7 @@ import { err } from 'lib/trap'
import { Configuration } from 'wasm-lib/kcl/bindings/Configuration'
import { DeepPartial } from 'lib/types'
import { ProjectConfiguration } from 'wasm-lib/kcl/bindings/ProjectConfiguration'
import { SketchGroup } from '../wasm-lib/kcl/bindings/SketchGroup'
export type { Program } from '../wasm-lib/kcl/bindings/Program'
export type { Expr } from '../wasm-lib/kcl/bindings/Expr'
@ -126,6 +127,7 @@ export const parse = (code: string | Error): Program | Error => {
const program: Program = parse_wasm(code)
return program
} catch (e: any) {
// throw e
const parsed: RustKclError = JSON.parse(e.toString())
return new KCLError(
parsed.kind,
@ -312,7 +314,7 @@ export class ProgramMemory {
*/
hasSketchOrExtrudeGroup(): boolean {
for (const node of this.visibleEntries().values()) {
if (node.type === 'ExtrudeGroup' || node.type === 'SketchGroup') {
if (node.type === 'ExtrudeGroup' || node.value?.type === 'SketchGroup') {
return true
}
}
@ -332,6 +334,25 @@ export class ProgramMemory {
}
}
// TODO: In the future, make the parameter be a KclValue.
export function sketchGroupFromKclValue(
obj: any,
varName: string | null
): SketchGroup | Error {
if (obj?.value?.type === 'SketchGroup') return obj.value
if (!varName) {
varName = 'a KCL value'
}
const actualType = obj?.value?.type ?? obj?.type
if (actualType) {
return new Error(
`Expected ${varName} to be a sketchGroup, but it was ${actualType} instead.`
)
} else {
return new Error(`Expected ${varName} to be a sketchGroup, but it wasn't.`)
}
}
export const executor = async (
node: Program,
programMemory: ProgramMemory | Error = ProgramMemory.empty(),

View File

@ -4,6 +4,7 @@ import {
VariableDeclarator,
parse,
recast,
sketchGroupFromKclValue,
} from 'lang/wasm'
import { Axis, Selection, Selections, updateSelections } from 'lib/selections'
import { assign, createMachine } from 'xstate'
@ -1187,8 +1188,11 @@ export const modelingMachine = createMachine(
)
if (err(varDecNode)) return
const sketchVar = varDecNode.node.declarations[0].id.name
const sketchGroup = kclManager.programMemory.get(sketchVar)
if (sketchGroup?.type !== 'SketchGroup') return
const sketchGroup = sketchGroupFromKclValue(
kclManager.programMemory.get(sketchVar),
sketchVar
)
if (trap(sketchGroup)) return
const extrusion = getExtrusionFromSuspectedPath(
sketchGroup.id,
engineCommandManager.artifactGraph

View File

@ -35,6 +35,7 @@ const createWindow = () => {
sandbox: false, // expose nodejs in preload
preload: path.join(__dirname, './preload.js'),
},
icon: path.resolve(process.cwd(), 'assets', 'icon.png'),
})
// and load the index.html of the app.

View File

@ -1424,6 +1424,7 @@ dependencies = [
"js-sys",
"kittycad",
"lazy_static",
"measurements",
"mime_guess",
"parse-display",
"pretty_assertions",
@ -1595,6 +1596,15 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
[[package]]
name = "measurements"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5b734b4e8187ea5777bc29c086f0970a27d8de42061b48f5af32cafc0ca904b"
dependencies = [
"libm",
]
[[package]]
name = "memchr"
version = "2.7.1"
@ -1859,7 +1869,7 @@ dependencies = [
"bincode",
"either",
"fnv",
"itertools 0.12.1",
"itertools 0.10.5",
"lazy_static",
"nom",
"quick-xml",

View File

@ -28,6 +28,7 @@ gltf-json = "1.4.1"
image = { version = "0.25.1", default-features = false, features = ["png"] }
kittycad = { workspace = true, features = ["clap"] }
lazy_static = "1.5.0"
measurements = "0.11.0"
mime_guess = "2.0.5"
parse-display = "0.9.1"
pyo3 = { version = "0.22.2", optional = true }

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,7 @@ use crate::{
docs::StdLibFn,
errors::{KclError, KclErrorDetails},
executor::{
BodyType, DynamicState, ExecutorContext, KclValue, Metadata, PipeInfo, ProgramMemory, SourceRange,
BodyType, DynamicState, ExecutorContext, KclValue, Metadata, PipeInfo, ProgramMemory, SketchGroup, SourceRange,
StatementKind, TagEngineInfo, TagIdentifier, UserVal,
},
parser::PIPE_OPERATOR,
@ -1149,6 +1149,15 @@ pub enum NonCodeValue {
NewLine,
}
impl NonCodeValue {
fn should_cause_array_newline(&self) -> bool {
match self {
Self::InlineComment { .. } => false,
Self::Shebang { .. } | Self::BlockComment { .. } | Self::NewLineBlockComment { .. } | Self::NewLine => true,
}
}
}
#[derive(Debug, Default, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
#[databake(path = kcl_lib::ast::types)]
#[ts(export)]
@ -1160,6 +1169,18 @@ pub struct NonCodeMeta {
pub digest: Option<Digest>,
}
impl NonCodeMeta {
/// Does this contain anything?
pub fn is_empty(&self) -> bool {
self.non_code_nodes.is_empty() && self.start.is_empty()
}
/// How many non-code values does this have?
pub fn non_code_nodes_len(&self) -> usize {
self.non_code_nodes.values().map(|x| x.len()).sum()
}
}
// implement Deserialize manually because we to force the keys of non_code_nodes to be usize
// and by default the ts type { [statementIndex: number]: NonCodeNode } serializes to a string i.e. "0", "1", etc.
impl<'de> Deserialize<'de> for NonCodeMeta {
@ -1356,10 +1377,13 @@ impl CallExpression {
// TODO: This could probably be done in a better way, but as of now this was my only idea
// and it works.
match result {
KclValue::SketchGroup(ref sketch_group) => {
for (_, tag) in sketch_group.tags.iter() {
memory.update_tag(&tag.value, tag.clone())?;
}
KclValue::UserVal(ref mut uval) => {
uval.mutate(|sketch_group: &mut SketchGroup| {
for (_, tag) in sketch_group.tags.iter() {
memory.update_tag(&tag.value, tag.clone())?;
}
Ok::<_, KclError>(())
})?;
}
KclValue::ExtrudeGroup(ref mut extrude_group) => {
for value in &extrude_group.value {
@ -2224,11 +2248,13 @@ impl From<PipeSubstitution> for Expr {
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
#[databake(path = kcl_lib::ast::types)]
#[ts(export)]
#[serde(tag = "type")]
#[serde(rename_all = "camelCase", tag = "type")]
pub struct ArrayExpression {
pub start: usize,
pub end: usize,
pub elements: Vec<Expr>,
#[serde(default, skip_serializing_if = "NonCodeMeta::is_empty")]
pub non_code_meta: NonCodeMeta,
pub digest: Option<Digest>,
}
@ -2247,6 +2273,7 @@ impl ArrayExpression {
start: 0,
end: 0,
elements,
non_code_meta: Default::default(),
digest: None,
}
}
@ -2280,38 +2307,70 @@ impl ArrayExpression {
}
fn recast(&self, options: &FormatOptions, indentation_level: usize, is_in_pipe: bool) -> String {
let flat_recast = format!(
"[{}]",
self.elements
.iter()
.map(|el| el.recast(options, 0, false))
.collect::<Vec<String>>()
.join(", ")
);
let max_array_length = 40;
if flat_recast.len() > max_array_length {
let inner_indentation = if is_in_pipe {
options.get_indentation_offset_pipe(indentation_level + 1)
} else {
options.get_indentation(indentation_level + 1)
};
format!(
"[\n{}{}\n{}]",
inner_indentation,
self.elements
.iter()
.map(|el| el.recast(options, indentation_level, is_in_pipe))
.collect::<Vec<String>>()
.join(format!(",\n{}", inner_indentation).as_str()),
if is_in_pipe {
options.get_indentation_offset_pipe(indentation_level)
// Reconstruct the order of items in the array.
// An item can be an element (i.e. an expression for a KCL value),
// or a non-code item (e.g. a comment)
let num_items = self.elements.len() + self.non_code_meta.non_code_nodes_len();
let mut elems = self.elements.iter();
let mut found_line_comment = false;
let mut format_items: Vec<_> = (0..num_items)
.flat_map(|i| {
if let Some(noncode) = self.non_code_meta.non_code_nodes.get(&i) {
noncode
.iter()
.map(|nc| {
found_line_comment |= nc.value.should_cause_array_newline();
nc.format("")
})
.collect::<Vec<_>>()
} else {
options.get_indentation(indentation_level)
},
)
} else {
flat_recast
let el = elems.next().unwrap();
let s = format!("{}, ", el.recast(options, 0, false));
vec![s]
}
})
.collect();
// Format these items into a one-line array.
if let Some(item) = format_items.last_mut() {
if let Some(norm) = item.strip_suffix(", ") {
*item = norm.to_owned();
}
}
let format_items = format_items; // Remove mutability
let flat_recast = format!("[{}]", format_items.join(""));
// We might keep the one-line representation, if it's short enough.
let max_array_length = 40;
let multi_line = flat_recast.len() > max_array_length || found_line_comment;
if !multi_line {
return flat_recast;
}
// Otherwise, we format a multi-line representation.
let inner_indentation = if is_in_pipe {
options.get_indentation_offset_pipe(indentation_level + 1)
} else {
options.get_indentation(indentation_level + 1)
};
let formatted_array_lines = format_items
.iter()
.map(|s| {
format!(
"{inner_indentation}{}{}",
if let Some(x) = s.strip_suffix(" ") { x } else { s },
if s.ends_with('\n') { "" } else { "\n" }
)
})
.collect::<Vec<String>>()
.join("")
.to_owned();
let end_indent = if is_in_pipe {
options.get_indentation_offset_pipe(indentation_level)
} else {
options.get_indentation(indentation_level)
};
format!("[\n{formatted_array_lines}{end_indent}]")
}
/// Returns a hover value that includes the given character position.
@ -5838,6 +5897,103 @@ const thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#;
}
}
#[test]
fn recast_array_with_comments() {
use winnow::Parser;
for (i, (input, expected, reason)) in [
(
"\
[
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
]",
"\
[
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20
]",
"preserves multi-line arrays",
),
(
"\
[
1,
// 2,
3
]",
"\
[
1,
// 2,
3
]",
"preserves comments",
),
(
"\
[
1,
2,
// 3
]",
"\
[
1,
2,
// 3
]",
"preserves comments at the end of the array",
),
]
.into_iter()
.enumerate()
{
let tokens = crate::token::lexer(input).unwrap();
let expr = crate::parser::parser_impl::array_elem_by_elem.parse(&tokens).unwrap();
assert_eq!(
expr.recast(&FormatOptions::new(), 0, false),
expected,
"failed test {i}, which is testing that recasting {reason}"
);
}
}
#[test]
fn required_params() {
for (i, (test_name, expected, function_expr)) in [

View File

@ -203,13 +203,22 @@ impl Environment {
}
for (_, val) in self.bindings.iter_mut() {
if let KclValue::SketchGroup(ref mut sketch_group) = val {
if sketch_group.original_id == sg.original_id {
for tag in sg.tags.iter() {
sketch_group.tags.insert(tag.0.clone(), tag.1.clone());
}
let KclValue::UserVal(v) = val else { continue };
let meta = v.meta.clone();
let maybe_sg: Result<SketchGroup, _> = serde_json::from_value(v.value.clone());
let Ok(mut sketch_group) = maybe_sg else {
continue;
};
if sketch_group.original_id == sg.original_id {
for tag in sg.tags.iter() {
sketch_group.tags.insert(tag.0.clone(), tag.1.clone());
}
}
*val = KclValue::UserVal(UserVal {
meta,
value: serde_json::to_value(sketch_group).expect("can always turn SketchGroup into JSON"),
});
}
}
}
@ -268,10 +277,7 @@ pub enum KclValue {
TagDeclarator(Box<TagDeclarator>),
Plane(Box<Plane>),
Face(Box<Face>),
SketchGroup(Box<SketchGroup>),
SketchGroups {
value: Vec<Box<SketchGroup>>,
},
ExtrudeGroup(Box<ExtrudeGroup>),
ExtrudeGroups {
value: Vec<Box<ExtrudeGroup>>,
@ -289,27 +295,8 @@ pub enum KclValue {
}
impl KclValue {
pub(crate) fn get_sketch_group_set(&self) -> Result<SketchGroupSet> {
match self {
KclValue::SketchGroup(s) => Ok(SketchGroupSet::SketchGroup(s.clone())),
KclValue::SketchGroups { value } => Ok(SketchGroupSet::SketchGroups(value.clone())),
KclValue::UserVal(value) => {
let value = value.value.clone();
match value {
JValue::Null | JValue::Bool(_) | JValue::Number(_) | JValue::String(_) => Err(anyhow::anyhow!(
"Failed to deserialize sketch group set from JSON {}",
human_friendly_type(&value)
)),
JValue::Array(_) => serde_json::from_value::<Vec<Box<SketchGroup>>>(value)
.map(SketchGroupSet::from)
.map_err(|e| anyhow::anyhow!("Failed to deserialize array of sketch groups from JSON: {}", e)),
JValue::Object(_) => serde_json::from_value::<Box<SketchGroup>>(value)
.map(SketchGroupSet::from)
.map_err(|e| anyhow::anyhow!("Failed to deserialize sketch group from JSON: {}", e)),
}
}
_ => anyhow::bail!("Not a sketch group or sketch groups: {:?}", self),
}
pub(crate) fn new_user_val<T: Serialize>(meta: Vec<Metadata>, val: T) -> Self {
Self::UserVal(UserVal::set(meta, val))
}
pub(crate) fn get_extrude_group_set(&self) -> Result<ExtrudeGroupSet> {
@ -342,8 +329,6 @@ impl KclValue {
KclValue::UserVal(u) => human_friendly_type(&u.value),
KclValue::TagDeclarator(_) => "TagDeclarator",
KclValue::TagIdentifier(_) => "TagIdentifier",
KclValue::SketchGroup(_) => "SketchGroup",
KclValue::SketchGroups { .. } => "SketchGroups",
KclValue::ExtrudeGroup(_) => "ExtrudeGroup",
KclValue::ExtrudeGroups { .. } => "ExtrudeGroups",
KclValue::ImportedGeometry(_) => "ImportedGeometry",
@ -356,20 +341,14 @@ impl KclValue {
impl From<SketchGroupSet> for KclValue {
fn from(sg: SketchGroupSet) -> Self {
match sg {
SketchGroupSet::SketchGroup(sg) => KclValue::SketchGroup(sg),
SketchGroupSet::SketchGroups(sgs) => KclValue::SketchGroups { value: sgs },
}
KclValue::UserVal(UserVal::set(sg.meta(), sg))
}
}
impl From<Vec<Box<SketchGroup>>> for KclValue {
fn from(sg: Vec<Box<SketchGroup>>) -> Self {
if sg.len() == 1 {
KclValue::SketchGroup(sg[0].clone())
} else {
KclValue::SketchGroups { value: sg }
}
let meta = sg.iter().flat_map(|sg| sg.meta.clone()).collect();
KclValue::UserVal(UserVal::set(meta, sg))
}
}
@ -428,6 +407,24 @@ pub enum SketchGroupSet {
SketchGroups(Vec<Box<SketchGroup>>),
}
impl SketchGroupSet {
pub fn meta(&self) -> Vec<Metadata> {
match self {
SketchGroupSet::SketchGroup(sg) => sg.meta.clone(),
SketchGroupSet::SketchGroups(sg) => sg.iter().flat_map(|sg| sg.meta.clone()).collect(),
}
}
}
impl From<SketchGroupSet> for Vec<SketchGroup> {
fn from(value: SketchGroupSet) -> Self {
match value {
SketchGroupSet::SketchGroup(sg) => vec![*sg],
SketchGroupSet::SketchGroups(sgs) => sgs.into_iter().map(|sg| *sg).collect(),
}
}
}
impl From<SketchGroup> for SketchGroupSet {
fn from(sg: SketchGroup) -> Self {
SketchGroupSet::SketchGroup(Box::new(sg))
@ -641,6 +638,43 @@ pub struct UserVal {
pub meta: Vec<Metadata>,
}
impl UserVal {
/// If the UserVal matches the type `T`, return it.
pub fn get<T: serde::de::DeserializeOwned>(&self) -> Option<(T, Vec<Metadata>)> {
let meta = self.meta.clone();
// TODO: This clone might cause performance problems, it'll happen a lot.
let res: Result<T, _> = serde_json::from_value(self.value.clone());
if let Ok(t) = res {
Some((t, meta))
} else {
None
}
}
/// If the UserVal matches the type `T`, then mutate it via the given closure.
/// If the closure returns Err, the mutation won't be applied.
pub fn mutate<T, F, E>(&mut self, mutate: F) -> Result<(), E>
where
T: serde::de::DeserializeOwned + Serialize,
F: FnOnce(&mut T) -> Result<(), E>,
{
let Some((mut val, meta)) = self.get::<T>() else {
return Ok(());
};
mutate(&mut val)?;
*self = Self::set(meta, val);
Ok(())
}
/// Put the given value into this UserVal.
pub fn set<T: serde::Serialize>(meta: Vec<Metadata>, val: T) -> Self {
Self {
meta,
value: serde_json::to_value(val).expect("all KCL values should be compatible with JSON"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(tag = "type", rename_all = "camelCase")]
@ -720,11 +754,6 @@ impl From<KclValue> for Vec<SourceRange> {
KclValue::UserVal(u) => u.meta.iter().map(|m| m.source_range).collect(),
KclValue::TagDeclarator(t) => t.into(),
KclValue::TagIdentifier(t) => t.meta.iter().map(|m| m.source_range).collect(),
KclValue::SketchGroup(s) => s.meta.iter().map(|m| m.source_range).collect(),
KclValue::SketchGroups { value } => value
.iter()
.flat_map(|sg| sg.meta.iter().map(|m| m.source_range))
.collect(),
KclValue::ExtrudeGroup(e) => e.meta.iter().map(|m| m.source_range).collect(),
KclValue::ExtrudeGroups { value } => value
.iter()
@ -1274,7 +1303,7 @@ impl Point2d {
}
}
#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, ts_rs::TS, JsonSchema)]
#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, ts_rs::TS, JsonSchema, Default)]
#[ts(export)]
pub struct Point3d {
pub x: f64,

View File

@ -1,4 +1,4 @@
use std::str::FromStr;
use std::{collections::HashMap, str::FromStr};
use winnow::{
combinator::{alt, delimited, opt, peek, preceded, repeat, separated, terminated},
@ -448,14 +448,91 @@ fn equals(i: TokenSlice) -> PResult<Token> {
.parse_next(i)
}
#[allow(clippy::large_enum_variant)]
pub enum NonCodeOr<T> {
NonCode(NonCodeNode),
Code(T),
}
/// Parse a KCL array of elements.
fn array(i: TokenSlice) -> PResult<ArrayExpression> {
alt((array_empty, array_elem_by_elem, array_end_start)).parse_next(i)
}
/// Match an empty array.
fn array_empty(i: TokenSlice) -> PResult<ArrayExpression> {
let start = open_bracket(i)?.start;
ignore_whitespace(i);
let elements = alt((integer_range, separated(0.., expression, comma_sep)))
.context(expected(
"array contents, either a numeric range (like 0..10) or a list of elements (like [1, 2, 3])",
))
let end = close_bracket(i)?.end;
Ok(ArrayExpression {
start,
end,
elements: Default::default(),
non_code_meta: Default::default(),
digest: None,
})
}
/// Match something that separates elements of an array.
fn array_separator(i: TokenSlice) -> PResult<()> {
alt((
// Normally you need a comma.
comma_sep,
// But, if the array is ending, no need for a comma.
peek(preceded(opt(whitespace), close_bracket)).void(),
))
.parse_next(i)
}
pub(crate) fn array_elem_by_elem(i: TokenSlice) -> PResult<ArrayExpression> {
let start = open_bracket(i)?.start;
ignore_whitespace(i);
let elements: Vec<_> = repeat(
0..,
alt((
terminated(expression.map(NonCodeOr::Code), array_separator),
terminated(non_code_node.map(NonCodeOr::NonCode), whitespace),
)),
)
.context(expected("array contents, a list of elements (like [1, 2, 3])"))
.parse_next(i)?;
ignore_whitespace(i);
let end = close_bracket(i)?.end;
// Sort the array's elements (i.e. expression nodes) from the noncode nodes.
let (elements, non_code_nodes): (Vec<_>, HashMap<usize, _>) = elements.into_iter().enumerate().fold(
(Vec::new(), HashMap::new()),
|(mut elements, mut non_code_nodes), (i, e)| {
match e {
NonCodeOr::NonCode(x) => {
non_code_nodes.insert(i, vec![x]);
}
NonCodeOr::Code(x) => {
elements.push(x);
}
}
(elements, non_code_nodes)
},
);
let non_code_meta = NonCodeMeta {
non_code_nodes,
start: Vec::new(),
digest: None,
};
Ok(ArrayExpression {
start,
end,
elements,
non_code_meta,
digest: None,
})
}
fn array_end_start(i: TokenSlice) -> PResult<ArrayExpression> {
let start = open_bracket(i)?.start;
ignore_whitespace(i);
let elements = integer_range
.context(expected("array contents, a numeric range (like 0..10)"))
.parse_next(i)?;
ignore_whitespace(i);
let end = close_bracket(i)?.end;
@ -463,6 +540,7 @@ fn array(i: TokenSlice) -> PResult<ArrayExpression> {
start,
end,
elements,
non_code_meta: Default::default(),
digest: None,
})
}
@ -2779,6 +2857,7 @@ e
init: Expr::ArrayExpression(Box::new(ArrayExpression {
start: 16,
end: 23,
non_code_meta: Default::default(),
elements: vec![
Expr::Literal(Box::new(Literal {
start: 17,
@ -2956,6 +3035,45 @@ e
let _ast = parser.ast().unwrap();
}
#[test]
fn array() {
let program = r#"[1, 2, 3]"#;
let tokens = crate::token::lexer(program).unwrap();
let mut sl: &[Token] = &tokens;
let _arr = array_elem_by_elem(&mut sl).unwrap();
}
#[test]
fn array_linesep_trailing_comma() {
let program = r#"[
1,
2,
3,
]"#;
let tokens = crate::token::lexer(program).unwrap();
let mut sl: &[Token] = &tokens;
let _arr = array_elem_by_elem(&mut sl).unwrap();
}
#[allow(unused)]
fn print_tokens(tokens: &[Token]) {
for (i, tok) in tokens.iter().enumerate() {
println!("{i:.2}: ({:?}):) '{}'", tok.token_type, tok.value.replace("\n", "\\n"));
}
}
#[test]
fn array_linesep_no_trailing_comma() {
let program = r#"[
1,
2,
3
]"#;
let tokens = crate::token::lexer(program).unwrap();
let mut sl: &[Token] = &tokens;
let _arr = array_elem_by_elem(&mut sl).unwrap();
}
#[test]
fn test_keyword_ok_in_fn_args_return() {
let some_program_string = r#"fn thing = (param) => {
@ -3145,7 +3263,11 @@ mod snapshot_tests {
Ok(x) => x,
Err(e) => panic!("could not parse test: {e:?}"),
};
insta::assert_json_snapshot!(actual);
let mut settings = insta::Settings::clone_current();
settings.set_sort_maps(true);
settings.bind(|| {
insta::assert_json_snapshot!(actual);
});
}
};
}
@ -3264,4 +3386,22 @@ mod snapshot_tests {
snapshot_test!(at, "line([0, l], %)");
snapshot_test!(au, include_str!("../../../tests/executor/inputs/cylinder.kcl"));
snapshot_test!(av, "fn f = (angle?) => { return default(angle, 360) }");
snapshot_test!(
aw,
"let numbers = [
1,
// A,
// B,
3,
]"
);
snapshot_test!(
ax,
"let numbers = [
1,
2,
// A,
// B,
]"
);
}

View File

@ -0,0 +1,98 @@
---
source: kcl/src/parser/parser_impl.rs
expression: actual
---
{
"start": 0,
"end": 91,
"body": [
{
"type": "VariableDeclaration",
"type": "VariableDeclaration",
"start": 0,
"end": 91,
"declarations": [
{
"type": "VariableDeclarator",
"start": 4,
"end": 91,
"id": {
"type": "Identifier",
"start": 4,
"end": 11,
"name": "numbers",
"digest": null
},
"init": {
"type": "ArrayExpression",
"type": "ArrayExpression",
"start": 14,
"end": 91,
"elements": [
{
"type": "Literal",
"type": "Literal",
"start": 28,
"end": 29,
"value": 1,
"raw": "1",
"digest": null
},
{
"type": "Literal",
"type": "Literal",
"start": 79,
"end": 80,
"value": 3,
"raw": "3",
"digest": null
}
],
"nonCodeMeta": {
"nonCodeNodes": {
"1": [
{
"type": "NonCodeNode",
"start": 43,
"end": 48,
"value": {
"type": "blockComment",
"value": "A,",
"style": "line"
},
"digest": null
}
],
"2": [
{
"type": "NonCodeNode",
"start": 61,
"end": 66,
"value": {
"type": "blockComment",
"value": "B,",
"style": "line"
},
"digest": null
}
]
},
"start": [],
"digest": null
},
"digest": null
},
"digest": null
}
],
"kind": "let",
"digest": null
}
],
"nonCodeMeta": {
"nonCodeNodes": {},
"start": [],
"digest": null
},
"digest": null
}

View File

@ -0,0 +1,98 @@
---
source: kcl/src/parser/parser_impl.rs
expression: actual
---
{
"start": 0,
"end": 91,
"body": [
{
"type": "VariableDeclaration",
"type": "VariableDeclaration",
"start": 0,
"end": 91,
"declarations": [
{
"type": "VariableDeclarator",
"start": 4,
"end": 91,
"id": {
"type": "Identifier",
"start": 4,
"end": 11,
"name": "numbers",
"digest": null
},
"init": {
"type": "ArrayExpression",
"type": "ArrayExpression",
"start": 14,
"end": 91,
"elements": [
{
"type": "Literal",
"type": "Literal",
"start": 28,
"end": 29,
"value": 1,
"raw": "1",
"digest": null
},
{
"type": "Literal",
"type": "Literal",
"start": 43,
"end": 44,
"value": 2,
"raw": "2",
"digest": null
}
],
"nonCodeMeta": {
"nonCodeNodes": {
"2": [
{
"type": "NonCodeNode",
"start": 58,
"end": 63,
"value": {
"type": "blockComment",
"value": "A,",
"style": "line"
},
"digest": null
}
],
"3": [
{
"type": "NonCodeNode",
"start": 76,
"end": 81,
"value": {
"type": "blockComment",
"value": "B,",
"style": "line"
},
"digest": null
}
]
},
"start": [],
"digest": null
},
"digest": null
},
"digest": null
}
],
"kind": "let",
"digest": null
}
],
"nonCodeMeta": {
"nonCodeNodes": {},
"start": [],
"digest": null
},
"digest": null
}

View File

@ -1,9 +1,9 @@
use std::any::type_name;
use anyhow::Result;
use kittycad::types::OkWebSocketResponseData;
use serde::de::DeserializeOwned;
use super::{shapes::SketchSurfaceOrGroup, sketch::FaceTag, FnAsArg};
use crate::{
ast::types::{parse_json_number_as_f64, TagDeclarator},
errors::{KclError, KclErrorDetails},
@ -11,6 +11,7 @@ use crate::{
DynamicState, ExecutorContext, ExtrudeGroup, ExtrudeGroupSet, ExtrudeSurface, KclValue, Metadata,
ProgramMemory, SketchGroup, SketchGroupSet, SketchSurface, SourceRange, TagIdentifier,
},
std::{shapes::SketchSurfaceOrGroup, sketch::FaceTag, FnAsArg},
};
#[derive(Debug, Clone)]
@ -39,6 +40,25 @@ impl Args {
}
}
#[cfg(test)]
pub(crate) async fn new_test_args() -> Result<Self> {
use std::sync::Arc;
Ok(Self {
args: Vec::new(),
source_range: SourceRange::default(),
ctx: ExecutorContext {
engine: Arc::new(Box::new(crate::engine::conn_mock::EngineConnection::new().await?)),
fs: Arc::new(crate::fs::FileManager::new()),
stdlib: Arc::new(crate::std::StdLib::new()),
settings: Default::default(),
is_mock: true,
},
current_program_memory: ProgramMemory::default(),
dynamic_state: DynamicState::default(),
})
}
// Add a modeling command to the batch but don't fire it right away.
pub(crate) async fn batch_modeling_cmd(
&self,
@ -251,11 +271,11 @@ impl Args {
FromArgs::from_args(self, 0)
}
pub(crate) fn get_sketch_groups(&self) -> Result<(SketchGroupSet, Box<SketchGroup>), KclError> {
pub(crate) fn get_sketch_groups(&self) -> Result<(SketchGroupSet, SketchGroup), KclError> {
FromArgs::from_args(self, 0)
}
pub(crate) fn get_sketch_group(&self) -> Result<Box<SketchGroup>, KclError> {
pub(crate) fn get_sketch_group(&self) -> Result<SketchGroup, KclError> {
FromArgs::from_args(self, 0)
}
@ -270,9 +290,7 @@ impl Args {
FromArgs::from_args(self, 0)
}
pub(crate) fn get_sketch_group_and_optional_tag(
&self,
) -> Result<(Box<SketchGroup>, Option<TagDeclarator>), KclError> {
pub(crate) fn get_sketch_group_and_optional_tag(&self) -> Result<(SketchGroup, Option<TagDeclarator>), KclError> {
FromArgs::from_args(self, 0)
}
@ -283,7 +301,7 @@ impl Args {
FromArgs::from_args(self, 0)
}
pub(crate) fn get_data_and_sketch_group<'a, T>(&'a self) -> Result<(T, Box<SketchGroup>), KclError>
pub(crate) fn get_data_and_sketch_group<'a, T>(&'a self) -> Result<(T, SketchGroup), KclError>
where
T: serde::de::DeserializeOwned + FromArgs<'a>,
{
@ -299,7 +317,7 @@ impl Args {
pub(crate) fn get_data_and_sketch_group_and_tag<'a, T>(
&'a self,
) -> Result<(T, Box<SketchGroup>, Option<TagDeclarator>), KclError>
) -> Result<(T, SketchGroup, Option<TagDeclarator>), KclError>
where
T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
{
@ -338,7 +356,7 @@ impl Args {
FromArgs::from_args(self, 0)
}
pub(crate) fn get_tag_to_number_sketch_group(&self) -> Result<(TagIdentifier, f64, Box<SketchGroup>), KclError> {
pub(crate) fn get_tag_to_number_sketch_group(&self) -> Result<(TagIdentifier, f64, SketchGroup), KclError> {
FromArgs::from_args(self, 0)
}
@ -550,15 +568,6 @@ impl<'a> FromKclValue<'a> for TagIdentifier {
}
}
impl<'a> FromKclValue<'a> for &'a SketchGroup {
fn from_mem_item(arg: &'a KclValue) -> Option<Self> {
let KclValue::SketchGroup(s) = arg else {
return None;
};
Some(s.as_ref())
}
}
macro_rules! impl_from_arg_via_json {
($typ:path) => {
impl<'a> FromKclValue<'a> for $typ {
@ -608,6 +617,7 @@ impl_from_arg_via_json!(super::revolve::RevolveData);
impl_from_arg_via_json!(super::sketch::SketchData);
impl_from_arg_via_json!(crate::std::import::ImportFormat);
impl_from_arg_via_json!(crate::std::polar::PolarCoordsData);
impl_from_arg_via_json!(SketchGroup);
impl_from_arg_via_json!(FaceTag);
impl_from_arg_via_json!(String);
impl_from_arg_via_json!(u32);
@ -618,21 +628,16 @@ impl_from_arg_via_json!(bool);
impl_from_arg_for_array!(2);
impl_from_arg_for_array!(3);
impl<'a> FromKclValue<'a> for &'a Box<SketchGroup> {
impl<'a> FromKclValue<'a> for SketchGroupSet {
fn from_mem_item(arg: &'a KclValue) -> Option<Self> {
let KclValue::SketchGroup(s) = arg else {
let KclValue::UserVal(uv) = arg else {
return None;
};
Some(s)
}
}
impl<'a> FromKclValue<'a> for Box<SketchGroup> {
fn from_mem_item(arg: &'a KclValue) -> Option<Self> {
let KclValue::SketchGroup(s) = arg else {
return None;
};
Some(s.to_owned())
if let Some((x, _meta)) = uv.get::<SketchGroup>() {
Some(SketchGroupSet::from(x))
} else {
uv.get::<Vec<SketchGroup>>().map(|x| x.0).map(SketchGroupSet::from)
}
}
}
@ -656,15 +661,16 @@ impl<'a> FromKclValue<'a> for ExtrudeGroupSet {
arg.get_extrude_group_set().ok()
}
}
impl<'a> FromKclValue<'a> for SketchGroupSet {
fn from_mem_item(arg: &'a KclValue) -> Option<Self> {
arg.get_sketch_group_set().ok()
}
}
impl<'a> FromKclValue<'a> for SketchSurfaceOrGroup {
fn from_mem_item(arg: &'a KclValue) -> Option<Self> {
match arg {
KclValue::SketchGroup(sg) => Some(Self::SketchGroup(sg.clone())),
KclValue::UserVal(uv) => {
if let Some((sg, _meta)) = uv.get() {
Some(Self::SketchGroup(sg))
} else {
None
}
}
KclValue::Plane(sg) => Some(Self::SketchSurface(SketchSurface::Plane(sg.clone()))),
KclValue::Face(sg) => Some(Self::SketchSurface(SketchSurface::Face(sg.clone()))),
_ => None,

View File

@ -72,8 +72,8 @@ pub async fn assert_gt(args: Args) -> Result<KclValue, KclError> {
///
/// ```no_run
/// let n = 1.0285
/// let m = 1.0286
/// assertEqual(n, m, 0.01, "n is within the given tolerance for m")
/// let o = 1.0286
/// assertEqual(n, o, 0.01, "n is within the given tolerance for o")
/// ```
#[stdlib {
name = "assertEqual",

View File

@ -76,7 +76,7 @@ async fn inner_extrude(length: f64, sketch_group_set: SketchGroupSet, args: Args
let id = uuid::Uuid::new_v4();
// Extrude the element(s).
let sketch_groups: Vec<Box<SketchGroup>> = sketch_group_set.into();
let sketch_groups: Vec<SketchGroup> = sketch_group_set.into();
let mut extrude_groups = Vec::new();
for sketch_group in &sketch_groups {
// Before we extrude, we need to enable the sketch mode.
@ -118,7 +118,7 @@ async fn inner_extrude(length: f64, sketch_group_set: SketchGroupSet, args: Args
}
pub(crate) async fn do_post_extrude(
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
length: f64,
id: Uuid,
args: Args,
@ -155,7 +155,7 @@ pub(crate) async fn do_post_extrude(
}));
};
let mut sketch_group = *sketch_group.clone();
let mut sketch_group = sketch_group.clone();
// If we were sketching on a face, we need the original face id.
if let SketchSurface::Face(ref face) = sketch_group.on {

View File

@ -18,6 +18,7 @@ pub mod shapes;
pub mod shell;
pub mod sketch;
pub mod types;
pub mod units;
pub mod utils;
use std::collections::HashMap;
@ -118,6 +119,12 @@ lazy_static! {
Box::new(crate::std::math::Ln),
Box::new(crate::std::math::ToDegrees),
Box::new(crate::std::math::ToRadians),
Box::new(crate::std::units::Mm),
Box::new(crate::std::units::Inch),
Box::new(crate::std::units::Ft),
Box::new(crate::std::units::M),
Box::new(crate::std::units::Cm),
Box::new(crate::std::units::Yd),
Box::new(crate::std::polar::Polar),
Box::new(crate::std::assert::Assert),
Box::new(crate::std::assert::AssertEqual),

View File

@ -254,7 +254,7 @@ async fn make_transform<'a>(
}
fn array_to_point3d(json: &serde_json::Value, source_ranges: Vec<SourceRange>) -> Result<Point3d, KclError> {
let serde_json::Value::Array(arr) = dbg!(json) else {
let serde_json::Value::Array(arr) = json else {
return Err(KclError::Semantic(KclErrorDetails {
message: "Expected an array of 3 numbers (i.e. a 3D point)".to_string(),
source_ranges,

View File

@ -102,7 +102,7 @@ impl RevolveAxisAndOrigin {
/// Revolve a sketch around an axis.
pub async fn revolve(args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group): (RevolveData, Box<SketchGroup>) = args.get_data_and_sketch_group()?;
let (data, sketch_group): (RevolveData, SketchGroup) = args.get_data_and_sketch_group()?;
let extrude_group = inner_revolve(data, sketch_group, args).await?;
Ok(KclValue::ExtrudeGroup(extrude_group))
@ -249,7 +249,7 @@ pub async fn revolve(args: Args) -> Result<KclValue, KclError> {
}]
async fn inner_revolve(
data: RevolveData,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
args: Args,
) -> Result<Box<ExtrudeGroup>, KclError> {
if let Some(angle) = data.angle {

View File

@ -108,7 +108,7 @@ pub async fn last_segment_x(args: Args) -> Result<KclValue, KclError> {
#[stdlib {
name = "lastSegX",
}]
fn inner_last_segment_x(sketch_group: Box<SketchGroup>, args: Args) -> Result<f64, KclError> {
fn inner_last_segment_x(sketch_group: SketchGroup, args: Args) -> Result<f64, KclError> {
let last_line = sketch_group
.value
.last()
@ -151,7 +151,7 @@ pub async fn last_segment_y(args: Args) -> Result<KclValue, KclError> {
#[stdlib {
name = "lastSegY",
}]
fn inner_last_segment_y(sketch_group: Box<SketchGroup>, args: Args) -> Result<f64, KclError> {
fn inner_last_segment_y(sketch_group: SketchGroup, args: Args) -> Result<f64, KclError> {
let last_line = sketch_group
.value
.last()
@ -281,7 +281,7 @@ pub async fn angle_to_match_length_x(args: Args) -> Result<KclValue, KclError> {
fn inner_angle_to_match_length_x(
tag: &TagIdentifier,
to: f64,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
args: Args,
) -> Result<f64, KclError> {
let line = args.get_tag_engine_info(tag)?;
@ -347,7 +347,7 @@ pub async fn angle_to_match_length_y(args: Args) -> Result<KclValue, KclError> {
fn inner_angle_to_match_length_y(
tag: &TagIdentifier,
to: f64,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
args: Args,
) -> Result<f64, KclError> {
let line = args.get_tag_engine_info(tag)?;

View File

@ -27,7 +27,7 @@ pub async fn circle(args: Args) -> Result<KclValue, KclError> {
args.get_circle_args()?;
let sketch_group = inner_circle(center, radius, sketch_surface_or_group, tag, args).await?;
Ok(KclValue::SketchGroup(sketch_group))
Ok(KclValue::new_user_val(sketch_group.meta.clone(), sketch_group))
}
/// Construct a 2-dimensional circle, of the specified radius, centered at
@ -60,7 +60,7 @@ async fn inner_circle(
sketch_surface_or_group: SketchSurfaceOrGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
let sketch_surface = match sketch_surface_or_group {
SketchSurfaceOrGroup::SketchSurface(surface) => surface,
SketchSurfaceOrGroup::SketchGroup(group) => group.on,

View File

@ -90,11 +90,11 @@ pub enum StartOrEnd {
/// Draw a line to a point.
pub async fn line_to(args: Args) -> Result<KclValue, KclError> {
let (to, sketch_group, tag): ([f64; 2], Box<SketchGroup>, Option<TagDeclarator>) =
let (to, sketch_group, tag): ([f64; 2], SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_line_to(to, sketch_group, tag, args).await?;
Ok(KclValue::SketchGroup(new_sketch_group))
Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group))
}
/// Draw a line from the current origin to some absolute (x, y) point.
@ -114,10 +114,10 @@ pub async fn line_to(args: Args) -> Result<KclValue, KclError> {
}]
async fn inner_line_to(
to: [f64; 2],
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
let from = sketch_group.current_pen_position()?;
let id = uuid::Uuid::new_v4();
@ -161,11 +161,11 @@ async fn inner_line_to(
/// Draw a line to a point on the x-axis.
pub async fn x_line_to(args: Args) -> Result<KclValue, KclError> {
let (to, sketch_group, tag): (f64, Box<SketchGroup>, Option<TagDeclarator>) =
let (to, sketch_group, tag): (f64, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_x_line_to(to, sketch_group, tag, args).await?;
Ok(KclValue::SketchGroup(new_sketch_group))
Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group))
}
/// Draw a line parallel to the X axis, that ends at the given X.
@ -196,10 +196,10 @@ pub async fn x_line_to(args: Args) -> Result<KclValue, KclError> {
}]
async fn inner_x_line_to(
to: f64,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
let from = sketch_group.current_pen_position()?;
let new_sketch_group = inner_line_to([to, from.y], sketch_group, tag, args).await?;
@ -209,11 +209,11 @@ async fn inner_x_line_to(
/// Draw a line to a point on the y-axis.
pub async fn y_line_to(args: Args) -> Result<KclValue, KclError> {
let (to, sketch_group, tag): (f64, Box<SketchGroup>, Option<TagDeclarator>) =
let (to, sketch_group, tag): (f64, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_y_line_to(to, sketch_group, tag, args).await?;
Ok(KclValue::SketchGroup(new_sketch_group))
Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group))
}
/// Draw a line parallel to the Y axis, that ends at the given Y.
@ -237,10 +237,10 @@ pub async fn y_line_to(args: Args) -> Result<KclValue, KclError> {
}]
async fn inner_y_line_to(
to: f64,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
let from = sketch_group.current_pen_position()?;
let new_sketch_group = inner_line_to([from.x, to], sketch_group, tag, args).await?;
@ -249,11 +249,11 @@ async fn inner_y_line_to(
/// Draw a line.
pub async fn line(args: Args) -> Result<KclValue, KclError> {
let (delta, sketch_group, tag): ([f64; 2], Box<SketchGroup>, Option<TagDeclarator>) =
let (delta, sketch_group, tag): ([f64; 2], SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_line(delta, sketch_group, tag, args).await?;
Ok(KclValue::SketchGroup(new_sketch_group))
Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group))
}
/// Draw a line relative to the current origin to a specified (x, y) away
@ -285,10 +285,10 @@ pub async fn line(args: Args) -> Result<KclValue, KclError> {
}]
async fn inner_line(
delta: [f64; 2],
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
let from = sketch_group.current_pen_position()?;
let to = [from.x + delta[0], from.y + delta[1]];
@ -334,11 +334,11 @@ async fn inner_line(
/// Draw a line on the x-axis.
pub async fn x_line(args: Args) -> Result<KclValue, KclError> {
let (length, sketch_group, tag): (f64, Box<SketchGroup>, Option<TagDeclarator>) =
let (length, sketch_group, tag): (f64, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_x_line(length, sketch_group, tag, args).await?;
Ok(KclValue::SketchGroup(new_sketch_group))
Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group))
}
/// Draw a line relative to the current origin to a specified distance away
@ -368,20 +368,20 @@ pub async fn x_line(args: Args) -> Result<KclValue, KclError> {
}]
async fn inner_x_line(
length: f64,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
inner_line([length, 0.0], sketch_group, tag, args).await
}
/// Draw a line on the y-axis.
pub async fn y_line(args: Args) -> Result<KclValue, KclError> {
let (length, sketch_group, tag): (f64, Box<SketchGroup>, Option<TagDeclarator>) =
let (length, sketch_group, tag): (f64, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_y_line(length, sketch_group, tag, args).await?;
Ok(KclValue::SketchGroup(new_sketch_group))
Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group))
}
/// Draw a line relative to the current origin to a specified distance away
@ -406,10 +406,10 @@ pub async fn y_line(args: Args) -> Result<KclValue, KclError> {
}]
async fn inner_y_line(
length: f64,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
inner_line([0.0, length], sketch_group, tag, args).await
}
@ -431,11 +431,11 @@ pub enum AngledLineData {
/// Draw an angled line.
pub async fn angled_line(args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (AngledLineData, Box<SketchGroup>, Option<TagDeclarator>) =
let (data, sketch_group, tag): (AngledLineData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_angled_line(data, sketch_group, tag, args).await?;
Ok(KclValue::SketchGroup(new_sketch_group))
Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group))
}
/// Draw a line segment relative to the current origin using the polar
@ -460,10 +460,10 @@ pub async fn angled_line(args: Args) -> Result<KclValue, KclError> {
}]
async fn inner_angled_line(
data: AngledLineData,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
let from = sketch_group.current_pen_position()?;
let (angle, length) = match data {
AngledLineData::AngleAndLengthNamed { angle, length } => (angle, length),
@ -520,11 +520,11 @@ async fn inner_angled_line(
/// Draw an angled line of a given x length.
pub async fn angled_line_of_x_length(args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (AngledLineData, Box<SketchGroup>, Option<TagDeclarator>) =
let (data, sketch_group, tag): (AngledLineData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_angled_line_of_x_length(data, sketch_group, tag, args).await?;
Ok(KclValue::SketchGroup(new_sketch_group))
Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group))
}
/// Create a line segment from the current 2-dimensional sketch origin
@ -545,10 +545,10 @@ pub async fn angled_line_of_x_length(args: Args) -> Result<KclValue, KclError> {
}]
async fn inner_angled_line_of_x_length(
data: AngledLineData,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
let (angle, length) = match data {
AngledLineData::AngleAndLengthNamed { angle, length } => (angle, length),
AngledLineData::AngleAndLengthPair(pair) => (pair[0], pair[1]),
@ -588,11 +588,11 @@ pub struct AngledLineToData {
/// Draw an angled line to a given x coordinate.
pub async fn angled_line_to_x(args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (AngledLineToData, Box<SketchGroup>, Option<TagDeclarator>) =
let (data, sketch_group, tag): (AngledLineToData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_angled_line_to_x(data, sketch_group, tag, args).await?;
Ok(KclValue::SketchGroup(new_sketch_group))
Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group))
}
/// Create a line segment from the current 2-dimensional sketch origin
@ -614,10 +614,10 @@ pub async fn angled_line_to_x(args: Args) -> Result<KclValue, KclError> {
}]
async fn inner_angled_line_to_x(
data: AngledLineToData,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
let from = sketch_group.current_pen_position()?;
let AngledLineToData { angle, to: x_to } = data;
@ -645,12 +645,12 @@ async fn inner_angled_line_to_x(
/// Draw an angled line of a given y length.
pub async fn angled_line_of_y_length(args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (AngledLineData, Box<SketchGroup>, Option<TagDeclarator>) =
let (data, sketch_group, tag): (AngledLineData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_angled_line_of_y_length(data, sketch_group, tag, args).await?;
Ok(KclValue::SketchGroup(new_sketch_group))
Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group))
}
/// Create a line segment from the current 2-dimensional sketch origin
@ -673,10 +673,10 @@ pub async fn angled_line_of_y_length(args: Args) -> Result<KclValue, KclError> {
}]
async fn inner_angled_line_of_y_length(
data: AngledLineData,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
let (angle, length) = match data {
AngledLineData::AngleAndLengthNamed { angle, length } => (angle, length),
AngledLineData::AngleAndLengthPair(pair) => (pair[0], pair[1]),
@ -705,11 +705,11 @@ async fn inner_angled_line_of_y_length(
/// Draw an angled line to a given y coordinate.
pub async fn angled_line_to_y(args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (AngledLineToData, Box<SketchGroup>, Option<TagDeclarator>) =
let (data, sketch_group, tag): (AngledLineToData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_angled_line_to_y(data, sketch_group, tag, args).await?;
Ok(KclValue::SketchGroup(new_sketch_group))
Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group))
}
/// Create a line segment from the current 2-dimensional sketch origin
@ -731,10 +731,10 @@ pub async fn angled_line_to_y(args: Args) -> Result<KclValue, KclError> {
}]
async fn inner_angled_line_to_y(
data: AngledLineToData,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
let from = sketch_group.current_pen_position()?;
let AngledLineToData { angle, to: y_to } = data;
@ -776,10 +776,10 @@ pub struct AngledLineThatIntersectsData {
/// Draw an angled line that intersects with a given line.
pub async fn angled_line_that_intersects(args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (AngledLineThatIntersectsData, Box<SketchGroup>, Option<TagDeclarator>) =
let (data, sketch_group, tag): (AngledLineThatIntersectsData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_angled_line_that_intersects(data, sketch_group, tag, args).await?;
Ok(KclValue::SketchGroup(new_sketch_group))
Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group))
}
/// Draw an angled line from the current origin, constructing a line segment
@ -806,10 +806,10 @@ pub async fn angled_line_that_intersects(args: Args) -> Result<KclValue, KclErro
}]
async fn inner_angled_line_that_intersects(
data: AngledLineThatIntersectsData,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
let intersect_path = args.get_tag_engine_info(&data.intersect_tag)?;
let path = intersect_path.path.clone().ok_or_else(|| {
KclError::Type(KclErrorDetails {
@ -835,7 +835,7 @@ pub async fn start_sketch_at(args: Args) -> Result<KclValue, KclError> {
let data: [f64; 2] = args.get_data()?;
let sketch_group = inner_start_sketch_at(data, args).await?;
Ok(KclValue::SketchGroup(sketch_group))
Ok(KclValue::new_user_val(sketch_group.meta.clone(), sketch_group))
}
/// Start a new 2-dimensional sketch at a given point on the 'XY' plane.
@ -872,7 +872,7 @@ pub async fn start_sketch_at(args: Args) -> Result<KclValue, KclError> {
#[stdlib {
name = "startSketchAt",
}]
async fn inner_start_sketch_at(data: [f64; 2], args: Args) -> Result<Box<SketchGroup>, KclError> {
async fn inner_start_sketch_at(data: [f64; 2], args: Args) -> Result<SketchGroup, KclError> {
// Let's assume it's the XY plane for now, this is just for backwards compatibility.
let xy_plane = PlaneData::XY;
let sketch_surface = inner_start_sketch_on(SketchData::Plane(xy_plane), None, args.clone()).await?;
@ -1204,7 +1204,7 @@ pub async fn start_profile_at(args: Args) -> Result<KclValue, KclError> {
args.get_data_and_sketch_surface()?;
let sketch_group = inner_start_profile_at(start, sketch_surface, tag, args).await?;
Ok(KclValue::SketchGroup(sketch_group))
Ok(KclValue::new_user_val(sketch_group.meta.clone(), sketch_group))
}
/// Start a new profile at a given point.
@ -1249,7 +1249,7 @@ pub(crate) async fn inner_start_profile_at(
sketch_surface: SketchSurface,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
if let SketchSurface::Face(face) = &sketch_surface {
// Flush the batch for our fillets/chamfers if there are any.
// If we do not do these for sketch on face, things will fail with face does not exist.
@ -1324,12 +1324,12 @@ pub(crate) async fn inner_start_profile_at(
},
start: current_path,
};
Ok(Box::new(sketch_group))
Ok(sketch_group)
}
/// Returns the X component of the sketch profile start point.
pub async fn profile_start_x(args: Args) -> Result<KclValue, KclError> {
let sketch_group: Box<SketchGroup> = args.get_sketch_group()?;
let sketch_group: SketchGroup = args.get_sketch_group()?;
let x = inner_profile_start_x(sketch_group)?;
args.make_user_val_from_f64(x)
}
@ -1347,13 +1347,13 @@ pub async fn profile_start_x(args: Args) -> Result<KclValue, KclError> {
#[stdlib {
name = "profileStartX"
}]
pub(crate) fn inner_profile_start_x(sketch_group: Box<SketchGroup>) -> Result<f64, KclError> {
pub(crate) fn inner_profile_start_x(sketch_group: SketchGroup) -> Result<f64, KclError> {
Ok(sketch_group.start.to[0])
}
/// Returns the Y component of the sketch profile start point.
pub async fn profile_start_y(args: Args) -> Result<KclValue, KclError> {
let sketch_group: Box<SketchGroup> = args.get_sketch_group()?;
let sketch_group: SketchGroup = args.get_sketch_group()?;
let x = inner_profile_start_y(sketch_group)?;
args.make_user_val_from_f64(x)
}
@ -1370,13 +1370,13 @@ pub async fn profile_start_y(args: Args) -> Result<KclValue, KclError> {
#[stdlib {
name = "profileStartY"
}]
pub(crate) fn inner_profile_start_y(sketch_group: Box<SketchGroup>) -> Result<f64, KclError> {
pub(crate) fn inner_profile_start_y(sketch_group: SketchGroup) -> Result<f64, KclError> {
Ok(sketch_group.start.to[1])
}
/// Returns the sketch profile start point.
pub async fn profile_start(args: Args) -> Result<KclValue, KclError> {
let sketch_group: Box<SketchGroup> = args.get_sketch_group()?;
let sketch_group: SketchGroup = args.get_sketch_group()?;
let point = inner_profile_start(sketch_group)?;
Ok(KclValue::UserVal(UserVal {
value: serde_json::to_value(point).map_err(|e| {
@ -1404,17 +1404,17 @@ pub async fn profile_start(args: Args) -> Result<KclValue, KclError> {
#[stdlib {
name = "profileStart"
}]
pub(crate) fn inner_profile_start(sketch_group: Box<SketchGroup>) -> Result<[f64; 2], KclError> {
pub(crate) fn inner_profile_start(sketch_group: SketchGroup) -> Result<[f64; 2], KclError> {
Ok(sketch_group.start.to)
}
/// Close the current sketch.
pub async fn close(args: Args) -> Result<KclValue, KclError> {
let (sketch_group, tag): (Box<SketchGroup>, Option<TagDeclarator>) = args.get_sketch_group_and_optional_tag()?;
let (sketch_group, tag): (SketchGroup, Option<TagDeclarator>) = args.get_sketch_group_and_optional_tag()?;
let new_sketch_group = inner_close(sketch_group, tag, args).await?;
Ok(KclValue::SketchGroup(new_sketch_group))
Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group))
}
/// Construct a line segment from the current origin back to the profile's
@ -1442,10 +1442,10 @@ pub async fn close(args: Args) -> Result<KclValue, KclError> {
name = "close",
}]
pub(crate) async fn inner_close(
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
let from = sketch_group.current_pen_position()?;
let to: Point2d = sketch_group.start.from.into();
@ -1517,11 +1517,11 @@ pub enum ArcData {
/// Draw an arc.
pub async fn arc(args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (ArcData, Box<SketchGroup>, Option<TagDeclarator>) =
let (data, sketch_group, tag): (ArcData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_arc(data, sketch_group, tag, args).await?;
Ok(KclValue::SketchGroup(new_sketch_group))
Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group))
}
/// Starting at the current sketch's origin, draw a curved line segment along
@ -1553,10 +1553,10 @@ pub async fn arc(args: Args) -> Result<KclValue, KclError> {
}]
pub(crate) async fn inner_arc(
data: ArcData,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
let from: Point2d = sketch_group.current_pen_position()?;
let (center, angle_start, angle_end, radius, end) = match &data {
@ -1640,11 +1640,11 @@ pub enum TangentialArcData {
/// Draw a tangential arc.
pub async fn tangential_arc(args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (TangentialArcData, Box<SketchGroup>, Option<TagDeclarator>) =
let (data, sketch_group, tag): (TangentialArcData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_tangential_arc(data, sketch_group, tag, args).await?;
Ok(KclValue::SketchGroup(new_sketch_group))
Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group))
}
/// Starting at the current sketch's origin, draw a curved line segment along
@ -1676,10 +1676,10 @@ pub async fn tangential_arc(args: Args) -> Result<KclValue, KclError> {
}]
async fn inner_tangential_arc(
data: TangentialArcData,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
let from: Point2d = sketch_group.current_pen_position()?;
// next set of lines is some undocumented voodoo from get_tangential_arc_to_info
let tangent_info = sketch_group.get_tangential_info_from_paths(); //this function desperately needs some documentation
@ -1793,7 +1793,7 @@ pub async fn tangential_arc_to(args: Args) -> Result<KclValue, KclError> {
// Get arguments to function call
let mut it = args.args.iter();
let to: [f64; 2] = get_arg(&mut it, src)?.get_json()?;
let sketch_group: Box<SketchGroup> = get_arg(&mut it, src)?.get_json()?;
let sketch_group: SketchGroup = get_arg(&mut it, src)?.get_json()?;
let tag = if let Ok(memory_item) = get_arg(&mut it, src) {
memory_item.get_json_opt()?
} else {
@ -1801,7 +1801,7 @@ pub async fn tangential_arc_to(args: Args) -> Result<KclValue, KclError> {
};
let new_sketch_group = inner_tangential_arc_to(to, sketch_group, tag, args).await?;
Ok(KclValue::SketchGroup(new_sketch_group))
Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group))
}
/// Starting at the current sketch's origin, draw a curved line segment along
@ -1826,10 +1826,10 @@ pub async fn tangential_arc_to(args: Args) -> Result<KclValue, KclError> {
}]
async fn inner_tangential_arc_to(
to: [f64; 2],
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
let from: Point2d = sketch_group.current_pen_position()?;
let tangent_info = sketch_group.get_tangential_info_from_paths();
let tan_previous_point = if tangent_info.is_center {
@ -1888,11 +1888,11 @@ pub struct BezierData {
/// Draw a bezier curve.
pub async fn bezier_curve(args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (BezierData, Box<SketchGroup>, Option<TagDeclarator>) =
let (data, sketch_group, tag): (BezierData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_bezier_curve(data, sketch_group, tag, args).await?;
Ok(KclValue::SketchGroup(new_sketch_group))
Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group))
}
/// Draw a smooth, continuous, curved line segment from the current origin to
@ -1918,10 +1918,10 @@ pub async fn bezier_curve(args: Args) -> Result<KclValue, KclError> {
}]
async fn inner_bezier_curve(
data: BezierData,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
let from = sketch_group.current_pen_position()?;
let relative = true;
@ -1980,10 +1980,10 @@ async fn inner_bezier_curve(
/// Use a sketch to cut a hole in another sketch.
pub async fn hole(args: Args) -> Result<KclValue, KclError> {
let (hole_sketch_group, sketch_group): (SketchGroupSet, Box<SketchGroup>) = args.get_sketch_groups()?;
let (hole_sketch_group, sketch_group): (SketchGroupSet, SketchGroup) = args.get_sketch_groups()?;
let new_sketch_group = inner_hole(hole_sketch_group, sketch_group, args).await?;
Ok(KclValue::SketchGroup(new_sketch_group))
Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group))
}
/// Use a 2-dimensional sketch to cut a hole in another 2-dimensional sketch.
@ -2022,10 +2022,10 @@ pub async fn hole(args: Args) -> Result<KclValue, KclError> {
}]
async fn inner_hole(
hole_sketch_group: SketchGroupSet,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
let hole_sketch_groups: Vec<Box<SketchGroup>> = hole_sketch_group.into();
) -> Result<SketchGroup, KclError> {
let hole_sketch_groups: Vec<SketchGroup> = hole_sketch_group.into();
for hole_sketch_group in hole_sketch_groups {
args.batch_modeling_cmd(
uuid::Uuid::new_v4(),

View File

@ -0,0 +1,336 @@
//! Functions related to unitsematics.
use anyhow::Result;
use derive_docs::stdlib;
use schemars::JsonSchema;
use crate::{errors::KclError, executor::KclValue, settings::types::UnitLength, std::Args};
/// Millimeters conversion factor for current projects units.
pub async fn mm(args: Args) -> Result<KclValue, KclError> {
let result = inner_mm(&args)?;
args.make_user_val_from_f64(result)
}
/// Millimeters conversion factor for current projects units.
///
/// No matter what units the current project uses, this function will always return the conversion
/// factor to millimeters.
///
/// For example, if the current project uses inches, this function will return `(1/25.4)`.
/// If the current project uses millimeters, this function will return `1`.
///
/// **Caution**: This function is only intended to be used when you absolutely MUST
/// have different units in your code than the project settings. Otherwise, it is
/// a bad pattern to use this function.
///
/// We merely provide these functions for convenience and readability, as
/// `10 * mm()` is more readable that your intent is "I want 10 millimeters" than
/// `10 * (1/25.4)`, if the project settings are in inches.
///
/// ```no_run
/// const totalWidth = 10 * mm()
/// ```
#[stdlib {
name = "mm",
tags = ["units"],
}]
fn inner_mm(args: &Args) -> Result<f64, KclError> {
match args.ctx.settings.units {
UnitLength::Mm => Ok(1.0),
UnitLength::In => Ok(measurements::Length::from_millimeters(1.0).as_inches()),
UnitLength::Ft => Ok(measurements::Length::from_millimeters(1.0).as_feet()),
UnitLength::M => Ok(measurements::Length::from_millimeters(1.0).as_meters()),
UnitLength::Cm => Ok(measurements::Length::from_millimeters(1.0).as_centimeters()),
UnitLength::Yd => Ok(measurements::Length::from_millimeters(1.0).as_yards()),
}
}
/// Inches conversion factor for current projects units.
pub async fn inch(args: Args) -> Result<KclValue, KclError> {
let result = inner_inch(&args)?;
args.make_user_val_from_f64(result)
}
/// Inches conversion factor for current projects units.
///
/// No matter what units the current project uses, this function will always return the conversion
/// factor to inches.
///
/// For example, if the current project uses inches, this function will return `1`.
/// If the current project uses millimeters, this function will return `25.4`.
///
/// **Caution**: This function is only intended to be used when you absolutely MUST
/// have different units in your code than the project settings. Otherwise, it is
/// a bad pattern to use this function.
///
/// We merely provide these functions for convenience and readability, as
/// `10 * inch()` is more readable that your intent is "I want 10 inches" than
/// `10 * 25.4`, if the project settings are in millimeters.
///
/// ```no_run
/// const totalWidth = 10 * inch()
/// ```
#[stdlib {
name = "inch",
tags = ["units"],
}]
fn inner_inch(args: &Args) -> Result<f64, KclError> {
match args.ctx.settings.units {
UnitLength::Mm => Ok(measurements::Length::from_inches(1.0).as_millimeters()),
UnitLength::In => Ok(1.0),
UnitLength::Ft => Ok(measurements::Length::from_inches(1.0).as_feet()),
UnitLength::M => Ok(measurements::Length::from_inches(1.0).as_meters()),
UnitLength::Cm => Ok(measurements::Length::from_inches(1.0).as_centimeters()),
UnitLength::Yd => Ok(measurements::Length::from_inches(1.0).as_yards()),
}
}
/// Feet conversion factor for current projects units.
pub async fn ft(args: Args) -> Result<KclValue, KclError> {
let result = inner_ft(&args)?;
args.make_user_val_from_f64(result)
}
/// Feet conversion factor for current projects units.
///
/// No matter what units the current project uses, this function will always return the conversion
/// factor to feet.
///
/// For example, if the current project uses inches, this function will return `12`.
/// If the current project uses millimeters, this function will return `304.8`.
/// If the current project uses feet, this function will return `1`.
///
/// **Caution**: This function is only intended to be used when you absolutely MUST
/// have different units in your code than the project settings. Otherwise, it is
/// a bad pattern to use this function.
///
/// We merely provide these functions for convenience and readability, as
/// `10 * ft()` is more readable that your intent is "I want 10 feet" than
/// `10 * 304.8`, if the project settings are in millimeters.
///
/// ```no_run
/// const totalWidth = 10 * ft()
/// ```
#[stdlib {
name = "ft",
tags = ["units"],
}]
fn inner_ft(args: &Args) -> Result<f64, KclError> {
match args.ctx.settings.units {
UnitLength::Mm => Ok(measurements::Length::from_feet(1.0).as_millimeters()),
UnitLength::In => Ok(measurements::Length::from_feet(1.0).as_inches()),
UnitLength::Ft => Ok(1.0),
UnitLength::M => Ok(measurements::Length::from_feet(1.0).as_meters()),
UnitLength::Cm => Ok(measurements::Length::from_feet(1.0).as_centimeters()),
UnitLength::Yd => Ok(measurements::Length::from_feet(1.0).as_yards()),
}
}
/// Meters conversion factor for current projects units.
pub async fn m(args: Args) -> Result<KclValue, KclError> {
let result = inner_m(&args)?;
args.make_user_val_from_f64(result)
}
/// Meters conversion factor for current projects units.
///
/// No matter what units the current project uses, this function will always return the conversion
/// factor to meters.
///
/// For example, if the current project uses inches, this function will return `39.3701`.
/// If the current project uses millimeters, this function will return `1000`.
/// If the current project uses meters, this function will return `1`.
///
/// **Caution**: This function is only intended to be used when you absolutely MUST
/// have different units in your code than the project settings. Otherwise, it is
/// a bad pattern to use this function.
///
/// We merely provide these functions for convenience and readability, as
/// `10 * m()` is more readable that your intent is "I want 10 meters" than
/// `10 * 1000`, if the project settings are in millimeters.
///
/// ```no_run
/// const totalWidth = 10 * m()
/// ```
#[stdlib {
name = "m",
tags = ["units"],
}]
fn inner_m(args: &Args) -> Result<f64, KclError> {
match args.ctx.settings.units {
UnitLength::Mm => Ok(measurements::Length::from_meters(1.0).as_millimeters()),
UnitLength::In => Ok(measurements::Length::from_meters(1.0).as_inches()),
UnitLength::Ft => Ok(measurements::Length::from_meters(1.0).as_feet()),
UnitLength::M => Ok(1.0),
UnitLength::Cm => Ok(measurements::Length::from_meters(1.0).as_centimeters()),
UnitLength::Yd => Ok(measurements::Length::from_meters(1.0).as_yards()),
}
}
/// Centimeters conversion factor for current projects units.
pub async fn cm(args: Args) -> Result<KclValue, KclError> {
let result = inner_cm(&args)?;
args.make_user_val_from_f64(result)
}
/// Centimeters conversion factor for current projects units.
///
/// No matter what units the current project uses, this function will always return the conversion
/// factor to centimeters.
///
/// For example, if the current project uses inches, this function will return `0.393701`.
/// If the current project uses millimeters, this function will return `10`.
/// If the current project uses centimeters, this function will return `1`.
///
/// **Caution**: This function is only intended to be used when you absolutely MUST
/// have different units in your code than the project settings. Otherwise, it is
/// a bad pattern to use this function.
///
/// We merely provide these functions for convenience and readability, as
/// `10 * cm()` is more readable that your intent is "I want 10 centimeters" than
/// `10 * 10`, if the project settings are in millimeters.
///
/// ```no_run
/// const totalWidth = 10 * cm()
/// ```
#[stdlib {
name = "cm",
tags = ["units"],
}]
fn inner_cm(args: &Args) -> Result<f64, KclError> {
match args.ctx.settings.units {
UnitLength::Mm => Ok(measurements::Length::from_centimeters(1.0).as_millimeters()),
UnitLength::In => Ok(measurements::Length::from_centimeters(1.0).as_inches()),
UnitLength::Ft => Ok(measurements::Length::from_centimeters(1.0).as_feet()),
UnitLength::M => Ok(measurements::Length::from_centimeters(1.0).as_meters()),
UnitLength::Cm => Ok(1.0),
UnitLength::Yd => Ok(measurements::Length::from_centimeters(1.0).as_yards()),
}
}
/// Yards conversion factor for current projects units.
pub async fn yd(args: Args) -> Result<KclValue, KclError> {
let result = inner_yd(&args)?;
args.make_user_val_from_f64(result)
}
/// Yards conversion factor for current projects units.
///
/// No matter what units the current project uses, this function will always return the conversion
/// factor to yards.
///
/// For example, if the current project uses inches, this function will return `36`.
/// If the current project uses millimeters, this function will return `914.4`.
/// If the current project uses yards, this function will return `1`.
///
/// **Caution**: This function is only intended to be used when you absolutely MUST
/// have different units in your code than the project settings. Otherwise, it is
/// a bad pattern to use this function.
///
/// We merely provide these functions for convenience and readability, as
/// `10 * yd()` is more readable that your intent is "I want 10 yards" than
/// `10 * 914.4`, if the project settings are in millimeters.
///
/// ```no_run
/// const totalWidth = 10 * yd()
/// ```
#[stdlib {
name = "yd",
tags = ["units"],
}]
fn inner_yd(args: &Args) -> Result<f64, KclError> {
match args.ctx.settings.units {
UnitLength::Mm => Ok(measurements::Length::from_yards(1.0).as_millimeters()),
UnitLength::In => Ok(measurements::Length::from_yards(1.0).as_inches()),
UnitLength::Ft => Ok(measurements::Length::from_yards(1.0).as_feet()),
UnitLength::M => Ok(measurements::Length::from_yards(1.0).as_meters()),
UnitLength::Cm => Ok(measurements::Length::from_yards(1.0).as_centimeters()),
UnitLength::Yd => Ok(1.0),
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use super::*;
#[tokio::test(flavor = "multi_thread")]
async fn test_units_inner_mm() {
let mut args = Args::new_test_args().await.unwrap();
args.ctx.settings.units = UnitLength::Mm;
let result = inner_mm(&args).unwrap();
assert_eq!(result, 1.0);
args.ctx.settings.units = UnitLength::In;
let result = inner_mm(&args).unwrap();
assert_eq!(result, 1.0 / 25.4);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_units_inner_inch() {
let mut args = Args::new_test_args().await.unwrap();
args.ctx.settings.units = UnitLength::In;
let result = inner_inch(&args).unwrap();
assert_eq!(result, 1.0);
args.ctx.settings.units = UnitLength::Mm;
let result = inner_inch(&args).unwrap();
assert_eq!(result, 25.4);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_units_inner_ft() {
let mut args = Args::new_test_args().await.unwrap();
args.ctx.settings.units = UnitLength::Ft;
let result = inner_ft(&args).unwrap();
assert_eq!(result, 1.0);
args.ctx.settings.units = UnitLength::Mm;
let result = inner_ft(&args).unwrap();
assert_eq!(result, 304.8);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_units_inner_m() {
let mut args = Args::new_test_args().await.unwrap();
args.ctx.settings.units = UnitLength::M;
let result = inner_m(&args).unwrap();
assert_eq!(result, 1.0);
args.ctx.settings.units = UnitLength::Mm;
let result = inner_m(&args).unwrap();
assert_eq!(result, 1000.0);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_units_inner_cm() {
let mut args = Args::new_test_args().await.unwrap();
args.ctx.settings.units = UnitLength::Cm;
let result = inner_cm(&args).unwrap();
assert_eq!(result, 1.0);
args.ctx.settings.units = UnitLength::Mm;
let result = inner_cm(&args).unwrap();
assert_eq!(result, 10.0);
}
#[tokio::test(flavor = "multi_thread")]
async fn test_units_inner_yd() {
let mut args = Args::new_test_args().await.unwrap();
args.ctx.settings.units = UnitLength::Yd;
let result = inner_yd(&args).unwrap();
assert_eq!(result, 1.0);
args.ctx.settings.units = UnitLength::Mm;
let result = inner_yd(&args).unwrap();
assert_eq!(result, 914.4);
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -1,13 +1,13 @@
const ANSWER = 41803
fn m = (s) => {
fn t = (s) => {
return (ANSWER * s + 12345) % 214748
}
let xs = 205804
let ys = 71816
let ox = 35 - (m(xs) % 70)
let oy = 35 - (m(ys) % 70)
let ox = 35 - (t(xs) % 70)
let oy = 35 - (t(ys) % 70)
const r = startSketchOn('XZ')
|> startProfileAt([ox, oy], %)
|> line([1, 0], %)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

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