Compare commits

...

6 Commits

Author SHA1 Message Date
bdc9b7267b Update src/lang/std/sketch.ts
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2024-08-28 11:05:13 -05:00
b54a17a4fa Merge main 2024-08-28 11:05:11 -05:00
956f00c5a1 Migrate code within JS 2024-08-28 10:56:43 -05:00
c585efa024 Update docs 2024-08-28 10:54:07 -05:00
a37d395b76 Fix up tests 2024-08-28 10:54:06 -05:00
548eac57af Add 'relative: bool' to tanArcTo, remove 'to' mode from tanArc 2024-08-28 10:52:53 -05:00
37 changed files with 7975 additions and 136 deletions

View File

@ -33,8 +33,18 @@ jobs:
rust:
- 'src/wasm-lib/**'
<<<<<<< HEAD
playwright-chrome:
timeout-minutes: ${{ matrix.os == 'macos-14' && 60 || 40 }}
||||||| parent of 1f27643b (Merge main)
playwright-ubuntu:
timeout-minutes: 30
runs-on: ubuntu-latest-8-cores
=======
playwright-ubuntu:
timeout-minutes: 30
runs-on: ubuntu-latest
>>>>>>> 1f27643b (Merge main)
strategy:
fail-fast: false
matrix:

View File

@ -217149,8 +217149,8 @@
},
{
"name": "tangentialArc",
"summary": "Starting at the current sketch's origin, draw a curved line segment along",
"description": "some part of an imaginary circle of the specified radius.\nThe arc is constructed such that the last line segment is placed tangent to the imaginary circle of the specified radius. The resulting arc is the segment of the imaginary circle from that tangent point for 'offset' degrees along the imaginary circle.",
"summary": "Draw a curved line segment along some part of an imaginary circle of the specified radius.",
"description": "If `relative` is true, the curve starts at the end of the previous path segment (i.e. the location of the \"pen\" which draws these lines). If `relative` is false, starts from the current sketch's origin.\nThe arc is constructed such that the last line segment is placed tangent to the imaginary circle of the specified radius. The resulting arc is the segment of the imaginary circle from that tangent point for 'offset' degrees along the imaginary circle.",
"tags": [],
"args": [
{
@ -217177,16 +217177,6 @@
"format": "double"
}
}
},
{
"description": "A point where the arc should end. Must lie in the same plane as the current path pen position. Must not be colinear with current path pen position.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 2,
"minItems": 2
}
]
},
@ -223895,6 +223885,14 @@
},
"required": true
},
{
"name": "relative",
"type": "bool",
"schema": {
"type": "boolean"
},
"required": true
},
{
"name": "sketch_group",
"type": "SketchGroup",
@ -230575,7 +230573,7 @@
"unpublished": false,
"deprecated": false,
"examples": [
"const exampleSketch = startSketchOn('XZ')\n |> startProfileAt([0, 0], %)\n |> angledLine({ angle: 60, length: 10 }, %)\n |> tangentialArcTo([15, 15], %)\n |> line([10, -15], %)\n |> close(%)\n\nconst example = extrude(10, exampleSketch)"
"const exampleSketch = startSketchOn('XZ')\n |> startProfileAt([0, 0], %)\n |> angledLine({ angle: 60, length: 10 }, %)\n |> tangentialArcTo([15, 15], false, %)\n |> line([10, -15], %)\n |> close(%)\n\nconst example = extrude(10, exampleSketch)"
]
},
{

View File

@ -1,12 +1,12 @@
---
title: "tangentialArc"
excerpt: "Starting at the current sketch's origin, draw a curved line segment along"
excerpt: "Draw a curved line segment along some part of an imaginary circle of the specified radius."
layout: manual
---
Starting at the current sketch's origin, draw a curved line segment along
Draw a curved line segment along some part of an imaginary circle of the specified radius.
some part of an imaginary circle of the specified radius.
If `relative` is true, the curve starts at the end of the previous path segment (i.e. the location of the "pen" which draws these lines). If `relative` is false, starts from the current sketch's origin.
The arc is constructed such that the last line segment is placed tangent to the imaginary circle of the specified radius. The resulting arc is the segment of the imaginary circle from that tangent point for 'offset' degrees along the imaginary circle.
```js
@ -37,8 +37,7 @@ const example = extrude(10, exampleSketch)
offset: number,
// Radius of the arc. Not to be confused with Raiders of the Lost Ark.
radius: number,
} |
[number, number]
}
```
* `sketch_group`: `SketchGroup` - A sketch group is a collection of paths. (REQUIRED)
```js

View File

@ -9,7 +9,7 @@ Starting at the current sketch's origin, draw a curved line segment along
some part of an imaginary circle until it reaches the desired (x, y) coordinates.
```js
tangentialArcTo(to: [number], sketch_group: SketchGroup, tag?: TagDeclarator) -> SketchGroup
tangentialArcTo(to: [number], relative: bool, sketch_group: SketchGroup, tag?: TagDeclarator) -> SketchGroup
```
### Examples
@ -18,7 +18,7 @@ tangentialArcTo(to: [number], sketch_group: SketchGroup, tag?: TagDeclarator) ->
const exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> angledLine({ angle: 60, length: 10 }, %)
|> tangentialArcTo([15, 15], %)
|> tangentialArcTo([15, 15], false, %)
|> line([10, -15], %)
|> close(%)
@ -30,6 +30,7 @@ const example = extrude(10, exampleSketch)
### Arguments
* `to`: `[number]` (REQUIRED)
* `relative`: `bool` (REQUIRED)
* `sketch_group`: `SketchGroup` - A sketch group is a collection of paths. (REQUIRED)
```js
{

View File

@ -763,7 +763,7 @@ test.describe('Editor tests', () => {
`const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -14.01], %)
|> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -5.38], %)
|> tangentialArcTo([24.95, -5.38], false, %)
|> close(%)`
)
})
@ -812,7 +812,7 @@ test.describe('Editor tests', () => {
// expect the code to have changed
await expect(page.locator('.cm-content')).toHaveText(
`const sketch001 = startSketchOn('XZ') |> startProfileAt([4.61, -14.01], %) |> line([12.73, -0.09], %) |> tangentialArcTo([24.95, -5.38], %) |> close(%)const extrude001 = extrude(5, sketch001)`
`const sketch001 = startSketchOn('XZ') |> startProfileAt([4.61, -14.01], %) |> line([12.73, -0.09], %) |> tangentialArcTo([24.95, -5.38], false, %) |> close(%)const extrude001 = extrude(5, sketch001)`
)
// Now hit undo
@ -825,7 +825,7 @@ test.describe('Editor tests', () => {
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -14.01], %)
|> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -5.38], %)
|> tangentialArcTo([24.95, -5.38], false, %)
|> close(%)`)
})
@ -837,7 +837,7 @@ test.describe('Editor tests', () => {
`const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -10.01], %)
|> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -0.38], %)
|> tangentialArcTo([24.95, -5.38], false, %)
|> close(%)
|> extrude(5, %)`
)
@ -928,7 +928,7 @@ test.describe('Editor tests', () => {
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([7.12, -12.68], %)
|> line([15.39, -2.78], %)
|> tangentialArcTo([27.6, -3.05], %)
|> tangentialArcTo([27.6, -3.05], false, %)
|> close(%)
|> extrude(5, %)
`)
@ -942,7 +942,7 @@ test.describe('Editor tests', () => {
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([7.12, -12.68], %)
|> line([15.39, -2.78], %)
|> tangentialArcTo([24.95, -0.38], %)
|> tangentialArcTo([24.95, -0.38], false, %)
|> close(%)
|> extrude(5, %)`)
@ -955,7 +955,7 @@ test.describe('Editor tests', () => {
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([7.12, -12.68], %)
|> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -0.38], %)
|> tangentialArcTo([24.95, -0.38], false, %)
|> close(%)
|> extrude(5, %)
`)
@ -970,7 +970,7 @@ test.describe('Editor tests', () => {
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -10.01], %)
|> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -0.38], %)
|> tangentialArcTo([24.95, -0.38], false, %)
|> close(%)
|> extrude(5, %)`)
})

View File

@ -61,7 +61,7 @@ test.describe('Sketch tests', () => {
const part002 = startSketchOn('-XZ')
${startProfileAt3}
|> xLine(width / 4, %)
|> tangentialArcTo([width / 2, 0], %)
|> tangentialArcTo([width / 2, 0], false, %)
|> xLine(-width / 4 + wireRadius, %)
|> yLine(wireOffset, %)
|> arc({
@ -115,7 +115,7 @@ test.describe('Sketch tests', () => {
`const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -14.01], %)
|> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -5.38], %)`
|> tangentialArcTo([24.95, -5.38], false, %)`
)
})
@ -125,7 +125,7 @@ test.describe('Sketch tests', () => {
await expect(async () => {
await page.mouse.click(700, 200)
await page.getByText('tangentialArcTo([24.95, -5.38], %)').click()
await page.getByText('tangentialArcTo([24.95, -5.38], false, %)').click()
await expect(
page.getByRole('button', { name: 'Edit Sketch' })
).toBeEnabled({ timeout: 1000 })
@ -134,7 +134,7 @@ test.describe('Sketch tests', () => {
await page.waitForTimeout(600) // wait for animation
await page.getByText('tangentialArcTo([24.95, -5.38], %)').click()
await page.getByText('tangentialArcTo([24.95, -5.38], false, %)').click()
await page.keyboard.press('End')
await page.keyboard.down('Shift')
await page.keyboard.press('ArrowUp')
@ -193,7 +193,7 @@ test.describe('Sketch tests', () => {
`const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -14.01], %)
|> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -5.38], %)
|> tangentialArcTo([24.95, -5.38], false, %)
|> close(%)`
)
})
@ -235,7 +235,7 @@ test.describe('Sketch tests', () => {
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -14.01], %)
|> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -5.38], %)
|> tangentialArcTo([24.95, -5.38], false, %)
|> close(%)`)
} else {
// Ensure we don't see the code.
@ -312,7 +312,7 @@ test.describe('Sketch tests', () => {
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([6.44, -12.07], %)
|> line([14.72, 1.97], %)
|> tangentialArcTo([24.95, -5.38], %)
|> tangentialArcTo([24.95, -5.38], false, %)
|> line([1.97, 2.06], %)
|> close(%)`)
}
@ -354,7 +354,7 @@ test.describe('Sketch tests', () => {
`const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -10.01], %)
|> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -0.38], %)
|> tangentialArcTo([24.95, -0.38], false, %)
|> close(%)
|> extrude(5, %)`
)
@ -441,7 +441,7 @@ test.describe('Sketch tests', () => {
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([7.12, -12.68], %)
|> line([15.39, -2.78], %)
|> tangentialArcTo([27.6, -3.05], %)
|> tangentialArcTo([27.6, -3.05], false, %)
|> close(%)
|> extrude(5, %)
`)
@ -457,7 +457,7 @@ test.describe('Sketch tests', () => {
`const sketch001 = startSketchOn('XZ')
|> startProfileAt([4.61, -14.01], %)
|> line([12.73, -0.09], %)
|> tangentialArcTo([24.95, -5.38], %)
|> tangentialArcTo([24.95, -5.38], false, %)
|> close(%)
|> revolve({ axis: "X",}, %)`
)
@ -543,7 +543,7 @@ test.describe('Sketch tests', () => {
.toHaveText(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([6.44, -12.07], %)
|> line([14.72, 1.97], %)
|> tangentialArcTo([24.95, -5.38], %)
|> tangentialArcTo([24.95, -5.38], false, %)
|> line([1.97, 2.06], %)
|> close(%)
|> revolve({ axis: "X" }, %)`)

View File

@ -594,7 +594,7 @@ test.describe(
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
code += `
|> tangentialArcTo([21.7, -2.44], %)`
|> tangentialArcTo([21.7, -2.44], false, %)`
await expect(u.codeLocator).toHaveText(code)
// click tangential arc tool again to unequip it
@ -697,7 +697,7 @@ test.describe(
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
code += `
|> tangentialArcTo([551.2, -62.01], %)`
|> tangentialArcTo([551.2, -62.01], false, %)`
await expect(u.codeLocator).toHaveText(code)
await page

View File

@ -84,7 +84,7 @@ export const TEST_CODE_GIZMO = `const part001 = startSketchOn('XZ')
intersectTag: a,
offset: 0
}, %)
|> tangentialArcTo([13.14 + 0, 13.14], %)
|> tangentialArcTo([13.14 + 0, 13.14], false, %)
|> close(%)
|> extrude(5 + 7, %)
`

View File

@ -314,6 +314,7 @@ export function normaliseKclNumbers(code: string, ignoreZero = true): string {
return replaceNumbers(code)
}
<<<<<<< HEAD
export async function getUtils(page: Page, test_?: typeof test) {
if (!test) {
console.warn(
@ -321,6 +322,11 @@ export async function getUtils(page: Page, test_?: typeof test) {
)
}
||||||| parent of 1f27643b (Merge main)
export async function getUtils(page: Page) {
=======
export async function getUtils(page: Page) {
>>>>>>> 1f27643b (Merge main)
// Chrome devtools protocol session only works in Chromium
const browserType = page.context().browser()?.browserType().name()
const cdpSession =

View File

@ -200,7 +200,7 @@ test.describe('Testing segment overlays', () => {
intersectTag: a,
offset: 9
}, %)
|> tangentialArcTo([5 + 3.14 + 13, 20 + 3.14], %)
|> tangentialArcTo([5 + 3.14 + 13, 20 + 3.14], false, %)
`
)
})
@ -438,7 +438,7 @@ const part001 = startSketchOn('XZ')
intersectTag: a,
offset: 9
}, %)
|> tangentialArcTo([3.14 + 13, 3.14], %)
|> tangentialArcTo([3.14 + 13, 3.14], false, %)
`
)
localStorage.setItem('disableAxis', 'true')
@ -566,7 +566,7 @@ const part001 = startSketchOn('XZ')
intersectTag: a,
offset: 9
}, %)
|> tangentialArcTo([3.14 + 13, 1.14], %)
|> tangentialArcTo([3.14 + 13, 1.14], false, %)
`
)
localStorage.setItem('disableAxis', 'true')
@ -722,7 +722,7 @@ const part001 = startSketchOn('XZ')
intersectTag: a,
offset: 9
}, %)
|> tangentialArcTo([3.14 + 13, -3.14], %)
|> tangentialArcTo([3.14 + 13, -3.14], false, %)
`
)
localStorage.setItem('disableAxis', 'true')
@ -755,9 +755,10 @@ const part001 = startSketchOn('XZ')
await clickConstrained({
hoverPos: { x: tangentialArcTo.x, y: tangentialArcTo.y },
constraintType: 'xAbsolute',
expectBeforeUnconstrained: 'tangentialArcTo([3.14 + 13, -3.14], %)',
expectAfterUnconstrained: 'tangentialArcTo([16.14, -3.14], %)',
expectFinal: 'tangentialArcTo([xAbs001, -3.14], %)',
expectBeforeUnconstrained:
'tangentialArcTo([3.14 + 13, -3.14], false, %)',
expectAfterUnconstrained: 'tangentialArcTo([16.14, -3.14], false, %)',
expectFinal: 'tangentialArcTo([xAbs001, -3.14], false, %)',
ang: ang + 180,
steps: 6,
locator: '[data-overlay-toolbar-index="12"]',
@ -766,9 +767,11 @@ const part001 = startSketchOn('XZ')
await clickUnconstrained({
hoverPos: { x: tangentialArcTo.x, y: tangentialArcTo.y },
constraintType: 'yAbsolute',
expectBeforeUnconstrained: 'tangentialArcTo([xAbs001, -3.14], %)',
expectAfterUnconstrained: 'tangentialArcTo([xAbs001, yAbs001], %)',
expectFinal: 'tangentialArcTo([xAbs001, -3.14], %)',
expectBeforeUnconstrained:
'tangentialArcTo([xAbs001, -3.14], false, %)',
expectAfterUnconstrained:
'tangentialArcTo([xAbs001, yAbs001], false, %)',
expectFinal: 'tangentialArcTo([xAbs001, -3.14], false, %)',
ang: ang + 180,
steps: 10,
locator: '[data-overlay-toolbar-index="12"]',
@ -835,7 +838,7 @@ const part001 = startSketchOn('XZ')
intersectTag: a,
offset: 9
}, %)
|> tangentialArcTo([3.14 + 13, 1.14], %)
|> tangentialArcTo([3.14 + 13, 1.14], false, %)
`
)
localStorage.setItem('disableAxis', 'true')
@ -866,7 +869,7 @@ const part001 = startSketchOn('XZ')
let ang = await u.getAngle(`[data-overlay-index="${12}"]`)
await deleteSegmentSequence({
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
codeToBeDeleted: 'tangentialArcTo([3.14 + 13, 1.14], %)',
codeToBeDeleted: 'tangentialArcTo([3.14 + 13, 1.14], false, %)',
stdLibFnName: 'tangentialArcTo',
ang: ang + 180,
steps: 6,

View File

@ -479,7 +479,7 @@ const sketch002 = startSketchOn(launderExtrudeThroughVar, seg02)
intersectTag: a,
offset: 0
}, %)
|> tangentialArcTo([13.14 + 0, 13.14], %)
|> tangentialArcTo([13.14 + 0, 13.14], false, %)
|> close(%)
|> extrude(5 + 7, %)
`
@ -683,7 +683,7 @@ const extrude001 = extrude(10, sketch001)`
},
{
pos: [1107, 161],
expectedCode: 'tangentialArcTo([167.95, -28.85], %)',
expectedCode: 'tangentialArcTo([167.95, -28.85], false, %)',
},
] as const
await page.addInitScript(

View File

@ -77,7 +77,17 @@ test.describe('Testing settings', () => {
exact: true,
})
const inputLocator = page.locator('input[name="modeling-showDebugPanel"]')
<<<<<<< HEAD
||||||| parent of 1f27643b (Merge main)
// Open the settings modal with the browser keyboard shortcut
await page.keyboard.press('Meta+Shift+,')
=======
// Open the settings modal with the browser keyboard shortcut
await page.keyboard.press('Meta+Shift+,')
>>>>>>> 1f27643b (Merge main)
<<<<<<< HEAD
// Open the settings modal with the browser keyboard shortcut
await page.keyboard.press('ControlOrMeta+Shift+,')
@ -128,7 +138,58 @@ test.describe('Testing settings', () => {
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
||||||| parent of 1f27643b (Merge main)
await expect(
page.getByRole('heading', { name: 'Settings', exact: true })
).toBeVisible()
await page
.locator('select[name="app-theme"]')
.selectOption({ value: 'light' })
// Verify the toast appeared
await expect(
page.getByText(`Set theme to "light" for this project`)
).toBeVisible()
// Check that the theme changed
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
// Check that the user setting was not changed
await page.getByRole('radio', { name: 'User' }).click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('dark')
// Roll back to default "system" theme
await page
.getByText(
'themeRoll back themeRoll back to match defaultThe overall appearance of the appl'
)
.hover()
await page
.getByRole('button', {
name: 'Roll back theme',
})
.click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('system')
// Check that the project setting did not change
await page.getByRole('radio', { name: 'Project' }).click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('light')
})
test('Project settings can be opened with keybinding from the editor', async ({
page,
}) => {
const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await page
.getByRole('button', { name: 'Start Sketch' })
.waitFor({ state: 'visible' })
=======
await expect(headingLocator).toBeVisible()
await page.locator('#showDebugPanel').getByText('OffOn').click()
>>>>>>> 1f27643b (Merge main)
<<<<<<< HEAD
await test.step('Open keybindings settings', async () => {
// Open the settings modal with the browser keyboard shortcut
await page.keyboard.press('ControlOrMeta+Shift+,')
@ -141,7 +202,31 @@ test.describe('Testing settings', () => {
// Go to the hotkey for Command Palette.
const commandPalette = page.getByText('Toggle Command Palette')
await commandPalette.scrollIntoViewIfNeeded()
||||||| parent of 1f27643b (Merge main)
// Put the cursor in the editor
await page.locator('.cm-content').click()
// Open the settings modal with the browser keyboard shortcut
await page.keyboard.press('Meta+Shift+,')
await expect(
page.getByRole('heading', { name: 'Settings', exact: true })
).toBeVisible()
await page
.locator('select[name="app-theme"]')
.selectOption({ value: 'light' })
=======
// Close it and open again with keyboard shortcut, while KCL editor is focused
// Put the cursor in the editor
await test.step('Open settings with keyboard shortcut', async () => {
await page.getByTestId('settings-close-button').click()
await page.locator('.cm-content').click()
await page.keyboard.press('Meta+Shift+,')
await expect(headingLocator).toBeVisible()
})
>>>>>>> 1f27643b (Merge main)
<<<<<<< HEAD
// The heading is above it and should be in view now.
const commandPaletteHeading = page.getByRole('heading', {
name: 'Command Palette',
@ -150,6 +235,63 @@ test.describe('Testing settings', () => {
const hotkey = commandPaletteHeading.locator('+ div kbd')
const text = process.platform === 'darwin' ? 'Command+K' : 'Control+K'
await expect(hotkey).toHaveText(text)
||||||| parent of 1f27643b (Merge main)
// Verify the toast appeared
await expect(
page.getByText(`Set theme to "light" for this project`)
).toBeVisible()
// Check that the theme changed
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
// Check that the user setting was not changed
await page.getByRole('radio', { name: 'User' }).click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('dark')
// Roll back to default "system" theme
await page
.getByText(
'themeRoll back themeRoll back to match defaultThe overall appearance of the appl'
)
.hover()
await page
.getByRole('button', {
name: 'Roll back theme',
})
.click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('system')
// Check that the project setting did not change
await page.getByRole('radio', { name: 'Project' }).click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('light')
=======
// Verify the toast appeared
await expect(
page.getByText(`Set show debug panel to "false" for this project`)
).toBeVisible()
// Check that the theme changed
await expect(paneButtonLocator).not.toBeVisible()
// Check that the user setting was not changed
await page.getByRole('radio', { name: 'User' }).click()
await expect(inputLocator).toBeChecked()
// Roll back to default of "off"
await await page
.getByText('show debug panelRoll back show debug panelRoll back to match')
.hover()
await page
.getByRole('button', {
name: 'Roll back show debug panel',
})
.click()
await expect(inputLocator).not.toBeChecked()
// Check that the project setting did not change
await page.getByRole('radio', { name: 'Project' }).click()
await expect(
page.locator('input[name="modeling-showDebugPanel"]')
).not.toBeChecked()
>>>>>>> 1f27643b (Merge main)
})
test('Project and user settings can be reset', async ({ page }) => {
@ -186,6 +328,7 @@ test.describe('Testing settings', () => {
// Set project-level value to 50
await themeColorSetting.fill(settingValues.project)
<<<<<<< HEAD
// Set user-level value to 120
await userSettingsTab.click()
@ -375,7 +518,14 @@ test.describe('Testing settings', () => {
await page
.getByRole('button', { name: 'Start Sketch' })
.waitFor({ state: 'visible' })
||||||| parent of 1f27643b (Merge main)
await page
.locator('select[name="app-theme"]')
.selectOption({ value: 'light' })
=======
>>>>>>> 1f27643b (Merge main)
<<<<<<< HEAD
const userSettingsTab = page.getByRole('radio', { name: 'User' })
// Open the settings modal with lower-right button
@ -383,13 +533,39 @@ test.describe('Testing settings', () => {
await expect(
page.getByRole('heading', { name: 'Settings', exact: true })
).toBeVisible()
||||||| parent of 1f27643b (Merge main)
// Verify the toast appeared
await expect(
page.getByText(`Set theme to "light" for this project`)
).toBeVisible()
// Check that the theme changed
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
await expect(page.locator('select[name="app-theme"]')).toHaveValue('light')
=======
// Set user-level value to 120
await userSettingsTab.click()
await themeColorSetting.fill(settingValues.user)
await projectSettingsTab.click()
})
>>>>>>> 1f27643b (Merge main)
<<<<<<< HEAD
const resetButton = page.getByRole('button', {
name: 'Restore default settings',
})
// Default unit should be mm
await resetButton.click()
||||||| parent of 1f27643b (Merge main)
// Check that the user setting was not changed
await page.getByRole('radio', { name: 'User' }).click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('system')
=======
await test.step('Reset project settings', async () => {
// Click the reset settings button.
await resetButton.click()
>>>>>>> 1f27643b (Merge main)
<<<<<<< HEAD
await test.step('Change modeling default unit within project tab', async () => {
const changeUnitOfMeasureInProjectTab = async (unitOfMeasure: string) => {
await test.step(`Set modeling default unit to ${unitOfMeasure}`, async () => {
@ -409,7 +585,15 @@ test.describe('Testing settings', () => {
await changeUnitOfMeasureInProjectTab('cm')
await changeUnitOfMeasureInProjectTab('m')
})
||||||| parent of 1f27643b (Merge main)
// Click the reset settings button.
await page.getByRole('button', { name: 'Restore default settings' }).click()
=======
// Verify it is now set to the inherited user value
await expect(themeColorSetting).toHaveValue(settingValues.default)
>>>>>>> 1f27643b (Merge main)
<<<<<<< HEAD
// Go to the user tab
await userSettingsTab.click()
await test.step('Change modeling default unit within user tab', async () => {
@ -431,11 +615,33 @@ test.describe('Testing settings', () => {
await changeUnitOfMeasureInUserTab('cm')
await changeUnitOfMeasureInUserTab('m')
})
||||||| parent of 1f27643b (Merge main)
// Verify it is now set to the default value
await expect(page.locator('select[name="app-theme"]')).toHaveValue('system')
=======
// Check that the user setting also rolled back
await userSettingsTab.click()
await expect(themeColorSetting).toHaveValue(settingValues.default)
await projectSettingsTab.click()
>>>>>>> 1f27643b (Merge main)
<<<<<<< HEAD
// Close settings
const settingsCloseButton = page.getByTestId('settings-close-button')
await settingsCloseButton.click()
||||||| parent of 1f27643b (Merge main)
// Set the user theme to light.
await page
.locator('select[name="app-theme"]')
.selectOption({ value: 'light' })
=======
// Set project-level value to 50 again to test the user-level reset
await themeColorSetting.fill(settingValues.project)
await userSettingsTab.click()
})
>>>>>>> 1f27643b (Merge main)
<<<<<<< HEAD
await test.step('Change modeling default unit within command bar', async () => {
const commands = page.getByRole('button', { name: 'Commands' })
const changeUnitOfMeasureInCommandBar = async (unitOfMeasure: string) => {
@ -445,13 +651,36 @@ test.describe('Testing settings', () => {
'Settings · modeling · default unit'
)
await settingsModelingDefaultUnitCommand.click()
||||||| parent of 1f27643b (Merge main)
// Verify the toast appeared
await expect(
page.getByText(`Set theme to "light" as a user default`)
).toBeVisible()
// Check that the theme changed
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
await expect(page.locator('select[name="app-theme"]')).toHaveValue('light')
=======
await test.step('Reset user settings', async () => {
// Change the setting and click the reset settings button.
await themeColorSetting.fill(settingValues.user)
await resetButton.click()
>>>>>>> 1f27643b (Merge main)
<<<<<<< HEAD
const commandOption = page.getByRole('option', {
name: unitOfMeasure,
exact: true,
})
await commandOption.click()
||||||| parent of 1f27643b (Merge main)
await page.getByRole('radio', { name: 'Project' }).click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('light')
=======
// Verify it is now set to the default value
await expect(themeColorSetting).toHaveValue(settingValues.default)
>>>>>>> 1f27643b (Merge main)
<<<<<<< HEAD
const toastMessage = page.getByText(
`Set default unit to "${unitOfMeasure}" for this project`
)
@ -491,6 +720,25 @@ test.describe('Testing settings', () => {
await changeUnitOfMeasureInGizmo('mm', 'Millimeters')
await changeUnitOfMeasureInGizmo('cm', 'Centimeters')
await changeUnitOfMeasureInGizmo('m', 'Meters')
||||||| parent of 1f27643b (Merge main)
// Click the reset settings button.
await page.getByRole('button', { name: 'Restore default settings' }).click()
// Verify it is now set to the default value
await expect(page.locator('select[name="app-theme"]')).toHaveValue('system')
await page.getByRole('radio', { name: 'User' }).click()
await expect(page.locator('select[name="app-theme"]')).toHaveValue('system')
// Click the reset settings button.
await page.getByRole('button', { name: 'Restore default settings' }).click()
// Verify it is now set to the default value
await expect(page.locator('select[name="app-theme"]')).toHaveValue('system')
=======
// Check that the project setting also changed
await projectSettingsTab.click()
await expect(themeColorSetting).toHaveValue(settingValues.default)
>>>>>>> 1f27643b (Merge main)
})
})
})

7295
src-tauri/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

47
src-tauri/Cargo.toml Normal file
View File

@ -0,0 +1,47 @@
[package]
name = "app"
version = "0.1.0"
description = "The Zoo Modeling App"
authors = ["Zoo Engineers <eng@zoo.dev>"]
license = ""
repository = "https://github.com/KittyCAD/modeling-app"
default-run = "app"
edition = "2021"
rust-version = "1.70"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
tauri-build = { version = "2.0.0-beta.18", features = [] }
[dependencies]
anyhow = "1"
kcl-lib = { version = "0.2", path = "../src/wasm-lib/kcl" }
kittycad = "0.3.12"
log = "0.4.21"
mdns-sd = "0.11.1"
oauth2 = "4.4.2"
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
serde_json = "1.0"
tauri = { version = "2.0.0-beta.23", features = [ "devtools", "unstable"] }
tauri-plugin-cli = { version = "2.0.0-beta.7" }
tauri-plugin-deep-link = { version = "2.0.0-beta.8" }
tauri-plugin-dialog = { version = "2.0.0-beta.6" }
tauri-plugin-fs = { version = "2.0.0-beta.10" }
tauri-plugin-http = { version = "2.0.0-beta.11" }
tauri-plugin-log = { version = "2.0.0-beta.7" }
tauri-plugin-os = { version = "2.0.0-beta.7" }
tauri-plugin-persisted-scope = { version = "2.0.0-beta.10" }
tauri-plugin-process = { version = "2.0.0-beta.7" }
tauri-plugin-shell = { version = "2.0.0-beta.8" }
tauri-plugin-updater = { version = "2.0.0-beta.9" }
tokio = { version = "1.37.0", features = ["time", "fs", "process"] }
toml = "0.8.2"
url = "2.5.0"
[features]
default = ["updater"]
# this feature is used for production builds or when `devPath` points to the filesystem and the built-in dev server is disabled.
# If you use cargo directly instead of tauri's cli you can use this feature flag to switch between tauri's `dev` and `build` modes.
# DO NOT REMOVE!!
custom-protocol = ["tauri/custom-protocol"]
updater = []

View File

@ -28,6 +28,12 @@ import { CoreDumpManager } from 'lib/coredump'
import { UnitsMenu } from 'components/UnitsMenu'
export function App() {
<<<<<<< HEAD
||||||| parent of 1f27643b (Merge main)
useRefreshSettings(paths.FILE + 'SETTINGS')
=======
useRefreshSettings(PATHS.FILE + 'SETTINGS')
>>>>>>> 1f27643b (Merge main)
const { project, file } = useLoaderData() as IndexLoaderData
useRefreshSettings(PATHS.FILE + 'SETTINGS')
const navigate = useNavigate()
@ -62,7 +68,14 @@ export function App() {
e.preventDefault()
})
useHotkeyWrapper(
<<<<<<< HEAD
[isDesktop() ? 'mod + ,' : 'shift + mod + ,'],
||||||| parent of 1f27643b (Merge main)
[isTauri() ? 'mod + ,' : 'shift + mod + ,'],
() => navigate(filePath + paths.SETTINGS),
=======
[isTauri() ? 'mod + ,' : 'shift + mod + ,'],
>>>>>>> 1f27643b (Merge main)
() => navigate(filePath + PATHS.SETTINGS),
{
splitKey: '|',

View File

@ -76,13 +76,27 @@ const router = createRouter([
// Redirect to the file if we have a file path.
if (projectStartupFile.length > 0) {
return redirect(
<<<<<<< HEAD
PATHS.FILE + '/' + encodeURIComponent(projectStartupFile)
||||||| parent of 1f27643b (Merge main)
paths.FILE + '/' + encodeURIComponent(appState.current_file)
=======
PATHS.FILE + '/' + encodeURIComponent(appState.current_file)
>>>>>>> 1f27643b (Merge main)
)
}
}
}
<<<<<<< HEAD
return onDesktop
||||||| parent of 1f27643b (Merge main)
return inTauri
? redirect(paths.HOME)
: redirect(paths.FILE + '/%2F' + BROWSER_PROJECT_NAME)
=======
return inTauri
>>>>>>> 1f27643b (Merge main)
? redirect(PATHS.HOME)
: redirect(PATHS.FILE + '/%2F' + BROWSER_PROJECT_NAME)
},

View File

@ -53,10 +53,18 @@ export const FileMachineProvider = ({
if (event.data && 'name' in event.data) {
commandBarSend({ type: 'Close' })
navigate(
<<<<<<< HEAD
`..${PATHS.FILE}/${encodeURIComponent(
context.selectedDirectory +
window.electron.path.sep +
event.data.name
||||||| parent of 1f27643b (Merge main)
`${paths.FILE}/${encodeURIComponent(
context.selectedDirectory + sep() + event.data.name
=======
`${PATHS.FILE}/${encodeURIComponent(
context.selectedDirectory + sep() + event.data.name
>>>>>>> 1f27643b (Merge main)
)}`
)
} else if (
@ -65,7 +73,13 @@ export const FileMachineProvider = ({
event.data.path.endsWith(FILE_EXT)
) {
// Don't navigate to newly created directories
<<<<<<< HEAD
navigate(`..${PATHS.FILE}/${encodeURIComponent(event.data.path)}`)
||||||| parent of 1f27643b (Merge main)
navigate(`${paths.FILE}/${encodeURIComponent(event.data.path)}`)
=======
navigate(`${PATHS.FILE}/${encodeURIComponent(event.data.path)}`)
>>>>>>> 1f27643b (Merge main)
}
},
addFileToRenamingQueue: assign({
@ -198,13 +212,29 @@ export const FileMachineProvider = ({
if (oldPath === file.path && project?.path) {
// If we just renamed the current file, navigate to the new path
<<<<<<< HEAD
navigate(`..${PATHS.FILE}/${encodeURIComponent(newPath)}`)
||||||| parent of 1f27643b (Merge main)
navigate(paths.FILE + '/' + encodeURIComponent(newPath))
=======
navigate(PATHS.FILE + '/' + encodeURIComponent(newPath))
>>>>>>> 1f27643b (Merge main)
} else if (file?.path.includes(oldPath)) {
// If we just renamed a directory that the current file is in, navigate to the new path
navigate(
<<<<<<< HEAD
`..${PATHS.FILE}/${encodeURIComponent(
file.path.replace(oldPath, newPath)
)}`
||||||| parent of 1f27643b (Merge main)
paths.FILE +
'/' +
encodeURIComponent(file.path.replace(oldPath, newDirPath))
=======
PATHS.FILE +
'/' +
encodeURIComponent(file.path.replace(oldPath, newDirPath))
>>>>>>> 1f27643b (Merge main)
)
}
@ -260,7 +290,13 @@ export const FileMachineProvider = ({
file?.path.includes(event.data.path)) &&
project?.path
) {
<<<<<<< HEAD
navigate(`../${PATHS.FILE}/${encodeURIComponent(project.path)}`)
||||||| parent of 1f27643b (Merge main)
navigate(paths.FILE + '/' + encodeURIComponent(project.path))
=======
navigate(PATHS.FILE + '/' + encodeURIComponent(project.path))
>>>>>>> 1f27643b (Merge main)
}
return `Successfully deleted ${isDir ? 'folder' : 'file'} "${

View File

@ -1,4 +1,11 @@
<<<<<<< HEAD
import type { IndexLoaderData } from 'lib/types'
||||||| parent of 1f27643b (Merge main)
import type { FileEntry, IndexLoaderData } from 'lib/types'
import { paths } from 'lib/paths'
=======
import type { FileEntry, IndexLoaderData } from 'lib/types'
>>>>>>> 1f27643b (Merge main)
import { PATHS } from 'lib/paths'
import { ActionButton } from './ActionButton'
import Tooltip from './Tooltip'
@ -476,10 +483,16 @@ export const FileTreeInner = ({
}, [documentHasFocus])
return (
<<<<<<< HEAD
<div
className="overflow-auto pb-12 absolute inset-0"
data-testid="file-pane-scroll-container"
>
||||||| parent of 1f27643b (Merge main)
<div className="overflow-auto max-h-full pb-12">
=======
<div className="overflow-auto pb-12 absolute inset-0">
>>>>>>> 1f27643b (Merge main)
<ul
className="m-0 p-0 text-sm"
onClickCapture={(e) => {

View File

@ -3,8 +3,16 @@ import Tooltip from './Tooltip'
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
import { CustomIcon } from './CustomIcon'
import { useLocation, useNavigate } from 'react-router-dom'
<<<<<<< HEAD
import { PATHS } from 'lib/paths'
import { createAndOpenNewProject } from 'lib/desktopFS'
||||||| parent of 1f27643b (Merge main)
import { createAndOpenNewProject } from 'lib/tauriFS'
import { paths } from 'lib/paths'
=======
import { createAndOpenNewProject } from 'lib/tauriFS'
import { PATHS } from 'lib/paths'
>>>>>>> 1f27643b (Merge main)
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
import { useLspContext } from './LspProvider'
import { openExternalBrowserIfDesktop } from 'lib/openWindow'

View File

@ -93,7 +93,15 @@ export function LowerRightControls({
<Link
to={
location.pathname.includes(PATHS.FILE)
<<<<<<< HEAD
? filePath + PATHS.SETTINGS + '?tab=project'
||||||| parent of 1f27643b (Merge main)
location.pathname.includes(paths.FILE)
? filePath + paths.SETTINGS + '?tab=project'
: paths.HOME + paths.SETTINGS
=======
? filePath + PATHS.SETTINGS_PROJECT
>>>>>>> 1f27643b (Merge main)
: PATHS.HOME + PATHS.SETTINGS
}
data-testid="settings-link"

View File

@ -15,7 +15,14 @@ import { Extension } from '@codemirror/state'
import { LanguageSupport } from '@codemirror/language'
import { useNavigate } from 'react-router-dom'
import { PATHS } from 'lib/paths'
<<<<<<< HEAD
import { FileEntry } from 'lib/project'
||||||| parent of 1f27643b (Merge main)
import { paths } from 'lib/paths'
import { FileEntry } from 'lib/types'
=======
import { FileEntry } from 'lib/types'
>>>>>>> 1f27643b (Merge main)
import Worker from 'editor/plugins/lsp/worker.ts?worker'
import {
KclWorkerOptions,

View File

@ -2,7 +2,14 @@ import { Popover, Transition } from '@headlessui/react'
import { ActionButton, ActionButtonProps } from './ActionButton'
import { type IndexLoaderData } from 'lib/types'
import { PATHS } from 'lib/paths'
<<<<<<< HEAD
import { isDesktop } from '../lib/isDesktop'
||||||| parent of 1f27643b (Merge main)
import { paths } from 'lib/paths'
import { isTauri } from '../lib/isTauri'
=======
import { isTauri } from '../lib/isTauri'
>>>>>>> 1f27643b (Merge main)
import { Link, useLocation, useNavigate } from 'react-router-dom'
import { Fragment, useMemo } from 'react'
import { Logo } from './Logo'

View File

@ -15,8 +15,16 @@ import { SettingsFieldInput } from './SettingsFieldInput'
import { getInitialDefaultDir } from 'lib/desktop'
import toast from 'react-hot-toast'
import { APP_VERSION } from 'routes/Settings'
<<<<<<< HEAD
import { PATHS } from 'lib/paths'
import { createAndOpenNewProject, getSettingsFolderPaths } from 'lib/desktopFS'
||||||| parent of 1f27643b (Merge main)
import { createAndOpenNewProject, getSettingsFolderPaths } from 'lib/tauriFS'
import { paths } from 'lib/paths'
=======
import { createAndOpenNewProject, getSettingsFolderPaths } from 'lib/tauriFS'
import { PATHS } from 'lib/paths'
>>>>>>> 1f27643b (Merge main)
import { useDotDotSlash } from 'hooks/useDotDotSlash'
import { ForwardedRef, forwardRef, useEffect } from 'react'
import { useLspContext } from 'components/LspProvider'
@ -45,12 +53,20 @@ export const AllSettingsFields = forwardRef(
location.pathname
.replace(PATHS.FILE + '/', '')
.replace(PATHS.SETTINGS, '')
<<<<<<< HEAD
.slice(
0,
decodeURI(location.pathname).lastIndexOf(
window.electron.path.sep
)
)
||||||| parent of 1f27643b (Merge main)
.replace(paths.FILE + '/', '')
.replace(paths.SETTINGS, '')
.slice(0, decodeURI(location.pathname).lastIndexOf(sep()))
=======
.slice(0, decodeURI(location.pathname).lastIndexOf(sep()))
>>>>>>> 1f27643b (Merge main)
)
: undefined

View File

@ -1,5 +1,12 @@
import { useMachine } from '@xstate/react'
<<<<<<< HEAD
import { useNavigate, useRouteLoaderData, useLocation } from 'react-router-dom'
||||||| parent of 1f27643b (Merge main)
import { useNavigate, useRouteLoaderData } from 'react-router-dom'
import { paths } from 'lib/paths'
=======
import { useNavigate, useRouteLoaderData } from 'react-router-dom'
>>>>>>> 1f27643b (Merge main)
import { PATHS } from 'lib/paths'
import { authMachine, TOKEN_PERSIST_KEY } from '../machines/authMachine'
import withBaseUrl from '../lib/withBaseURL'
@ -302,7 +309,14 @@ export const SettingsAuthProviderBase = ({
logout()
},
goToIndexPage: () => {
<<<<<<< HEAD
if (location.pathname.includes(PATHS.SIGN_IN)) {
||||||| parent of 1f27643b (Merge main)
if (window.location.pathname.includes(paths.SIGN_IN)) {
navigate(paths.INDEX)
=======
if (window.location.pathname.includes(PATHS.SIGN_IN)) {
>>>>>>> 1f27643b (Merge main)
navigate(PATHS.INDEX)
}
},

View File

@ -584,7 +584,7 @@ describe('Testing removeSingleConstraintInfo', () => {
intersectTag: a,
offset: 0 + 0
}, %)
|> tangentialArcTo([3.14 + 0, 13.14 + 0], %)`
|> tangentialArcTo([3.14 + 0, 13.14 + 0], false, %)`
test.each([
[' line([3 + 0, 4], %)', 'arrayIndex', 1],
[
@ -626,7 +626,7 @@ describe('Testing removeSingleConstraintInfo', () => {
'objectProperty',
'offset',
],
['tangentialArcTo([3.14 + 0, 13.14], %)', 'arrayIndex', 1],
['tangentialArcTo([3.14 + 0, 13.14], false, %)', 'arrayIndex', 1],
])('stdlib fn: %s', async (expectedFinish, key, value) => {
const ast = parse(code)
if (err(ast)) throw ast

View File

@ -256,6 +256,14 @@ const runFilletTest = async (
return new Error('Path to extrude node not found')
}
<<<<<<< HEAD
||||||| parent of 1f27643b (Merge main)
// const radius = createLiteral(5) as Value
=======
// const radius = createLiteral(5) as Expr
>>>>>>> 1f27643b (Merge main)
const result = addFillet(ast, pathToSegmentNode, pathToExtrudeNode, radius)
if (err(result)) {
return result
@ -278,8 +286,8 @@ describe('Testing addFillet', () => {
|> line([60.04, -55.72], %)
|> line([1.29, -115.74], %)
|> line([-87.24, -47.08], %)
|> tangentialArcTo([56.15, -94.58], %)
|> tangentialArcTo([14.68, -104.52], %)
|> tangentialArcTo([56.15, -94.58], false, %)
|> tangentialArcTo([14.68, -104.52], false, %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
const extrude001 = extrude(50, sketch001)
@ -293,8 +301,8 @@ describe('Testing addFillet', () => {
|> line([60.04, -55.72], %, $seg01)
|> line([1.29, -115.74], %)
|> line([-87.24, -47.08], %)
|> tangentialArcTo([56.15, -94.58], %)
|> tangentialArcTo([14.68, -104.52], %)
|> tangentialArcTo([56.15, -94.58], false, %)
|> tangentialArcTo([14.68, -104.52], false, %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
const extrude001 = extrude(50, sketch001)
@ -321,8 +329,8 @@ const extrude001 = extrude(50, sketch001)
|> line([60.04, -55.72], %)
|> line([1.29, -115.74], %)
|> line([-87.24, -47.08], %, $seg01)
|> tangentialArcTo([56.15, -94.58], %)
|> tangentialArcTo([14.68, -104.52], %)
|> tangentialArcTo([56.15, -94.58], false, %)
|> tangentialArcTo([14.68, -104.52], false, %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
const extrude001 = extrude(50, sketch001)
@ -336,8 +344,8 @@ const extrude001 = extrude(50, sketch001)
|> line([60.04, -55.72], %, $seg02)
|> line([1.29, -115.74], %)
|> line([-87.24, -47.08], %, $seg01)
|> tangentialArcTo([56.15, -94.58], %)
|> tangentialArcTo([14.68, -104.52], %)
|> tangentialArcTo([56.15, -94.58], false, %)
|> tangentialArcTo([14.68, -104.52], false, %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
const extrude001 = extrude(50, sketch001)
@ -364,8 +372,8 @@ const extrude001 = extrude(50, sketch001)
|> line([60.04, -55.72], %)
|> line([1.29, -115.74], %)
|> line([-87.24, -47.08], %, $seg03)
|> tangentialArcTo([56.15, -94.58], %)
|> tangentialArcTo([14.68, -104.52], %)
|> tangentialArcTo([56.15, -94.58], false, %)
|> tangentialArcTo([14.68, -104.52], false, %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
const extrude001 = extrude(50, sketch001)
@ -379,8 +387,8 @@ const extrude001 = extrude(50, sketch001)
|> line([60.04, -55.72], %)
|> line([1.29, -115.74], %)
|> line([-87.24, -47.08], %, $seg03)
|> tangentialArcTo([56.15, -94.58], %)
|> tangentialArcTo([14.68, -104.52], %)
|> tangentialArcTo([56.15, -94.58], false, %)
|> tangentialArcTo([14.68, -104.52], false, %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
const extrude001 = extrude(50, sketch001)
@ -406,8 +414,8 @@ const extrude001 = extrude(50, sketch001)
|> line([60.04, -55.72], %)
|> line([1.29, -115.74], %)
|> line([-87.24, -47.08], %, $seg03)
|> tangentialArcTo([56.15, -94.58], %)
|> tangentialArcTo([14.68, -104.52], %)
|> tangentialArcTo([56.15, -94.58], false, %)
|> tangentialArcTo([14.68, -104.52], false, %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
const extrude001 = extrude(50, sketch001)
@ -421,8 +429,8 @@ const extrude001 = extrude(50, sketch001)
|> line([60.04, -55.72], %, $seg01)
|> line([1.29, -115.74], %)
|> line([-87.24, -47.08], %, $seg03)
|> tangentialArcTo([56.15, -94.58], %)
|> tangentialArcTo([14.68, -104.52], %)
|> tangentialArcTo([56.15, -94.58], false, %)
|> tangentialArcTo([14.68, -104.52], false, %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
const extrude001 = extrude(50, sketch001)

View File

@ -4,7 +4,12 @@ import {
ObjectExpression,
PathToNode,
Program,
<<<<<<< HEAD
ProgramMemory,
||||||| parent of 1f27643b (Merge main)
Value,
=======
>>>>>>> 1f27643b (Merge main)
Expr,
VariableDeclaration,
VariableDeclarator,
@ -159,7 +164,15 @@ export function addFillet(
ast: Program,
pathToSegmentNode: PathToNode,
pathToExtrudeNode: PathToNode,
<<<<<<< HEAD
radius: Expr = createLiteral(5)
||||||| parent of 1f27643b (Merge main)
radius = createLiteral(5) as Value
// shouldPipe = false, // TODO: Implement this feature
=======
radius = createLiteral(5) as Expr
// shouldPipe = false, // TODO: Implement this feature
>>>>>>> 1f27643b (Merge main)
): { modifiedAst: Program; pathToFilletNode: PathToNode } | Error {
// Clone AST to ensure safe mutations
const astClone = structuredClone(ast)

View File

@ -270,7 +270,7 @@ describe('testing getConstraintInfo', () => {
intersectTag: 'a',
offset: 0
}, %)
|> tangentialArcTo([3.14, 13.14], %)`
|> tangentialArcTo([3.14, 13.14], false, %)`
const ast = parse(code)
test.each([
[
@ -629,7 +629,7 @@ describe('testing getConstraintInfo', () => {
intersectTag: 'a',
offset: 0
}, %)
|> tangentialArcTo([3.14, 13.14], %)`
|> tangentialArcTo([3.14, 13.14], false, %)`
const ast = parse(code)
test.each([
[
@ -783,7 +783,7 @@ describe('testing getConstraintInfo', () => {
intersectTag: 'a',
offset: 0 + 0
}, %)
|> tangentialArcTo([3.14 + 0, 13.14 + 0], %)`
|> tangentialArcTo([3.14 + 0, 13.14 + 0], false, %)`
const ast = parse(code)
test.each([
[

View File

@ -812,6 +812,7 @@ export const tangentialArcTo: SketchLineHelper = {
}
const newLine = createCallExpression('tangentialArcTo', [
createArrayExpression([toX, toY]),
createLiteral(false),
createPipeSubstitution(),
])
if (pipe.type === 'PipeExpression') {

View File

@ -25,6 +25,7 @@ type OnboardingPaths = {
const SETTINGS = '/settings' as const
<<<<<<< HEAD
export type ProjectRoute = {
projectName: string | null
projectPath: string
@ -32,6 +33,10 @@ export type ProjectRoute = {
currentFilePath: string | null
}
||||||| parent of 1f27643b (Merge main)
export const paths = {
=======
>>>>>>> 1f27643b (Merge main)
export const PATHS = {
INDEX: '/',
HOME: '/home',

View File

@ -1,7 +1,15 @@
import { ActionFunction, LoaderFunction, redirect } from 'react-router-dom'
import { FileLoaderData, HomeLoaderData, IndexLoaderData } from './types'
<<<<<<< HEAD
import { getProjectMetaByRouteId, PATHS } from './paths'
import { isDesktop } from './isDesktop'
||||||| parent of 1f27643b (Merge main)
import { isTauri } from './isTauri'
import { getProjectMetaByRouteId, paths } from './paths'
=======
import { isTauri } from './isTauri'
import { getProjectMetaByRouteId, PATHS } from './paths'
>>>>>>> 1f27643b (Merge main)
import { BROWSER_PATH } from 'lib/paths'
import {
BROWSER_FILE_NAME,
@ -10,6 +18,15 @@ import {
} from 'lib/constants'
import { loadAndValidateSettings } from './settings/settingsUtils'
import makeUrlPathRelative from './makeUrlPathRelative'
<<<<<<< HEAD
||||||| parent of 1f27643b (Merge main)
import { sep } from '@tauri-apps/api/path'
import { readTextFile } from '@tauri-apps/plugin-fs'
import { codeManager, kclManager } from 'lib/singletons'
=======
import { sep } from '@tauri-apps/api/path'
import { readTextFile } from '@tauri-apps/plugin-fs'
>>>>>>> 1f27643b (Merge main)
import { codeManager } from 'lib/singletons'
import { fileSystemManager } from 'lang/std/fileSystemManager'
import {
@ -86,6 +103,7 @@ export const fileLoader: LoaderFunction = async (
const { projectName, projectPath, currentFileName, currentFilePath } =
projectPathData
<<<<<<< HEAD
const urlObj = new URL(routerData.request.url)
let code = ''
@ -120,8 +138,50 @@ export const fileLoader: LoaderFunction = async (
// the file system and not the editor.
codeManager.updateCurrentFilePath(currentFilePath)
codeManager.updateCodeStateEditor(code)
||||||| parent of 1f27643b (Merge main)
if (!current_file_name || !current_file_path || !project_name) {
return redirect(
`${paths.FILE}/${encodeURIComponent(
`${params.id}${isTauri() ? sep() : '/'}${PROJECT_ENTRYPOINT}`
)}`
)
=======
if (!current_file_name || !current_file_path || !project_name) {
return redirect(
`${PATHS.FILE}/${encodeURIComponent(
`${params.id}${isTauri() ? sep() : '/'}${PROJECT_ENTRYPOINT}`
)}`
)
>>>>>>> 1f27643b (Merge main)
}
<<<<<<< HEAD
||||||| parent of 1f27643b (Merge main)
// TODO: PROJECT_ENTRYPOINT is hardcoded
// until we support setting a project's entrypoint file
const code = await readTextFile(current_file_path)
// Update both the state and the editor's code.
// We explicitly do not write to the file here since we are loading from
// the file system and not the editor.
codeManager.updateCurrentFilePath(current_file_path)
codeManager.updateCodeStateEditor(code)
// We don't want to call await on execute code since we don't want to block the UI
kclManager.executeCode(true)
=======
// TODO: PROJECT_ENTRYPOINT is hardcoded
// until we support setting a project's entrypoint file
const code = await readTextFile(current_file_path)
// Update both the state and the editor's code.
// We explicitly do not write to the file here since we are loading from
// the file system and not the editor.
codeManager.updateCurrentFilePath(current_file_path)
codeManager.updateCodeStateEditor(code)
>>>>>>> 1f27643b (Merge main)
// Set the file system manager to the project path
// So that WASM gets an updated path for operations
fileSystemManager.dir = projectPath
@ -181,7 +241,14 @@ export const fileLoader: LoaderFunction = async (
export const homeLoader: LoaderFunction = async (): Promise<
HomeLoaderData | Response
> => {
<<<<<<< HEAD
if (!isDesktop()) {
||||||| parent of 1f27643b (Merge main)
if (!isTauri()) {
return redirect(paths.FILE + '/%2F' + BROWSER_PROJECT_NAME)
=======
if (!isTauri()) {
>>>>>>> 1f27643b (Merge main)
return redirect(PATHS.FILE + '/%2F' + BROWSER_PROJECT_NAME)
}
const { configuration } = await loadAndValidateSettings()

View File

@ -42,7 +42,12 @@ import { Project } from 'lib/project'
// This route only opens in the desktop context for now,
// as defined in Router.tsx, so we can use the desktop APIs and types.
const Home = () => {
<<<<<<< HEAD
const { projects: loadedProjects } = useLoaderData() as HomeLoaderData
||||||| parent of 1f27643b (Merge main)
useRefreshSettings(paths.HOME + 'SETTINGS')
=======
>>>>>>> 1f27643b (Merge main)
useRefreshSettings(PATHS.HOME + 'SETTINGS')
const { commandBarSend } = useCommandsContext()
const navigate = useNavigate()
@ -60,7 +65,14 @@ const Home = () => {
e.preventDefault()
})
useHotkeys(
<<<<<<< HEAD
isDesktop() ? 'mod+,' : 'shift+mod+,',
||||||| parent of 1f27643b (Merge main)
isTauri() ? 'mod+,' : 'shift+mod+,',
() => navigate(paths.HOME + paths.SETTINGS),
=======
isTauri() ? 'mod+,' : 'shift+mod+,',
>>>>>>> 1f27643b (Merge main)
() => navigate(PATHS.HOME + PATHS.SETTINGS),
{
splitKey: '|',
@ -280,8 +292,14 @@ const Home = () => {
<p className="my-4 text-sm text-chalkboard-80 dark:text-chalkboard-30">
Loaded from{' '}
<Link
<<<<<<< HEAD
data-testid="project-directory-settings-link"
to={`${PATHS.HOME + PATHS.SETTINGS_USER}#projectDirectory`}
||||||| parent of 1f27643b (Merge main)
to="settings?tab=user#projectDirectory"
=======
to={`${PATHS.SETTINGS_USER}#projectDirectory`}
>>>>>>> 1f27643b (Merge main)
className="text-chalkboard-90 dark:text-chalkboard-20 underline underline-offset-2"
>
{settings.app.projectDirectory.current}

View File

@ -3309,7 +3309,7 @@ mod snapshot_tests {
a,
r#"const boxSketch = startSketchAt([0, 0])
|> line([0, 10], %)
|> tangentialArc([-5, 5], %)
|> tangentialArcTo([-5, 5], true, %)
|> line([5, -15], %)
|> extrude(10, %)
"#

View File

@ -5,7 +5,7 @@ use kittycad::types::OkWebSocketResponseData;
use serde::de::DeserializeOwned;
use crate::{
ast::types::{parse_json_number_as_f64, TagDeclarator},
ast::types::{parse_json_number_as_f64, KclNone, TagDeclarator},
errors::{KclError, KclErrorDetails},
executor::{
DynamicState, ExecutorContext, ExtrudeGroup, ExtrudeGroupSet, ExtrudeSurface, KclValue, Metadata,
@ -495,6 +495,9 @@ where
{
fn from_args(args: &'a Args, i: usize) -> Result<Self, KclError> {
let Some(arg) = args.args.get(i) else { return Ok(None) };
if let Some(_kcl_none) = KclNone::from_mem_item(arg) {
return Ok(None);
}
let Some(val) = T::from_mem_item(arg) else {
return Err(KclError::Semantic(KclErrorDetails {
message: format!(
@ -624,6 +627,7 @@ impl_from_arg_via_json!(u32);
impl_from_arg_via_json!(u64);
impl_from_arg_via_json!(f64);
impl_from_arg_via_json!(bool);
impl_from_arg_via_json!(KclNone);
impl_from_arg_for_array!(2);
impl_from_arg_for_array!(3);

View File

@ -14,7 +14,7 @@ use crate::{
errors::{KclError, KclErrorDetails},
executor::{
BasePath, ExtrudeGroup, Face, GeoMeta, KclValue, Path, Plane, PlaneType, Point2d, Point3d, SketchGroup,
SketchGroupSet, SketchSurface, SourceRange, TagEngineInfo, TagIdentifier, UserVal,
SketchGroupSet, SketchSurface, TagEngineInfo, TagIdentifier, UserVal,
},
std::{
utils::{
@ -1634,8 +1634,6 @@ pub enum TangentialArcData {
/// Offset of the arc, in degrees.
offset: f64,
},
/// A point where the arc should end. Must lie in the same plane as the current path pen position. Must not be colinear with current path pen position.
Point([f64; 2]),
}
/// Draw a tangential arc.
@ -1647,8 +1645,11 @@ pub async fn tangential_arc(args: Args) -> Result<KclValue, KclError> {
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
/// some part of an imaginary circle of the specified radius.
/// Draw a curved line segment along some part of an imaginary circle of the specified radius.
///
/// If `relative` is true, the curve starts at the end of the previous path segment
/// (i.e. the location of the "pen" which draws these lines). If `relative` is false,
/// starts from the current sketch's origin.
///
/// The arc is constructed such that the last line segment is placed tangent
/// to the imaginary circle of the specified radius. The resulting arc is the
@ -1728,13 +1729,6 @@ async fn inner_tangential_arc(
.await?;
(center, to.into(), ccw)
}
TangentialArcData::Point(to) => {
args.batch_modeling_cmd(id, tan_arc_to(&sketch_group, &to)).await?;
// TODO: Figure out these calculations.
let ccw = false;
let center = Point2d { x: 0.0, y: 0.0 };
(center, to, ccw)
}
};
let current_path = Path::TangentialArc {
@ -1775,32 +1769,13 @@ fn tan_arc_to(sketch_group: &SketchGroup, to: &[f64; 2]) -> ModelingCmd {
}
}
fn too_few_args(source_range: SourceRange) -> KclError {
KclError::Syntax(KclErrorDetails {
source_ranges: vec![source_range],
message: "too few arguments".to_owned(),
})
}
fn get_arg<I: Iterator>(it: &mut I, src: SourceRange) -> Result<I::Item, KclError> {
it.next().ok_or_else(|| too_few_args(src))
}
/// Draw a tangential arc to a specific point.
pub async fn tangential_arc_to(args: Args) -> Result<KclValue, KclError> {
let src = args.source_range;
// 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: 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 {
None
};
let (to, relative, sketch_group, tag): ([f64; 2], bool, SketchGroup, Option<TagDeclarator>) =
super::args::FromArgs::from_args(&args, 0)?;
let new_sketch_group = inner_tangential_arc_to(to, sketch_group, tag, args).await?;
let new_sketch_group = inner_tangential_arc_to(to, relative, sketch_group, tag, args).await?;
Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group))
}
@ -1815,7 +1790,7 @@ pub async fn tangential_arc_to(args: Args) -> Result<KclValue, KclError> {
/// angle: 60,
/// length: 10,
/// }, %)
/// |> tangentialArcTo([15, 15], %)
/// |> tangentialArcTo([15, 15], false, %)
/// |> line([10, -15], %)
/// |> close(%)
///
@ -1826,6 +1801,7 @@ pub async fn tangential_arc_to(args: Args) -> Result<KclValue, KclError> {
}]
async fn inner_tangential_arc_to(
to: [f64; 2],
relative: bool,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
@ -1845,9 +1821,13 @@ async fn inner_tangential_arc_to(
obtuse: true,
});
let delta = [to_x - from.x, to_y - from.y];
let to = if relative {
[to_x, to_y]
} else {
[to_x - from.x, to_y - from.y]
};
let id = uuid::Uuid::new_v4();
args.batch_modeling_cmd(id, tan_arc_to(&sketch_group, &delta)).await?;
args.batch_modeling_cmd(id, tan_arc_to(&sketch_group, &to)).await?;
let current_path = Path::TangentialArcTo {
base: BasePath {

View File

@ -9,40 +9,40 @@ let corner_radius = 5.0
// because your wrist isn't a perfect cylindrical surface
let brace_base = startSketchAt([corner_radius, 0])
|> line([width - corner_radius, 0.0], %)
|> tangentialArc([corner_radius, corner_radius], %)
|> tangentialArcTo([corner_radius, corner_radius], true, %)
|> yLine(25.0 - corner_radius, %)
|> tangentialArc([-corner_radius, corner_radius], %)
|> tangentialArcTo([-corner_radius, corner_radius], true, %)
|> xLine(-(d_wrist_circumference[0] - (corner_radius * 2)), %)
|> tangentialArc([-corner_radius, corner_radius], %)
|> tangentialArcTo([-corner_radius, corner_radius], true, %)
|> yLine(length - 25.0 - 23.0 - (corner_radius * 2), %)
|> tangentialArc([corner_radius, corner_radius], %)
|> tangentialArcTo([corner_radius, corner_radius], true, %)
|> xLine(15.0 - (corner_radius * 2), %)
|> tangentialArc([corner_radius, corner_radius], %)
|> tangentialArcTo([corner_radius, corner_radius], true, %)
|> yLine(23.0 - corner_radius, %)
|> tangentialArc([-corner_radius, corner_radius], %)
|> tangentialArcTo([-corner_radius, corner_radius], true, %)
|> xLine(-(hand_thickness + 15.0 + 15.0 - (corner_radius * 2)), %)
|> tangentialArc([-corner_radius, -corner_radius], %)
|> tangentialArcTo([-corner_radius, -corner_radius], true, %)
|> yLine(-(23.0 - corner_radius), %)
|> tangentialArc([corner_radius, -corner_radius], %)
|> tangentialArcTo([corner_radius, -corner_radius], true, %)
|> xLine(15.0 - (corner_radius * 2), %)
|> tangentialArc([corner_radius, -corner_radius], %)
|> tangentialArcTo([corner_radius, -corner_radius], true, %)
|> yLine(-(length - 25.0 - 23.0 - (corner_radius * 2)), %)
|> tangentialArc([-corner_radius, -corner_radius], %)
|> tangentialArcTo([-corner_radius, -corner_radius], true, %)
|> xLine(-(d_wrist_circumference[1] + d_wrist_circumference[2] + d_wrist_circumference[3] - hand_thickness - corner_radius), %)
|> tangentialArc([-corner_radius, -corner_radius], %)
|> tangentialArcTo([-corner_radius, -corner_radius], true, %)
|> yLine(-(25.0 - corner_radius), %)
|> tangentialArc([corner_radius, -corner_radius], %)
|> tangentialArcTo([corner_radius, -corner_radius], true, %)
|> close(%)
let inner = startSketchAt([0, 0])
|> xLine(1.0, %)
|> tangentialArc([corner_radius, corner_radius], %)
|> tangentialArcTo([corner_radius, corner_radius], true, %)
|> yLine(25.0 - (corner_radius * 2), %)
|> tangentialArc([-corner_radius, corner_radius], %)
|> tangentialArcTo([-corner_radius, corner_radius], true, %)
|> xLine(-1.0, %)
|> tangentialArc([-corner_radius, -corner_radius], %)
|> tangentialArcTo([-corner_radius, -corner_radius], true, %)
|> yLine(-(25.0 - (corner_radius * 2)), %)
|> tangentialArc([corner_radius, -corner_radius], %)
|> tangentialArcTo([corner_radius, -corner_radius], true, %)
|> close(%)
let final = brace_base

View File

@ -186,7 +186,7 @@ async fn kcl_test_negative_args() {
async fn kcl_test_basic_tangential_arc_with_point() {
let code = r#"const boxSketch = startSketchAt([0, 0])
|> line([0, 10], %)
|> tangentialArc([-5, 5], %)
|> tangentialArcTo([-5, 5], true, %)
|> line([5, -15], %)
|> extrude(10, %)
"#;
@ -199,7 +199,7 @@ async fn kcl_test_basic_tangential_arc_with_point() {
async fn kcl_test_basic_tangential_arc_to() {
let code = r#"const boxSketch = startSketchAt([0, 0])
|> line([0, 10], %)
|> tangentialArcTo([-5, 15], %)
|> tangentialArcTo([-5, 15], false, %)
|> line([5, -15], %)
|> extrude(10, %)
"#;
@ -332,7 +332,7 @@ const thing = other_circle([2, 2], 20)
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_rounded_with_holes() {
let code = r#"fn tarc = (to, sketchGroup, tag?) => {
return tangentialArcTo(to, sketchGroup, tag)
return tangentialArcTo(to, false, sketchGroup, tag)
}
fn roundedRectangle = (pos, w, l, cornerRadius) => {
@ -1341,7 +1341,7 @@ async fn kcl_test_error_empty_start_sketch_on_string() {
|> line([190.03, -118.13], %)
|> line([-33.38, -202.86], %)
|> line([-315.86, -64.2], %)
|> tangentialArcTo([-147.66, 121.34], %)
|> tangentialArcTo([-147.66, 121.34], false, %)
|> close(%)
|> extrude(100, %)