profile start constrain overlays (#6795)

* constrain profile start

* add test

* make sure it works on segment drag too, fix tests

* remove old log

* some tests fixes

* Bump more segment counters

* Two more fixes

* Two more test fixes

* small test fix

* moretest fixes

* another test

---------

Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
Co-authored-by: Pierre Jacquier <pierre@zoo.dev>
This commit is contained in:
Kurt Hutten
2025-05-10 12:29:41 +10:00
committed by GitHub
parent 85469f2d7d
commit 39f512d32d
15 changed files with 497 additions and 145 deletions

View File

@ -138,7 +138,9 @@ extrude001 = extrude(sketch001, length = 5)`
// Ensure badge is present // Ensure badge is present
const codePaneButtonHolder = page.locator('#code-button-holder') const codePaneButtonHolder = page.locator('#code-button-holder')
await expect(codePaneButtonHolder).toContainText('notification') await expect(codePaneButtonHolder).toContainText('notification', {
timeout: 20_000,
})
// Ensure we have no errors in the gutter, since error out of view. // Ensure we have no errors in the gutter, since error out of view.
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible() await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()

View File

@ -1180,6 +1180,8 @@ sketch001 = startSketchOn(XZ)
page, page,
homePage, homePage,
editor, editor,
scene,
cmdBar,
}) => { }) => {
const u = await getUtils(page) const u = await getUtils(page)
await page.addInitScript(async () => { await page.addInitScript(async () => {
@ -1198,9 +1200,7 @@ sketch001 = startSketchOn(XZ)
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await expect( await scene.settled(cmdBar)
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled()
await page.waitForTimeout(100) await page.waitForTimeout(100)
await u.openAndClearDebugPanel() await u.openAndClearDebugPanel()
@ -1236,7 +1236,7 @@ sketch001 = startSketchOn(XZ)
await page.waitForTimeout(400) await page.waitForTimeout(400)
let prevContent = await page.locator('.cm-content').innerText() let prevContent = await page.locator('.cm-content').innerText()
await expect(page.getByTestId('segment-overlay')).toHaveCount(2) await expect(page.getByTestId('segment-overlay')).toHaveCount(3)
// drag startProfileAt handle // drag startProfileAt handle
await page.dragAndDrop('#stream', '#stream', { await page.dragAndDrop('#stream', '#stream', {
@ -1278,9 +1278,9 @@ sketch001 = startSketchOn(XZ)
// expect the code to have changed // expect the code to have changed
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
`sketch001 = startSketchOn(XZ) `sketch001 = startSketchOn(XZ)
|> startProfile(at = [2.71, -2.71]) |> startProfile(at = [5.36, -5.36])
|> line(end = [15.4, -2.78]) |> line(end = [12.73, -0.09])
|> tangentialArc(endAbsolute = [27.6, -3.05]) |> tangentialArc(endAbsolute = [24.95, -0.38])
|> close() |> close()
|> extrude(length = 5)`, |> extrude(length = 5)`,
{ shouldNormalise: true } { shouldNormalise: true }
@ -1294,7 +1294,7 @@ sketch001 = startSketchOn(XZ)
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
`sketch001 = startSketchOn(XZ) `sketch001 = startSketchOn(XZ)
|> startProfile(at = [2.71, -2.71]) |> startProfile(at = [2.71, -2.71])
|> line(end = [15.4, -2.78]) |> line(end = [12.73, -0.09])
|> tangentialArc(endAbsolute = [24.95, -0.38]) |> tangentialArc(endAbsolute = [24.95, -0.38])
|> close() |> close()
|> extrude(length = 5)`, |> extrude(length = 5)`,
@ -1308,7 +1308,7 @@ sketch001 = startSketchOn(XZ)
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
`sketch001 = startSketchOn(XZ) `sketch001 = startSketchOn(XZ)
|> startProfile(at = [2.71, -2.71]) |> startProfile(at = [4.61, -10.01])
|> line(end = [12.73, -0.09]) |> line(end = [12.73, -0.09])
|> tangentialArc(endAbsolute = [24.95, -0.38]) |> tangentialArc(endAbsolute = [24.95, -0.38])
|> close() |> close()
@ -1505,7 +1505,7 @@ sketch001 = startSketchOn(XZ)
await page.waitForTimeout(1000) await page.waitForTimeout(1000)
// Verify segment is selected (you can check for visual indicators or state) // Verify segment is selected (you can check for visual indicators or state)
const element = page.locator('[data-overlay-index="1"]') const element = page.locator('[data-overlay-index="2"]')
await expect(element).toHaveAttribute('data-overlay-visible', 'true') await expect(element).toHaveAttribute('data-overlay-visible', 'true')
}) })

View File

@ -543,6 +543,9 @@ sketch001 = startSketchOn(XZ)
page, page,
homePage, homePage,
editor, editor,
toolbar,
scene,
cmdBar,
}) => { }) => {
const u = await getUtils(page) const u = await getUtils(page)
await page.addInitScript(async () => { await page.addInitScript(async () => {
@ -559,10 +562,12 @@ sketch001 = startSketchOn(XZ)
}) })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await toolbar.waitForFeatureTreeToBeBuilt()
await scene.settled(cmdBar)
await expect( await expect(
page.getByRole('button', { name: 'Start Sketch' }) page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled() ).not.toBeDisabled({ timeout: 10_000 })
await page.waitForTimeout(100) await page.waitForTimeout(100)
await u.openAndClearDebugPanel() await u.openAndClearDebugPanel()
@ -598,7 +603,7 @@ sketch001 = startSketchOn(XZ)
await page.waitForTimeout(400) await page.waitForTimeout(400)
let prevContent = await page.locator('.cm-content').innerText() let prevContent = await page.locator('.cm-content').innerText()
await expect(page.getByTestId('segment-overlay')).toHaveCount(2) await expect(page.getByTestId('segment-overlay')).toHaveCount(3)
// drag startProfileAt handle // drag startProfileAt handle
await page.dragAndDrop('#stream', '#stream', { await page.dragAndDrop('#stream', '#stream', {
@ -612,7 +617,7 @@ sketch001 = startSketchOn(XZ)
// drag line handle // drag line handle
await page.waitForTimeout(100) await page.waitForTimeout(100)
const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]') const lineEnd = await u.getBoundingBox('[data-overlay-index="1"]')
await page.dragAndDrop('#stream', '#stream', { await page.dragAndDrop('#stream', '#stream', {
sourcePosition: { x: lineEnd.x - 15, y: lineEnd.y }, sourcePosition: { x: lineEnd.x - 15, y: lineEnd.y },
targetPosition: { x: lineEnd.x, y: lineEnd.y + 15 }, targetPosition: { x: lineEnd.x, y: lineEnd.y + 15 },
@ -622,7 +627,7 @@ sketch001 = startSketchOn(XZ)
prevContent = await page.locator('.cm-content').innerText() prevContent = await page.locator('.cm-content').innerText()
// drag tangentialArc handle // drag tangentialArc handle
const tangentEnd = await u.getBoundingBox('[data-overlay-index="1"]') const tangentEnd = await u.getBoundingBox('[data-overlay-index="0"]')
await page.dragAndDrop('#stream', '#stream', { await page.dragAndDrop('#stream', '#stream', {
sourcePosition: { x: tangentEnd.x + 10, y: tangentEnd.y - 5 }, sourcePosition: { x: tangentEnd.x + 10, y: tangentEnd.y - 5 },
targetPosition: { targetPosition: {
@ -638,7 +643,7 @@ sketch001 = startSketchOn(XZ)
`sketch001 = startSketchOn(XZ) `sketch001 = startSketchOn(XZ)
|> startProfile(at = [7.12, -12.68]) |> startProfile(at = [7.12, -12.68])
|> line(end = [12.68, -1.09]) |> line(end = [12.68, -1.09])
|> tangentialArc(endAbsolute = [24.89, 0.68]) |> tangentialArc(endAbsolute = [24.95, -0.38])
|> close() |> close()
|> extrude(length = 5)`, |> extrude(length = 5)`,
{ shouldNormalise: true } { shouldNormalise: true }
@ -709,7 +714,7 @@ sketch001 = startSketchOn(XZ)
const step5 = { steps: 5 } const step5 = { steps: 5 }
await expect(page.getByTestId('segment-overlay')).toHaveCount(2) await expect(page.getByTestId('segment-overlay')).toHaveCount(3)
// drag startProfileAt handle // drag startProfileAt handle
await page.mouse.move(startPX[0], startPX[1]) await page.mouse.move(startPX[0], startPX[1])
@ -744,10 +749,10 @@ sketch001 = startSketchOn(XZ)
// expect the code to have changed // expect the code to have changed
await editor.expectEditor.toContain( await editor.expectEditor.toContain(
`sketch001 = startSketchOn(XZ) `sketch001 = startSketchOn(XZ)
|> startProfile(at = [6.44, -12.07]) |> startProfile(at = [8.41, -9.97])
|> line(end = [14.72, 1.97]) |> line(end = [12.73, -0.09])
|> line(end = [1.99, 2.06])
|> tangentialArc(endAbsolute = [24.95, -5.38]) |> tangentialArc(endAbsolute = [24.95, -5.38])
|> line(end = [1.97, 2.06])
|> close() |> close()
|> revolve(axis = X)`, |> revolve(axis = X)`,
{ shouldNormalise: true } { shouldNormalise: true }

View File

@ -108,7 +108,7 @@ test.describe('Testing constraints', () => {
// Wait for overlays to populate // Wait for overlays to populate
await page.waitForTimeout(1000) await page.waitForTimeout(1000)
const line3 = await u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`) const line3 = await u.getSegmentBodyCoords(`[data-overlay-index="${3}"]`)
await page.mouse.click(line3.x, line3.y) await page.mouse.click(line3.x, line3.y)
await page.waitForTimeout(100) // this wait is needed for webkit - not sure why await page.waitForTimeout(100) // this wait is needed for webkit - not sure why
@ -127,7 +127,7 @@ test.describe('Testing constraints', () => {
) )
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state // checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
await expect(page.getByTestId('segment-overlay')).toHaveCount(4) await expect(page.getByTestId('segment-overlay')).toHaveCount(5)
}) })
test.describe('Test perpendicular distance constraint', () => { test.describe('Test perpendicular distance constraint', () => {
const cases = [ const cases = [
@ -174,8 +174,8 @@ test.describe('Testing constraints', () => {
await page.waitForTimeout(1000) await page.waitForTimeout(1000)
const [line1, line3] = await Promise.all([ const [line1, line3] = await Promise.all([
u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`), u.getSegmentBodyCoords(`[data-overlay-index="${1}"]`),
u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`), u.getSegmentBodyCoords(`[data-overlay-index="${3}"]`),
]) ])
await page.mouse.click(line1.x, line1.y) await page.mouse.click(line1.x, line1.y)
@ -223,7 +223,7 @@ test.describe('Testing constraints', () => {
) )
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state // checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
await expect(page.getByTestId('segment-overlay')).toHaveCount(4) await expect(page.getByTestId('segment-overlay')).toHaveCount(5)
}) })
} }
}) })
@ -289,8 +289,8 @@ test.describe('Testing constraints', () => {
await page.waitForTimeout(1000) await page.waitForTimeout(1000)
const [line1, line3] = await Promise.all([ const [line1, line3] = await Promise.all([
u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`), u.getSegmentBodyCoords(`[data-overlay-index="${1}"]`),
u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`), u.getSegmentBodyCoords(`[data-overlay-index="${3}"]`),
]) ])
await page.mouse.click(line1.x, line1.y) await page.mouse.click(line1.x, line1.y)
@ -335,7 +335,7 @@ test.describe('Testing constraints', () => {
) )
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state // checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
await expect(page.getByTestId('segment-overlay')).toHaveCount(4) await expect(page.getByTestId('segment-overlay')).toHaveCount(5)
}) })
} }
}) })
@ -405,7 +405,7 @@ test.describe('Testing constraints', () => {
await page.waitForTimeout(1000) await page.waitForTimeout(1000)
const [line3] = await Promise.all([ const [line3] = await Promise.all([
u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`), u.getSegmentBodyCoords(`[data-overlay-index="${3}"]`),
]) ])
if (constraint === 'Absolute X') { if (constraint === 'Absolute X') {
@ -454,7 +454,7 @@ test.describe('Testing constraints', () => {
) )
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state // checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
await expect(page.getByTestId('segment-overlay')).toHaveCount(4) await expect(page.getByTestId('segment-overlay')).toHaveCount(5)
}) })
} }
}) })
@ -519,8 +519,8 @@ test.describe('Testing constraints', () => {
await page.waitForTimeout(1000) await page.waitForTimeout(1000)
const [line1, line3] = await Promise.all([ const [line1, line3] = await Promise.all([
u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`), u.getSegmentBodyCoords(`[data-overlay-index="${1}"]`),
u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`), u.getSegmentBodyCoords(`[data-overlay-index="${3}"]`),
]) ])
if (axisSelect) { if (axisSelect) {
@ -569,7 +569,7 @@ test.describe('Testing constraints', () => {
) )
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state // checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
await expect(page.getByTestId('segment-overlay')).toHaveCount(4) await expect(page.getByTestId('segment-overlay')).toHaveCount(5)
}) })
} }
}) })
@ -622,7 +622,7 @@ test.describe('Testing constraints', () => {
await page.waitForTimeout(1000) await page.waitForTimeout(1000)
const line3 = await u.getSegmentBodyCoords( const line3 = await u.getSegmentBodyCoords(
`[data-overlay-index="${2}"]` `[data-overlay-index="${3}"]`
) )
await page.mouse.click(line3.x, line3.y) await page.mouse.click(line3.x, line3.y)
@ -647,7 +647,7 @@ test.describe('Testing constraints', () => {
await expect(page.locator('.cm-activeLine')).toHaveText(changedCode) await expect(page.locator('.cm-activeLine')).toHaveText(changedCode)
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state // checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
await expect(page.getByTestId('segment-overlay')).toHaveCount(4) await expect(page.getByTestId('segment-overlay')).toHaveCount(5)
}) })
} }
}) })
@ -715,7 +715,7 @@ part002 = startSketchOn(XZ)
await page.getByRole('button', { name: 'Edit Sketch' }).click() await page.getByRole('button', { name: 'Edit Sketch' }).click()
const line3 = await u.getSegmentBodyCoords( const line3 = await u.getSegmentBodyCoords(
`[data-overlay-index="${2}"]` `[data-overlay-index="${3}"]`
) )
await page.mouse.click(line3.x, line3.y) await page.mouse.click(line3.x, line3.y)
@ -733,16 +733,17 @@ part002 = startSketchOn(XZ)
}) })
} }
await expect(cmdBarKclInput).toHaveText('78.33') await expect(cmdBarKclInput).toHaveText('78.33')
await cmdBarSubmitButton.click() await page.waitForTimeout(500)
const [ang, len] = value.split(', ') const [ang, len] = value.split(', ')
const changedCode = `|> angledLine(angle = ${ang}, length = ${len})` const changedCode = `|> angledLine(angle = ${ang}, length = ${len})`
await cmdBarSubmitButton.click()
await expect(page.locator('.cm-content')).toContainText(changedCode) await expect(page.locator('.cm-content')).toContainText(changedCode)
// checking active assures the cursor is where it should be // checking active assures the cursor is where it should be
await expect(page.locator('.cm-activeLine')).toHaveText(changedCode) await expect(page.locator('.cm-activeLine')).toHaveText(changedCode)
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state // checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
await expect(page.getByTestId('segment-overlay')).toHaveCount(4) await expect(page.getByTestId('segment-overlay')).toHaveCount(5)
}) })
} }
}) })
@ -799,13 +800,13 @@ part002 = startSketchOn(XZ)
await page.waitForTimeout(1000) await page.waitForTimeout(1000)
const line1 = await u.getSegmentBodyCoords( const line1 = await u.getSegmentBodyCoords(
`[data-overlay-index="${0}"]` `[data-overlay-index="${1}"]`
) )
const line3 = await u.getSegmentBodyCoords( const line3 = await u.getSegmentBodyCoords(
`[data-overlay-index="${2}"]` `[data-overlay-index="${3}"]`
) )
const line4 = await u.getSegmentBodyCoords( const line4 = await u.getSegmentBodyCoords(
`[data-overlay-index="${3}"]` `[data-overlay-index="${4}"]`
) )
// select two segments by holding down shift // select two segments by holding down shift
@ -899,8 +900,8 @@ part002 = startSketchOn(XZ)
// Wait for overlays to populate // Wait for overlays to populate
await page.waitForTimeout(1000) await page.waitForTimeout(1000)
const line1 = await u.getBoundingBox(`[data-overlay-index="${0}"]`) const line1 = await u.getBoundingBox(`[data-overlay-index="${1}"]`)
const line3 = await u.getBoundingBox(`[data-overlay-index="${2}"]`) const line3 = await u.getBoundingBox(`[data-overlay-index="${3}"]`)
// select two segments by holding down shift // select two segments by holding down shift
await page.mouse.click(line1.x - 20, line1.y + 20) await page.mouse.click(line1.x - 20, line1.y + 20)
@ -981,7 +982,7 @@ part002 = startSketchOn(XZ)
// Wait for overlays to populate // Wait for overlays to populate
await page.waitForTimeout(1000) await page.waitForTimeout(1000)
const line3 = await u.getBoundingBox(`[data-overlay-index="${2}"]`) const line3 = await u.getBoundingBox(`[data-overlay-index="${3}"]`)
// select segment and axis by holding down shift // select segment and axis by holding down shift
await page.mouse.click(line3.x - 3, line3.y + 20) await page.mouse.click(line3.x - 3, line3.y + 20)
@ -1044,7 +1045,7 @@ part002 = startSketchOn(XZ)
await page.waitForTimeout(100) await page.waitForTimeout(100)
const lineBefore = await u.getSegmentBodyCoords( const lineBefore = await u.getSegmentBodyCoords(
`[data-overlay-index="1"]`, `[data-overlay-index="2"]`,
0 0
) )
expect( expect(
@ -1075,15 +1076,15 @@ part002 = startSketchOn(XZ)
// If the overlay-angle is updated the THREE.js scene is in a good state // If the overlay-angle is updated the THREE.js scene is in a good state
await expect( await expect(
await page.locator('[data-overlay-index="1"]') await page.locator('[data-overlay-index="2"]')
).toHaveAttribute('data-overlay-angle', '0') ).toHaveAttribute('data-overlay-angle', '0')
const lineAfter = await u.getSegmentBodyCoords( const lineAfter = await u.getSegmentBodyCoords(
`[data-overlay-index="1"]`, `[data-overlay-index="2"]`,
0 0
) )
const linebb = await u.getBoundingBox('[data-overlay-index="1"]') const linebb = await u.getBoundingBox('[data-overlay-index="2"]')
await page.mouse.move(linebb.x, linebb.y, { steps: 25 }) await page.mouse.move(linebb.x, linebb.y, { steps: 25 })
await page.mouse.click(linebb.x, linebb.y) await page.mouse.click(linebb.x, linebb.y)
@ -1097,6 +1098,7 @@ part002 = startSketchOn(XZ)
await page.waitForTimeout(200) await page.waitForTimeout(200)
// await page.getByRole('button', { name: 'length', exact: true }).click() // await page.getByRole('button', { name: 'length', exact: true }).click()
await page.getByTestId('constraint-length').click() await page.getByTestId('constraint-length').click()
await page.waitForTimeout(500)
await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('10') await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('10')
await page await page
@ -1112,7 +1114,7 @@ part002 = startSketchOn(XZ)
) )
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state // checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
await expect(page.getByTestId('segment-overlay')).toHaveCount(2) await expect(page.getByTestId('segment-overlay')).toHaveCount(3)
}) })
}) })
test.describe('Electron constraint tests', () => { test.describe('Electron constraint tests', () => {

View File

@ -691,7 +691,13 @@ test.describe('Testing segment overlays', () => {
locator: '[data-overlay-toolbar-index="11"]', locator: '[data-overlay-toolbar-index="11"]',
}) })
}) })
test('for segment [tangentialArc]', async ({ page, editor, homePage }) => { test('for segment [tangentialArc]', async ({
page,
editor,
homePage,
scene,
cmdBar,
}) => {
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
@ -719,6 +725,7 @@ test.describe('Testing segment overlays', () => {
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.settled(cmdBar)
// wait for execution done // wait for execution done
await u.openDebugPanel() await u.openDebugPanel()
@ -730,13 +737,13 @@ test.describe('Testing segment overlays', () => {
await page.getByRole('button', { name: 'Edit Sketch' }).click() await page.getByRole('button', { name: 'Edit Sketch' }).click()
await page.waitForTimeout(500) await page.waitForTimeout(500)
await expect(page.getByTestId('segment-overlay')).toHaveCount(13) await expect(page.getByTestId('segment-overlay')).toHaveCount(14)
const clickUnconstrained = _clickUnconstrained(page, editor) const clickUnconstrained = _clickUnconstrained(page, editor)
const clickConstrained = _clickConstrained(page, editor) const clickConstrained = _clickConstrained(page, editor)
const tangentialArc = await u.getBoundingBox('[data-overlay-index="12"]') const tangentialArc = await u.getBoundingBox('[data-overlay-index="13"]')
let ang = await u.getAngle('[data-overlay-index="12"]') let ang = await u.getAngle('[data-overlay-index="13"]')
console.log('tangentialArc') console.log('tangentialArc')
await clickConstrained({ await clickConstrained({
hoverPos: { x: tangentialArc.x, y: tangentialArc.y }, hoverPos: { x: tangentialArc.x, y: tangentialArc.y },
@ -747,7 +754,7 @@ test.describe('Testing segment overlays', () => {
expectFinal: 'tangentialArc(endAbsolute = [xAbs001, -3.14])', expectFinal: 'tangentialArc(endAbsolute = [xAbs001, -3.14])',
ang: ang + 180, ang: ang + 180,
steps: 6, steps: 6,
locator: '[data-overlay-toolbar-index="12"]', locator: '[data-overlay-toolbar-index="13"]',
}) })
console.log('tangentialArc2') console.log('tangentialArc2')
await clickUnconstrained({ await clickUnconstrained({
@ -760,7 +767,7 @@ test.describe('Testing segment overlays', () => {
expectFinal: 'tangentialArc(endAbsolute = [xAbs001, -3.14])', expectFinal: 'tangentialArc(endAbsolute = [xAbs001, -3.14])',
ang: ang + 180, ang: ang + 180,
steps: 10, steps: 10,
locator: '[data-overlay-toolbar-index="12"]', locator: '[data-overlay-toolbar-index="13"]',
}) })
}) })
test('for segment [arcTo]', async ({ test('for segment [arcTo]', async ({
@ -983,7 +990,7 @@ part001 = startSketchOn(XZ)
'persistCode', 'persistCode',
`@settings(defaultLengthUnit = in) `@settings(defaultLengthUnit = in)
part001 = startSketchOn(XZ) part001 = startSketchOn(XZ)
|>startProfile(at = [0, 0]) |> startProfile(at = [0, 0])
|> line(end = [0.5, -14 + 0]) |> line(end = [0.5, -14 + 0])
|> angledLine(angle = 3 + 0, length = 32 + 0) |> angledLine(angle = 3 + 0, length = 32 + 0)
|> line(endAbsolute = [33, 11.5 + 0]) |> line(endAbsolute = [33, 11.5 + 0])
@ -1017,141 +1024,167 @@ part001 = startSketchOn(XZ)
await page.getByRole('button', { name: 'Edit Sketch' }).click() await page.getByRole('button', { name: 'Edit Sketch' }).click()
await page.waitForTimeout(500) await page.waitForTimeout(500)
await expect(page.getByTestId('segment-overlay')).toHaveCount(16) await expect(page.getByTestId('segment-overlay')).toHaveCount(17)
const deleteSegmentSequence = _deleteSegmentSequence(page, editor) const deleteSegmentSequence = _deleteSegmentSequence(page, editor)
let segmentToDelete let segmentToDelete
let ang = 0
const getOverlayByIndex = (index: number) => const getOverlayByIndex = (index: number) =>
u.getBoundingBox(`[data-overlay-index="${index}"]`) u.getBoundingBox(`[data-overlay-index="${index}"]`)
segmentToDelete = await getOverlayByIndex(14) let overlayIndex = 15
let ang = await u.getAngle('[data-overlay-index="14"]')
await editor.scrollToText('angleEnd')
segmentToDelete = await getOverlayByIndex(overlayIndex)
ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
await deleteSegmentSequence({ await deleteSegmentSequence({
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y }, hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
codeToBeDeleted: `arc(angleStart = 40.27, angleEnd = -38.05, radius = 9.03)`, codeToBeDeleted: `arc(angleStart = 40.27, angleEnd = -38.05, radius = 9.03)`,
stdLibFnName: 'arc', stdLibFnName: 'arc',
ang: ang + 180, ang: ang + 180,
steps: 6, steps: 6,
locator: '[data-overlay-toolbar-index="14"]', locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
}) })
segmentToDelete = await getOverlayByIndex(13)
ang = await u.getAngle('[data-overlay-index="13"]') overlayIndex--
segmentToDelete = await getOverlayByIndex(overlayIndex)
ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
await deleteSegmentSequence({ await deleteSegmentSequence({
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y }, hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
codeToBeDeleted: `arc(interiorAbsolute = [16.25, 5.12], endAbsolute = [21.61, 4.15])`, codeToBeDeleted: `arc(interiorAbsolute = [16.25, 5.12], endAbsolute = [21.61, 4.15])`,
stdLibFnName: 'arc', stdLibFnName: 'arc',
ang: ang, ang: ang,
steps: 6, steps: 6,
locator: '[data-overlay-toolbar-index="13"]', locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
}) })
segmentToDelete = await getOverlayByIndex(12)
ang = await u.getAngle('[data-overlay-index="12"]') overlayIndex--
segmentToDelete = await getOverlayByIndex(overlayIndex)
ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
await deleteSegmentSequence({ await deleteSegmentSequence({
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y }, hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
codeToBeDeleted: 'tangentialArc(endAbsolute = [3.14 + 13, 1.14])', codeToBeDeleted: 'tangentialArc(endAbsolute = [3.14 + 13, 1.14])',
stdLibFnName: 'tangentialArc', stdLibFnName: 'tangentialArc',
ang: ang + 180, ang: ang + 180,
steps: 6, steps: 6,
locator: '[data-overlay-toolbar-index="12"]', locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
}) })
segmentToDelete = await getOverlayByIndex(11) overlayIndex--
ang = await u.getAngle('[data-overlay-index="11"]')
segmentToDelete = await getOverlayByIndex(overlayIndex)
ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
await deleteSegmentSequence({ await deleteSegmentSequence({
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y }, hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
codeToBeDeleted: `angledLineThatIntersects(angle = 4.14, intersectTag = a, offset = 9)`, codeToBeDeleted: `angledLineThatIntersects(angle = 4.14, intersectTag = a, offset = 9)`,
stdLibFnName: 'angledLineThatIntersects', stdLibFnName: 'angledLineThatIntersects',
ang: ang + 180, ang: ang + 180,
steps: 7, steps: 7,
locator: '[data-overlay-toolbar-index="11"]', locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
}) })
segmentToDelete = await getOverlayByIndex(10) overlayIndex--
ang = await u.getAngle('[data-overlay-index="10"]')
segmentToDelete = await getOverlayByIndex(overlayIndex)
ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
await deleteSegmentSequence({ await deleteSegmentSequence({
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y }, hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
codeToBeDeleted: 'angledLine(angle = 89, endAbsoluteY = 9.14 + 0)', codeToBeDeleted: 'angledLine(angle = 89, endAbsoluteY = 9.14 + 0)',
stdLibFnName: 'angledLineToY', stdLibFnName: 'angledLineToY',
ang: ang + 180, ang: ang + 180,
locator: '[data-overlay-toolbar-index="10"]', locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
}) })
segmentToDelete = await getOverlayByIndex(9) overlayIndex--
ang = await u.getAngle('[data-overlay-index="9"]')
segmentToDelete = await getOverlayByIndex(overlayIndex)
ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
await deleteSegmentSequence({ await deleteSegmentSequence({
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y }, hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
codeToBeDeleted: 'angledLine(angle = 3 + 0, endAbsoluteX = 26)', codeToBeDeleted: 'angledLine(angle = 3 + 0, endAbsoluteX = 26)',
stdLibFnName: 'angledLineToX', stdLibFnName: 'angledLineToX',
ang: ang + 180, ang: ang + 180,
locator: '[data-overlay-toolbar-index="9"]', locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
}) })
segmentToDelete = await getOverlayByIndex(8) overlayIndex--
ang = await u.getAngle('[data-overlay-index="8"]')
segmentToDelete = await getOverlayByIndex(overlayIndex)
ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
await deleteSegmentSequence({ await deleteSegmentSequence({
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y }, hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
codeToBeDeleted: 'angledLine(angle = -91, lengthY = 19 + 0)', codeToBeDeleted: 'angledLine(angle = -91, lengthY = 19 + 0)',
stdLibFnName: 'angledLineOfYLength', stdLibFnName: 'angledLineOfYLength',
ang: ang + 180, ang: ang + 180,
locator: '[data-overlay-toolbar-index="8"]', locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
}) })
segmentToDelete = await getOverlayByIndex(7) overlayIndex--
ang = await u.getAngle('[data-overlay-index="7"]')
segmentToDelete = await getOverlayByIndex(overlayIndex)
ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
await deleteSegmentSequence({ await deleteSegmentSequence({
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y }, hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
codeToBeDeleted: 'angledLine(angle = 181 + 0, lengthX = 23.14)', codeToBeDeleted: 'angledLine(angle = 181 + 0, lengthX = 23.14)',
stdLibFnName: 'angledLineOfXLength', stdLibFnName: 'angledLineOfXLength',
ang: ang + 180, ang: ang + 180,
locator: '[data-overlay-toolbar-index="7"]', locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
}) })
segmentToDelete = await getOverlayByIndex(6) overlayIndex--
ang = await u.getAngle('[data-overlay-index="6"]')
segmentToDelete = await getOverlayByIndex(overlayIndex)
ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
await deleteSegmentSequence({ await deleteSegmentSequence({
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y }, hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
codeToBeDeleted: 'yLine(length = 21.14 + 0)', codeToBeDeleted: 'yLine(length = 21.14 + 0)',
stdLibFnName: 'yLine', stdLibFnName: 'yLine',
ang: ang + 180, ang: ang + 180,
locator: '[data-overlay-toolbar-index="6"]', locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
}) })
segmentToDelete = await getOverlayByIndex(5) overlayIndex--
ang = await u.getAngle('[data-overlay-index="5"]')
segmentToDelete = await getOverlayByIndex(overlayIndex)
ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
await deleteSegmentSequence({ await deleteSegmentSequence({
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y }, hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
codeToBeDeleted: 'xLine(length = 26.04)', codeToBeDeleted: 'xLine(length = 26.04)',
stdLibFnName: 'xLine', stdLibFnName: 'xLine',
ang: ang + 180, ang: ang + 180,
locator: '[data-overlay-toolbar-index="5"]', locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
}) })
segmentToDelete = await getOverlayByIndex(4) overlayIndex--
ang = await u.getAngle('[data-overlay-index="4"]')
segmentToDelete = await getOverlayByIndex(overlayIndex)
ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
await deleteSegmentSequence({ await deleteSegmentSequence({
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y }, hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
codeToBeDeleted: 'yLine(endAbsolute = -10.77, tag = $a)', codeToBeDeleted: 'yLine(endAbsolute = -10.77, tag = $a)',
stdLibFnName: 'yLineTo', stdLibFnName: 'yLineTo',
ang: ang + 180, ang: ang + 180,
locator: '[data-overlay-toolbar-index="4"]', locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
}) })
segmentToDelete = await getOverlayByIndex(3) overlayIndex--
ang = await u.getAngle('[data-overlay-index="3"]')
segmentToDelete = await getOverlayByIndex(overlayIndex)
ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
await deleteSegmentSequence({ await deleteSegmentSequence({
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y }, hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
codeToBeDeleted: 'xLine(endAbsolute = 9 - 5)', codeToBeDeleted: 'xLine(endAbsolute = 9 - 5)',
stdLibFnName: 'xLineTo', stdLibFnName: 'xLineTo',
ang: ang + 180, ang: ang + 180,
locator: '[data-overlay-toolbar-index="3"]', locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
}) })
segmentToDelete = await getOverlayByIndex(2) overlayIndex--
ang = await u.getAngle('[data-overlay-index="2"]')
segmentToDelete = await getOverlayByIndex(overlayIndex)
ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
await expect(page.getByText('Added variable')).not.toBeVisible() await expect(page.getByText('Added variable')).not.toBeVisible()
const hoverPos = { x: segmentToDelete.x, y: segmentToDelete.y } const hoverPos = { x: segmentToDelete.x, y: segmentToDelete.y }
@ -1167,7 +1200,7 @@ part001 = startSketchOn(XZ)
ang, ang,
10, 10,
5, 5,
'[data-overlay-toolbar-index="2"]' `[data-overlay-toolbar-index="${overlayIndex}"]`
) )
await page.mouse.move(hoverPos.x, hoverPos.y) await page.mouse.move(hoverPos.x, hoverPos.y)
@ -1183,18 +1216,22 @@ part001 = startSketchOn(XZ)
shouldNormalise: true, shouldNormalise: true,
}) })
segmentToDelete = await getOverlayByIndex(1) overlayIndex--
ang = await u.getAngle('[data-overlay-index="1"]')
segmentToDelete = await getOverlayByIndex(overlayIndex)
ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
await deleteSegmentSequence({ await deleteSegmentSequence({
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y }, hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
codeToBeDeleted: 'angledLine(angle = 3 + 0, length = 32 + 0)', codeToBeDeleted: 'angledLine(angle = 3 + 0, length = 32 + 0)',
stdLibFnName: 'angledLine', stdLibFnName: 'angledLine',
ang: ang + 180, ang: ang + 180,
locator: '[data-overlay-toolbar-index="1"]', locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
}) })
segmentToDelete = await getOverlayByIndex(0) overlayIndex--
ang = await u.getAngle('[data-overlay-index="0"]')
segmentToDelete = await getOverlayByIndex(overlayIndex)
ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
await deleteSegmentSequence({ await deleteSegmentSequence({
hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y }, hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
codeToBeDeleted: 'line(end = [0.5, -14 + 0])', codeToBeDeleted: 'line(end = [0.5, -14 + 0])',
@ -1414,11 +1451,11 @@ part001 = startSketchOn(XZ)
await page.getByRole('button', { name: 'Edit Sketch' }).click() await page.getByRole('button', { name: 'Edit Sketch' }).click()
await page.waitForTimeout(500) await page.waitForTimeout(500)
await expect(page.getByTestId('segment-overlay')).toHaveCount(3) await expect(page.getByTestId('segment-overlay')).toHaveCount(4)
await expect(page.getByText('Added variable')).not.toBeVisible() await expect(page.getByText('Added variable')).not.toBeVisible()
const hoverPos = await u.getBoundingBox(`[data-overlay-index="0"]`) const hoverPos = await u.getBoundingBox(`[data-overlay-index="1"]`)
let ang = await u.getAngle('[data-overlay-index="0"]') let ang = await u.getAngle('[data-overlay-index="1"]')
ang += 180 ang += 180
await page.mouse.move(0, 0) await page.mouse.move(0, 0)
@ -1437,7 +1474,7 @@ part001 = startSketchOn(XZ)
ang, ang,
10, 10,
5, 5,
'[data-overlay-toolbar-index="0"]' '[data-overlay-toolbar-index="1"]'
) )
await page.mouse.move(x, y) await page.mouse.move(x, y)
@ -1451,7 +1488,7 @@ part001 = startSketchOn(XZ)
// check the cursor was left in the correct place after transform // check the cursor was left in the correct place after transform
await expect(page.locator('.cm-activeLine')).toHaveText('|> ' + after) await expect(page.locator('.cm-activeLine')).toHaveText('|> ' + after)
await expect(page.getByTestId('segment-overlay')).toHaveCount(3) await expect(page.getByTestId('segment-overlay')).toHaveCount(4)
}) })
} }
}) })
@ -1587,6 +1624,87 @@ profile002 = circle(sketch001, center = [345, 0], radius = 238.38)
) )
}) })
}) })
test('startProfile x y overlays', async ({
page,
editor,
homePage,
scene,
cmdBar,
toolbar,
}) => {
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`sketch001 = startSketchOn(XZ)
profile001 = startProfile(sketch001, at = [15, 15])
|> line(end = [114.78, 232])
|> line(end = [228.75, -208.39])
`
)
// Set flag to always show overlays without hover
localStorage.setItem('showAllOverlays', 'true')
})
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene()
await scene.connectionEstablished()
await scene.settled(cmdBar)
await toolbar.waitForFeatureTreeToBeBuilt()
await toolbar.editSketch(0)
await page.waitForTimeout(600)
await expect(page.getByTestId('segment-overlay')).toHaveCount(3)
// 1. constrain x coordinate
const xConstraintBtn = page.locator(
'[data-constraint-type="xAbsolute"][data-is-constrained="false"]'
)
await expect(xConstraintBtn).toBeVisible()
await xConstraintBtn.click()
await expect(
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
).toBeFocused()
await cmdBar.progressCmdBar()
await editor.expectEditor.toContain('at = [xAbs001, 15]', {
shouldNormalise: true,
})
// 2. constrain y coordinate
const yConstraintBtn = page.locator(
'[data-constraint-type="yAbsolute"][data-is-constrained="false"]'
)
await expect(yConstraintBtn).toBeVisible()
await yConstraintBtn.click()
await expect(
page.getByTestId('cmd-bar-arg-value').getByRole('textbox')
).toBeFocused()
await cmdBar.progressCmdBar()
await editor.expectEditor.toContain('at = [xAbs001, yAbs001]', {
shouldNormalise: true,
})
// 3. unconstrain x coordinate
const constrainedXBtn = page.locator(
'[data-constraint-type="xAbsolute"][data-is-constrained="true"]'
)
await expect(constrainedXBtn).toBeVisible()
await constrainedXBtn.click()
await editor.expectEditor.toContain('at = [15, yAbs001]', {
shouldNormalise: true,
})
// 4. unconstrain y coordinate
const constrainedYBtn = page.locator(
'[data-constraint-type="yAbsolute"][data-is-constrained="true"]'
)
await expect(constrainedYBtn).toBeVisible()
await constrainedYBtn.click()
await editor.expectEditor.toContain('at = [15, 15]', {
shouldNormalise: true,
})
})
test('arc with interiorAbsolute and endAbsolute kwargs overlay constraints', async ({ test('arc with interiorAbsolute and endAbsolute kwargs overlay constraints', async ({
page, page,
editor, editor,
@ -1605,7 +1723,7 @@ profile001 = circleThreePoint(
p2 = [445.16, 116.92], p2 = [445.16, 116.92],
p3 = [546.85, 103], p3 = [546.85, 103],
) )
profile003 = startProfileAt([64.39, 35.16], sketch001) profile003 = startProfile(sketch001, at = [64.39, 35.16])
|> line(end = [60.69, 23.02]) |> line(end = [60.69, 23.02])
|> arc(interiorAbsolute = [159.26, 100.58], endAbsolute = [237.05, 84.07]) |> arc(interiorAbsolute = [159.26, 100.58], endAbsolute = [237.05, 84.07])
|> line(end = [70.31, 42.28])` |> line(end = [70.31, 42.28])`
@ -1628,7 +1746,7 @@ profile003 = startProfileAt([64.39, 35.16], sketch001)
// Verify overlays are visible // Verify overlays are visible
// 3 for the three point arc, and 4 for the 3 segments (arc has two) // 3 for the three point arc, and 4 for the 3 segments (arc has two)
await expect(page.getByTestId('segment-overlay')).toHaveCount(7) await expect(page.getByTestId('segment-overlay')).toHaveCount(8)
// ---- Testing interior point constraints ---- // ---- Testing interior point constraints ----
@ -1637,7 +1755,7 @@ profile003 = startProfileAt([64.39, 35.16], sketch001)
.locator( .locator(
'[data-constraint-type="xAbsolute"][data-is-constrained="false"]' '[data-constraint-type="xAbsolute"][data-is-constrained="false"]'
) )
.nth(3) .nth(4)
await expect(interiorXConstraintBtn).toBeVisible() await expect(interiorXConstraintBtn).toBeVisible()
await interiorXConstraintBtn.click() await interiorXConstraintBtn.click()
@ -1660,7 +1778,7 @@ profile003 = startProfileAt([64.39, 35.16], sketch001)
.locator( .locator(
'[data-constraint-type="yAbsolute"][data-is-constrained="false"]' '[data-constraint-type="yAbsolute"][data-is-constrained="false"]'
) )
.nth(3) .nth(4)
await expect(interiorYConstraintBtn).toBeVisible() await expect(interiorYConstraintBtn).toBeVisible()
await interiorYConstraintBtn.click() await interiorYConstraintBtn.click()
@ -1685,7 +1803,7 @@ profile003 = startProfileAt([64.39, 35.16], sketch001)
.locator( .locator(
'[data-constraint-type="xAbsolute"][data-is-constrained="false"]' '[data-constraint-type="xAbsolute"][data-is-constrained="false"]'
) )
.nth(3) // still number 3 because the interior ones are now constrained .nth(4) // still number 3 because the interior ones are now constrained
await expect(endXConstraintBtn).toBeVisible() await expect(endXConstraintBtn).toBeVisible()
await endXConstraintBtn.click() await endXConstraintBtn.click()
@ -1705,7 +1823,7 @@ profile003 = startProfileAt([64.39, 35.16], sketch001)
.locator( .locator(
'[data-constraint-type="yAbsolute"][data-is-constrained="false"]' '[data-constraint-type="yAbsolute"][data-is-constrained="false"]'
) )
.nth(3) // still number 3 because the interior ones are now constrained .nth(4) // still number 3 because the interior ones are now constrained
await expect(endYConstraintBtn).toBeVisible() await expect(endYConstraintBtn).toBeVisible()
await endYConstraintBtn.click() await endYConstraintBtn.click()

View File

@ -797,7 +797,7 @@ test.describe(
// We use the line tool as a proxy for sketch mode // We use the line tool as a proxy for sketch mode
await expect(lineToolButton).toBeVisible() await expect(lineToolButton).toBeVisible()
await expect(segmentOverlays).toHaveCount(4) await expect(segmentOverlays).toHaveCount(5)
// but we allow more time to pass for animating to the sketch // but we allow more time to pass for animating to the sketch
await page.waitForTimeout(1000) await page.waitForTimeout(1000)
}) })

View File

@ -318,18 +318,19 @@ const Overlay = ({
this will likely change soon when we implement multi-profile so we'll leave it for now this will likely change soon when we implement multi-profile so we'll leave it for now
issue: https://github.com/KittyCAD/modeling-app/issues/3910 issue: https://github.com/KittyCAD/modeling-app/issues/3910
*/} */}
{callExpression?.callee?.name.name !== 'circle' && {!['circleThreePoint', 'circle', 'startProfile'].includes(
callExpression?.callee?.name.name !== 'circleThreePoint' && ( callExpression?.callee?.name.name
<SegmentMenu ) && (
verticalPosition={ <SegmentMenu
overlay.windowCoords[1] > window.innerHeight / 2 verticalPosition={
? 'top' overlay.windowCoords[1] > window.innerHeight / 2
: 'bottom' ? 'top'
} : 'bottom'
pathToNode={overlay.pathToNode} }
stdLibFnName={constraints[0]?.stdLibFnName} pathToNode={overlay.pathToNode}
/> stdLibFnName={constraints[0]?.stdLibFnName}
)} />
)}
</div> </div>
)} )}
</div> </div>

View File

@ -321,6 +321,18 @@ export class SceneEntities {
callBack && !err(callBack) && callbacks.push(callBack) callBack && !err(callBack) && callbacks.push(callBack)
if (segment.name === PROFILE_START) { if (segment.name === PROFILE_START) {
segment.scale.set(factor, factor, factor) segment.scale.set(factor, factor, factor)
const startProfileCallBack: () => SegmentOverlayPayload | null = () => {
return this.sceneInfra.updateOverlayDetails({
handle: segment,
group: segment,
isHandlesVisible: true,
from: segment.userData.from,
to: segment.userData.to,
hasThreeDotMenu: false,
})
}
callbacks.push(startProfileCallBack)
} }
}) })
if (this.axisGroup) { if (this.axisGroup) {
@ -702,10 +714,7 @@ export class SceneEntities {
maybeModdedAst, maybeModdedAst,
sourceRangeFromRust(sketch.start.__geoMeta.sourceRange) sourceRangeFromRust(sketch.start.__geoMeta.sourceRange)
) )
if ( if (!['Circle', 'CircleThreePoint'].includes(sketch?.paths?.[0]?.type)) {
['Circle', 'CircleThreePoint'].includes(sketch?.paths?.[0]?.type) ===
false
) {
const _profileStart = createProfileStartHandle({ const _profileStart = createProfileStartHandle({
from: sketch.start.from, from: sketch.start.from,
id: sketch.start.__geoMeta.id, id: sketch.start.__geoMeta.id,
@ -723,6 +732,18 @@ export class SceneEntities {
} }
group.add(_profileStart) group.add(_profileStart)
this.activeSegments[JSON.stringify(segPathToNode)] = _profileStart this.activeSegments[JSON.stringify(segPathToNode)] = _profileStart
const startProfileCallBack: () => SegmentOverlayPayload | null = () => {
return this.sceneInfra.updateOverlayDetails({
handle: _profileStart,
group: _profileStart,
isHandlesVisible: true,
from: sketch.start.from,
to: sketch.start.to,
hasThreeDotMenu: true,
})
}
callbacks.push(startProfileCallBack)
} }
sketch.paths.forEach((segment, index) => { sketch.paths.forEach((segment, index) => {
const isLastInProfile = const isLastInProfile =
@ -3086,7 +3107,7 @@ export class SceneEntities {
variables, variables,
kclManager: this.kclManager, kclManager: this.kclManager,
}) })
const callBacks: (() => SegmentOverlayPayload | null)[] = [] const callbacks: (() => SegmentOverlayPayload | null)[] = []
for (const sketchInfo of sketchesInfo) { for (const sketchInfo of sketchesInfo) {
const { sketch, pathToNode: _pathToNode } = sketchInfo const { sketch, pathToNode: _pathToNode } = sketchInfo
const varDecIndex = Number(_pathToNode[1][0]) const varDecIndex = Number(_pathToNode[1][0])
@ -3106,7 +3127,19 @@ export class SceneEntities {
snappedToTangent snappedToTangent
) )
callBacks.push( const startProfileCallBack: () => SegmentOverlayPayload | null = () => {
return this.sceneInfra.updateOverlayDetails({
handle: group,
group: group,
isHandlesVisible: true,
from: sketch.start.from,
to: sketch.start.to,
hasThreeDotMenu: true,
})
}
callbacks.push(startProfileCallBack)
callbacks.push(
...sgPaths.map((group, index) => ...sgPaths.map((group, index) =>
this.updateSegment( this.updateSegment(
group, group,
@ -3120,7 +3153,7 @@ export class SceneEntities {
) )
) )
} }
this.sceneInfra.overlayCallbacks(callBacks) this.sceneInfra.overlayCallbacks(callbacks)
})().catch(reportRejection) })().catch(reportRejection)
} }

View File

@ -47,6 +47,7 @@ import type {
MouseState, MouseState,
SegmentOverlayPayload, SegmentOverlayPayload,
} from '@src/machines/modelingMachine' } from '@src/machines/modelingMachine'
import { PROFILE_START } from '@src/clientSideScene/sceneConstants'
type SendType = ReturnType<typeof useModelingContext>['send'] type SendType = ReturnType<typeof useModelingContext>['send']
@ -230,7 +231,10 @@ export class SceneInfra {
// Project that position to screen space // Project that position to screen space
vector.project(this.camControls.camera) vector.project(this.camControls.camera)
const _angle = typeof angle === 'number' ? angle : getAngle(from, to) let _angle = 45
if (group.name !== PROFILE_START) {
_angle = typeof angle === 'number' ? angle : getAngle(from, to)
}
const x = (vector.x * 0.5 + 0.5) * window.innerWidth const x = (vector.x * 0.5 + 0.5) * window.innerWidth
const y = (-vector.y * 0.5 + 0.5) * window.innerHeight const y = (-vector.y * 0.5 + 0.5) * window.innerHeight

View File

@ -26,7 +26,7 @@ import {
applyConstraintLength, applyConstraintLength,
} from '@src/components/Toolbar/setAngleLength' } from '@src/components/Toolbar/setAngleLength'
import { import {
SEGMENT_BODIES, SEGMENT_BODIES_PLUS_PROFILE_START,
getParentGroup, getParentGroup,
} from '@src/clientSideScene/sceneConstants' } from '@src/clientSideScene/sceneConstants'
import { useFileContext } from '@src/hooks/useFileContext' import { useFileContext } from '@src/hooks/useFileContext'
@ -222,14 +222,15 @@ export const ModelingMachineProvider = ({
if (event.type !== 'Set mouse state') return {} if (event.type !== 'Set mouse state') return {}
const nextSegmentHoverMap = () => { const nextSegmentHoverMap = () => {
if (event.data.type === 'isHovering') { if (event.data.type === 'isHovering') {
const parent = getParentGroup(event.data.on, SEGMENT_BODIES) const parent = getParentGroup(
event.data.on,
SEGMENT_BODIES_PLUS_PROFILE_START
)
const pathToNode = parent?.userData?.pathToNode const pathToNode = parent?.userData?.pathToNode
const pathToNodeString = JSON.stringify(pathToNode) const pathToNodeString = JSON.stringify(pathToNode)
if (!parent || !pathToNode) return context.segmentHoverMap if (!parent || !pathToNode) return context.segmentHoverMap
if (context.segmentHoverMap[pathToNodeString] !== undefined) if (context.segmentHoverMap[pathToNodeString] !== undefined)
clearTimeout( clearTimeout(context.segmentHoverMap[pathToNodeString])
context.segmentHoverMap[JSON.stringify(pathToNode)]
)
return { return {
...context.segmentHoverMap, ...context.segmentHoverMap,
[pathToNodeString]: 0, [pathToNodeString]: 0,
@ -240,7 +241,7 @@ export const ModelingMachineProvider = ({
) { ) {
const mouseOnParent = getParentGroup( const mouseOnParent = getParentGroup(
context.mouseState.on, context.mouseState.on,
SEGMENT_BODIES SEGMENT_BODIES_PLUS_PROFILE_START
) )
if (!mouseOnParent || !mouseOnParent?.userData?.pathToNode) if (!mouseOnParent || !mouseOnParent?.userData?.pathToNode)
return context.segmentHoverMap return context.segmentHoverMap
@ -276,10 +277,11 @@ export const ModelingMachineProvider = ({
'Set Segment Overlays': assign({ 'Set Segment Overlays': assign({
segmentOverlays: ({ context: { segmentOverlays }, event }) => { segmentOverlays: ({ context: { segmentOverlays }, event }) => {
if (event.type !== 'Set Segment Overlays') return {} if (event.type !== 'Set Segment Overlays') return {}
if (event.data.type === 'set-many') if (event.data.type === 'set-many') {
return { return {
...event.data.overlays, ...event.data.overlays,
} }
}
if (event.data.type === 'set-one') if (event.data.type === 'set-one')
return { return {
...segmentOverlays, ...segmentOverlays,

View File

@ -28,6 +28,7 @@ export type ToolTip =
| 'circleThreePoint' | 'circleThreePoint'
| 'arcTo' | 'arcTo'
| 'arc' | 'arc'
| 'startProfile'
export const toolTips: Array<ToolTip> = [ export const toolTips: Array<ToolTip> = [
'line', 'line',
@ -46,6 +47,7 @@ export const toolTips: Array<ToolTip> = [
'circleThreePoint', 'circleThreePoint',
'arc', 'arc',
'arcTo', 'arcTo',
'startProfile',
] ]
interface ExecutionResult { interface ExecutionResult {

View File

@ -1187,6 +1187,159 @@ export const tangentialArc: SketchLineHelperKw = {
return constraints return constraints
}, },
} }
export const startProfile: SketchLineHelperKw = {
updateArgs: ({ node, pathToNode, input }) => {
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
const { to } = input
const _node = { ...node }
const nodeMeta = getNodeFromPath<CallExpressionKw>(_node, pathToNode)
if (err(nodeMeta)) return nodeMeta
const { node: callExpression } = nodeMeta
const toArrExp = createArrayExpression([
createLiteral(roundOff(to[0], 2)),
createLiteral(roundOff(to[1], 2)),
])
mutateKwArg(ARG_AT, callExpression, toArrExp)
return {
modifiedAst: _node,
pathToNode,
}
},
getTag: getTagKwArg(),
addTag: addTagKw(),
add: ({ node, pathToNode, replaceExistingCallback, segmentInput }) => {
console.log('segmentInput', segmentInput)
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
const { to } = segmentInput
const _node = structuredClone(node)
const nodeMeta = getNodeFromPath<PipeExpression | CallExpressionKw>(
_node,
pathToNode,
'PipeExpression'
)
if (err(nodeMeta)) return nodeMeta
const { node: pipe } = nodeMeta
const nodeMeta2 = getNodeFromPath<VariableDeclarator>(
_node,
pathToNode,
'VariableDeclarator'
)
if (err(nodeMeta2)) return nodeMeta2
const newXVal = createLiteral(roundOff(to[0], 2))
const newYVal = createLiteral(roundOff(to[1], 2))
if (!replaceExistingCallback && pipe.type === 'PipeExpression') {
const callExp = createCallExpressionStdLibKw('line', null, [
createLabeledArg(ARG_AT, createArrayExpression([newXVal, newYVal])),
])
const pathToNodeIndex = pathToNode.findIndex(
(x) => x[1] === 'PipeExpression'
)
const pipeIndex = pathToNode[pathToNodeIndex + 1][0]
if (typeof pipeIndex === 'undefined' || typeof pipeIndex === 'string') {
return new Error('pipeIndex is wrong')
}
pipe.body = [
...pipe.body.slice(0, pipeIndex),
callExp,
...pipe.body.slice(pipeIndex),
]
return {
modifiedAst: _node,
pathToNode,
}
}
if (replaceExistingCallback && pipe.type === 'PipeExpression') {
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
const result = replaceExistingCallback([
{
type: 'labeledArgArrayItem',
key: ARG_AT,
index: 0,
argType: 'xAbsolute',
expr: newXVal,
},
{
type: 'labeledArgArrayItem',
key: ARG_AT,
index: 1,
argType: 'yAbsolute',
expr: newYVal,
},
])
if (err(result)) return result
const { callExp, valueUsedInTransform } = result
pipe.body[callIndex] = callExp
return {
modifiedAst: _node,
pathToNode: [...pathToNode],
valueUsedInTransform,
}
}
return {
modifiedAst: _node,
pathToNode,
}
},
getConstraintInfo: (callExp, code, pathToNode) => {
if (callExp.type !== 'CallExpressionKw') return []
if (callExp.callee.name.name !== 'startProfile') return []
const expr = findKwArgWithIndex(ARG_AT, callExp)?.expr
if (expr?.type !== 'ArrayExpression') {
return []
}
const argIndex = findKwArgAnyIndex([ARG_AT], callExp)
if (argIndex === undefined) {
return []
}
const pathToXYArray: PathToNode = [
...pathToNode,
['arguments', 'CallExpressionKw'],
[argIndex, ARG_INDEX_FIELD],
['arg', LABELED_ARG_FIELD],
['elements', 'ArrayExpression'],
]
const xArg = expr.elements[0]
const yArg = expr.elements[1]
const constraints: ConstrainInfo[] = [
{
stdLibFnName: 'startProfile',
type: 'xAbsolute',
isConstrained: isNotLiteralArrayOrStatic(xArg),
sourceRange: topLevelRange(xArg.start, xArg.end),
pathToNode: [...pathToXYArray, [0, 'index']],
value: code.slice(xArg.start, xArg.end),
argPosition: {
type: 'labeledArgArrayItem',
index: 0,
key: ARG_AT,
},
},
{
stdLibFnName: 'startProfile',
type: 'yAbsolute',
isConstrained: isNotLiteralArrayOrStatic(yArg),
sourceRange: topLevelRange(yArg.start, yArg.end),
pathToNode: [...pathToXYArray, [1, 'index']],
value: code.slice(yArg.start, yArg.end),
argPosition: {
type: 'labeledArgArrayItem',
index: 1,
key: ARG_AT,
},
},
]
return constraints
},
}
export const circle: SketchLineHelperKw = { export const circle: SketchLineHelperKw = {
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => { add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
if (segmentInput.type !== 'arc-segment') return ARC_SEGMENT_ERR() if (segmentInput.type !== 'arc-segment') return ARC_SEGMENT_ERR()
@ -3123,6 +3276,7 @@ export const sketchLineHelperMapKw: { [key: string]: SketchLineHelperKw } = {
angledLineToX, angledLineToX,
angledLineToY, angledLineToY,
tangentialArc, tangentialArc,
startProfile,
} as const } as const
export function changeSketchArguments( export function changeSketchArguments(
@ -3215,6 +3369,7 @@ export function fnNameToToolTipFromSegment(
case 'circle': case 'circle':
case 'tangentialArc': case 'tangentialArc':
case 'angledLine': case 'angledLine':
case 'startProfile':
return fnName return fnName
default: default:
const err = `Unknown sketch line function ${fnName}` const err = `Unknown sketch line function ${fnName}`
@ -3254,6 +3409,7 @@ export function fnNameToTooltip(
case 'circleThreePoint': case 'circleThreePoint':
case 'circle': case 'circle':
case 'tangentialArc': case 'tangentialArc':
case 'startProfile':
return fnName return fnName
case 'angledLine': { case 'angledLine': {
const argmap: Record<string, ToolTip> = { const argmap: Record<string, ToolTip> = {
@ -3878,6 +4034,28 @@ export const getArc = (
) )
} }
export const getStartProfile = (
callExp: CallExpressionKw
):
| {
val: [Expr, Expr]
tag?: Expr
}
| Error => {
const absoluteCoords = findKwArg('at', callExp)
const tag = findKwArg(ARG_TAG, callExp)
if (absoluteCoords) {
if (absoluteCoords.type === 'ArrayExpression') {
console.log('absoluteCoords', absoluteCoords)
return {
val: [absoluteCoords.elements[0], absoluteCoords.elements[1]],
tag,
}
}
}
return new Error('expected the arguments to be for a start profile')
}
/** /**
Given a line call, return whether it's using absolute or relative end. Given a line call, return whether it's using absolute or relative end.
*/ */
@ -3916,6 +4094,8 @@ export function isAbsoluteLine(lineCall: CallExpressionKw): boolean | Error {
findKwArgAny([ARG_END_ABSOLUTE_X, ARG_END_ABSOLUTE_Y], lineCall) !== findKwArgAny([ARG_END_ABSOLUTE_X, ARG_END_ABSOLUTE_Y], lineCall) !==
undefined undefined
) )
case 'startProfile':
return true
} }
return new Error(`Unknown sketch function ${name}`) return new Error(`Unknown sketch function ${name}`)
} }
@ -3946,6 +4126,8 @@ export function getArgForEnd(lineCall: CallExpressionKw):
return getAngledLineThatIntersects(lineCall) return getAngledLineThatIntersects(lineCall)
case 'arc': case 'arc':
return getArc(lineCall) return getArc(lineCall)
case 'startProfile':
return getStartProfile(lineCall)
case 'yLine': case 'yLine':
case 'xLine': { case 'xLine': {
const arg = findKwArgAny(DETERMINING_ARGS, lineCall) const arg = findKwArgAny(DETERMINING_ARGS, lineCall)

View File

@ -1648,7 +1648,7 @@ function getTransformMapPathKw(
} }
return false return false
} }
if (name === 'startProfile') { if (name === 'startProfile' || name === 'startSketchOn') {
return false return false
} }
const tooltip = fnNameToTooltip(allLabels(sketchFnExp), name) const tooltip = fnNameToTooltip(allLabels(sketchFnExp), name)

View File

@ -1,6 +1,7 @@
import type { Node } from '@rust/kcl-lib/bindings/Node' import type { Node } from '@rust/kcl-lib/bindings/Node'
import type { import type {
ARG_AT,
ARG_END_ABSOLUTE, ARG_END_ABSOLUTE,
ARG_END_ABSOLUTE_X, ARG_END_ABSOLUTE_X,
ARG_END_ABSOLUTE_Y, ARG_END_ABSOLUTE_Y,
@ -116,6 +117,7 @@ export type InputArgKeys =
| 'p2' | 'p2'
| 'p3' | 'p3'
| 'end' | 'end'
| typeof ARG_AT
| typeof ARG_INTERIOR_ABSOLUTE | typeof ARG_INTERIOR_ABSOLUTE
| typeof ARG_END_ABSOLUTE | typeof ARG_END_ABSOLUTE
| typeof ARG_END_ABSOLUTE_X | typeof ARG_END_ABSOLUTE_X

View File

@ -314,7 +314,6 @@ export type ModelingMachineEvent =
type: 'Delete selection' type: 'Delete selection'
} }
| { type: 'Sketch no face' } | { type: 'Sketch no face' }
| { type: 'Toggle gui mode' }
| { type: 'Cancel'; cleanup?: () => void } | { type: 'Cancel'; cleanup?: () => void }
| { type: 'CancelSketch' } | { type: 'CancelSketch' }
| { | {