fix: merging main?
@ -9,7 +9,7 @@ Repeat a 2-dimensional sketch along some dimension, with a dynamic amount
|
||||
of distance between each repetition, some specified number of times.
|
||||
|
||||
```js
|
||||
patternLinear2d(data: LinearPattern2dData, sketch_set: SketchSet) -> [Sketch]
|
||||
patternLinear2d(data: LinearPattern2dData, sketch_set: SketchSet, use_original?: bool) -> [Sketch]
|
||||
```
|
||||
|
||||
|
||||
@ -19,6 +19,7 @@ patternLinear2d(data: LinearPattern2dData, sketch_set: SketchSet) -> [Sketch]
|
||||
|----------|------|-------------|----------|
|
||||
| `data` | [`LinearPattern2dData`](/docs/kcl/types/LinearPattern2dData) | Data for a linear pattern on a 2D sketch. | Yes |
|
||||
| `sketch_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | Yes |
|
||||
| `use_original` | `bool` | | No |
|
||||
|
||||
### Returns
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Repeat a 3-dimensional solid along a linear path, with a dynamic amount
|
||||
of distance between each repetition, some specified number of times.
|
||||
|
||||
```js
|
||||
patternLinear3d(data: LinearPattern3dData, solid_set: SolidSet) -> [Solid]
|
||||
patternLinear3d(data: LinearPattern3dData, solid_set: SolidSet, use_original?: bool) -> [Solid]
|
||||
```
|
||||
|
||||
|
||||
@ -19,6 +19,7 @@ patternLinear3d(data: LinearPattern3dData, solid_set: SolidSet) -> [Solid]
|
||||
|----------|------|-------------|----------|
|
||||
| `data` | [`LinearPattern3dData`](/docs/kcl/types/LinearPattern3dData) | Data for a linear pattern on a 3D model. | Yes |
|
||||
| `solid_set` | [`SolidSet`](/docs/kcl/types/SolidSet) | A solid or a group of solids. | Yes |
|
||||
| `use_original` | `bool` | | No |
|
||||
|
||||
### Returns
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ The transform function returns a transform object. All properties of the object
|
||||
- `rotation.origin` (either "local" i.e. rotate around its own center, "global" i.e. rotate around the scene's center, or a 3D point, defaults to "local")
|
||||
|
||||
```js
|
||||
patternTransform(total_instances: integer, transform_function: FunctionParam, solid_set: SolidSet) -> [Solid]
|
||||
patternTransform(total_instances: integer, transform_function: FunctionParam, solid_set: SolidSet, use_original?: bool) -> [Solid]
|
||||
```
|
||||
|
||||
|
||||
@ -46,6 +46,7 @@ patternTransform(total_instances: integer, transform_function: FunctionParam, so
|
||||
| `total_instances` | `integer` | | Yes |
|
||||
| `transform_function` | `FunctionParam` | | Yes |
|
||||
| `solid_set` | [`SolidSet`](/docs/kcl/types/SolidSet) | A solid or a group of solids. | Yes |
|
||||
| `use_original` | `bool` | | No |
|
||||
|
||||
### Returns
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ Just like patternTransform, but works on 2D sketches not 3D solids.
|
||||
|
||||
|
||||
```js
|
||||
patternTransform2d(total_instances: integer, transform_function: FunctionParam, solid_set: SketchSet) -> [Sketch]
|
||||
patternTransform2d(total_instances: integer, transform_function: FunctionParam, solid_set: SketchSet, use_original?: bool) -> [Sketch]
|
||||
```
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@ patternTransform2d(total_instances: integer, transform_function: FunctionParam,
|
||||
| `total_instances` | `integer` | | Yes |
|
||||
| `transform_function` | `FunctionParam` | | Yes |
|
||||
| `solid_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | Yes |
|
||||
| `use_original` | `bool` | | No |
|
||||
|
||||
### Returns
|
||||
|
||||
|
||||
17937
docs/kcl/std.json
@ -20,5 +20,6 @@ Data for a circular pattern on a 2D sketch.
|
||||
| `center` |`[number, number]`| The center about which to make the pattern. This is a 2D vector. | No |
|
||||
| `arcDegrees` |`number`| The arc angle (in degrees) to place the repetitions. Must be greater than 0. | No |
|
||||
| `rotateDuplicates` |`boolean`| Whether or not to rotate the duplicates as they are copied. | No |
|
||||
| `useOriginal` |`boolean`| If the target being patterned is itself a pattern, then, should you use the original solid, or the pattern? | No |
|
||||
|
||||
|
||||
|
||||
@ -21,5 +21,6 @@ Data for a circular pattern on a 3D model.
|
||||
| `center` |`[number, number, number]`| The center about which to make the pattern. This is a 3D vector. | No |
|
||||
| `arcDegrees` |`number`| The arc angle (in degrees) to place the repetitions. Must be greater than 0. | No |
|
||||
| `rotateDuplicates` |`boolean`| Whether or not to rotate the duplicates as they are copied. | No |
|
||||
| `useOriginal` |`boolean`| If the target being patterned is itself a pattern, then, should you use the original solid, or the pattern? | No |
|
||||
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ A sketch is a collection of paths.
|
||||
| `start` |[`BasePath`](/docs/kcl/types/BasePath)| The starting path. | No |
|
||||
| `tags` |`object`| Tag identifiers that have been declared in this sketch. | No |
|
||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The original id of the sketch. This stays the same even if the sketch is is sketched on face etc. | No |
|
||||
| `originalId` |`string`| | No |
|
||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A sketch is a collection of paths. | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| Metadata. | No |
|
||||
|
||||
|
||||
@ -31,6 +31,7 @@ A sketch is a collection of paths.
|
||||
| `start` |[`BasePath`](/docs/kcl/types/BasePath)| The starting path. | No |
|
||||
| `tags` |`object`| Tag identifiers that have been declared in this sketch. | No |
|
||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The original id of the sketch. This stays the same even if the sketch is is sketched on face etc. | No |
|
||||
| `originalId` |`string`| | No |
|
||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A sketch or a group of sketches. | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| Metadata. | No |
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@ export class ToolbarFixture {
|
||||
shellButton!: Locator
|
||||
revolveButton!: Locator
|
||||
offsetPlaneButton!: Locator
|
||||
helixButton!: Locator
|
||||
startSketchBtn!: Locator
|
||||
lineBtn!: Locator
|
||||
rectangleBtn!: Locator
|
||||
@ -49,6 +50,7 @@ export class ToolbarFixture {
|
||||
this.shellButton = page.getByTestId('shell')
|
||||
this.revolveButton = page.getByTestId('revolve')
|
||||
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
||||
this.helixButton = page.getByTestId('helix')
|
||||
this.startSketchBtn = page.getByTestId('sketch')
|
||||
this.lineBtn = page.getByTestId('line')
|
||||
this.rectangleBtn = page.getByTestId('corner-rectangle')
|
||||
|
||||
@ -27,7 +27,7 @@ test.describe('Onboarding tests', () => {
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ context, page, homePage }) => {
|
||||
async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
@ -68,7 +68,7 @@ test.describe('Onboarding tests', () => {
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ page, homePage }, testInfo) => {
|
||||
async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
const viewportSize = { width: 1200, height: 500 }
|
||||
@ -154,7 +154,7 @@ test.describe('Onboarding tests', () => {
|
||||
)
|
||||
|
||||
test(
|
||||
'Click through each onboarding step',
|
||||
'Click through each onboarding step and back',
|
||||
{
|
||||
appSettings: {
|
||||
app: {
|
||||
@ -187,15 +187,21 @@ test.describe('Onboarding tests', () => {
|
||||
).toBeVisible()
|
||||
|
||||
const nextButton = page.getByTestId('onboarding-next')
|
||||
const prevButton = page.getByTestId('onboarding-prev')
|
||||
|
||||
while ((await nextButton.innerText()) !== 'Finish') {
|
||||
await nextButton.hover()
|
||||
await nextButton.click()
|
||||
}
|
||||
|
||||
// Finish the onboarding
|
||||
await nextButton.hover()
|
||||
await nextButton.click()
|
||||
while ((await prevButton.innerText()) !== 'Dismiss') {
|
||||
await prevButton.hover()
|
||||
await prevButton.click()
|
||||
}
|
||||
|
||||
// Dismiss the onboarding
|
||||
await prevButton.hover()
|
||||
await prevButton.click()
|
||||
|
||||
// Test that the onboarding pane is gone
|
||||
await expect(page.getByTestId('onboarding-content')).not.toBeVisible()
|
||||
@ -269,7 +275,7 @@ test.describe('Onboarding tests', () => {
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
|
||||
async ({ context, page, homePage }) => {
|
||||
async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
const badCode = `// This is bad code we shouldn't see`
|
||||
|
||||
@ -336,10 +342,10 @@ test.describe('Onboarding tests', () => {
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// Test that the text in this step is correct
|
||||
const avatarLocator = await page
|
||||
const avatarLocator = page
|
||||
.getByTestId('user-sidebar-toggle')
|
||||
.locator('img')
|
||||
const onboardingOverlayLocator = await page
|
||||
const onboardingOverlayLocator = page
|
||||
.getByTestId('onboarding-content')
|
||||
.locator('div')
|
||||
.nth(1)
|
||||
@ -447,7 +453,7 @@ test.fixme(
|
||||
},
|
||||
cleanProjectDir: true,
|
||||
},
|
||||
async ({ context, page, homePage }, testInfo) => {
|
||||
async ({ context, page }) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const routerTemplateDir = join(dir, 'router-template-slate')
|
||||
await fsp.mkdir(routerTemplateDir, { recursive: true })
|
||||
@ -486,10 +492,6 @@ test.fixme(
|
||||
})
|
||||
|
||||
await test.step('Navigate into project', async () => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
page.on('console', console.log)
|
||||
|
||||
await expect(
|
||||
page.getByRole('heading', { name: 'Your Projects' })
|
||||
).toBeVisible()
|
||||
|
||||
@ -782,6 +782,71 @@ openSketch = startSketchOn('XY')
|
||||
})
|
||||
})
|
||||
|
||||
test('Helix point-and-click', async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
editor,
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
// One dumb hardcoded screen pixel value
|
||||
const testPoint = { x: 620, y: 257 }
|
||||
const expectedOutput = `helix001 = helix(revolutions = 1, angleStart = 360, counterClockWise = false, radius = 5, axis = 'X', length = 5)`
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await test.step(`Look for the red of the default plane`, async () => {
|
||||
await scene.expectPixelColor([96, 52, 52], testPoint, 15)
|
||||
})
|
||||
await test.step(`Go through the command bar flow`, async () => {
|
||||
await toolbar.helixButton.click()
|
||||
await cmdBar.expectState({
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'revolutions',
|
||||
currentArgValue: '1',
|
||||
headerArguments: {
|
||||
AngleStart: '',
|
||||
Axis: '',
|
||||
CounterClockWise: '',
|
||||
Length: '',
|
||||
Radius: '',
|
||||
Revolutions: '',
|
||||
},
|
||||
highlightedHeaderArg: 'revolutions',
|
||||
commandName: 'Helix',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
})
|
||||
|
||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||
await editor.expectEditor.toContain(expectedOutput)
|
||||
await editor.expectState({
|
||||
diagnostics: [],
|
||||
activeLines: [expectedOutput],
|
||||
highlightedCode: '',
|
||||
})
|
||||
// Red plane is now gone, white helix is there
|
||||
await scene.expectPixelColor([250, 250, 250], testPoint, 15)
|
||||
})
|
||||
|
||||
await test.step('Delete offset plane via feature tree selection', async () => {
|
||||
await editor.closePane()
|
||||
const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Backspace')
|
||||
// Red plane is back
|
||||
await scene.expectPixelColor([96, 52, 52], testPoint, 15)
|
||||
})
|
||||
})
|
||||
|
||||
const loftPointAndClickCases = [
|
||||
{ shouldPreselect: true },
|
||||
{ shouldPreselect: false },
|
||||
@ -972,7 +1037,7 @@ sketch002 = startSketchOn('XZ')
|
||||
testPoint.x - 50,
|
||||
testPoint.y
|
||||
)
|
||||
const sweepDeclaration = 'sweep001 = sweep({ path = sketch002 }, sketch001)'
|
||||
const sweepDeclaration = 'sweep001 = sweep(sketch001, path = sketch002)'
|
||||
|
||||
await test.step(`Look for sketch001`, async () => {
|
||||
await toolbar.closePane('code')
|
||||
@ -1891,7 +1956,7 @@ chamfer04 = chamfer({ length = 5, tags = [getOppositeEdge(seg02)]}, extrude001
|
||||
const testPoint = { x: 575, y: 200 }
|
||||
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||
const shellDeclaration =
|
||||
"shell001 = shell({ faces = ['end'], thickness = 5 }, extrude001)"
|
||||
"shell001 = shell(extrude001, faces = ['end'], thickness = 5)"
|
||||
|
||||
await test.step(`Look for the grey of the shape`, async () => {
|
||||
await scene.expectPixelColor([127, 127, 127], testPoint, 15)
|
||||
@ -1992,8 +2057,7 @@ extrude001 = extrude(sketch001, length = 40)
|
||||
const [clickOnWall] = scene.makeMouseHelpers(testPoint.x, testPoint.y + 70)
|
||||
const mutatedCode = 'xLine(-40, %, $seg01)'
|
||||
const shellDeclaration =
|
||||
"shell001 = shell({ faces = ['end', seg01], thickness = 5}, extrude001)"
|
||||
const formattedOutLastLine = '}, extrude001)'
|
||||
"shell001 = shell(extrude001, faces = ['end', seg01], thickness = 5)"
|
||||
|
||||
await test.step(`Look for the grey of the shape`, async () => {
|
||||
await scene.expectPixelColor([99, 99, 99], testPoint, 15)
|
||||
@ -2036,7 +2100,7 @@ extrude001 = extrude(sketch001, length = 40)
|
||||
await editor.expectEditor.toContain(shellDeclaration)
|
||||
await editor.expectState({
|
||||
diagnostics: [],
|
||||
activeLines: [formattedOutLastLine],
|
||||
activeLines: [shellDeclaration],
|
||||
highlightedCode: '',
|
||||
})
|
||||
await scene.expectPixelColor([49, 49, 49], testPoint, 15)
|
||||
@ -2090,9 +2154,8 @@ extrude002 = extrude(sketch002, length = 50)
|
||||
// One dumb hardcoded screen pixel value
|
||||
const testPoint = { x: 550, y: 295 }
|
||||
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||
const shellDeclaration = `shell001 = shell({ faces = ['end'], thickness = 5 }, ${
|
||||
hasExtrudesInPipe ? 'sketch002' : 'extrude002'
|
||||
})`
|
||||
const shellTarget = hasExtrudesInPipe ? 'sketch002' : 'extrude002'
|
||||
const shellDeclaration = `shell001 = shell(${shellTarget}, faces = ['end'], thickness = 5)`
|
||||
|
||||
await test.step(`Look for the grey of the shape`, async () => {
|
||||
await toolbar.closePane('code')
|
||||
@ -2160,7 +2223,7 @@ extrude002 = extrude(sketch002, length = 50)
|
||||
sketch002 = startSketchOn('XZ')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> xLine(-2000, %)
|
||||
sweep001 = sweep({ path = sketch002 }, sketch001)
|
||||
sweep001 = sweep(sketch001, path = sketch002)
|
||||
`
|
||||
await context.addInitScript((initialCode) => {
|
||||
localStorage.setItem('persistCode', initialCode)
|
||||
|
||||
@ -253,7 +253,7 @@ extrude001 = extrude(sketch001, length = 50)
|
||||
|>
|
||||
|
||||
example = extrude(exampleSketch, length = 5)
|
||||
shell({ faces: ['end'], thickness: 0.25 }, exampleSketch)`
|
||||
shell(exampleSketch, faces = ['end'], thickness = 0.25)`
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
@ -1187,9 +1187,7 @@ sweepSketch = startSketchOn('XY')
|
||||
angleStart = 0,
|
||||
radius = 2
|
||||
}, %)
|
||||
|> sweep({
|
||||
path = sweepPath,
|
||||
}, %)
|
||||
|> sweep(path = sweepPath)
|
||||
|> appearance({
|
||||
color = "#bb00ff",
|
||||
metalness = 90,
|
||||
@ -1235,9 +1233,7 @@ sweepSketch = startSketchOn('XY')
|
||||
angleStart = 0,
|
||||
radius = 2
|
||||
}, %)
|
||||
|> sweep({
|
||||
path = sweepPath,
|
||||
}, %)
|
||||
|> sweep(path = sweepPath)
|
||||
|> appearance({
|
||||
color = "#bb00ff",
|
||||
metalness = 90,
|
||||
|
||||
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 145 KiB After Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
@ -3,80 +3,81 @@ import { test, expect } from './zoo-test'
|
||||
import { commonPoints, getUtils } from './test-utils'
|
||||
|
||||
test.describe('Test network and connection issues', () => {
|
||||
test('simulate network down and network little widget', async ({
|
||||
page,
|
||||
homePage,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
test(
|
||||
'simulate network down and network little widget',
|
||||
{ tag: '@skipLocalEngine' },
|
||||
async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
const networkToggle = page.getByTestId('network-toggle')
|
||||
const networkToggle = page.getByTestId('network-toggle')
|
||||
|
||||
// This is how we wait until the stream is online
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled({ timeout: 15000 })
|
||||
// This is how we wait until the stream is online
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled({ timeout: 15000 })
|
||||
|
||||
const networkWidget = page.locator('[data-testid="network-toggle"]')
|
||||
await expect(networkWidget).toBeVisible()
|
||||
await networkWidget.hover()
|
||||
const networkWidget = page.locator('[data-testid="network-toggle"]')
|
||||
await expect(networkWidget).toBeVisible()
|
||||
await networkWidget.hover()
|
||||
|
||||
const networkPopover = page.locator('[data-testid="network-popover"]')
|
||||
await expect(networkPopover).not.toBeVisible()
|
||||
const networkPopover = page.locator('[data-testid="network-popover"]')
|
||||
await expect(networkPopover).not.toBeVisible()
|
||||
|
||||
// (First check) Expect the network to be up
|
||||
await expect(networkToggle).toContainText('Connected')
|
||||
// (First check) Expect the network to be up
|
||||
await expect(networkToggle).toContainText('Connected')
|
||||
|
||||
// Click the network widget
|
||||
await networkWidget.click()
|
||||
// Click the network widget
|
||||
await networkWidget.click()
|
||||
|
||||
// Check the modal opened.
|
||||
await expect(networkPopover).toBeVisible()
|
||||
// Check the modal opened.
|
||||
await expect(networkPopover).toBeVisible()
|
||||
|
||||
// Click off the modal.
|
||||
await page.mouse.click(100, 100)
|
||||
await expect(networkPopover).not.toBeVisible()
|
||||
// Click off the modal.
|
||||
await page.mouse.click(100, 100)
|
||||
await expect(networkPopover).not.toBeVisible()
|
||||
|
||||
// Turn off the network
|
||||
await u.emulateNetworkConditions({
|
||||
offline: true,
|
||||
// values of 0 remove any active throttling. crbug.com/456324#c9
|
||||
latency: 0,
|
||||
downloadThroughput: -1,
|
||||
uploadThroughput: -1,
|
||||
})
|
||||
// Turn off the network
|
||||
await u.emulateNetworkConditions({
|
||||
offline: true,
|
||||
// values of 0 remove any active throttling. crbug.com/456324#c9
|
||||
latency: 0,
|
||||
downloadThroughput: -1,
|
||||
uploadThroughput: -1,
|
||||
})
|
||||
|
||||
// Expect the network to be down
|
||||
await expect(networkToggle).toContainText('Problem')
|
||||
// Expect the network to be down
|
||||
await expect(networkToggle).toContainText('Problem')
|
||||
|
||||
// Click the network widget
|
||||
await networkWidget.click()
|
||||
// Click the network widget
|
||||
await networkWidget.click()
|
||||
|
||||
// Check the modal opened.
|
||||
await expect(networkPopover).toBeVisible()
|
||||
// Check the modal opened.
|
||||
await expect(networkPopover).toBeVisible()
|
||||
|
||||
// Click off the modal.
|
||||
await page.mouse.click(0, 0)
|
||||
await expect(networkPopover).not.toBeVisible()
|
||||
// Click off the modal.
|
||||
await page.mouse.click(0, 0)
|
||||
await expect(networkPopover).not.toBeVisible()
|
||||
|
||||
// Turn back on the network
|
||||
await u.emulateNetworkConditions({
|
||||
offline: false,
|
||||
// values of 0 remove any active throttling. crbug.com/456324#c9
|
||||
latency: 0,
|
||||
downloadThroughput: -1,
|
||||
uploadThroughput: -1,
|
||||
})
|
||||
// Turn back on the network
|
||||
await u.emulateNetworkConditions({
|
||||
offline: false,
|
||||
// values of 0 remove any active throttling. crbug.com/456324#c9
|
||||
latency: 0,
|
||||
downloadThroughput: -1,
|
||||
uploadThroughput: -1,
|
||||
})
|
||||
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled({ timeout: 15000 })
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled({ timeout: 15000 })
|
||||
|
||||
// (Second check) expect the network to be up
|
||||
await expect(networkToggle).toContainText('Connected')
|
||||
})
|
||||
// (Second check) expect the network to be up
|
||||
await expect(networkToggle).toContainText('Connected')
|
||||
}
|
||||
)
|
||||
|
||||
test('Engine disconnect & reconnect in sketch mode', async ({
|
||||
page,
|
||||
|
||||
@ -85,7 +85,7 @@
|
||||
"fmt": "prettier --write ./src *.ts *.json *.js ./e2e ./packages",
|
||||
"fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e ./packages",
|
||||
"fetch:wasm": "./get-latest-wasm-bundle.sh",
|
||||
"fetch:samples": "echo \"Fetching latest KCL samples...\" && curl -o public/kcl-samples-manifest-fallback.json https://raw.githubusercontent.com/KittyCAD/kcl-samples/main/manifest.json",
|
||||
"fetch:samples": "echo \"Fetching latest KCL samples...\" && curl -o public/kcl-samples-manifest-fallback.json https://raw.githubusercontent.com/KittyCAD/kcl-samples/achalmers/kw-shell/manifest.json",
|
||||
"isomorphic-copy-wasm": "(copy src/wasm-lib/pkg/wasm_lib_bg.wasm public || cp src/wasm-lib/pkg/wasm_lib_bg.wasm public)",
|
||||
"build:wasm-dev": "yarn wasm-prep && (cd src/wasm-lib && wasm-pack build --dev --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && yarn isomorphic-copy-wasm && yarn fmt",
|
||||
"build:wasm": "yarn wasm-prep && cd src/wasm-lib && wasm-pack build --release --target web --out-dir pkg && cargo test -p kcl-lib export_bindings && cd ../.. && yarn isomorphic-copy-wasm && yarn fmt",
|
||||
@ -159,7 +159,7 @@
|
||||
"@types/electron": "^1.6.10",
|
||||
"@types/isomorphic-fetch": "^0.0.39",
|
||||
"@types/minimist": "^1.2.5",
|
||||
"@types/mocha": "^10.0.6",
|
||||
"@types/mocha": "^10.0.10",
|
||||
"@types/node": "^22.13.1",
|
||||
"@types/pixelmatch": "^5.2.6",
|
||||
"@types/pngjs": "^6.0.4",
|
||||
|
||||
@ -59,7 +59,9 @@ UnaryOp { AddOp | BangOp }
|
||||
|
||||
ObjectProperty { PropertyName (":" | Equals) expression }
|
||||
|
||||
ArgumentList { "(" commaSep<expression> ")" }
|
||||
LabeledArgument { ArgumentLabel Equals expression }
|
||||
|
||||
ArgumentList { "(" commaSep<LabeledArgument | expression> ")" }
|
||||
|
||||
type[@isGroup=Type] {
|
||||
@specialize[@name=PrimitiveType]<
|
||||
@ -74,6 +76,8 @@ VariableDefinition { identifier }
|
||||
|
||||
VariableName { identifier }
|
||||
|
||||
ArgumentLabel { identifier }
|
||||
|
||||
@skip { whitespace | LineComment | BlockComment }
|
||||
|
||||
kw<term> { @specialize[@name={term}]<identifier, term> }
|
||||
|
||||
85
packages/codemirror-lang-kcl/test/call.txt
Normal file
@ -0,0 +1,85 @@
|
||||
# empty
|
||||
|
||||
f()
|
||||
|
||||
==>
|
||||
Program(ExpressionStatement(CallExpression(VariableName,
|
||||
ArgumentList)))
|
||||
|
||||
# single anon arg
|
||||
|
||||
f(1)
|
||||
|
||||
==>
|
||||
Program(ExpressionStatement(CallExpression(VariableName,
|
||||
ArgumentList(Number))))
|
||||
|
||||
# deprecated multiple anon args
|
||||
|
||||
f(1, 2)
|
||||
|
||||
==>
|
||||
Program(ExpressionStatement(CallExpression(VariableName,
|
||||
ArgumentList(Number,
|
||||
Number))))
|
||||
|
||||
# deprecated trailing %
|
||||
|
||||
startSketchOn('XY')
|
||||
|> line([thickness, 0], %)
|
||||
|
||||
==>
|
||||
Program(ExpressionStatement(PipeExpression(CallExpression(VariableName,
|
||||
ArgumentList(String)),
|
||||
PipeOperator,
|
||||
CallExpression(VariableName,
|
||||
ArgumentList(ArrayExpression(VariableName,
|
||||
Number),
|
||||
PipeSubstitution)))))
|
||||
|
||||
# % and named arg
|
||||
|
||||
startSketchOn('XY')
|
||||
|> line(%, end = [thickness, 0])
|
||||
|
||||
==>
|
||||
Program(ExpressionStatement(PipeExpression(CallExpression(VariableName,
|
||||
ArgumentList(String)),
|
||||
PipeOperator,
|
||||
CallExpression(VariableName,
|
||||
ArgumentList(PipeSubstitution,
|
||||
LabeledArgument(ArgumentLabel,
|
||||
Equals,
|
||||
ArrayExpression(VariableName,
|
||||
Number)))))))
|
||||
|
||||
# implied % and named arg
|
||||
|
||||
startSketchOn('XY')
|
||||
|> line(end = [thickness, 0])
|
||||
|
||||
==>
|
||||
Program(ExpressionStatement(PipeExpression(CallExpression(VariableName,
|
||||
ArgumentList(String)),
|
||||
PipeOperator,
|
||||
CallExpression(VariableName,
|
||||
ArgumentList(LabeledArgument(ArgumentLabel,
|
||||
Equals,
|
||||
ArrayExpression(VariableName,
|
||||
Number)))))))
|
||||
|
||||
# multiple named arg
|
||||
|
||||
ngon(plane = "XY", numSides = 5, radius = pentR)
|
||||
|
||||
==>
|
||||
Program(ExpressionStatement(CallExpression(VariableName,
|
||||
ArgumentList(LabeledArgument(ArgumentLabel,
|
||||
Equals,
|
||||
String),
|
||||
LabeledArgument(ArgumentLabel,
|
||||
Equals,
|
||||
Number),
|
||||
LabeledArgument(ArgumentLabel,
|
||||
Equals,
|
||||
VariableName)))))
|
||||
@ -57,7 +57,7 @@ function CommandComboBox({
|
||||
onKeyDown={(event) => {
|
||||
if (
|
||||
(event.metaKey && event.key === 'k') ||
|
||||
(event.key === 'Backspace' && !event.currentTarget.value)
|
||||
event.key === 'Escape'
|
||||
) {
|
||||
event.preventDefault()
|
||||
commandBarActor.send({ type: 'Close' })
|
||||
|
||||
@ -2,11 +2,15 @@ import { Dialog } from '@headlessui/react'
|
||||
import { ActionButton } from './ActionButton'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { useState } from 'react'
|
||||
import { useSearchParams } from 'react-router-dom'
|
||||
import { CREATE_FILE_URL_PARAM } from 'lib/constants'
|
||||
|
||||
const DownloadAppBanner = () => {
|
||||
const [searchParams] = useSearchParams()
|
||||
const hasCreateFileParam = searchParams.has(CREATE_FILE_URL_PARAM)
|
||||
const { settings } = useSettingsAuthContext()
|
||||
const [isBannerDismissed, setIsBannerDismissed] = useState(
|
||||
settings.context.app.dismissWebBanner.current
|
||||
settings.context.app.dismissWebBanner.current || hasCreateFileParam
|
||||
)
|
||||
|
||||
return (
|
||||
|
||||
@ -168,7 +168,7 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||
height: 'auto',
|
||||
}}
|
||||
minWidth={200}
|
||||
maxWidth={800}
|
||||
maxWidth={window.innerWidth - 10}
|
||||
handleWrapperClass="sidebar-resize-handles"
|
||||
handleClasses={{
|
||||
right:
|
||||
|
||||
@ -104,7 +104,7 @@ function ProjectMenuPopover({
|
||||
const location = useLocation()
|
||||
const navigate = useNavigate()
|
||||
const filePath = useAbsoluteFilePath()
|
||||
const { settings } = useSettingsAuthContext()
|
||||
useSettingsAuthContext()
|
||||
const token = useToken()
|
||||
const machineManager = useContext(MachineManagerContext)
|
||||
const commands = useSelector(commandBarActor, commandsSelector)
|
||||
@ -193,14 +193,13 @@ function ProjectMenuPopover({
|
||||
{
|
||||
id: 'share-link',
|
||||
Element: 'button',
|
||||
children: 'Share link to file',
|
||||
disabled: IS_NIGHTLY_OR_DEBUG || !findCommand(shareCommandInfo),
|
||||
children: 'Share current part (via Zoo link)',
|
||||
disabled: !(IS_NIGHTLY_OR_DEBUG && findCommand(shareCommandInfo)),
|
||||
onClick: async () => {
|
||||
await copyFileShareLink({
|
||||
token: token ?? '',
|
||||
code: codeManager.code,
|
||||
name: project?.name || '',
|
||||
units: settings.context.modeling.defaultUnit.current,
|
||||
})
|
||||
},
|
||||
},
|
||||
@ -263,7 +262,7 @@ function ProjectMenuPopover({
|
||||
as={Fragment}
|
||||
>
|
||||
<Popover.Panel
|
||||
className={`z-10 absolute top-full left-0 mt-1 pb-1 w-48 bg-chalkboard-10 dark:bg-chalkboard-90
|
||||
className={`z-10 absolute top-full left-0 mt-1 pb-1 w-52 bg-chalkboard-10 dark:bg-chalkboard-90
|
||||
border border-solid border-chalkboard-20 dark:border-chalkboard-90 rounded
|
||||
shadow-lg`}
|
||||
>
|
||||
|
||||
@ -30,15 +30,7 @@ import {
|
||||
FILE_EXT,
|
||||
PROJECT_ENTRYPOINT,
|
||||
} from 'lib/constants'
|
||||
import { DeepPartial } from 'lib/types'
|
||||
import { Configuration } from 'wasm-lib/kcl/bindings/Configuration'
|
||||
import { codeManager } from 'lib/singletons'
|
||||
import {
|
||||
loadAndValidateSettings,
|
||||
projectConfigurationToSettingsPayload,
|
||||
saveSettings,
|
||||
setSettingsAtLevel,
|
||||
} from 'lib/settings/settingsUtils'
|
||||
import { codeManager, kclManager } from 'lib/singletons'
|
||||
import { Project } from 'lib/project'
|
||||
|
||||
type MachineContext<T extends AnyStateMachine> = {
|
||||
@ -86,7 +78,7 @@ const ProjectsContextWeb = ({ children }: { children: React.ReactNode }) => {
|
||||
setSearchParams(searchParams)
|
||||
}, [searchParams, setSearchParams])
|
||||
const {
|
||||
settings: { context: settings, send: settingsSend },
|
||||
settings: { context: settings },
|
||||
} = useSettingsAuthContext()
|
||||
|
||||
const [state, send, actor] = useMachine(
|
||||
@ -132,17 +124,10 @@ const ProjectsContextWeb = ({ children }: { children: React.ReactNode }) => {
|
||||
clearImportSearchParams()
|
||||
codeManager.updateCodeStateEditor(input.code || '')
|
||||
await codeManager.writeToFile()
|
||||
|
||||
settingsSend({
|
||||
type: 'set.modeling.defaultUnit',
|
||||
data: {
|
||||
level: 'project',
|
||||
value: input.units,
|
||||
},
|
||||
})
|
||||
await kclManager.executeCode(true)
|
||||
|
||||
return {
|
||||
message: 'File and units overwritten successfully',
|
||||
message: 'File overwritten successfully',
|
||||
fileName: input.name,
|
||||
projectName: '',
|
||||
}
|
||||
@ -392,16 +377,6 @@ const ProjectsContextDesktop = ({
|
||||
? input.name
|
||||
: input.name + FILE_EXT
|
||||
let message = 'File created successfully'
|
||||
const unitsConfiguration: DeepPartial<Configuration> = {
|
||||
settings: {
|
||||
project: {
|
||||
directory: settings.app.projectDirectory.current,
|
||||
},
|
||||
modeling: {
|
||||
base_unit: input.units,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const needsInterpolated = doesProjectNameNeedInterpolated(projectName)
|
||||
if (needsInterpolated) {
|
||||
@ -414,28 +389,10 @@ const ProjectsContextDesktop = ({
|
||||
|
||||
// Create the project around the file if newProject
|
||||
if (input.method === 'newProject') {
|
||||
await createNewProjectDirectory(
|
||||
projectName,
|
||||
input.code,
|
||||
unitsConfiguration
|
||||
)
|
||||
await createNewProjectDirectory(projectName, input.code)
|
||||
message = `Project "${projectName}" created successfully with link contents`
|
||||
} else {
|
||||
let projectPath = window.electron.join(
|
||||
settings.app.projectDirectory.current,
|
||||
projectName
|
||||
)
|
||||
|
||||
message = `File "${fileName}" created successfully`
|
||||
const existingConfiguration = await loadAndValidateSettings(
|
||||
projectPath
|
||||
)
|
||||
const settingsToSave = setSettingsAtLevel(
|
||||
existingConfiguration.settings,
|
||||
'project',
|
||||
projectConfigurationToSettingsPayload(unitsConfiguration)
|
||||
)
|
||||
await saveSettings(settingsToSave, projectPath)
|
||||
}
|
||||
|
||||
// Create the file
|
||||
|
||||
@ -6,7 +6,6 @@ import { useSettingsAuthContext } from './useSettingsAuthContext'
|
||||
import { isDesktop } from 'lib/isDesktop'
|
||||
import { FileLinkParams } from 'lib/links'
|
||||
import { ProjectsCommandSchema } from 'lib/commandBarConfigs/projectsCommandConfig'
|
||||
import { baseUnitsUnion } from 'lib/settings/settingsTypes'
|
||||
|
||||
// For initializing the command arguments, we actually want `method` to be undefined
|
||||
// so that we don't skip it in the command palette.
|
||||
@ -37,13 +36,7 @@ export function useCreateFileLinkQuery(
|
||||
code: base64ToString(
|
||||
decodeURIComponent(searchParams.get('code') ?? '')
|
||||
),
|
||||
|
||||
name: searchParams.get('name') ?? DEFAULT_FILE_NAME,
|
||||
|
||||
units:
|
||||
(baseUnitsUnion.find((unit) => searchParams.get('units') === unit) ||
|
||||
settings.context.modeling.defaultUnit.default) ??
|
||||
settings.context.modeling.defaultUnit.current,
|
||||
}
|
||||
|
||||
const argDefaultValues: CreateFileSchemaMethodOptional = {
|
||||
@ -55,7 +48,6 @@ export function useCreateFileLinkQuery(
|
||||
? settings.context.projects.defaultProjectName.current
|
||||
: DEFAULT_FILE_NAME,
|
||||
code: params.code || '',
|
||||
units: params.units,
|
||||
method: isDesktop() ? undefined : 'existingProject',
|
||||
}
|
||||
|
||||
|
||||
@ -55,6 +55,7 @@ const mySketch001 = startSketchOn('XY')
|
||||
],
|
||||
id: expect.any(String),
|
||||
artifactId: expect.any(String),
|
||||
originalId: expect.any(String),
|
||||
units: {
|
||||
type: 'Mm',
|
||||
},
|
||||
@ -98,6 +99,7 @@ const mySketch001 = startSketchOn('XY')
|
||||
],
|
||||
sketch: {
|
||||
id: expect.any(String),
|
||||
originalId: expect.any(String),
|
||||
artifactId: expect.any(String),
|
||||
units: {
|
||||
type: 'Mm',
|
||||
@ -203,6 +205,7 @@ const sk2 = startSketchOn('XY')
|
||||
],
|
||||
sketch: {
|
||||
id: expect.any(String),
|
||||
originalId: expect.any(String),
|
||||
artifactId: expect.any(String),
|
||||
__meta: expect.any(Array),
|
||||
on: expect.any(Object),
|
||||
@ -308,6 +311,7 @@ const sk2 = startSketchOn('XY')
|
||||
],
|
||||
sketch: {
|
||||
id: expect.any(String),
|
||||
originalId: expect.any(String),
|
||||
artifactId: expect.any(String),
|
||||
units: {
|
||||
type: 'Mm',
|
||||
|
||||
@ -221,6 +221,7 @@ const newVar = myVar + 1`
|
||||
},
|
||||
],
|
||||
id: expect.any(String),
|
||||
originalId: expect.any(String),
|
||||
artifactId: expect.any(String),
|
||||
units: {
|
||||
type: 'Mm',
|
||||
|
||||
@ -28,7 +28,14 @@ try {
|
||||
console.log(e)
|
||||
}
|
||||
|
||||
child_process.spawnSync('git', ['clone', URL_GIT_KCL_SAMPLES, DIR_KCL_SAMPLES])
|
||||
child_process.spawnSync('git', [
|
||||
'clone',
|
||||
'--single-branch',
|
||||
'--branch',
|
||||
'achalmers/kw-shell',
|
||||
URL_GIT_KCL_SAMPLES,
|
||||
DIR_KCL_SAMPLES,
|
||||
])
|
||||
|
||||
// @ts-expect-error
|
||||
let files = await fs.readdir(DIR_KCL_SAMPLES)
|
||||
|
||||
@ -431,10 +431,11 @@ export function addSweep(
|
||||
} {
|
||||
const modifiedAst = structuredClone(node)
|
||||
const name = findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.SWEEP)
|
||||
const sweep = createCallExpressionStdLib('sweep', [
|
||||
createObjectExpression({ path: createIdentifier(pathDeclarator.id.name) }),
|
||||
const sweep = createCallExpressionStdLibKw(
|
||||
'sweep',
|
||||
createIdentifier(profileDeclarator.id.name),
|
||||
])
|
||||
[createLabeledArg('path', createIdentifier(pathDeclarator.id.name))]
|
||||
)
|
||||
const declaration = createVariableDeclaration(name, sweep)
|
||||
modifiedAst.body.push(declaration)
|
||||
const pathToNode: PathToNode = [
|
||||
@ -442,8 +443,9 @@ export function addSweep(
|
||||
[modifiedAst.body.length - 1, 'index'],
|
||||
['declaration', 'VariableDeclaration'],
|
||||
['init', 'VariableDeclarator'],
|
||||
['arguments', 'CallExpression'],
|
||||
[0, 'index'],
|
||||
['arguments', 'CallExpressionKw'],
|
||||
[0, ARG_INDEX_FIELD],
|
||||
['arg', LABELED_ARG_FIELD],
|
||||
]
|
||||
|
||||
return {
|
||||
@ -683,6 +685,63 @@ export function addOffsetPlane({
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a helix to the AST
|
||||
*/
|
||||
export function addHelix({
|
||||
node,
|
||||
revolutions,
|
||||
angleStart,
|
||||
counterClockWise,
|
||||
radius,
|
||||
axis,
|
||||
length,
|
||||
}: {
|
||||
node: Node<Program>
|
||||
revolutions: Expr
|
||||
angleStart: Expr
|
||||
counterClockWise: boolean
|
||||
radius: Expr
|
||||
axis: string
|
||||
length: Expr
|
||||
}): { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
||||
const modifiedAst = structuredClone(node)
|
||||
const name = findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.HELIX)
|
||||
const variable = createVariableDeclaration(
|
||||
name,
|
||||
createCallExpressionStdLibKw(
|
||||
'helix',
|
||||
null, // Not in a pipeline
|
||||
[
|
||||
createLabeledArg('revolutions', revolutions),
|
||||
createLabeledArg('angleStart', angleStart),
|
||||
createLabeledArg('counterClockWise', createLiteral(counterClockWise)),
|
||||
createLabeledArg('radius', radius),
|
||||
createLabeledArg('axis', createLiteral(axis)),
|
||||
createLabeledArg('length', length),
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
// TODO: figure out smart insertion than just appending at the end
|
||||
const argIndex = 0
|
||||
modifiedAst.body.push(variable)
|
||||
const pathToNode: PathToNode = [
|
||||
['body', ''],
|
||||
[modifiedAst.body.length - 1, 'index'],
|
||||
['declaration', 'VariableDeclaration'],
|
||||
['init', 'VariableDeclarator'],
|
||||
['arguments', 'CallExpressionKw'],
|
||||
[argIndex, ARG_INDEX_FIELD],
|
||||
['arg', LABELED_ARG_FIELD],
|
||||
]
|
||||
|
||||
return {
|
||||
modifiedAst,
|
||||
pathToNode,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a modified clone of an AST with a named constant inserted into the body
|
||||
*/
|
||||
|
||||
@ -17,6 +17,8 @@ import {
|
||||
createObjectExpression,
|
||||
createArrayExpression,
|
||||
createVariableDeclaration,
|
||||
createCallExpressionStdLibKw,
|
||||
createLabeledArg,
|
||||
} from 'lang/modifyAst'
|
||||
import { KCL_DEFAULT_CONSTANT_PREFIXES } from 'lib/constants'
|
||||
import { KclManager } from 'lang/KclSingleton'
|
||||
@ -121,13 +123,14 @@ export function addShell({
|
||||
}
|
||||
|
||||
const name = findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.SHELL)
|
||||
const shell = createCallExpressionStdLib('shell', [
|
||||
createObjectExpression({
|
||||
faces: createArrayExpression(expressions),
|
||||
thickness,
|
||||
}),
|
||||
const shell = createCallExpressionStdLibKw(
|
||||
'shell',
|
||||
createIdentifier(extrudeNode.node.id.name),
|
||||
])
|
||||
[
|
||||
createLabeledArg('faces', createArrayExpression(expressions)),
|
||||
createLabeledArg('thickness', thickness),
|
||||
]
|
||||
)
|
||||
const declaration = createVariableDeclaration(name, shell)
|
||||
|
||||
// TODO: check if we should append at the end like here or right after the extrude
|
||||
@ -137,8 +140,7 @@ export function addShell({
|
||||
[modifiedAst.body.length - 1, 'index'],
|
||||
['declaration', 'VariableDeclaration'],
|
||||
['init', 'VariableDeclarator'],
|
||||
['arguments', 'CallExpression'],
|
||||
[0, 'index'],
|
||||
['unlabeled', 'CallExpressionKw'],
|
||||
]
|
||||
return {
|
||||
modifiedAst,
|
||||
|
||||
@ -76,6 +76,14 @@ export type ModelingCommandSchema = {
|
||||
plane: Selections
|
||||
distance: KclCommandValue
|
||||
}
|
||||
Helix: {
|
||||
revolutions: KclCommandValue
|
||||
angleStart: KclCommandValue
|
||||
counterClockWise: boolean
|
||||
radius: KclCommandValue
|
||||
axis: string
|
||||
length: KclCommandValue
|
||||
}
|
||||
'change tool': {
|
||||
tool: SketchTool
|
||||
}
|
||||
@ -447,6 +455,53 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
||||
},
|
||||
},
|
||||
},
|
||||
Helix: {
|
||||
description: 'Create a helix or spiral in 3D about an axis.',
|
||||
icon: 'helix',
|
||||
status: 'development',
|
||||
needsReview: true,
|
||||
args: {
|
||||
revolutions: {
|
||||
inputType: 'kcl',
|
||||
defaultValue: '1',
|
||||
required: true,
|
||||
warningMessage:
|
||||
'The helix workflow is new and under tested. Please break it and report issues.',
|
||||
},
|
||||
angleStart: {
|
||||
inputType: 'kcl',
|
||||
defaultValue: KCL_DEFAULT_DEGREE,
|
||||
required: true,
|
||||
},
|
||||
counterClockWise: {
|
||||
inputType: 'options',
|
||||
required: true,
|
||||
options: [
|
||||
{ name: 'True', isCurrent: false, value: true },
|
||||
{ name: 'False', isCurrent: true, value: false },
|
||||
],
|
||||
},
|
||||
radius: {
|
||||
inputType: 'kcl',
|
||||
defaultValue: KCL_DEFAULT_LENGTH,
|
||||
required: true,
|
||||
},
|
||||
axis: {
|
||||
inputType: 'options',
|
||||
required: true,
|
||||
options: [
|
||||
{ name: 'X Axis', isCurrent: true, value: 'X' },
|
||||
{ name: 'Y Axis', isCurrent: false, value: 'Y' },
|
||||
{ name: 'Z Axis', isCurrent: false, value: 'Z' },
|
||||
],
|
||||
},
|
||||
length: {
|
||||
inputType: 'kcl',
|
||||
defaultValue: KCL_DEFAULT_LENGTH,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Fillet: {
|
||||
description: 'Fillet edge',
|
||||
icon: 'fillet3d',
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
import { UnitLength_type } from '@kittycad/lib/dist/types/src/models'
|
||||
import { CommandBarOverwriteWarning } from 'components/CommandBarOverwriteWarning'
|
||||
import { StateMachineCommandSetConfig } from 'lib/commandTypes'
|
||||
import { isDesktop } from 'lib/isDesktop'
|
||||
import { baseUnitLabels, baseUnitsUnion } from 'lib/settings/settingsTypes'
|
||||
import { projectsMachine } from 'machines/projectsMachine'
|
||||
|
||||
export type ProjectsCommandSchema = {
|
||||
@ -23,7 +21,6 @@ export type ProjectsCommandSchema = {
|
||||
'Import file from URL': {
|
||||
name: string
|
||||
code?: string
|
||||
units: UnitLength_type
|
||||
method: 'newProject' | 'existingProject'
|
||||
projectName?: string
|
||||
}
|
||||
@ -157,15 +154,6 @@ export const projectsCommandBarConfig: StateMachineCommandSetConfig<
|
||||
return `${lineCount} line${lineCount === 1 ? '' : 's'}`
|
||||
},
|
||||
},
|
||||
units: {
|
||||
inputType: 'options',
|
||||
required: false,
|
||||
skip: true,
|
||||
options: baseUnitsUnion.map((unit) => ({
|
||||
name: baseUnitLabels[unit],
|
||||
value: unit,
|
||||
})),
|
||||
},
|
||||
},
|
||||
reviewMessage(commandBarContext) {
|
||||
return isDesktop()
|
||||
|
||||
@ -58,6 +58,7 @@ export const KCL_DEFAULT_CONSTANT_PREFIXES = {
|
||||
SEGMENT: 'seg',
|
||||
REVOLVE: 'revolve',
|
||||
PLANE: 'plane',
|
||||
HELIX: 'helix',
|
||||
} as const
|
||||
/** The default KCL length expression */
|
||||
export const KCL_DEFAULT_LENGTH = `5`
|
||||
|
||||
@ -136,7 +136,7 @@ export function kclCommands(commandProps: KclCommandConfig): Command[] {
|
||||
},
|
||||
{
|
||||
name: 'share-file-link',
|
||||
displayName: 'Share file',
|
||||
displayName: 'Share current part (via Zoo link)',
|
||||
hide: IS_NIGHTLY_OR_DEBUG ? undefined : 'desktop',
|
||||
description: 'Create a link that contains a copy of the current file.',
|
||||
groupId: 'code',
|
||||
@ -147,7 +147,6 @@ export function kclCommands(commandProps: KclCommandConfig): Command[] {
|
||||
token: commandProps.authToken,
|
||||
code: codeManager.code,
|
||||
name: commandProps.projectData.project?.name || '',
|
||||
units: commandProps.settings.defaultUnit,
|
||||
}).catch(reportRejection)
|
||||
},
|
||||
},
|
||||
|
||||
@ -5,13 +5,12 @@ describe(`link creation tests`, () => {
|
||||
test(`createCreateFileUrl happy path`, async () => {
|
||||
const code = `extrusionDistance = 12`
|
||||
const name = `test`
|
||||
const units = `mm`
|
||||
|
||||
// Converted with external online tools
|
||||
const expectedEncodedCode = `ZXh0cnVzaW9uRGlzdGFuY2UgPSAxMg%3D%3D`
|
||||
const expectedLink = `${VITE_KC_SITE_APP_URL}/?create-file=true&name=test&units=mm&code=${expectedEncodedCode}&ask-open-desktop=true`
|
||||
const expectedLink = `${VITE_KC_SITE_APP_URL}/?create-file=true&name=test&code=${expectedEncodedCode}&ask-open-desktop=true`
|
||||
|
||||
const result = createCreateFileUrl({ code, name, units })
|
||||
const result = createCreateFileUrl({ code, name })
|
||||
expect(result.toString()).toBe(expectedLink)
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { UnitLength_type } from '@kittycad/lib/dist/types/src/models'
|
||||
import { ASK_TO_OPEN_QUERY_PARAM, CREATE_FILE_URL_PARAM } from './constants'
|
||||
import { stringToBase64 } from './base64'
|
||||
import { VITE_KC_API_BASE_URL, VITE_KC_SITE_APP_URL } from 'env'
|
||||
@ -7,7 +6,6 @@ import { err } from './trap'
|
||||
export interface FileLinkParams {
|
||||
code: string
|
||||
name: string
|
||||
units: UnitLength_type
|
||||
}
|
||||
|
||||
export async function copyFileShareLink(
|
||||
@ -46,12 +44,11 @@ export async function copyFileShareLink(
|
||||
* With the additional step of asking the user if they want to
|
||||
* open the URL in the desktop app.
|
||||
*/
|
||||
export function createCreateFileUrl({ code, name, units }: FileLinkParams) {
|
||||
export function createCreateFileUrl({ code, name }: FileLinkParams) {
|
||||
let origin = VITE_KC_SITE_APP_URL
|
||||
const searchParams = new URLSearchParams({
|
||||
[CREATE_FILE_URL_PARAM]: String(true),
|
||||
name,
|
||||
units,
|
||||
code: stringToBase64(code),
|
||||
[ASK_TO_OPEN_QUERY_PARAM]: String(true),
|
||||
})
|
||||
|
||||
@ -290,9 +290,15 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
||||
],
|
||||
{
|
||||
id: 'helix',
|
||||
onClick: () => console.error('Helix not yet implemented'),
|
||||
onClick: () => {
|
||||
commandBarActor.send({
|
||||
type: 'Find and select command',
|
||||
data: { name: 'Helix', groupId: 'modeling' },
|
||||
})
|
||||
},
|
||||
hotkey: 'H',
|
||||
icon: 'helix',
|
||||
status: 'kcl-only',
|
||||
status: DEV || IS_NIGHTLY_OR_DEBUG ? 'available' : 'kcl-only',
|
||||
title: 'Helix',
|
||||
description: 'Create a helix or spiral in 3D about an axis.',
|
||||
links: [{ label: 'KCL docs', url: 'https://zoo.dev/docs/kcl/helix' }],
|
||||
|
||||
@ -42,6 +42,7 @@ import {
|
||||
} from 'components/Toolbar/EqualLength'
|
||||
import { revolveSketch } from 'lang/modifyAst/addRevolve'
|
||||
import {
|
||||
addHelix,
|
||||
addOffsetPlane,
|
||||
addSweep,
|
||||
deleteFromSelection,
|
||||
@ -276,6 +277,7 @@ export type ModelingMachineEvent =
|
||||
| { type: 'Fillet'; data?: ModelingCommandSchema['Fillet'] }
|
||||
| { type: 'Chamfer'; data?: ModelingCommandSchema['Chamfer'] }
|
||||
| { type: 'Offset plane'; data: ModelingCommandSchema['Offset plane'] }
|
||||
| { type: 'Helix'; data: ModelingCommandSchema['Helix'] }
|
||||
| { type: 'Text-to-CAD'; data: ModelingCommandSchema['Text-to-CAD'] }
|
||||
| { type: 'Prompt-to-edit'; data: ModelingCommandSchema['Prompt-to-edit'] }
|
||||
| {
|
||||
@ -1615,6 +1617,73 @@ export const modelingMachine = setup({
|
||||
}
|
||||
}
|
||||
),
|
||||
helixAstMod: fromPromise(
|
||||
async ({
|
||||
input,
|
||||
}: {
|
||||
input: ModelingCommandSchema['Helix'] | undefined
|
||||
}) => {
|
||||
if (!input) return new Error('No input provided')
|
||||
// Extract inputs
|
||||
const ast = kclManager.ast
|
||||
const {
|
||||
revolutions,
|
||||
angleStart,
|
||||
counterClockWise,
|
||||
radius,
|
||||
axis,
|
||||
length,
|
||||
} = input
|
||||
|
||||
for (const variable of [revolutions, angleStart, radius, length]) {
|
||||
// Insert the variable if it exists
|
||||
if (
|
||||
'variableName' in variable &&
|
||||
variable.variableName &&
|
||||
variable.insertIndex !== undefined
|
||||
) {
|
||||
const newBody = [...ast.body]
|
||||
newBody.splice(
|
||||
variable.insertIndex,
|
||||
0,
|
||||
variable.variableDeclarationAst
|
||||
)
|
||||
ast.body = newBody
|
||||
}
|
||||
}
|
||||
|
||||
const valueOrVariable = (variable: KclCommandValue) =>
|
||||
'variableName' in variable
|
||||
? variable.variableIdentifierAst
|
||||
: variable.valueAst
|
||||
|
||||
const result = addHelix({
|
||||
node: ast,
|
||||
revolutions: valueOrVariable(revolutions),
|
||||
angleStart: valueOrVariable(angleStart),
|
||||
counterClockWise,
|
||||
radius: valueOrVariable(radius),
|
||||
axis,
|
||||
length: valueOrVariable(length),
|
||||
})
|
||||
|
||||
const updateAstResult = await kclManager.updateAst(
|
||||
result.modifiedAst,
|
||||
true,
|
||||
{
|
||||
focusPath: [result.pathToNode],
|
||||
}
|
||||
)
|
||||
|
||||
await codeManager.updateEditorWithAstAndWriteToFile(
|
||||
updateAstResult.newAst
|
||||
)
|
||||
|
||||
if (updateAstResult?.selections) {
|
||||
editorManager.selectRange(updateAstResult?.selections)
|
||||
}
|
||||
}
|
||||
),
|
||||
sweepAstMod: fromPromise(
|
||||
async ({
|
||||
input,
|
||||
@ -1974,6 +2043,11 @@ export const modelingMachine = setup({
|
||||
reenter: true,
|
||||
},
|
||||
|
||||
Helix: {
|
||||
target: 'Applying helix',
|
||||
reenter: true,
|
||||
},
|
||||
|
||||
'Prompt-to-edit': 'Applying Prompt-to-edit',
|
||||
},
|
||||
|
||||
@ -2734,6 +2808,19 @@ export const modelingMachine = setup({
|
||||
},
|
||||
},
|
||||
|
||||
'Applying helix': {
|
||||
invoke: {
|
||||
src: 'helixAstMod',
|
||||
id: 'helixAstMod',
|
||||
input: ({ event }) => {
|
||||
if (event.type !== 'Helix') return undefined
|
||||
return event.data
|
||||
},
|
||||
onDone: ['idle'],
|
||||
onError: ['idle'],
|
||||
},
|
||||
},
|
||||
|
||||
'Applying sweep': {
|
||||
invoke: {
|
||||
src: 'sweepAstMod',
|
||||
|
||||
@ -306,7 +306,6 @@ export const projectsMachine = setup({
|
||||
return {
|
||||
code: '',
|
||||
name: '',
|
||||
units: 'mm',
|
||||
method: 'existingProject',
|
||||
projects: context.projects,
|
||||
}
|
||||
@ -314,7 +313,6 @@ export const projectsMachine = setup({
|
||||
return {
|
||||
code: event.data.code || '',
|
||||
name: event.data.name,
|
||||
units: event.data.units,
|
||||
method: event.data.method,
|
||||
projectName: event.data.projectName,
|
||||
projects: context.projects,
|
||||
|
||||
@ -329,6 +329,7 @@ ipcMain.handle('kittycad', (event, data) => {
|
||||
)(data.args)
|
||||
})
|
||||
|
||||
// Used to find other devices on the local network, e.g. 3D printers, CNC machines, etc.
|
||||
ipcMain.handle('find_machine_api', () => {
|
||||
const timeoutAfterMs = 5000
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -339,7 +340,6 @@ ipcMain.handle('find_machine_api', () => {
|
||||
console.error(error)
|
||||
resolve(null)
|
||||
})
|
||||
console.log('Looking for machine API...')
|
||||
bonjourEt.find(
|
||||
{ protocol: 'tcp', type: 'machine-api' },
|
||||
(service: Service) => {
|
||||
|
||||
@ -26,7 +26,7 @@ export default function Units() {
|
||||
<div className="fixed inset-0 z-50 grid items-end justify-start px-4 pointer-events-none">
|
||||
<div
|
||||
className={
|
||||
'pointer-events-auto max-w-2xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded'
|
||||
'relative pointer-events-auto max-w-2xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded'
|
||||
}
|
||||
>
|
||||
<SettingsSection
|
||||
@ -72,9 +72,7 @@ export default function Units() {
|
||||
</SettingsSection>
|
||||
<OnboardingButtons
|
||||
currentSlug={onboardingPaths.CAMERA}
|
||||
dismiss={dismiss}
|
||||
next={next}
|
||||
nextText="Next: Streaming"
|
||||
dismissClassName="right-auto left-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,19 +1,17 @@
|
||||
import usePlatform from 'hooks/usePlatform'
|
||||
import { OnboardingButtons, kbdClasses, useDismiss, useNextClick } from '.'
|
||||
import { OnboardingButtons, kbdClasses } from '.'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { hotkeyDisplay } from 'lib/hotkeyWrapper'
|
||||
import { COMMAND_PALETTE_HOTKEY } from 'components/CommandBar/CommandBar'
|
||||
|
||||
export default function CmdK() {
|
||||
const dismiss = useDismiss()
|
||||
const next = useNextClick(onboardingPaths.USER_MENU)
|
||||
const platformName = usePlatform()
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 grid items-end justify-center pointer-events-none">
|
||||
<div
|
||||
className={
|
||||
'pointer-events-auto max-w-full xl:max-w-4xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded'
|
||||
'relative pointer-events-auto max-w-full xl:max-w-4xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded'
|
||||
}
|
||||
>
|
||||
<h2 className="text-2xl font-bold">Command Bar</h2>
|
||||
@ -38,12 +36,7 @@ export default function CmdK() {
|
||||
. You can control settings, authentication, and file management from
|
||||
the command bar, as well as a growing number of modeling commands.
|
||||
</p>
|
||||
<OnboardingButtons
|
||||
currentSlug={onboardingPaths.COMMAND_K}
|
||||
dismiss={dismiss}
|
||||
next={next}
|
||||
nextText="Next: User Menu"
|
||||
/>
|
||||
<OnboardingButtons currentSlug={onboardingPaths.COMMAND_K} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,22 +1,14 @@
|
||||
import {
|
||||
kbdClasses,
|
||||
OnboardingButtons,
|
||||
useDemoCode,
|
||||
useDismiss,
|
||||
useNextClick,
|
||||
} from '.'
|
||||
import { kbdClasses, OnboardingButtons, useDemoCode } from '.'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
|
||||
export default function OnboardingCodeEditor() {
|
||||
useDemoCode()
|
||||
const dismiss = useDismiss()
|
||||
const next = useNextClick(onboardingPaths.PARAMETRIC_MODELING)
|
||||
|
||||
return (
|
||||
<div className="fixed grid justify-end items-center inset-0 z-50 pointer-events-none">
|
||||
<div
|
||||
className={
|
||||
'pointer-events-auto z-10 max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-[75vh] flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded'
|
||||
'relative pointer-events-auto z-10 max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-[75vh] flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded'
|
||||
}
|
||||
>
|
||||
<section className="flex-1 overflow-y-auto">
|
||||
@ -73,12 +65,7 @@ export default function OnboardingCodeEditor() {
|
||||
pressing <kbd className={kbdClasses}>Shift + C</kbd>.
|
||||
</p>
|
||||
</section>
|
||||
<OnboardingButtons
|
||||
currentSlug={onboardingPaths.EDITOR}
|
||||
dismiss={dismiss}
|
||||
next={next}
|
||||
nextText="Next: Parametric Modeling"
|
||||
/>
|
||||
<OnboardingButtons currentSlug={onboardingPaths.EDITOR} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,16 +1,13 @@
|
||||
import { APP_NAME } from 'lib/constants'
|
||||
import { OnboardingButtons, useDismiss, useNextClick } from '.'
|
||||
import { OnboardingButtons } from '.'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
|
||||
export default function Export() {
|
||||
const dismiss = useDismiss()
|
||||
const next = useNextClick(onboardingPaths.SKETCHING)
|
||||
|
||||
return (
|
||||
<div className="fixed grid justify-center items-end inset-0 z-50 pointer-events-none">
|
||||
<div
|
||||
className={
|
||||
'pointer-events-auto max-w-full xl:max-w-2xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded'
|
||||
'relative pointer-events-auto max-w-full xl:max-w-2xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded'
|
||||
}
|
||||
>
|
||||
<section className="flex-1">
|
||||
@ -52,12 +49,7 @@ export default function Export() {
|
||||
!
|
||||
</p>
|
||||
</section>
|
||||
<OnboardingButtons
|
||||
currentSlug={onboardingPaths.EXPORT}
|
||||
next={next}
|
||||
dismiss={dismiss}
|
||||
nextText="Next: Sketching"
|
||||
/>
|
||||
<OnboardingButtons currentSlug={onboardingPaths.EXPORT} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { OnboardingButtons, useDemoCode, useDismiss } from '.'
|
||||
import { OnboardingButtons, useDemoCode } from '.'
|
||||
import { useEffect } from 'react'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
import { APP_NAME } from 'lib/constants'
|
||||
@ -7,7 +7,6 @@ import { sceneInfra } from 'lib/singletons'
|
||||
|
||||
export default function FutureWork() {
|
||||
const { send } = useModelingContext()
|
||||
const dismiss = useDismiss()
|
||||
|
||||
// Reset the code, the camera, and the modeling state
|
||||
useDemoCode()
|
||||
@ -19,7 +18,7 @@ export default function FutureWork() {
|
||||
|
||||
return (
|
||||
<div className="fixed grid justify-center items-center inset-0 bg-chalkboard-100/50 z-50">
|
||||
<div className="max-w-full xl:max-w-2xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded">
|
||||
<div className="relative max-w-full xl:max-w-2xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded">
|
||||
<h1 className="text-2xl font-bold">Future Work</h1>
|
||||
<p className="my-4">
|
||||
We have curves, cuts, multi-profile sketch mode, and many more CAD
|
||||
@ -59,9 +58,6 @@ export default function FutureWork() {
|
||||
<OnboardingButtons
|
||||
currentSlug={onboardingPaths.FUTURE_WORK}
|
||||
className="mt-6"
|
||||
dismiss={dismiss}
|
||||
next={dismiss}
|
||||
nextText="Finish"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,23 +1,15 @@
|
||||
import {
|
||||
OnboardingButtons,
|
||||
kbdClasses,
|
||||
useDemoCode,
|
||||
useDismiss,
|
||||
useNextClick,
|
||||
} from '.'
|
||||
import { OnboardingButtons, kbdClasses, useDemoCode } from '.'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { bracketWidthConstantLine } from 'lib/exampleKcl'
|
||||
|
||||
export default function OnboardingInteractiveNumbers() {
|
||||
useDemoCode()
|
||||
const dismiss = useDismiss()
|
||||
const next = useNextClick(onboardingPaths.COMMAND_K)
|
||||
|
||||
return (
|
||||
<div className="fixed grid justify-end items-center inset-0 z-50 pointer-events-none">
|
||||
<div
|
||||
className={
|
||||
'pointer-events-auto z-10 max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-[75vh] flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded'
|
||||
'relative pointer-events-auto z-10 max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-[75vh] flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded'
|
||||
}
|
||||
>
|
||||
<section className="flex-1 overflow-y-auto mb-6">
|
||||
@ -88,12 +80,7 @@ export default function OnboardingInteractiveNumbers() {
|
||||
your ideas for how to make it better.
|
||||
</p>
|
||||
</section>
|
||||
<OnboardingButtons
|
||||
currentSlug={onboardingPaths.INTERACTIVE_NUMBERS}
|
||||
dismiss={dismiss}
|
||||
next={next}
|
||||
nextText="Next: Command Bar"
|
||||
/>
|
||||
<OnboardingButtons currentSlug={onboardingPaths.INTERACTIVE_NUMBERS} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { OnboardingButtons, useDemoCode, useDismiss, useNextClick } from '.'
|
||||
import { OnboardingButtons, useDemoCode } from '.'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import { Themes, getSystemTheme } from 'lib/theme'
|
||||
@ -8,13 +8,12 @@ import { isDesktop } from 'lib/isDesktop'
|
||||
import { useNavigate, useRouteLoaderData } from 'react-router-dom'
|
||||
import { codeManager, kclManager } from 'lib/singletons'
|
||||
import { APP_NAME } from 'lib/constants'
|
||||
import { useState } from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { IndexLoaderData } from 'lib/types'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { useFileContext } from 'hooks/useFileContext'
|
||||
import { useLspContext } from 'components/LspProvider'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
import { toSync } from 'lib/utils'
|
||||
|
||||
/**
|
||||
* Show either a welcome screen or a warning screen
|
||||
@ -39,7 +38,7 @@ interface OnboardingResetWarningProps {
|
||||
function OnboardingResetWarning(props: OnboardingResetWarningProps) {
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 grid place-content-center bg-chalkboard-110/50">
|
||||
<div className="max-w-3xl p-8 rounded bg-chalkboard-10 dark:bg-chalkboard-90">
|
||||
<div className="relative max-w-3xl p-8 rounded bg-chalkboard-10 dark:bg-chalkboard-90">
|
||||
{!isDesktop() ? (
|
||||
<OnboardingWarningWeb {...props} />
|
||||
) : (
|
||||
@ -52,7 +51,6 @@ function OnboardingResetWarning(props: OnboardingResetWarningProps) {
|
||||
|
||||
function OnboardingWarningDesktop(props: OnboardingResetWarningProps) {
|
||||
const navigate = useNavigate()
|
||||
const dismiss = useDismiss()
|
||||
const loaderData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
|
||||
const { context: fileContext } = useFileContext()
|
||||
const { onProjectClose, onProjectOpen } = useLspContext()
|
||||
@ -81,17 +79,28 @@ function OnboardingWarningDesktop(props: OnboardingResetWarningProps) {
|
||||
</section>
|
||||
<OnboardingButtons
|
||||
className="mt-6"
|
||||
dismiss={dismiss}
|
||||
next={toSync(onAccept, reportRejection)}
|
||||
nextText="Make a new project"
|
||||
onNextOverride={() => {
|
||||
onAccept().catch(reportRejection)
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
function OnboardingWarningWeb(props: OnboardingResetWarningProps) {
|
||||
const dismiss = useDismiss()
|
||||
useEffect(() => {
|
||||
async function beforeNavigate() {
|
||||
// We do want to update both the state and editor here.
|
||||
codeManager.updateCodeStateEditor(bracket)
|
||||
await codeManager.writeToFile()
|
||||
|
||||
await kclManager.executeCode(true)
|
||||
props.setShouldShowWarning(false)
|
||||
}
|
||||
return () => {
|
||||
beforeNavigate().catch(reportRejection)
|
||||
}
|
||||
}, [])
|
||||
return (
|
||||
<>
|
||||
<h1 className="text-3xl font-bold text-warn-80 dark:text-warn-10">
|
||||
@ -101,19 +110,7 @@ function OnboardingWarningWeb(props: OnboardingResetWarningProps) {
|
||||
We see you have some of your own code written in this project. Please
|
||||
save it somewhere else before continuing the onboarding.
|
||||
</p>
|
||||
<OnboardingButtons
|
||||
className="mt-6"
|
||||
dismiss={dismiss}
|
||||
next={toSync(async () => {
|
||||
// We do want to update both the state and editor here.
|
||||
codeManager.updateCodeStateEditor(bracket)
|
||||
await codeManager.writeToFile()
|
||||
|
||||
await kclManager.executeCode(true)
|
||||
props.setShouldShowWarning(false)
|
||||
}, reportRejection)}
|
||||
nextText="Overwrite code and continue"
|
||||
/>
|
||||
<OnboardingButtons className="mt-6" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -136,12 +133,10 @@ function OnboardingIntroductionInner() {
|
||||
(theme.current === Themes.System && getSystemTheme() === Themes.Light)
|
||||
? '-dark'
|
||||
: ''
|
||||
const dismiss = useDismiss()
|
||||
const next = useNextClick(onboardingPaths.CAMERA)
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 grid place-content-center bg-chalkboard-110/50">
|
||||
<div className="max-w-3xl p-8 rounded bg-chalkboard-10 dark:bg-chalkboard-90">
|
||||
<div className="relative max-w-3xl p-8 rounded bg-chalkboard-10 dark:bg-chalkboard-90">
|
||||
<h1 className="flex flex-wrap items-center gap-4 text-3xl font-bold">
|
||||
<img
|
||||
src={`${isDesktop() ? '.' : ''}/zma-logomark${getLogoTheme()}.svg`}
|
||||
@ -192,9 +187,6 @@ function OnboardingIntroductionInner() {
|
||||
<OnboardingButtons
|
||||
currentSlug={onboardingPaths.INDEX}
|
||||
className="mt-6"
|
||||
dismiss={dismiss}
|
||||
next={next}
|
||||
nextText="Mouse Controls"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { OnboardingButtons, useDemoCode, useDismiss, useNextClick } from '.'
|
||||
import { OnboardingButtons, useDemoCode } from '.'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { Themes, getSystemTheme } from 'lib/theme'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
@ -21,14 +21,12 @@ export default function OnboardingParametricModeling() {
|
||||
(theme === Themes.System && getSystemTheme() === Themes.Light)
|
||||
? '-dark'
|
||||
: ''
|
||||
const dismiss = useDismiss()
|
||||
const next = useNextClick(onboardingPaths.INTERACTIVE_NUMBERS)
|
||||
|
||||
return (
|
||||
<div className="fixed grid justify-end items-center inset-0 z-50 pointer-events-none">
|
||||
<div
|
||||
className={
|
||||
'pointer-events-auto z-10 max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-[75vh] flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded'
|
||||
'relative pointer-events-auto z-10 max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-[75vh] flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded'
|
||||
}
|
||||
>
|
||||
<section className="flex-1 overflow-y-auto mb-6">
|
||||
@ -77,12 +75,7 @@ export default function OnboardingParametricModeling() {
|
||||
</figcaption>
|
||||
</figure>
|
||||
</section>
|
||||
<OnboardingButtons
|
||||
currentSlug={onboardingPaths.PARAMETRIC_MODELING}
|
||||
dismiss={dismiss}
|
||||
next={next}
|
||||
nextText="Next: Interactive Numbers"
|
||||
/>
|
||||
<OnboardingButtons currentSlug={onboardingPaths.PARAMETRIC_MODELING} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,17 +1,15 @@
|
||||
import { OnboardingButtons, useDismiss, useNextClick } from '.'
|
||||
import { OnboardingButtons } from '.'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { isDesktop } from 'lib/isDesktop'
|
||||
|
||||
export default function ProjectMenu() {
|
||||
const dismiss = useDismiss()
|
||||
const next = useNextClick(onboardingPaths.EXPORT)
|
||||
const onDesktop = isDesktop()
|
||||
|
||||
return (
|
||||
<div className="fixed grid justify-center items-start inset-0 z-50 pointer-events-none">
|
||||
<div
|
||||
className={
|
||||
'pointer-events-auto max-w-xl flex flex-col border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded'
|
||||
'relative pointer-events-auto max-w-xl flex flex-col border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded'
|
||||
}
|
||||
>
|
||||
<section className="flex-1">
|
||||
@ -57,12 +55,7 @@ export default function ProjectMenu() {
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
<OnboardingButtons
|
||||
currentSlug={onboardingPaths.PROJECT_MENU}
|
||||
next={next}
|
||||
dismiss={dismiss}
|
||||
nextText="Next: Export"
|
||||
/>
|
||||
<OnboardingButtons currentSlug={onboardingPaths.PROJECT_MENU} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,12 +1,9 @@
|
||||
import { OnboardingButtons, useDismiss, useNextClick } from '.'
|
||||
import { OnboardingButtons } from '.'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { useEffect } from 'react'
|
||||
import { codeManager, kclManager } from 'lib/singletons'
|
||||
|
||||
export default function Sketching() {
|
||||
const dismiss = useDismiss()
|
||||
const next = useNextClick(onboardingPaths.FUTURE_WORK)
|
||||
|
||||
useEffect(() => {
|
||||
async function clearEditor() {
|
||||
// We do want to update both the state and editor here.
|
||||
@ -22,7 +19,7 @@ export default function Sketching() {
|
||||
<div className="fixed grid justify-center items-end inset-0 z-50 pointer-events-none">
|
||||
<div
|
||||
className={
|
||||
'pointer-events-auto max-w-full xl:max-w-2xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded'
|
||||
'relative pointer-events-auto max-w-full xl:max-w-2xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded'
|
||||
}
|
||||
>
|
||||
<h1 className="text-2xl font-bold">Sketching</h1>
|
||||
@ -45,9 +42,6 @@ export default function Sketching() {
|
||||
<OnboardingButtons
|
||||
currentSlug={onboardingPaths.SKETCHING}
|
||||
className="mt-6"
|
||||
next={next}
|
||||
dismiss={dismiss}
|
||||
nextText="Next: Future Work"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,15 +1,12 @@
|
||||
import { OnboardingButtons, useDismiss, useNextClick } from '.'
|
||||
import { OnboardingButtons } from '.'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
|
||||
export default function Streaming() {
|
||||
const dismiss = useDismiss()
|
||||
const next = useNextClick(onboardingPaths.EDITOR)
|
||||
|
||||
return (
|
||||
<div className="fixed grid justify-start items-center inset-0 z-50 pointer-events-none">
|
||||
<div
|
||||
className={
|
||||
'pointer-events-auto max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-[75vh] flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded'
|
||||
'relative pointer-events-auto max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-[75vh] flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded'
|
||||
}
|
||||
>
|
||||
<section className="flex-1 overflow-y-auto">
|
||||
@ -44,9 +41,7 @@ export default function Streaming() {
|
||||
</section>
|
||||
<OnboardingButtons
|
||||
currentSlug={onboardingPaths.STREAMING}
|
||||
dismiss={dismiss}
|
||||
next={next}
|
||||
nextText="Next: Code Editor"
|
||||
dismissClassName="right-auto left-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,12 +1,10 @@
|
||||
import { OnboardingButtons, useDismiss, useNextClick } from '.'
|
||||
import { OnboardingButtons } from '.'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useUser } from 'machines/appMachine'
|
||||
|
||||
export default function UserMenu() {
|
||||
const user = useUser()
|
||||
const dismiss = useDismiss()
|
||||
const next = useNextClick(onboardingPaths.PROJECT_MENU)
|
||||
const [avatarErrored, setAvatarErrored] = useState(false)
|
||||
|
||||
const errorOrNoImage = !user?.image || avatarErrored
|
||||
@ -32,7 +30,7 @@ export default function UserMenu() {
|
||||
<div className="fixed grid justify-center items-start inset-0 z-50 pointer-events-none">
|
||||
<div
|
||||
className={
|
||||
'pointer-events-auto max-w-xl flex flex-col border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded'
|
||||
'relative pointer-events-auto max-w-xl flex flex-col border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded'
|
||||
}
|
||||
>
|
||||
<section className="flex-1">
|
||||
@ -48,12 +46,7 @@ export default function UserMenu() {
|
||||
only apply to the current project.
|
||||
</p>
|
||||
</section>
|
||||
<OnboardingButtons
|
||||
currentSlug={onboardingPaths.USER_MENU}
|
||||
dismiss={dismiss}
|
||||
next={next}
|
||||
nextText="Next: Project Menu"
|
||||
/>
|
||||
<OnboardingButtons currentSlug={onboardingPaths.USER_MENU} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -26,6 +26,9 @@ import { reportRejection } from 'lib/trap'
|
||||
import { useNetworkContext } from 'hooks/useNetworkContext'
|
||||
import { NetworkHealthState } from 'hooks/useNetworkStatus'
|
||||
import { EngineConnectionStateType } from 'lang/std/engineConnection'
|
||||
import { CustomIcon } from 'components/CustomIcon'
|
||||
import Tooltip from 'components/Tooltip'
|
||||
import { commandBarActor } from 'machines/commandBarMachine'
|
||||
|
||||
export const kbdClasses =
|
||||
'py-0.5 px-1 text-sm rounded bg-chalkboard-10 dark:bg-chalkboard-100 border border-chalkboard-50 border-b-2'
|
||||
@ -163,58 +166,99 @@ export function useStepNumber(
|
||||
: onboardingRoutes.findIndex(
|
||||
(r) => r.path === makeUrlPathRelative(slug)
|
||||
) + 1
|
||||
: undefined
|
||||
: 1
|
||||
}
|
||||
|
||||
export function OnboardingButtons({
|
||||
next,
|
||||
nextText,
|
||||
dismiss,
|
||||
currentSlug,
|
||||
className,
|
||||
dismissClassName,
|
||||
onNextOverride,
|
||||
...props
|
||||
}: {
|
||||
next: () => void
|
||||
nextText?: string
|
||||
dismiss: () => void
|
||||
currentSlug?: (typeof onboardingPaths)[keyof typeof onboardingPaths]
|
||||
className?: string
|
||||
dismissClassName?: string
|
||||
onNextOverride?: () => void
|
||||
} & React.HTMLAttributes<HTMLDivElement>) {
|
||||
const dismiss = useDismiss()
|
||||
const stepNumber = useStepNumber(currentSlug)
|
||||
const previousStep =
|
||||
!stepNumber || stepNumber === 0 ? null : onboardingRoutes[stepNumber - 2]
|
||||
const goToPrevious = useNextClick(
|
||||
onboardingPaths.INDEX + (previousStep?.path ?? '')
|
||||
)
|
||||
const nextStep =
|
||||
!stepNumber || stepNumber === onboardingRoutes.length
|
||||
? null
|
||||
: onboardingRoutes[stepNumber]
|
||||
const goToNext = useNextClick(onboardingPaths.INDEX + (nextStep?.path ?? ''))
|
||||
|
||||
return (
|
||||
<div
|
||||
className={'flex items-center justify-between ' + (className ?? '')}
|
||||
{...props}
|
||||
>
|
||||
<ActionButton
|
||||
Element="button"
|
||||
<>
|
||||
<button
|
||||
onClick={dismiss}
|
||||
iconStart={{
|
||||
icon: 'close',
|
||||
className: 'text-chalkboard-10',
|
||||
bgClassName: 'bg-destroy-80 group-hover:bg-destroy-80',
|
||||
}}
|
||||
className="hover:border-destroy-40 hover:bg-destroy-10/50 dark:hover:bg-destroy-80/50"
|
||||
className={
|
||||
'group block !absolute left-auto right-full top-[-3px] m-2.5 p-0 border-none bg-transparent hover:bg-transparent ' +
|
||||
dismissClassName
|
||||
}
|
||||
data-testid="onboarding-dismiss"
|
||||
>
|
||||
Dismiss
|
||||
</ActionButton>
|
||||
{stepNumber !== undefined && (
|
||||
<p className="font-mono text-xs text-center m-0">
|
||||
{stepNumber} / {onboardingRoutes.length}
|
||||
</p>
|
||||
)}
|
||||
<ActionButton
|
||||
autoFocus
|
||||
Element="button"
|
||||
onClick={next}
|
||||
iconStart={{ icon: 'arrowRight', bgClassName: 'dark:bg-chalkboard-80' }}
|
||||
className="dark:hover:bg-chalkboard-80/50"
|
||||
data-testid="onboarding-next"
|
||||
<CustomIcon
|
||||
name="close"
|
||||
className="w-5 h-5 rounded-sm bg-destroy-10 text-destroy-80 dark:bg-destroy-80 dark:text-destroy-10 group-hover:brightness-110"
|
||||
/>
|
||||
<Tooltip position="bottom" delay={500}>
|
||||
Dismiss <kbd className="hotkey ml-4 dark:!bg-chalkboard-80">esc</kbd>
|
||||
</Tooltip>
|
||||
</button>
|
||||
<div
|
||||
className={'flex items-center justify-between ' + (className ?? '')}
|
||||
{...props}
|
||||
>
|
||||
{nextText ?? 'Next'}
|
||||
</ActionButton>
|
||||
</div>
|
||||
<ActionButton
|
||||
Element="button"
|
||||
onClick={() =>
|
||||
previousStep?.path || previousStep?.index
|
||||
? goToPrevious()
|
||||
: dismiss()
|
||||
}
|
||||
iconStart={{
|
||||
icon: previousStep ? 'arrowLeft' : 'close',
|
||||
className: 'text-chalkboard-10',
|
||||
bgClassName: 'bg-destroy-80 group-hover:bg-destroy-80',
|
||||
}}
|
||||
className="hover:border-destroy-40 hover:bg-destroy-10/50 dark:hover:bg-destroy-80/50"
|
||||
data-testid="onboarding-prev"
|
||||
>
|
||||
{previousStep ? `Back` : 'Dismiss'}
|
||||
</ActionButton>
|
||||
{stepNumber !== undefined && (
|
||||
<p className="font-mono text-xs text-center m-0">
|
||||
{stepNumber} / {onboardingRoutes.length}
|
||||
</p>
|
||||
)}
|
||||
<ActionButton
|
||||
autoFocus
|
||||
Element="button"
|
||||
onClick={() => {
|
||||
if (nextStep?.path) {
|
||||
onNextOverride ? onNextOverride() : goToNext()
|
||||
} else {
|
||||
dismiss()
|
||||
}
|
||||
}}
|
||||
iconStart={{
|
||||
icon: nextStep ? 'arrowRight' : 'checkmark',
|
||||
bgClassName: 'dark:bg-chalkboard-80',
|
||||
}}
|
||||
className="dark:hover:bg-chalkboard-80/50"
|
||||
data-testid="onboarding-next"
|
||||
>
|
||||
{nextStep ? `Next` : 'Finish'}
|
||||
</ActionButton>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -32,7 +32,8 @@ export const PACKAGE_NAME = isDesktop()
|
||||
|
||||
export const IS_NIGHTLY = PACKAGE_NAME.indexOf('-nightly') > -1
|
||||
|
||||
export const IS_NIGHTLY_OR_DEBUG = IS_NIGHTLY || APP_VERSION === '0.0.0'
|
||||
export const IS_NIGHTLY_OR_DEBUG =
|
||||
IS_NIGHTLY || APP_VERSION === '0.0.0' || APP_VERSION === '11.22.33'
|
||||
|
||||
export function getReleaseUrl(version: string = APP_VERSION) {
|
||||
return `https://github.com/KittyCAD/modeling-app/releases/tag/${
|
||||
|
||||
@ -1038,12 +1038,7 @@ mod tests {
|
||||
fn get_autocomplete_snippet_sweep() {
|
||||
let sweep_fn: Box<dyn StdLibFn> = Box::new(crate::std::sweep::Sweep);
|
||||
let snippet = sweep_fn.to_autocomplete_snippet().unwrap();
|
||||
assert_eq!(
|
||||
snippet,
|
||||
r#"sweep({
|
||||
path = ${0:sketch000},
|
||||
}, ${1:%})${}"#
|
||||
);
|
||||
assert_eq!(snippet, r#"sweep(${0:%}, path = ${1:sketch000})${}"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -242,7 +242,7 @@ firstSketch = startSketchOn('XY')
|
||||
|> extrude(length = 6)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||
shell(firstSketch, faces = ['end'], thickness = 0.25)"#;
|
||||
|
||||
let (program, ctx, _) = parse_execute(new).await.unwrap();
|
||||
|
||||
@ -273,7 +273,7 @@ firstSketch = startSketchOn('XY')
|
||||
|> extrude(length = 6)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch) "#;
|
||||
shell(firstSketch, faces = ['end'], thickness = 0.25) "#;
|
||||
|
||||
let new = r#"// Remove the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
@ -285,7 +285,7 @@ firstSketch = startSketchOn('XY')
|
||||
|> extrude(length = 6)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||
shell(firstSketch, faces = ['end'], thickness = 0.25)"#;
|
||||
|
||||
let (program_old, ctx, _) = parse_execute(old).await.unwrap();
|
||||
|
||||
@ -318,7 +318,7 @@ firstSketch = startSketchOn('XY')
|
||||
|> extrude(length = 6)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch) "#;
|
||||
shell(firstSketch, faces = ['end'], thickness = 0.25) "#;
|
||||
|
||||
let new = r#"// Remove the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
@ -330,7 +330,7 @@ firstSketch = startSketchOn('XY')
|
||||
|> extrude(length = 6)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||
shell(firstSketch, faces = ['end'], thickness = 0.25)"#;
|
||||
|
||||
let (program, ctx, _) = parse_execute(old).await.unwrap();
|
||||
|
||||
@ -363,7 +363,7 @@ firstSketch = startSketchOn('XY')
|
||||
|> extrude(length = 6)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch) "#;
|
||||
shell(firstSketch, faces = ['end'], thickness = 0.25) "#;
|
||||
|
||||
let new = r#"// Remove the end face for the extrusion.
|
||||
firstSketch = startSketchOn('XY')
|
||||
@ -375,7 +375,7 @@ firstSketch = startSketchOn('XY')
|
||||
|> extrude(length = 6)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||
shell(firstSketch, faces = ['end'], thickness = 0.25)"#;
|
||||
|
||||
let (program, ctx, _) = parse_execute(old).await.unwrap();
|
||||
|
||||
@ -409,7 +409,7 @@ firstSketch = startSketchOn('XY')
|
||||
|> extrude(length = 6)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||
shell(firstSketch, faces = ['end'], thickness = 0.25)"#;
|
||||
|
||||
let (program, mut ctx, _) = parse_execute(new).await.unwrap();
|
||||
|
||||
@ -451,7 +451,7 @@ firstSketch = startSketchOn('XY')
|
||||
|> extrude(length = 6)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||
shell(firstSketch, faces = ['end'], thickness = 0.25)"#;
|
||||
|
||||
let (program, mut ctx, _) = parse_execute(new).await.unwrap();
|
||||
|
||||
@ -486,7 +486,7 @@ firstSketch = startSketchOn('XY')
|
||||
|> extrude(length = 6)
|
||||
|
||||
// Remove the end face for the extrusion.
|
||||
shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||
shell(firstSketch, faces = ['end'], thickness = 0.25)"#;
|
||||
|
||||
let (program, mut ctx, _) = parse_execute(new).await.unwrap();
|
||||
|
||||
|
||||
@ -32,6 +32,16 @@ impl Geometry {
|
||||
Geometry::Solid(e) => e.id,
|
||||
}
|
||||
}
|
||||
|
||||
/// If this geometry is the result of a pattern, then return the ID of
|
||||
/// the original sketch which was patterned.
|
||||
/// Equivalent to the `id()` method if this isn't a pattern.
|
||||
pub fn original_id(&self) -> uuid::Uuid {
|
||||
match self {
|
||||
Geometry::Sketch(s) => s.original_id,
|
||||
Geometry::Solid(e) => e.sketch.original_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A set of geometry.
|
||||
@ -419,6 +429,8 @@ pub struct Sketch {
|
||||
/// The original id of the sketch. This stays the same even if the sketch is
|
||||
/// is sketched on face etc.
|
||||
pub artifact_id: ArtifactId,
|
||||
#[ts(skip)]
|
||||
pub original_id: uuid::Uuid,
|
||||
pub units: UnitLen,
|
||||
/// Metadata.
|
||||
#[serde(rename = "__meta")]
|
||||
|
||||
@ -1590,10 +1590,10 @@ let w = f() + f()
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|> extrude(length = 40.14)
|
||||
|> shell({
|
||||
faces: [seg01],
|
||||
thickness: 3.14,
|
||||
}, %)
|
||||
|> shell(
|
||||
thickness = 3.14,
|
||||
faces = [seg01]
|
||||
)
|
||||
"#;
|
||||
|
||||
let ctx = crate::test_server::new_context(UnitLength::Mm, true, None)
|
||||
@ -1620,10 +1620,10 @@ let w = f() + f()
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|> extrude(length = 40.14)
|
||||
|> shell({
|
||||
faces: [seg01],
|
||||
thickness: 3.14,
|
||||
}, %)
|
||||
|> shell(
|
||||
faces = [seg01],
|
||||
thickness = 3.14,
|
||||
)
|
||||
"#;
|
||||
|
||||
// Execute a slightly different program again.
|
||||
|
||||
@ -160,7 +160,7 @@ impl Program {
|
||||
|
||||
/// Get the meta settings for the kcl file from the annotations.
|
||||
pub fn meta_settings(&self) -> Result<Option<crate::MetaSettings>, KclError> {
|
||||
self.ast.get_meta_settings()
|
||||
self.ast.meta_settings()
|
||||
}
|
||||
|
||||
/// Change the meta settings for the kcl file.
|
||||
|
||||
@ -269,7 +269,7 @@ impl Node<Program> {
|
||||
}
|
||||
|
||||
/// Get the annotations for the meta settings from the kcl file.
|
||||
pub fn get_meta_settings(&self) -> Result<Option<crate::execution::MetaSettings>, KclError> {
|
||||
pub fn meta_settings(&self) -> Result<Option<crate::execution::MetaSettings>, KclError> {
|
||||
for annotation_node in self.annotations() {
|
||||
let annotation = &annotation_node.value;
|
||||
if annotation.annotation_name() == Some(annotations::SETTINGS) {
|
||||
@ -3881,7 +3881,7 @@ const cylinder = startSketchOn('-XZ')
|
||||
|
||||
startSketchOn('XY')"#;
|
||||
let program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||
let result = program.get_meta_settings().unwrap();
|
||||
let result = program.meta_settings().unwrap();
|
||||
assert!(result.is_some());
|
||||
let meta_settings = result.unwrap();
|
||||
|
||||
@ -3897,7 +3897,7 @@ startSketchOn('XY')"#;
|
||||
|
||||
startSketchOn('XY')"#;
|
||||
let mut program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||
let result = program.get_meta_settings().unwrap();
|
||||
let result = program.meta_settings().unwrap();
|
||||
assert!(result.is_some());
|
||||
let meta_settings = result.unwrap();
|
||||
|
||||
@ -3914,7 +3914,7 @@ startSketchOn('XY')"#;
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let result = new_program.get_meta_settings().unwrap();
|
||||
let result = new_program.meta_settings().unwrap();
|
||||
assert!(result.is_some());
|
||||
let meta_settings = result.unwrap();
|
||||
|
||||
@ -3939,7 +3939,7 @@ startSketchOn('XY')
|
||||
async fn test_parse_get_meta_settings_nothing_to_mm() {
|
||||
let some_program_string = r#"startSketchOn('XY')"#;
|
||||
let mut program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||
let result = program.get_meta_settings().unwrap();
|
||||
let result = program.meta_settings().unwrap();
|
||||
assert!(result.is_none());
|
||||
|
||||
// Edit the ast.
|
||||
@ -3950,7 +3950,7 @@ startSketchOn('XY')
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let result = new_program.get_meta_settings().unwrap();
|
||||
let result = new_program.meta_settings().unwrap();
|
||||
assert!(result.is_some());
|
||||
let meta_settings = result.unwrap();
|
||||
|
||||
|
||||
@ -120,10 +120,11 @@ pub async fn appearance(_exec_state: &mut ExecState, args: Args) -> Result<KclVa
|
||||
/// |> close()
|
||||
/// |> extrude(length = 6)
|
||||
///
|
||||
/// shell({
|
||||
/// shell(
|
||||
/// firstSketch,
|
||||
/// faces = ['end'],
|
||||
/// thickness = 0.25,
|
||||
/// }, firstSketch)
|
||||
/// )
|
||||
/// |> appearance({
|
||||
/// color = '#ff0000',
|
||||
/// metalness = 90,
|
||||
@ -147,10 +148,11 @@ pub async fn appearance(_exec_state: &mut ExecState, args: Args) -> Result<KclVa
|
||||
/// roughness = 90
|
||||
/// }, %)
|
||||
///
|
||||
/// shell({
|
||||
/// shell(
|
||||
/// firstSketch,
|
||||
/// faces = ['end'],
|
||||
/// thickness = 0.25,
|
||||
/// }, firstSketch)
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
@ -252,9 +254,7 @@ pub async fn appearance(_exec_state: &mut ExecState, args: Args) -> Result<KclVa
|
||||
/// radius = 2,
|
||||
/// }, %)
|
||||
/// |> hole(pipeHole, %)
|
||||
/// |> sweep({
|
||||
/// path: sweepPath,
|
||||
/// }, %)
|
||||
/// |> sweep(path = sweepPath)
|
||||
/// |> appearance({
|
||||
/// color: "#ff0000",
|
||||
/// metalness: 50,
|
||||
|
||||
@ -356,7 +356,7 @@ impl Args {
|
||||
Ok(numbers)
|
||||
}
|
||||
|
||||
pub(crate) fn get_pattern_transform_args(&self) -> Result<(u32, FnAsArg<'_>, SolidSet), KclError> {
|
||||
pub(crate) fn get_pattern_transform_args(&self) -> Result<(u32, FnAsArg<'_>, SolidSet, Option<bool>), KclError> {
|
||||
FromArgs::from_args(self, 0)
|
||||
}
|
||||
|
||||
@ -764,6 +764,10 @@ macro_rules! let_field_of {
|
||||
($obj:ident, $field:ident?) => {
|
||||
let $field = $obj.get(stringify!($field)).and_then(FromKclValue::from_kcl_val);
|
||||
};
|
||||
// Optional field but with a different string used as the key
|
||||
($obj:ident, $field:ident? $key:literal) => {
|
||||
let $field = $obj.get($key).and_then(FromKclValue::from_kcl_val);
|
||||
};
|
||||
// Mandatory field, but with a different string used as the key.
|
||||
($obj:ident, $field:ident $key:literal) => {
|
||||
let $field = $obj.get($key).and_then(FromKclValue::from_kcl_val)?;
|
||||
@ -947,12 +951,14 @@ impl<'a> FromKclValue<'a> for super::patterns::CircularPattern3dData {
|
||||
let_field_of!(obj, rotate_duplicates "rotateDuplicates");
|
||||
let_field_of!(obj, axis);
|
||||
let_field_of!(obj, center);
|
||||
let_field_of!(obj, use_original? "useOriginal");
|
||||
Some(Self {
|
||||
instances,
|
||||
axis,
|
||||
center,
|
||||
arc_degrees,
|
||||
rotate_duplicates,
|
||||
use_original,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -964,11 +970,13 @@ impl<'a> FromKclValue<'a> for super::patterns::CircularPattern2dData {
|
||||
let_field_of!(obj, arc_degrees "arcDegrees");
|
||||
let_field_of!(obj, rotate_duplicates "rotateDuplicates");
|
||||
let_field_of!(obj, center);
|
||||
let_field_of!(obj, use_original? "useOriginal");
|
||||
Some(Self {
|
||||
instances,
|
||||
center,
|
||||
arc_degrees,
|
||||
rotate_duplicates,
|
||||
use_original,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1011,15 +1019,6 @@ impl<'a> FromKclValue<'a> for super::sketch::BezierData {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for super::shell::ShellData {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
let obj = arg.as_object()?;
|
||||
let_field_of!(obj, thickness);
|
||||
let_field_of!(obj, faces);
|
||||
Some(Self { thickness, faces })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for super::chamfer::ChamferData {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
let obj = arg.as_object()?;
|
||||
@ -1043,20 +1042,6 @@ impl<'a> FromKclValue<'a> for super::fillet::FilletData {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for super::sweep::SweepData {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
let obj = arg.as_object()?;
|
||||
let_field_of!(obj, path);
|
||||
let_field_of!(obj, sectional?);
|
||||
let_field_of!(obj, tolerance?);
|
||||
Some(Self {
|
||||
path,
|
||||
sectional,
|
||||
tolerance,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for super::appearance::AppearanceData {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
let obj = arg.as_object()?;
|
||||
|
||||
@ -43,7 +43,7 @@ pub async fn helix(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// // Create a spring by sweeping around the helix path.
|
||||
/// springSketch = startSketchOn('YZ')
|
||||
/// |> circle({ center = [0, 0], radius = 0.5 }, %)
|
||||
/// |> sweep({ path = helixPath }, %)
|
||||
/// |> sweep(path = helixPath)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
@ -64,7 +64,7 @@ pub async fn helix(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// // Create a spring by sweeping around the helix path.
|
||||
/// springSketch = startSketchOn('XY')
|
||||
/// |> circle({ center = [0, 0], radius = 0.5 }, %)
|
||||
/// |> sweep({ path = helixPath }, %)
|
||||
/// |> sweep(path = helixPath)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
@ -86,7 +86,7 @@ pub async fn helix(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// // Create a spring by sweeping around the helix path.
|
||||
/// springSketch = startSketchOn('XY')
|
||||
/// |> circle({ center = [0, 0], radius = 1 }, %)
|
||||
/// |> sweep({ path = helixPath }, %)
|
||||
/// |> sweep(path = helixPath)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "helix",
|
||||
|
||||
@ -63,7 +63,7 @@ pub struct LinearPattern3dData {
|
||||
|
||||
/// Repeat some 3D solid, changing each repetition slightly.
|
||||
pub async fn pattern_transform(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (num_repetitions, transform, extr) = args.get_pattern_transform_args()?;
|
||||
let (num_repetitions, transform, extr, use_original) = args.get_pattern_transform_args()?;
|
||||
|
||||
let solids = inner_pattern_transform(
|
||||
num_repetitions,
|
||||
@ -75,6 +75,7 @@ pub async fn pattern_transform(exec_state: &mut ExecState, args: Args) -> Result
|
||||
memory: *transform.memory,
|
||||
},
|
||||
extr,
|
||||
use_original,
|
||||
exec_state,
|
||||
&args,
|
||||
)
|
||||
@ -84,7 +85,7 @@ pub async fn pattern_transform(exec_state: &mut ExecState, args: Args) -> Result
|
||||
|
||||
/// Repeat some 2D sketch, changing each repetition slightly.
|
||||
pub async fn pattern_transform_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (num_repetitions, transform, sketch): (u32, super::FnAsArg<'_>, SketchSet) =
|
||||
let (num_repetitions, transform, sketch, use_original): (u32, super::FnAsArg<'_>, SketchSet, Option<bool>) =
|
||||
super::args::FromArgs::from_args(&args, 0)?;
|
||||
|
||||
let sketches = inner_pattern_transform_2d(
|
||||
@ -97,6 +98,7 @@ pub async fn pattern_transform_2d(exec_state: &mut ExecState, args: Args) -> Res
|
||||
memory: *transform.memory,
|
||||
},
|
||||
sketch,
|
||||
use_original,
|
||||
exec_state,
|
||||
&args,
|
||||
)
|
||||
@ -295,6 +297,7 @@ async fn inner_pattern_transform<'a>(
|
||||
total_instances: u32,
|
||||
transform_function: FunctionParam<'a>,
|
||||
solid_set: SolidSet,
|
||||
use_original: Option<bool>,
|
||||
exec_state: &mut ExecState,
|
||||
args: &'a Args,
|
||||
) -> Result<Vec<Box<Solid>>, KclError> {
|
||||
@ -310,7 +313,7 @@ async fn inner_pattern_transform<'a>(
|
||||
let t = make_transform::<Box<Solid>>(i, &transform_function, args.source_range, exec_state).await?;
|
||||
transform.push(t);
|
||||
}
|
||||
execute_pattern_transform(transform, solid_set, exec_state, args).await
|
||||
execute_pattern_transform(transform, solid_set, use_original.unwrap_or_default(), exec_state, args).await
|
||||
}
|
||||
|
||||
/// Just like patternTransform, but works on 2D sketches not 3D solids.
|
||||
@ -332,6 +335,7 @@ async fn inner_pattern_transform_2d<'a>(
|
||||
total_instances: u32,
|
||||
transform_function: FunctionParam<'a>,
|
||||
solid_set: SketchSet,
|
||||
use_original: Option<bool>,
|
||||
exec_state: &mut ExecState,
|
||||
args: &'a Args,
|
||||
) -> Result<Vec<Box<Sketch>>, KclError> {
|
||||
@ -347,12 +351,13 @@ async fn inner_pattern_transform_2d<'a>(
|
||||
let t = make_transform::<Box<Sketch>>(i, &transform_function, args.source_range, exec_state).await?;
|
||||
transform.push(t);
|
||||
}
|
||||
execute_pattern_transform(transform, solid_set, exec_state, args).await
|
||||
execute_pattern_transform(transform, solid_set, use_original.unwrap_or_default(), exec_state, args).await
|
||||
}
|
||||
|
||||
async fn execute_pattern_transform<T: GeometryTrait>(
|
||||
transforms: Vec<Vec<Transform>>,
|
||||
geo_set: T::Set,
|
||||
use_original: bool,
|
||||
exec_state: &mut ExecState,
|
||||
args: &Args,
|
||||
) -> Result<Vec<T>, KclError> {
|
||||
@ -368,7 +373,7 @@ async fn execute_pattern_transform<T: GeometryTrait>(
|
||||
|
||||
let mut output = Vec::new();
|
||||
for geo in starting {
|
||||
let new = send_pattern_transform(transforms.clone(), &geo, exec_state, args).await?;
|
||||
let new = send_pattern_transform(transforms.clone(), &geo, use_original, exec_state, args).await?;
|
||||
output.extend(new)
|
||||
}
|
||||
Ok(output)
|
||||
@ -379,6 +384,7 @@ async fn send_pattern_transform<T: GeometryTrait>(
|
||||
// https://github.com/KittyCAD/modeling-app/issues/2821
|
||||
transforms: Vec<Vec<Transform>>,
|
||||
solid: &T,
|
||||
use_original: bool,
|
||||
exec_state: &mut ExecState,
|
||||
args: &Args,
|
||||
) -> Result<Vec<T>, KclError> {
|
||||
@ -388,7 +394,7 @@ async fn send_pattern_transform<T: GeometryTrait>(
|
||||
.send_modeling_cmd(
|
||||
id,
|
||||
ModelingCmd::from(mcmd::EntityLinearPatternTransform {
|
||||
entity_id: solid.id(),
|
||||
entity_id: if use_original { solid.original_id() } else { solid.id() },
|
||||
transform: Default::default(),
|
||||
transforms,
|
||||
}),
|
||||
@ -602,6 +608,7 @@ fn array_to_point2d(val: &KclValue, source_ranges: Vec<SourceRange>) -> Result<P
|
||||
trait GeometryTrait: Clone {
|
||||
type Set: Into<Vec<Self>> + Clone;
|
||||
fn id(&self) -> Uuid;
|
||||
fn original_id(&self) -> Uuid;
|
||||
fn set_id(&mut self, id: Uuid);
|
||||
fn array_to_point3d(val: &KclValue, source_ranges: Vec<SourceRange>) -> Result<Point3d, KclError>;
|
||||
async fn flush_batch(args: &Args, exec_state: &mut ExecState, set: Self::Set) -> Result<(), KclError>;
|
||||
@ -615,6 +622,9 @@ impl GeometryTrait for Box<Sketch> {
|
||||
fn id(&self) -> Uuid {
|
||||
self.id
|
||||
}
|
||||
fn original_id(&self) -> Uuid {
|
||||
self.original_id
|
||||
}
|
||||
fn array_to_point3d(val: &KclValue, source_ranges: Vec<SourceRange>) -> Result<Point3d, KclError> {
|
||||
let Point2d { x, y } = array_to_point2d(val, source_ranges)?;
|
||||
Ok(Point3d { x, y, z: 0.0 })
|
||||
@ -634,6 +644,11 @@ impl GeometryTrait for Box<Solid> {
|
||||
fn id(&self) -> Uuid {
|
||||
self.id
|
||||
}
|
||||
|
||||
fn original_id(&self) -> Uuid {
|
||||
self.sketch.original_id
|
||||
}
|
||||
|
||||
fn array_to_point3d(val: &KclValue, source_ranges: Vec<SourceRange>) -> Result<Point3d, KclError> {
|
||||
array_to_point3d(val, source_ranges)
|
||||
}
|
||||
@ -674,7 +689,8 @@ mod tests {
|
||||
|
||||
/// A linear pattern on a 2D sketch.
|
||||
pub async fn pattern_linear_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (data, sketch_set): (LinearPattern2dData, SketchSet) = args.get_data_and_sketch_set()?;
|
||||
let (data, sketch_set, use_original): (LinearPattern2dData, SketchSet, Option<bool>) =
|
||||
super::args::FromArgs::from_args(&args, 0)?;
|
||||
|
||||
if data.axis == [0.0, 0.0] {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
@ -685,7 +701,7 @@ pub async fn pattern_linear_2d(exec_state: &mut ExecState, args: Args) -> Result
|
||||
}));
|
||||
}
|
||||
|
||||
let sketches = inner_pattern_linear_2d(data, sketch_set, exec_state, args).await?;
|
||||
let sketches = inner_pattern_linear_2d(data, sketch_set, use_original, exec_state, args).await?;
|
||||
Ok(sketches.into())
|
||||
}
|
||||
|
||||
@ -709,6 +725,7 @@ pub async fn pattern_linear_2d(exec_state: &mut ExecState, args: Args) -> Result
|
||||
async fn inner_pattern_linear_2d(
|
||||
data: LinearPattern2dData,
|
||||
sketch_set: SketchSet,
|
||||
use_original: Option<bool>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Vec<Box<Sketch>>, KclError> {
|
||||
@ -726,12 +743,20 @@ async fn inner_pattern_linear_2d(
|
||||
}]
|
||||
})
|
||||
.collect();
|
||||
execute_pattern_transform(transforms, sketch_set, exec_state, &args).await
|
||||
execute_pattern_transform(
|
||||
transforms,
|
||||
sketch_set,
|
||||
use_original.unwrap_or_default(),
|
||||
exec_state,
|
||||
&args,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// A linear pattern on a 3D model.
|
||||
pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (data, solid_set): (LinearPattern3dData, SolidSet) = args.get_data_and_solid_set()?;
|
||||
let (data, solid_set, use_original): (LinearPattern3dData, SolidSet, Option<bool>) =
|
||||
super::args::FromArgs::from_args(&args, 0)?;
|
||||
|
||||
if data.axis == [0.0, 0.0, 0.0] {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
@ -742,7 +767,7 @@ pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result
|
||||
}));
|
||||
}
|
||||
|
||||
let solids = inner_pattern_linear_3d(data, solid_set, exec_state, args).await?;
|
||||
let solids = inner_pattern_linear_3d(data, solid_set, use_original, exec_state, args).await?;
|
||||
Ok(solids.into())
|
||||
}
|
||||
|
||||
@ -771,6 +796,7 @@ pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result
|
||||
async fn inner_pattern_linear_3d(
|
||||
data: LinearPattern3dData,
|
||||
solid_set: SolidSet,
|
||||
use_original: Option<bool>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Vec<Box<Solid>>, KclError> {
|
||||
@ -788,7 +814,14 @@ async fn inner_pattern_linear_3d(
|
||||
}]
|
||||
})
|
||||
.collect();
|
||||
execute_pattern_transform(transforms, solid_set, exec_state, &args).await
|
||||
execute_pattern_transform(
|
||||
transforms,
|
||||
solid_set,
|
||||
use_original.unwrap_or_default(),
|
||||
exec_state,
|
||||
&args,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Data for a circular pattern on a 2D sketch.
|
||||
@ -807,6 +840,10 @@ pub struct CircularPattern2dData {
|
||||
pub arc_degrees: f64,
|
||||
/// Whether or not to rotate the duplicates as they are copied.
|
||||
pub rotate_duplicates: bool,
|
||||
/// If the target being patterned is itself a pattern, then, should you use the original solid,
|
||||
/// or the pattern?
|
||||
#[serde(default)]
|
||||
pub use_original: Option<bool>,
|
||||
}
|
||||
|
||||
/// Data for a circular pattern on a 3D model.
|
||||
@ -827,6 +864,10 @@ pub struct CircularPattern3dData {
|
||||
pub arc_degrees: f64,
|
||||
/// Whether or not to rotate the duplicates as they are copied.
|
||||
pub rotate_duplicates: bool,
|
||||
/// If the target being patterned is itself a pattern, then, should you use the original solid,
|
||||
/// or the pattern?
|
||||
#[serde(default)]
|
||||
pub use_original: Option<bool>,
|
||||
}
|
||||
|
||||
pub enum CircularPattern {
|
||||
@ -889,6 +930,13 @@ impl CircularPattern {
|
||||
CircularPattern::ThreeD(lp) => lp.rotate_duplicates,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn use_original(&self) -> bool {
|
||||
match self {
|
||||
CircularPattern::TwoD(lp) => lp.use_original.unwrap_or_default(),
|
||||
CircularPattern::ThreeD(lp) => lp.use_original.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A circular pattern on a 2D sketch.
|
||||
@ -1055,7 +1103,11 @@ async fn pattern_circular(
|
||||
id,
|
||||
ModelingCmd::from(mcmd::EntityCircularPattern {
|
||||
axis: kcmc::shared::Point3d::from(data.axis()),
|
||||
entity_id: geometry.id(),
|
||||
entity_id: if data.use_original() {
|
||||
geometry.original_id()
|
||||
} else {
|
||||
geometry.id()
|
||||
},
|
||||
center: kcmc::shared::Point3d {
|
||||
x: LengthUnit(center[0]),
|
||||
y: LengthUnit(center[1]),
|
||||
|
||||
@ -4,8 +4,6 @@ use anyhow::Result;
|
||||
use derive_docs::stdlib;
|
||||
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, ModelingCmd};
|
||||
use kittycad_modeling_cmds as kcmc;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
@ -13,22 +11,13 @@ use crate::{
|
||||
std::{sketch::FaceTag, Args},
|
||||
};
|
||||
|
||||
/// Data for shells.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ShellData {
|
||||
/// The thickness of the shell.
|
||||
pub thickness: f64,
|
||||
/// The faces you want removed.
|
||||
pub faces: Vec<FaceTag>,
|
||||
}
|
||||
|
||||
/// Create a shell.
|
||||
pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (data, solid_set): (ShellData, SolidSet) = args.get_data_and_solid_set()?;
|
||||
let solid_set = args.get_unlabeled_kw_arg("solidSet")?;
|
||||
let thickness = args.get_kw_arg("thickness")?;
|
||||
let faces = args.get_kw_arg("faces")?;
|
||||
|
||||
let result = inner_shell(data, solid_set, exec_state, args).await?;
|
||||
let result = inner_shell(solid_set, thickness, faces, exec_state, args).await?;
|
||||
Ok(result.into())
|
||||
}
|
||||
|
||||
@ -47,10 +36,11 @@ pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// |> extrude(length = 6)
|
||||
///
|
||||
/// // Remove the end face for the extrusion.
|
||||
/// shell({
|
||||
/// shell(
|
||||
/// firstSketch,
|
||||
/// faces = ['end'],
|
||||
/// thickness = 0.25,
|
||||
/// }, firstSketch)
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
@ -64,10 +54,11 @@ pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// |> extrude(length = 6)
|
||||
///
|
||||
/// // Remove the start face for the extrusion.
|
||||
/// shell({
|
||||
/// shell(
|
||||
/// firstSketch,
|
||||
/// faces = ['start'],
|
||||
/// thickness = 0.25,
|
||||
/// }, firstSketch)
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
@ -81,10 +72,11 @@ pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// |> extrude(length = 6)
|
||||
///
|
||||
/// // Remove a tagged face for the extrusion.
|
||||
/// shell({
|
||||
/// shell(
|
||||
/// firstSketch,
|
||||
/// faces = [myTag],
|
||||
/// thickness = 0.25,
|
||||
/// }, firstSketch)
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
@ -98,10 +90,11 @@ pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// |> extrude(length = 6)
|
||||
///
|
||||
/// // Remove a tagged face and the end face for the extrusion.
|
||||
/// shell({
|
||||
/// shell(
|
||||
/// firstSketch,
|
||||
/// faces = [myTag, 'end'],
|
||||
/// thickness = 0.25,
|
||||
/// }, firstSketch)
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
@ -124,7 +117,7 @@ pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// |> extrude(length = 50)
|
||||
///
|
||||
/// // We put "case" in the shell function to shell the entire object.
|
||||
/// shell({ faces = ['start'], thickness = 5 }, case)
|
||||
/// shell(case, faces = ['start'], thickness = 5)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
@ -147,7 +140,7 @@ pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// |> extrude(length = 50)
|
||||
///
|
||||
/// // We put "thing1" in the shell function to shell the end face of the object.
|
||||
/// shell({ faces = ['end'], thickness = 5 }, thing1)
|
||||
/// shell(thing1, faces = ['end'], thickness = 5)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
@ -172,21 +165,29 @@ pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// |> extrude(length = 50)
|
||||
///
|
||||
/// // We put "thing1" and "thing2" in the shell function to shell the end face of the object.
|
||||
/// shell({ faces = ['end'], thickness = 5 }, [thing1, thing2])
|
||||
/// shell([thing1, thing2], faces = ['end'], thickness = 5)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "shell",
|
||||
feature_tree_operation = true,
|
||||
keywords = true,
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
solid_set = { docs = "Which solid (or solids) to shell out"},
|
||||
thickness = {docs = "The thickness of the shell"},
|
||||
faces = {docs = "The faces you want removed"},
|
||||
}
|
||||
}]
|
||||
async fn inner_shell(
|
||||
data: ShellData,
|
||||
solid_set: SolidSet,
|
||||
thickness: f64,
|
||||
faces: Vec<FaceTag>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<SolidSet, KclError> {
|
||||
if data.faces.is_empty() {
|
||||
if faces.is_empty() {
|
||||
return Err(KclError::Type(KclErrorDetails {
|
||||
message: "Expected at least one face".to_string(),
|
||||
message: "You must shell at least one face".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
}
|
||||
@ -194,7 +195,7 @@ async fn inner_shell(
|
||||
let solids: Vec<Box<Solid>> = solid_set.clone().into();
|
||||
if solids.is_empty() {
|
||||
return Err(KclError::Type(KclErrorDetails {
|
||||
message: "Expected at least one solid".to_string(),
|
||||
message: "You must shell at least one solid".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
}
|
||||
@ -205,7 +206,7 @@ async fn inner_shell(
|
||||
// If we do not do these for sketch on face, things will fail with face does not exist.
|
||||
args.flush_batch_for_solid_set(exec_state, solid.clone().into()).await?;
|
||||
|
||||
for tag in &data.faces {
|
||||
for tag in &faces {
|
||||
let extrude_plane_id = tag.get_face_id(solid, exec_state, &args, false).await?;
|
||||
|
||||
face_ids.push(extrude_plane_id);
|
||||
@ -235,7 +236,7 @@ async fn inner_shell(
|
||||
hollow: false,
|
||||
face_ids,
|
||||
object_id: solids[0].id,
|
||||
shell_thickness: LengthUnit(data.thickness),
|
||||
shell_thickness: LengthUnit(thickness),
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -1360,6 +1360,7 @@ pub(crate) async fn inner_start_profile_at(
|
||||
|
||||
let sketch = Sketch {
|
||||
id: path_id,
|
||||
original_id: path_id,
|
||||
artifact_id: path_id.into(),
|
||||
on: sketch_surface.clone(),
|
||||
paths: vec![],
|
||||
|
||||
@ -22,24 +22,14 @@ pub enum SweepPath {
|
||||
Helix(Box<Helix>),
|
||||
}
|
||||
|
||||
/// Data for a sweep.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
pub struct SweepData {
|
||||
/// The path to sweep along.
|
||||
pub path: SweepPath,
|
||||
/// If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components.
|
||||
pub sectional: Option<bool>,
|
||||
/// Tolerance for the sweep operation.
|
||||
#[serde(default)]
|
||||
pub tolerance: Option<f64>,
|
||||
}
|
||||
|
||||
/// Extrude a sketch along a path.
|
||||
pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (data, sketch): (SweepData, Sketch) = args.get_data_and_sketch()?;
|
||||
let sketch = args.get_unlabeled_kw_arg("sketch")?;
|
||||
let path: SweepPath = args.get_kw_arg("path")?;
|
||||
let sectional = args.get_kw_arg_opt("sectional")?;
|
||||
let tolerance = args.get_kw_arg_opt("tolerance")?;
|
||||
|
||||
let value = inner_sweep(data, sketch, exec_state, args).await?;
|
||||
let value = inner_sweep(sketch, path, sectional, tolerance, exec_state, args).await?;
|
||||
Ok(KclValue::Solid { value })
|
||||
}
|
||||
|
||||
@ -82,9 +72,7 @@ pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// radius = 2,
|
||||
/// }, %)
|
||||
/// |> hole(pipeHole, %)
|
||||
/// |> sweep({
|
||||
/// path: sweepPath,
|
||||
/// }, %)
|
||||
/// |> sweep(path = sweepPath)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
@ -104,15 +92,25 @@ pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// // Create a spring by sweeping around the helix path.
|
||||
/// springSketch = startSketchOn('YZ')
|
||||
/// |> circle({ center = [0, 0], radius = 1 }, %)
|
||||
/// |> sweep({ path = helixPath }, %)
|
||||
/// |> sweep(path = helixPath)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "sweep",
|
||||
feature_tree_operation = true,
|
||||
keywords = true,
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
sketch = { docs = "The sketch that should be swept in space" },
|
||||
path = { docs = "The path to sweep the sketch along" },
|
||||
sectional = { docs = "If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components." },
|
||||
tolerance = { docs = "Tolerance for this operation" },
|
||||
}
|
||||
}]
|
||||
async fn inner_sweep(
|
||||
data: SweepData,
|
||||
sketch: Sketch,
|
||||
path: SweepPath,
|
||||
sectional: Option<bool>,
|
||||
tolerance: Option<f64>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Box<Solid>, KclError> {
|
||||
@ -121,12 +119,12 @@ async fn inner_sweep(
|
||||
id,
|
||||
ModelingCmd::from(mcmd::Sweep {
|
||||
target: sketch.id.into(),
|
||||
trajectory: match data.path {
|
||||
trajectory: match path {
|
||||
SweepPath::Sketch(sketch) => sketch.id.into(),
|
||||
SweepPath::Helix(helix) => helix.value.into(),
|
||||
},
|
||||
sectional: data.sectional.unwrap_or(false),
|
||||
tolerance: LengthUnit(data.tolerance.unwrap_or(default_tolerance(&args.ctx.settings.units))),
|
||||
sectional: sectional.unwrap_or(false),
|
||||
tolerance: LengthUnit(tolerance.unwrap_or(default_tolerance(&args.ctx.settings.units))),
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Program memory after executing angled_line.kcl
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
@ -346,6 +347,7 @@ description: Program memory after executing angled_line.kcl
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
|
||||
@ -374,6 +374,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -904,6 +905,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -975,6 +977,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -1412,6 +1415,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -1878,6 +1882,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -1949,6 +1954,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
|
||||
@ -468,6 +468,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -609,6 +610,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
|
||||
@ -200,6 +200,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
|
||||
@ -274,6 +274,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -695,6 +696,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -757,6 +759,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -1332,6 +1335,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -1394,6 +1398,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -1517,6 +1522,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -2236,6 +2242,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -2298,6 +2305,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -2421,6 +2429,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -2483,6 +2492,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -2836,6 +2846,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -3202,6 +3213,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -3264,6 +3276,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -3779,6 +3792,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -3841,6 +3855,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -3964,6 +3979,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -4628,6 +4644,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -4690,6 +4707,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -4813,6 +4831,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -4875,6 +4894,7 @@ snapshot_kind: text
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Program memory after executing basic_fillet_cube_close_opposite.kcl
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
@ -422,6 +423,7 @@ description: Program memory after executing basic_fillet_cube_close_opposite.kcl
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Program memory after executing basic_fillet_cube_end.kcl
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
@ -353,6 +354,7 @@ description: Program memory after executing basic_fillet_cube_end.kcl
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Program memory after executing basic_fillet_cube_next_adjacent.kcl
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
@ -491,6 +492,7 @@ description: Program memory after executing basic_fillet_cube_next_adjacent.kcl
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Program memory after executing basic_fillet_cube_previous_adjacent.kcl
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
@ -491,6 +492,7 @@ description: Program memory after executing basic_fillet_cube_previous_adjacent.
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Program memory after executing basic_fillet_cube_start.kcl
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
@ -353,6 +354,7 @@ description: Program memory after executing basic_fillet_cube_start.kcl
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Program memory after executing big_number_angle_to_match_length_x.kcl
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
@ -253,6 +254,7 @@ description: Program memory after executing big_number_angle_to_match_length_x.k
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Program memory after executing big_number_angle_to_match_length_y.kcl
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
@ -253,6 +254,7 @@ description: Program memory after executing big_number_angle_to_match_length_y.k
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Program memory after executing circle_three_point.kcl
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
@ -126,6 +127,7 @@ description: Program memory after executing circle_three_point.kcl
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Program memory after executing cube.kcl
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
@ -944,6 +945,7 @@ description: Program memory after executing cube.kcl
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
|
||||
@ -2542,7 +2542,7 @@ snapshot_kind: text
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
2047,
|
||||
2110,
|
||||
2102,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Result of parsing fillet-and-shell.kcl
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"Ok": {
|
||||
@ -2582,68 +2583,45 @@ description: Result of parsing fillet-and-shell.kcl
|
||||
"type": "ExpressionStatement"
|
||||
},
|
||||
{
|
||||
"end": 2110,
|
||||
"end": 2102,
|
||||
"expression": {
|
||||
"arguments": [
|
||||
{
|
||||
"end": 2103,
|
||||
"properties": [
|
||||
{
|
||||
"end": 2072,
|
||||
"key": {
|
||||
"end": 2062,
|
||||
"name": "faces",
|
||||
"start": 2057,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 2057,
|
||||
"type": "ObjectProperty",
|
||||
"value": {
|
||||
"elements": [
|
||||
{
|
||||
"end": 2071,
|
||||
"raw": "'end'",
|
||||
"start": 2066,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": "end"
|
||||
}
|
||||
],
|
||||
"end": 2072,
|
||||
"start": 2065,
|
||||
"type": "ArrayExpression",
|
||||
"type": "ArrayExpression"
|
||||
"type": "LabeledArg",
|
||||
"label": {
|
||||
"type": "Identifier",
|
||||
"name": "faces"
|
||||
},
|
||||
"arg": {
|
||||
"elements": [
|
||||
{
|
||||
"end": 2073,
|
||||
"raw": "'end'",
|
||||
"start": 2068,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": "end"
|
||||
}
|
||||
},
|
||||
{
|
||||
"end": 2101,
|
||||
"key": {
|
||||
"end": 2085,
|
||||
"name": "thickness",
|
||||
"start": 2076,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 2076,
|
||||
"type": "ObjectProperty",
|
||||
"value": {
|
||||
"end": 2101,
|
||||
"name": "caseThickness",
|
||||
"start": 2088,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
}
|
||||
}
|
||||
],
|
||||
"start": 2053,
|
||||
"type": "ObjectExpression",
|
||||
"type": "ObjectExpression"
|
||||
],
|
||||
"end": 2074,
|
||||
"start": 2067,
|
||||
"type": "ArrayExpression",
|
||||
"type": "ArrayExpression"
|
||||
}
|
||||
},
|
||||
{
|
||||
"end": 2109,
|
||||
"name": "case",
|
||||
"start": 2105,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
"type": "LabeledArg",
|
||||
"label": {
|
||||
"type": "Identifier",
|
||||
"name": "thickness"
|
||||
},
|
||||
"arg": {
|
||||
"end": 2101,
|
||||
"name": "caseThickness",
|
||||
"start": 2088,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
}
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
@ -2652,17 +2630,24 @@ description: Result of parsing fillet-and-shell.kcl
|
||||
"start": 2047,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 2110,
|
||||
"end": 2102,
|
||||
"start": 2047,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
"type": "CallExpressionKw",
|
||||
"type": "CallExpressionKw",
|
||||
"unlabeled": {
|
||||
"end": 2057,
|
||||
"name": "case",
|
||||
"start": 2053,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
}
|
||||
},
|
||||
"start": 2047,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 2111,
|
||||
"end": 2103,
|
||||
"nonCodeMeta": {
|
||||
"nonCodeNodes": {
|
||||
"1": [
|
||||
|
||||
@ -74,7 +74,4 @@ m25Screw(border + rpizWidth / 2 + widthBetweenScrews / 2, 0 + border + rpizLengt
|
||||
|
||||
m25Screw(border + rpizWidth / 2 + widthBetweenScrews / 2, 0 + border + rpizLength / 2 - (lengthBetweenScrews / 2), screwHeight)
|
||||
|
||||
shell({
|
||||
faces = ['end'],
|
||||
thickness = caseThickness
|
||||
}, case)
|
||||
shell(case, faces = ['end'], thickness = caseThickness)
|
||||
|
||||
@ -402,17 +402,17 @@ snapshot_kind: text
|
||||
},
|
||||
{
|
||||
"labeledArgs": {
|
||||
"data": {
|
||||
"faces": {
|
||||
"sourceRange": [
|
||||
2053,
|
||||
2103,
|
||||
2067,
|
||||
2074,
|
||||
0
|
||||
]
|
||||
},
|
||||
"solid_set": {
|
||||
"thickness": {
|
||||
"sourceRange": [
|
||||
2105,
|
||||
2109,
|
||||
2088,
|
||||
2101,
|
||||
0
|
||||
]
|
||||
}
|
||||
@ -420,10 +420,16 @@ snapshot_kind: text
|
||||
"name": "shell",
|
||||
"sourceRange": [
|
||||
2047,
|
||||
2110,
|
||||
2102,
|
||||
0
|
||||
],
|
||||
"type": "StdLibCall",
|
||||
"unlabeledArg": null
|
||||
"unlabeledArg": {
|
||||
"sourceRange": [
|
||||
2053,
|
||||
2057,
|
||||
0
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Program memory after executing fillet-and-shell.kcl
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
@ -504,6 +505,7 @@ description: Program memory after executing fillet-and-shell.kcl
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -1787,6 +1789,7 @@ description: Program memory after executing fillet-and-shell.kcl
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -2389,6 +2392,7 @@ description: Program memory after executing fillet-and-shell.kcl
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -2674,6 +2678,7 @@ description: Program memory after executing fillet-and-shell.kcl
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Program memory after executing function_sketch.kcl
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
@ -622,6 +623,7 @@ description: Program memory after executing function_sketch.kcl
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Program memory after executing function_sketch_with_position.kcl
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
@ -608,6 +609,7 @@ description: Program memory after executing function_sketch_with_position.kcl
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Program memory after executing helix_ccw.kcl
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
@ -126,6 +127,7 @@ description: Program memory after executing helix_ccw.kcl
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Program memory after executing i_shape.kcl
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
@ -643,6 +644,7 @@ description: Program memory after executing i_shape.kcl
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -1623,6 +1625,7 @@ description: Program memory after executing i_shape.kcl
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -1923,6 +1926,7 @@ description: Program memory after executing i_shape.kcl
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Program memory after executing import_function_not_sketch.kcl
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
@ -471,6 +472,7 @@ description: Program memory after executing import_function_not_sketch.kcl
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
@ -905,6 +907,7 @@ description: Program memory after executing import_function_not_sketch.kcl
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
|
||||