Merge branch 'main' into pierremtb/issue3528-Add-electron-updater
BIN
assets/icon.icns
Normal file
BIN
assets/icon.ico
Normal file
|
After Width: | Height: | Size: 183 KiB |
|
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
BIN
assets/icon@2x.icns
Normal file
38
docs/kcl/cm.md
Normal file
38
docs/kcl/ft.md
Normal file
38
docs/kcl/inch.md
Normal 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)
|
||||
|
||||
38
docs/kcl/m.md
Normal file
38
docs/kcl/mm.md
Normal 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()"
|
||||
]
|
||||
}
|
||||
]
|
||||
38
docs/kcl/yd.md
Normal 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',
|
||||
|
||||
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
@ -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: [
|
||||
{
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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())
|
||||
|
||||
@ -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') {
|
||||
|
||||
@ -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 () => {
|
||||
|
||||
@ -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([
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
12
src/wasm-lib/Cargo.lock
generated
@ -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",
|
||||
|
||||
@ -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 }
|
||||
|
||||
433
src/wasm-lib/kcl/fuzz/Cargo.lock
generated
@ -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 [
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
]"
|
||||
);
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)?;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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(),
|
||||
|
||||
336
src/wasm-lib/kcl/src/std/units.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 106 KiB |
BIN
src/wasm-lib/kcl/tests/outputs/serial_test_example_cm0.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
BIN
src/wasm-lib/kcl/tests/outputs/serial_test_example_ft0.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 58 KiB |
BIN
src/wasm-lib/kcl/tests/outputs/serial_test_example_inch0.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 88 KiB |
BIN
src/wasm-lib/kcl/tests/outputs/serial_test_example_m0.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
src/wasm-lib/kcl/tests/outputs/serial_test_example_mm0.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 149 KiB After Width: | Height: | Size: 150 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 62 KiB |
BIN
src/wasm-lib/kcl/tests/outputs/serial_test_example_yd0.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
@ -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], %)
|
||||
|
||||
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |