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:
		@ -138,7 +138,9 @@ extrude001 = extrude(sketch001, length = 5)`
 | 
			
		||||
 | 
			
		||||
    // Ensure badge is present
 | 
			
		||||
    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.
 | 
			
		||||
    await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
 | 
			
		||||
 | 
			
		||||
@ -1180,6 +1180,8 @@ sketch001 = startSketchOn(XZ)
 | 
			
		||||
    page,
 | 
			
		||||
    homePage,
 | 
			
		||||
    editor,
 | 
			
		||||
    scene,
 | 
			
		||||
    cmdBar,
 | 
			
		||||
  }) => {
 | 
			
		||||
    const u = await getUtils(page)
 | 
			
		||||
    await page.addInitScript(async () => {
 | 
			
		||||
@ -1198,9 +1200,7 @@ sketch001 = startSketchOn(XZ)
 | 
			
		||||
    await page.setBodyDimensions({ width: 1200, height: 500 })
 | 
			
		||||
 | 
			
		||||
    await homePage.goToModelingScene()
 | 
			
		||||
    await expect(
 | 
			
		||||
      page.getByRole('button', { name: 'Start Sketch' })
 | 
			
		||||
    ).not.toBeDisabled()
 | 
			
		||||
    await scene.settled(cmdBar)
 | 
			
		||||
 | 
			
		||||
    await page.waitForTimeout(100)
 | 
			
		||||
    await u.openAndClearDebugPanel()
 | 
			
		||||
@ -1236,7 +1236,7 @@ sketch001 = startSketchOn(XZ)
 | 
			
		||||
    await page.waitForTimeout(400)
 | 
			
		||||
    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
 | 
			
		||||
    await page.dragAndDrop('#stream', '#stream', {
 | 
			
		||||
@ -1278,9 +1278,9 @@ sketch001 = startSketchOn(XZ)
 | 
			
		||||
    // expect the code to have changed
 | 
			
		||||
    await editor.expectEditor.toContain(
 | 
			
		||||
      `sketch001 = startSketchOn(XZ)
 | 
			
		||||
    |> startProfile(at = [2.71, -2.71])
 | 
			
		||||
    |> line(end = [15.4, -2.78])
 | 
			
		||||
    |> tangentialArc(endAbsolute = [27.6, -3.05])
 | 
			
		||||
    |> startProfile(at = [5.36, -5.36])
 | 
			
		||||
    |> line(end = [12.73, -0.09])
 | 
			
		||||
    |> tangentialArc(endAbsolute = [24.95, -0.38])
 | 
			
		||||
    |> close()
 | 
			
		||||
    |> extrude(length = 5)`,
 | 
			
		||||
      { shouldNormalise: true }
 | 
			
		||||
@ -1294,7 +1294,7 @@ sketch001 = startSketchOn(XZ)
 | 
			
		||||
    await editor.expectEditor.toContain(
 | 
			
		||||
      `sketch001 = startSketchOn(XZ)
 | 
			
		||||
    |> startProfile(at = [2.71, -2.71])
 | 
			
		||||
    |> line(end = [15.4, -2.78])
 | 
			
		||||
    |> line(end = [12.73, -0.09])
 | 
			
		||||
    |> tangentialArc(endAbsolute = [24.95, -0.38])
 | 
			
		||||
    |> close()
 | 
			
		||||
    |> extrude(length = 5)`,
 | 
			
		||||
@ -1308,7 +1308,7 @@ sketch001 = startSketchOn(XZ)
 | 
			
		||||
 | 
			
		||||
    await editor.expectEditor.toContain(
 | 
			
		||||
      `sketch001 = startSketchOn(XZ)
 | 
			
		||||
    |> startProfile(at = [2.71, -2.71])
 | 
			
		||||
    |> startProfile(at = [4.61, -10.01])
 | 
			
		||||
    |> line(end = [12.73, -0.09])
 | 
			
		||||
    |> tangentialArc(endAbsolute = [24.95, -0.38])
 | 
			
		||||
    |> close()
 | 
			
		||||
@ -1505,7 +1505,7 @@ sketch001 = startSketchOn(XZ)
 | 
			
		||||
    await page.waitForTimeout(1000)
 | 
			
		||||
 | 
			
		||||
    // 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')
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -543,6 +543,9 @@ sketch001 = startSketchOn(XZ)
 | 
			
		||||
    page,
 | 
			
		||||
    homePage,
 | 
			
		||||
    editor,
 | 
			
		||||
    toolbar,
 | 
			
		||||
    scene,
 | 
			
		||||
    cmdBar,
 | 
			
		||||
  }) => {
 | 
			
		||||
    const u = await getUtils(page)
 | 
			
		||||
    await page.addInitScript(async () => {
 | 
			
		||||
@ -559,10 +562,12 @@ sketch001 = startSketchOn(XZ)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    await homePage.goToModelingScene()
 | 
			
		||||
    await toolbar.waitForFeatureTreeToBeBuilt()
 | 
			
		||||
    await scene.settled(cmdBar)
 | 
			
		||||
 | 
			
		||||
    await expect(
 | 
			
		||||
      page.getByRole('button', { name: 'Start Sketch' })
 | 
			
		||||
    ).not.toBeDisabled()
 | 
			
		||||
    ).not.toBeDisabled({ timeout: 10_000 })
 | 
			
		||||
 | 
			
		||||
    await page.waitForTimeout(100)
 | 
			
		||||
    await u.openAndClearDebugPanel()
 | 
			
		||||
@ -598,7 +603,7 @@ sketch001 = startSketchOn(XZ)
 | 
			
		||||
    await page.waitForTimeout(400)
 | 
			
		||||
    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
 | 
			
		||||
    await page.dragAndDrop('#stream', '#stream', {
 | 
			
		||||
@ -612,7 +617,7 @@ sketch001 = startSketchOn(XZ)
 | 
			
		||||
    // drag line handle
 | 
			
		||||
    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', {
 | 
			
		||||
      sourcePosition: { x: lineEnd.x - 15, y: lineEnd.y },
 | 
			
		||||
      targetPosition: { x: lineEnd.x, y: lineEnd.y + 15 },
 | 
			
		||||
@ -622,7 +627,7 @@ sketch001 = startSketchOn(XZ)
 | 
			
		||||
    prevContent = await page.locator('.cm-content').innerText()
 | 
			
		||||
 | 
			
		||||
    // 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', {
 | 
			
		||||
      sourcePosition: { x: tangentEnd.x + 10, y: tangentEnd.y - 5 },
 | 
			
		||||
      targetPosition: {
 | 
			
		||||
@ -638,7 +643,7 @@ sketch001 = startSketchOn(XZ)
 | 
			
		||||
      `sketch001 = startSketchOn(XZ)
 | 
			
		||||
    |> startProfile(at = [7.12, -12.68])
 | 
			
		||||
    |> line(end = [12.68, -1.09])
 | 
			
		||||
    |> tangentialArc(endAbsolute = [24.89, 0.68])
 | 
			
		||||
    |> tangentialArc(endAbsolute = [24.95, -0.38])
 | 
			
		||||
    |> close()
 | 
			
		||||
    |> extrude(length = 5)`,
 | 
			
		||||
      { shouldNormalise: true }
 | 
			
		||||
@ -709,7 +714,7 @@ sketch001 = startSketchOn(XZ)
 | 
			
		||||
 | 
			
		||||
    const step5 = { steps: 5 }
 | 
			
		||||
 | 
			
		||||
    await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
 | 
			
		||||
    await expect(page.getByTestId('segment-overlay')).toHaveCount(3)
 | 
			
		||||
 | 
			
		||||
    // drag startProfileAt handle
 | 
			
		||||
    await page.mouse.move(startPX[0], startPX[1])
 | 
			
		||||
@ -744,10 +749,10 @@ sketch001 = startSketchOn(XZ)
 | 
			
		||||
    // expect the code to have changed
 | 
			
		||||
    await editor.expectEditor.toContain(
 | 
			
		||||
      `sketch001 = startSketchOn(XZ)
 | 
			
		||||
  |> startProfile(at = [6.44, -12.07])
 | 
			
		||||
  |> line(end = [14.72, 1.97])
 | 
			
		||||
  |> startProfile(at = [8.41, -9.97]) 
 | 
			
		||||
  |> line(end = [12.73, -0.09])
 | 
			
		||||
  |> line(end = [1.99, 2.06])
 | 
			
		||||
  |> tangentialArc(endAbsolute = [24.95, -5.38])
 | 
			
		||||
  |> line(end = [1.97, 2.06])
 | 
			
		||||
  |> close()
 | 
			
		||||
  |> revolve(axis = X)`,
 | 
			
		||||
      { shouldNormalise: true }
 | 
			
		||||
 | 
			
		||||
@ -108,7 +108,7 @@ test.describe('Testing constraints', () => {
 | 
			
		||||
    // Wait for overlays to populate
 | 
			
		||||
    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.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
 | 
			
		||||
    await expect(page.getByTestId('segment-overlay')).toHaveCount(4)
 | 
			
		||||
    await expect(page.getByTestId('segment-overlay')).toHaveCount(5)
 | 
			
		||||
  })
 | 
			
		||||
  test.describe('Test perpendicular distance constraint', () => {
 | 
			
		||||
    const cases = [
 | 
			
		||||
@ -174,8 +174,8 @@ test.describe('Testing constraints', () => {
 | 
			
		||||
        await page.waitForTimeout(1000)
 | 
			
		||||
 | 
			
		||||
        const [line1, line3] = await Promise.all([
 | 
			
		||||
          u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`),
 | 
			
		||||
          u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`),
 | 
			
		||||
          u.getSegmentBodyCoords(`[data-overlay-index="${1}"]`),
 | 
			
		||||
          u.getSegmentBodyCoords(`[data-overlay-index="${3}"]`),
 | 
			
		||||
        ])
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
        const [line1, line3] = await Promise.all([
 | 
			
		||||
          u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`),
 | 
			
		||||
          u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`),
 | 
			
		||||
          u.getSegmentBodyCoords(`[data-overlay-index="${1}"]`),
 | 
			
		||||
          u.getSegmentBodyCoords(`[data-overlay-index="${3}"]`),
 | 
			
		||||
        ])
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
        const [line3] = await Promise.all([
 | 
			
		||||
          u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`),
 | 
			
		||||
          u.getSegmentBodyCoords(`[data-overlay-index="${3}"]`),
 | 
			
		||||
        ])
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
        const [line1, line3] = await Promise.all([
 | 
			
		||||
          u.getSegmentBodyCoords(`[data-overlay-index="${0}"]`),
 | 
			
		||||
          u.getSegmentBodyCoords(`[data-overlay-index="${2}"]`),
 | 
			
		||||
          u.getSegmentBodyCoords(`[data-overlay-index="${1}"]`),
 | 
			
		||||
          u.getSegmentBodyCoords(`[data-overlay-index="${3}"]`),
 | 
			
		||||
        ])
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
        const line3 = await u.getSegmentBodyCoords(
 | 
			
		||||
          `[data-overlay-index="${2}"]`
 | 
			
		||||
          `[data-overlay-index="${3}"]`
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        await page.mouse.click(line3.x, line3.y)
 | 
			
		||||
@ -647,7 +647,7 @@ test.describe('Testing constraints', () => {
 | 
			
		||||
        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
 | 
			
		||||
        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()
 | 
			
		||||
 | 
			
		||||
        const line3 = await u.getSegmentBodyCoords(
 | 
			
		||||
          `[data-overlay-index="${2}"]`
 | 
			
		||||
          `[data-overlay-index="${3}"]`
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        await page.mouse.click(line3.x, line3.y)
 | 
			
		||||
@ -733,16 +733,17 @@ part002 = startSketchOn(XZ)
 | 
			
		||||
          })
 | 
			
		||||
        }
 | 
			
		||||
        await expect(cmdBarKclInput).toHaveText('78.33')
 | 
			
		||||
        await cmdBarSubmitButton.click()
 | 
			
		||||
 | 
			
		||||
        await page.waitForTimeout(500)
 | 
			
		||||
        const [ang, len] = value.split(', ')
 | 
			
		||||
        const changedCode = `|> angledLine(angle = ${ang}, length = ${len})`
 | 
			
		||||
        await cmdBarSubmitButton.click()
 | 
			
		||||
        await expect(page.locator('.cm-content')).toContainText(changedCode)
 | 
			
		||||
 | 
			
		||||
        // checking active assures the cursor is where it should be
 | 
			
		||||
        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
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
        const line1 = await u.getSegmentBodyCoords(
 | 
			
		||||
          `[data-overlay-index="${0}"]`
 | 
			
		||||
          `[data-overlay-index="${1}"]`
 | 
			
		||||
        )
 | 
			
		||||
        const line3 = await u.getSegmentBodyCoords(
 | 
			
		||||
          `[data-overlay-index="${2}"]`
 | 
			
		||||
          `[data-overlay-index="${3}"]`
 | 
			
		||||
        )
 | 
			
		||||
        const line4 = await u.getSegmentBodyCoords(
 | 
			
		||||
          `[data-overlay-index="${3}"]`
 | 
			
		||||
          `[data-overlay-index="${4}"]`
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        // select two segments by holding down shift
 | 
			
		||||
@ -899,8 +900,8 @@ part002 = startSketchOn(XZ)
 | 
			
		||||
        // Wait for overlays to populate
 | 
			
		||||
        await page.waitForTimeout(1000)
 | 
			
		||||
 | 
			
		||||
        const line1 = await u.getBoundingBox(`[data-overlay-index="${0}"]`)
 | 
			
		||||
        const line3 = await u.getBoundingBox(`[data-overlay-index="${2}"]`)
 | 
			
		||||
        const line1 = await u.getBoundingBox(`[data-overlay-index="${1}"]`)
 | 
			
		||||
        const line3 = await u.getBoundingBox(`[data-overlay-index="${3}"]`)
 | 
			
		||||
 | 
			
		||||
        // select two segments by holding down shift
 | 
			
		||||
        await page.mouse.click(line1.x - 20, line1.y + 20)
 | 
			
		||||
@ -981,7 +982,7 @@ part002 = startSketchOn(XZ)
 | 
			
		||||
        // Wait for overlays to populate
 | 
			
		||||
        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
 | 
			
		||||
        await page.mouse.click(line3.x - 3, line3.y + 20)
 | 
			
		||||
@ -1044,7 +1045,7 @@ part002 = startSketchOn(XZ)
 | 
			
		||||
 | 
			
		||||
    await page.waitForTimeout(100)
 | 
			
		||||
    const lineBefore = await u.getSegmentBodyCoords(
 | 
			
		||||
      `[data-overlay-index="1"]`,
 | 
			
		||||
      `[data-overlay-index="2"]`,
 | 
			
		||||
      0
 | 
			
		||||
    )
 | 
			
		||||
    expect(
 | 
			
		||||
@ -1075,15 +1076,15 @@ part002 = startSketchOn(XZ)
 | 
			
		||||
 | 
			
		||||
    // If the overlay-angle is updated the THREE.js scene is in a good state
 | 
			
		||||
    await expect(
 | 
			
		||||
      await page.locator('[data-overlay-index="1"]')
 | 
			
		||||
      await page.locator('[data-overlay-index="2"]')
 | 
			
		||||
    ).toHaveAttribute('data-overlay-angle', '0')
 | 
			
		||||
 | 
			
		||||
    const lineAfter = await u.getSegmentBodyCoords(
 | 
			
		||||
      `[data-overlay-index="1"]`,
 | 
			
		||||
      `[data-overlay-index="2"]`,
 | 
			
		||||
      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.click(linebb.x, linebb.y)
 | 
			
		||||
 | 
			
		||||
@ -1097,6 +1098,7 @@ part002 = startSketchOn(XZ)
 | 
			
		||||
    await page.waitForTimeout(200)
 | 
			
		||||
    // await page.getByRole('button', { name: 'length', exact: true }).click()
 | 
			
		||||
    await page.getByTestId('constraint-length').click()
 | 
			
		||||
    await page.waitForTimeout(500)
 | 
			
		||||
 | 
			
		||||
    await page.getByTestId('cmd-bar-arg-value').getByRole('textbox').fill('10')
 | 
			
		||||
    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
 | 
			
		||||
    await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
 | 
			
		||||
    await expect(page.getByTestId('segment-overlay')).toHaveCount(3)
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
test.describe('Electron constraint tests', () => {
 | 
			
		||||
 | 
			
		||||
@ -691,7 +691,13 @@ test.describe('Testing segment overlays', () => {
 | 
			
		||||
        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 () => {
 | 
			
		||||
        localStorage.setItem(
 | 
			
		||||
          'persistCode',
 | 
			
		||||
@ -719,6 +725,7 @@ test.describe('Testing segment overlays', () => {
 | 
			
		||||
      await page.setBodyDimensions({ width: 1200, height: 500 })
 | 
			
		||||
 | 
			
		||||
      await homePage.goToModelingScene()
 | 
			
		||||
      await scene.settled(cmdBar)
 | 
			
		||||
 | 
			
		||||
      // wait for execution done
 | 
			
		||||
      await u.openDebugPanel()
 | 
			
		||||
@ -730,13 +737,13 @@ test.describe('Testing segment overlays', () => {
 | 
			
		||||
      await page.getByRole('button', { name: 'Edit Sketch' }).click()
 | 
			
		||||
      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 clickConstrained = _clickConstrained(page, editor)
 | 
			
		||||
 | 
			
		||||
      const tangentialArc = await u.getBoundingBox('[data-overlay-index="12"]')
 | 
			
		||||
      let ang = await u.getAngle('[data-overlay-index="12"]')
 | 
			
		||||
      const tangentialArc = await u.getBoundingBox('[data-overlay-index="13"]')
 | 
			
		||||
      let ang = await u.getAngle('[data-overlay-index="13"]')
 | 
			
		||||
      console.log('tangentialArc')
 | 
			
		||||
      await clickConstrained({
 | 
			
		||||
        hoverPos: { x: tangentialArc.x, y: tangentialArc.y },
 | 
			
		||||
@ -747,7 +754,7 @@ test.describe('Testing segment overlays', () => {
 | 
			
		||||
        expectFinal: 'tangentialArc(endAbsolute = [xAbs001, -3.14])',
 | 
			
		||||
        ang: ang + 180,
 | 
			
		||||
        steps: 6,
 | 
			
		||||
        locator: '[data-overlay-toolbar-index="12"]',
 | 
			
		||||
        locator: '[data-overlay-toolbar-index="13"]',
 | 
			
		||||
      })
 | 
			
		||||
      console.log('tangentialArc2')
 | 
			
		||||
      await clickUnconstrained({
 | 
			
		||||
@ -760,7 +767,7 @@ test.describe('Testing segment overlays', () => {
 | 
			
		||||
        expectFinal: 'tangentialArc(endAbsolute = [xAbs001, -3.14])',
 | 
			
		||||
        ang: ang + 180,
 | 
			
		||||
        steps: 10,
 | 
			
		||||
        locator: '[data-overlay-toolbar-index="12"]',
 | 
			
		||||
        locator: '[data-overlay-toolbar-index="13"]',
 | 
			
		||||
      })
 | 
			
		||||
    })
 | 
			
		||||
    test('for segment [arcTo]', async ({
 | 
			
		||||
@ -983,7 +990,7 @@ part001 = startSketchOn(XZ)
 | 
			
		||||
          'persistCode',
 | 
			
		||||
          `@settings(defaultLengthUnit = in)
 | 
			
		||||
part001 = startSketchOn(XZ)
 | 
			
		||||
  |>startProfile(at = [0, 0])
 | 
			
		||||
  |> startProfile(at = [0, 0])
 | 
			
		||||
  |> line(end = [0.5, -14 + 0])
 | 
			
		||||
  |> angledLine(angle = 3 + 0, length = 32 + 0)
 | 
			
		||||
  |> line(endAbsolute = [33, 11.5 + 0])
 | 
			
		||||
@ -1017,141 +1024,167 @@ part001 = startSketchOn(XZ)
 | 
			
		||||
      await page.getByRole('button', { name: 'Edit Sketch' }).click()
 | 
			
		||||
      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)
 | 
			
		||||
 | 
			
		||||
      let segmentToDelete
 | 
			
		||||
      let ang = 0
 | 
			
		||||
 | 
			
		||||
      const getOverlayByIndex = (index: number) =>
 | 
			
		||||
        u.getBoundingBox(`[data-overlay-index="${index}"]`)
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(14)
 | 
			
		||||
      let ang = await u.getAngle('[data-overlay-index="14"]')
 | 
			
		||||
 | 
			
		||||
      await editor.scrollToText('angleEnd')
 | 
			
		||||
      let overlayIndex = 15
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(overlayIndex)
 | 
			
		||||
      ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
 | 
			
		||||
      await deleteSegmentSequence({
 | 
			
		||||
        hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
 | 
			
		||||
        codeToBeDeleted: `arc(angleStart = 40.27, angleEnd = -38.05, radius = 9.03)`,
 | 
			
		||||
        stdLibFnName: 'arc',
 | 
			
		||||
        ang: ang + 180,
 | 
			
		||||
        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({
 | 
			
		||||
        hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
 | 
			
		||||
        codeToBeDeleted: `arc(interiorAbsolute = [16.25, 5.12], endAbsolute = [21.61, 4.15])`,
 | 
			
		||||
        stdLibFnName: 'arc',
 | 
			
		||||
        ang: ang,
 | 
			
		||||
        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({
 | 
			
		||||
        hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
 | 
			
		||||
        codeToBeDeleted: 'tangentialArc(endAbsolute = [3.14 + 13, 1.14])',
 | 
			
		||||
        stdLibFnName: 'tangentialArc',
 | 
			
		||||
        ang: ang + 180,
 | 
			
		||||
        steps: 6,
 | 
			
		||||
        locator: '[data-overlay-toolbar-index="12"]',
 | 
			
		||||
        locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(11)
 | 
			
		||||
      ang = await u.getAngle('[data-overlay-index="11"]')
 | 
			
		||||
      overlayIndex--
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(overlayIndex)
 | 
			
		||||
      ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
 | 
			
		||||
      await deleteSegmentSequence({
 | 
			
		||||
        hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
 | 
			
		||||
        codeToBeDeleted: `angledLineThatIntersects(angle = 4.14, intersectTag = a, offset = 9)`,
 | 
			
		||||
        stdLibFnName: 'angledLineThatIntersects',
 | 
			
		||||
        ang: ang + 180,
 | 
			
		||||
        steps: 7,
 | 
			
		||||
        locator: '[data-overlay-toolbar-index="11"]',
 | 
			
		||||
        locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(10)
 | 
			
		||||
      ang = await u.getAngle('[data-overlay-index="10"]')
 | 
			
		||||
      overlayIndex--
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(overlayIndex)
 | 
			
		||||
      ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
 | 
			
		||||
      await deleteSegmentSequence({
 | 
			
		||||
        hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
 | 
			
		||||
        codeToBeDeleted: 'angledLine(angle = 89, endAbsoluteY = 9.14 + 0)',
 | 
			
		||||
        stdLibFnName: 'angledLineToY',
 | 
			
		||||
        ang: ang + 180,
 | 
			
		||||
        locator: '[data-overlay-toolbar-index="10"]',
 | 
			
		||||
        locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(9)
 | 
			
		||||
      ang = await u.getAngle('[data-overlay-index="9"]')
 | 
			
		||||
      overlayIndex--
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(overlayIndex)
 | 
			
		||||
      ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
 | 
			
		||||
      await deleteSegmentSequence({
 | 
			
		||||
        hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
 | 
			
		||||
        codeToBeDeleted: 'angledLine(angle = 3 + 0, endAbsoluteX = 26)',
 | 
			
		||||
        stdLibFnName: 'angledLineToX',
 | 
			
		||||
        ang: ang + 180,
 | 
			
		||||
        locator: '[data-overlay-toolbar-index="9"]',
 | 
			
		||||
        locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(8)
 | 
			
		||||
      ang = await u.getAngle('[data-overlay-index="8"]')
 | 
			
		||||
      overlayIndex--
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(overlayIndex)
 | 
			
		||||
      ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
 | 
			
		||||
      await deleteSegmentSequence({
 | 
			
		||||
        hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
 | 
			
		||||
        codeToBeDeleted: 'angledLine(angle = -91, lengthY = 19 + 0)',
 | 
			
		||||
        stdLibFnName: 'angledLineOfYLength',
 | 
			
		||||
        ang: ang + 180,
 | 
			
		||||
        locator: '[data-overlay-toolbar-index="8"]',
 | 
			
		||||
        locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(7)
 | 
			
		||||
      ang = await u.getAngle('[data-overlay-index="7"]')
 | 
			
		||||
      overlayIndex--
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(overlayIndex)
 | 
			
		||||
      ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
 | 
			
		||||
      await deleteSegmentSequence({
 | 
			
		||||
        hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
 | 
			
		||||
        codeToBeDeleted: 'angledLine(angle = 181 + 0, lengthX = 23.14)',
 | 
			
		||||
        stdLibFnName: 'angledLineOfXLength',
 | 
			
		||||
        ang: ang + 180,
 | 
			
		||||
        locator: '[data-overlay-toolbar-index="7"]',
 | 
			
		||||
        locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(6)
 | 
			
		||||
      ang = await u.getAngle('[data-overlay-index="6"]')
 | 
			
		||||
      overlayIndex--
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(overlayIndex)
 | 
			
		||||
      ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
 | 
			
		||||
      await deleteSegmentSequence({
 | 
			
		||||
        hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
 | 
			
		||||
        codeToBeDeleted: 'yLine(length = 21.14 + 0)',
 | 
			
		||||
        stdLibFnName: 'yLine',
 | 
			
		||||
        ang: ang + 180,
 | 
			
		||||
        locator: '[data-overlay-toolbar-index="6"]',
 | 
			
		||||
        locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(5)
 | 
			
		||||
      ang = await u.getAngle('[data-overlay-index="5"]')
 | 
			
		||||
      overlayIndex--
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(overlayIndex)
 | 
			
		||||
      ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
 | 
			
		||||
      await deleteSegmentSequence({
 | 
			
		||||
        hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
 | 
			
		||||
        codeToBeDeleted: 'xLine(length = 26.04)',
 | 
			
		||||
        stdLibFnName: 'xLine',
 | 
			
		||||
        ang: ang + 180,
 | 
			
		||||
        locator: '[data-overlay-toolbar-index="5"]',
 | 
			
		||||
        locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(4)
 | 
			
		||||
      ang = await u.getAngle('[data-overlay-index="4"]')
 | 
			
		||||
      overlayIndex--
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(overlayIndex)
 | 
			
		||||
      ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
 | 
			
		||||
      await deleteSegmentSequence({
 | 
			
		||||
        hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
 | 
			
		||||
        codeToBeDeleted: 'yLine(endAbsolute = -10.77, tag = $a)',
 | 
			
		||||
        stdLibFnName: 'yLineTo',
 | 
			
		||||
        ang: ang + 180,
 | 
			
		||||
        locator: '[data-overlay-toolbar-index="4"]',
 | 
			
		||||
        locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(3)
 | 
			
		||||
      ang = await u.getAngle('[data-overlay-index="3"]')
 | 
			
		||||
      overlayIndex--
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(overlayIndex)
 | 
			
		||||
      ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
 | 
			
		||||
      await deleteSegmentSequence({
 | 
			
		||||
        hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
 | 
			
		||||
        codeToBeDeleted: 'xLine(endAbsolute = 9 - 5)',
 | 
			
		||||
        stdLibFnName: 'xLineTo',
 | 
			
		||||
        ang: ang + 180,
 | 
			
		||||
        locator: '[data-overlay-toolbar-index="3"]',
 | 
			
		||||
        locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(2)
 | 
			
		||||
      ang = await u.getAngle('[data-overlay-index="2"]')
 | 
			
		||||
      overlayIndex--
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(overlayIndex)
 | 
			
		||||
      ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
 | 
			
		||||
      await expect(page.getByText('Added variable')).not.toBeVisible()
 | 
			
		||||
 | 
			
		||||
      const hoverPos = { x: segmentToDelete.x, y: segmentToDelete.y }
 | 
			
		||||
@ -1167,7 +1200,7 @@ part001 = startSketchOn(XZ)
 | 
			
		||||
        ang,
 | 
			
		||||
        10,
 | 
			
		||||
        5,
 | 
			
		||||
        '[data-overlay-toolbar-index="2"]'
 | 
			
		||||
        `[data-overlay-toolbar-index="${overlayIndex}"]`
 | 
			
		||||
      )
 | 
			
		||||
      await page.mouse.move(hoverPos.x, hoverPos.y)
 | 
			
		||||
 | 
			
		||||
@ -1183,18 +1216,22 @@ part001 = startSketchOn(XZ)
 | 
			
		||||
        shouldNormalise: true,
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(1)
 | 
			
		||||
      ang = await u.getAngle('[data-overlay-index="1"]')
 | 
			
		||||
      overlayIndex--
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(overlayIndex)
 | 
			
		||||
      ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
 | 
			
		||||
      await deleteSegmentSequence({
 | 
			
		||||
        hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
 | 
			
		||||
        codeToBeDeleted: 'angledLine(angle = 3 + 0, length = 32 + 0)',
 | 
			
		||||
        stdLibFnName: 'angledLine',
 | 
			
		||||
        ang: ang + 180,
 | 
			
		||||
        locator: '[data-overlay-toolbar-index="1"]',
 | 
			
		||||
        locator: `[data-overlay-toolbar-index="${overlayIndex}"]`,
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(0)
 | 
			
		||||
      ang = await u.getAngle('[data-overlay-index="0"]')
 | 
			
		||||
      overlayIndex--
 | 
			
		||||
 | 
			
		||||
      segmentToDelete = await getOverlayByIndex(overlayIndex)
 | 
			
		||||
      ang = await u.getAngle(`[data-overlay-index="${overlayIndex}"]`)
 | 
			
		||||
      await deleteSegmentSequence({
 | 
			
		||||
        hoverPos: { x: segmentToDelete.x, y: segmentToDelete.y },
 | 
			
		||||
        codeToBeDeleted: 'line(end = [0.5, -14 + 0])',
 | 
			
		||||
@ -1414,11 +1451,11 @@ part001 = startSketchOn(XZ)
 | 
			
		||||
        await page.getByRole('button', { name: 'Edit Sketch' }).click()
 | 
			
		||||
        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()
 | 
			
		||||
 | 
			
		||||
        const hoverPos = await u.getBoundingBox(`[data-overlay-index="0"]`)
 | 
			
		||||
        let ang = await u.getAngle('[data-overlay-index="0"]')
 | 
			
		||||
        const hoverPos = await u.getBoundingBox(`[data-overlay-index="1"]`)
 | 
			
		||||
        let ang = await u.getAngle('[data-overlay-index="1"]')
 | 
			
		||||
        ang += 180
 | 
			
		||||
 | 
			
		||||
        await page.mouse.move(0, 0)
 | 
			
		||||
@ -1437,7 +1474,7 @@ part001 = startSketchOn(XZ)
 | 
			
		||||
          ang,
 | 
			
		||||
          10,
 | 
			
		||||
          5,
 | 
			
		||||
          '[data-overlay-toolbar-index="0"]'
 | 
			
		||||
          '[data-overlay-toolbar-index="1"]'
 | 
			
		||||
        )
 | 
			
		||||
        await page.mouse.move(x, y)
 | 
			
		||||
 | 
			
		||||
@ -1451,7 +1488,7 @@ part001 = startSketchOn(XZ)
 | 
			
		||||
 | 
			
		||||
        // check the cursor was left in the correct place after transform
 | 
			
		||||
        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 ({
 | 
			
		||||
    page,
 | 
			
		||||
    editor,
 | 
			
		||||
@ -1605,7 +1723,7 @@ profile001 = circleThreePoint(
 | 
			
		||||
  p2 = [445.16, 116.92],
 | 
			
		||||
  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])
 | 
			
		||||
  |> arc(interiorAbsolute = [159.26, 100.58], endAbsolute = [237.05, 84.07])
 | 
			
		||||
  |> line(end = [70.31, 42.28])`
 | 
			
		||||
@ -1628,7 +1746,7 @@ profile003 = startProfileAt([64.39, 35.16], sketch001)
 | 
			
		||||
 | 
			
		||||
    // Verify overlays are visible
 | 
			
		||||
    // 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 ----
 | 
			
		||||
 | 
			
		||||
@ -1637,7 +1755,7 @@ profile003 = startProfileAt([64.39, 35.16], sketch001)
 | 
			
		||||
      .locator(
 | 
			
		||||
        '[data-constraint-type="xAbsolute"][data-is-constrained="false"]'
 | 
			
		||||
      )
 | 
			
		||||
      .nth(3)
 | 
			
		||||
      .nth(4)
 | 
			
		||||
    await expect(interiorXConstraintBtn).toBeVisible()
 | 
			
		||||
    await interiorXConstraintBtn.click()
 | 
			
		||||
 | 
			
		||||
@ -1660,7 +1778,7 @@ profile003 = startProfileAt([64.39, 35.16], sketch001)
 | 
			
		||||
      .locator(
 | 
			
		||||
        '[data-constraint-type="yAbsolute"][data-is-constrained="false"]'
 | 
			
		||||
      )
 | 
			
		||||
      .nth(3)
 | 
			
		||||
      .nth(4)
 | 
			
		||||
    await expect(interiorYConstraintBtn).toBeVisible()
 | 
			
		||||
    await interiorYConstraintBtn.click()
 | 
			
		||||
 | 
			
		||||
@ -1685,7 +1803,7 @@ profile003 = startProfileAt([64.39, 35.16], sketch001)
 | 
			
		||||
      .locator(
 | 
			
		||||
        '[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 endXConstraintBtn.click()
 | 
			
		||||
 | 
			
		||||
@ -1705,7 +1823,7 @@ profile003 = startProfileAt([64.39, 35.16], sketch001)
 | 
			
		||||
      .locator(
 | 
			
		||||
        '[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 endYConstraintBtn.click()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -797,7 +797,7 @@ test.describe(
 | 
			
		||||
 | 
			
		||||
        // We use the line tool as a proxy for sketch mode
 | 
			
		||||
        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
 | 
			
		||||
        await page.waitForTimeout(1000)
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
@ -318,18 +318,19 @@ const Overlay = ({
 | 
			
		||||
          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
 | 
			
		||||
          */}
 | 
			
		||||
          {callExpression?.callee?.name.name !== 'circle' &&
 | 
			
		||||
            callExpression?.callee?.name.name !== 'circleThreePoint' && (
 | 
			
		||||
              <SegmentMenu
 | 
			
		||||
                verticalPosition={
 | 
			
		||||
                  overlay.windowCoords[1] > window.innerHeight / 2
 | 
			
		||||
                    ? 'top'
 | 
			
		||||
                    : 'bottom'
 | 
			
		||||
                }
 | 
			
		||||
                pathToNode={overlay.pathToNode}
 | 
			
		||||
                stdLibFnName={constraints[0]?.stdLibFnName}
 | 
			
		||||
              />
 | 
			
		||||
            )}
 | 
			
		||||
          {!['circleThreePoint', 'circle', 'startProfile'].includes(
 | 
			
		||||
            callExpression?.callee?.name.name
 | 
			
		||||
          ) && (
 | 
			
		||||
            <SegmentMenu
 | 
			
		||||
              verticalPosition={
 | 
			
		||||
                overlay.windowCoords[1] > window.innerHeight / 2
 | 
			
		||||
                  ? 'top'
 | 
			
		||||
                  : 'bottom'
 | 
			
		||||
              }
 | 
			
		||||
              pathToNode={overlay.pathToNode}
 | 
			
		||||
              stdLibFnName={constraints[0]?.stdLibFnName}
 | 
			
		||||
            />
 | 
			
		||||
          )}
 | 
			
		||||
        </div>
 | 
			
		||||
      )}
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
@ -321,6 +321,18 @@ export class SceneEntities {
 | 
			
		||||
      callBack && !err(callBack) && callbacks.push(callBack)
 | 
			
		||||
      if (segment.name === PROFILE_START) {
 | 
			
		||||
        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) {
 | 
			
		||||
@ -702,10 +714,7 @@ export class SceneEntities {
 | 
			
		||||
        maybeModdedAst,
 | 
			
		||||
        sourceRangeFromRust(sketch.start.__geoMeta.sourceRange)
 | 
			
		||||
      )
 | 
			
		||||
      if (
 | 
			
		||||
        ['Circle', 'CircleThreePoint'].includes(sketch?.paths?.[0]?.type) ===
 | 
			
		||||
        false
 | 
			
		||||
      ) {
 | 
			
		||||
      if (!['Circle', 'CircleThreePoint'].includes(sketch?.paths?.[0]?.type)) {
 | 
			
		||||
        const _profileStart = createProfileStartHandle({
 | 
			
		||||
          from: sketch.start.from,
 | 
			
		||||
          id: sketch.start.__geoMeta.id,
 | 
			
		||||
@ -723,6 +732,18 @@ export class SceneEntities {
 | 
			
		||||
        }
 | 
			
		||||
        group.add(_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) => {
 | 
			
		||||
        const isLastInProfile =
 | 
			
		||||
@ -3086,7 +3107,7 @@ export class SceneEntities {
 | 
			
		||||
        variables,
 | 
			
		||||
        kclManager: this.kclManager,
 | 
			
		||||
      })
 | 
			
		||||
      const callBacks: (() => SegmentOverlayPayload | null)[] = []
 | 
			
		||||
      const callbacks: (() => SegmentOverlayPayload | null)[] = []
 | 
			
		||||
      for (const sketchInfo of sketchesInfo) {
 | 
			
		||||
        const { sketch, pathToNode: _pathToNode } = sketchInfo
 | 
			
		||||
        const varDecIndex = Number(_pathToNode[1][0])
 | 
			
		||||
@ -3106,7 +3127,19 @@ export class SceneEntities {
 | 
			
		||||
          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) =>
 | 
			
		||||
            this.updateSegment(
 | 
			
		||||
              group,
 | 
			
		||||
@ -3120,7 +3153,7 @@ export class SceneEntities {
 | 
			
		||||
          )
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
      this.sceneInfra.overlayCallbacks(callBacks)
 | 
			
		||||
      this.sceneInfra.overlayCallbacks(callbacks)
 | 
			
		||||
    })().catch(reportRejection)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -47,6 +47,7 @@ import type {
 | 
			
		||||
  MouseState,
 | 
			
		||||
  SegmentOverlayPayload,
 | 
			
		||||
} from '@src/machines/modelingMachine'
 | 
			
		||||
import { PROFILE_START } from '@src/clientSideScene/sceneConstants'
 | 
			
		||||
 | 
			
		||||
type SendType = ReturnType<typeof useModelingContext>['send']
 | 
			
		||||
 | 
			
		||||
@ -230,7 +231,10 @@ export class SceneInfra {
 | 
			
		||||
      // Project that position to screen space
 | 
			
		||||
      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 y = (-vector.y * 0.5 + 0.5) * window.innerHeight
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,7 @@ import {
 | 
			
		||||
  applyConstraintLength,
 | 
			
		||||
} from '@src/components/Toolbar/setAngleLength'
 | 
			
		||||
import {
 | 
			
		||||
  SEGMENT_BODIES,
 | 
			
		||||
  SEGMENT_BODIES_PLUS_PROFILE_START,
 | 
			
		||||
  getParentGroup,
 | 
			
		||||
} from '@src/clientSideScene/sceneConstants'
 | 
			
		||||
import { useFileContext } from '@src/hooks/useFileContext'
 | 
			
		||||
@ -222,14 +222,15 @@ export const ModelingMachineProvider = ({
 | 
			
		||||
          if (event.type !== 'Set mouse state') return {}
 | 
			
		||||
          const nextSegmentHoverMap = () => {
 | 
			
		||||
            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 pathToNodeString = JSON.stringify(pathToNode)
 | 
			
		||||
              if (!parent || !pathToNode) return context.segmentHoverMap
 | 
			
		||||
              if (context.segmentHoverMap[pathToNodeString] !== undefined)
 | 
			
		||||
                clearTimeout(
 | 
			
		||||
                  context.segmentHoverMap[JSON.stringify(pathToNode)]
 | 
			
		||||
                )
 | 
			
		||||
                clearTimeout(context.segmentHoverMap[pathToNodeString])
 | 
			
		||||
              return {
 | 
			
		||||
                ...context.segmentHoverMap,
 | 
			
		||||
                [pathToNodeString]: 0,
 | 
			
		||||
@ -240,7 +241,7 @@ export const ModelingMachineProvider = ({
 | 
			
		||||
            ) {
 | 
			
		||||
              const mouseOnParent = getParentGroup(
 | 
			
		||||
                context.mouseState.on,
 | 
			
		||||
                SEGMENT_BODIES
 | 
			
		||||
                SEGMENT_BODIES_PLUS_PROFILE_START
 | 
			
		||||
              )
 | 
			
		||||
              if (!mouseOnParent || !mouseOnParent?.userData?.pathToNode)
 | 
			
		||||
                return context.segmentHoverMap
 | 
			
		||||
@ -276,10 +277,11 @@ export const ModelingMachineProvider = ({
 | 
			
		||||
        'Set Segment Overlays': assign({
 | 
			
		||||
          segmentOverlays: ({ context: { segmentOverlays }, event }) => {
 | 
			
		||||
            if (event.type !== 'Set Segment Overlays') return {}
 | 
			
		||||
            if (event.data.type === 'set-many')
 | 
			
		||||
            if (event.data.type === 'set-many') {
 | 
			
		||||
              return {
 | 
			
		||||
                ...event.data.overlays,
 | 
			
		||||
              }
 | 
			
		||||
            }
 | 
			
		||||
            if (event.data.type === 'set-one')
 | 
			
		||||
              return {
 | 
			
		||||
                ...segmentOverlays,
 | 
			
		||||
 | 
			
		||||
@ -28,6 +28,7 @@ export type ToolTip =
 | 
			
		||||
  | 'circleThreePoint'
 | 
			
		||||
  | 'arcTo'
 | 
			
		||||
  | 'arc'
 | 
			
		||||
  | 'startProfile'
 | 
			
		||||
 | 
			
		||||
export const toolTips: Array<ToolTip> = [
 | 
			
		||||
  'line',
 | 
			
		||||
@ -46,6 +47,7 @@ export const toolTips: Array<ToolTip> = [
 | 
			
		||||
  'circleThreePoint',
 | 
			
		||||
  'arc',
 | 
			
		||||
  'arcTo',
 | 
			
		||||
  'startProfile',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
interface ExecutionResult {
 | 
			
		||||
 | 
			
		||||
@ -1187,6 +1187,159 @@ export const tangentialArc: SketchLineHelperKw = {
 | 
			
		||||
    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 = {
 | 
			
		||||
  add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
 | 
			
		||||
    if (segmentInput.type !== 'arc-segment') return ARC_SEGMENT_ERR()
 | 
			
		||||
@ -3123,6 +3276,7 @@ export const sketchLineHelperMapKw: { [key: string]: SketchLineHelperKw } = {
 | 
			
		||||
  angledLineToX,
 | 
			
		||||
  angledLineToY,
 | 
			
		||||
  tangentialArc,
 | 
			
		||||
  startProfile,
 | 
			
		||||
} as const
 | 
			
		||||
 | 
			
		||||
export function changeSketchArguments(
 | 
			
		||||
@ -3215,6 +3369,7 @@ export function fnNameToToolTipFromSegment(
 | 
			
		||||
    case 'circle':
 | 
			
		||||
    case 'tangentialArc':
 | 
			
		||||
    case 'angledLine':
 | 
			
		||||
    case 'startProfile':
 | 
			
		||||
      return fnName
 | 
			
		||||
    default:
 | 
			
		||||
      const err = `Unknown sketch line function ${fnName}`
 | 
			
		||||
@ -3254,6 +3409,7 @@ export function fnNameToTooltip(
 | 
			
		||||
    case 'circleThreePoint':
 | 
			
		||||
    case 'circle':
 | 
			
		||||
    case 'tangentialArc':
 | 
			
		||||
    case 'startProfile':
 | 
			
		||||
      return fnName
 | 
			
		||||
    case 'angledLine': {
 | 
			
		||||
      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.
 | 
			
		||||
*/
 | 
			
		||||
@ -3916,6 +4094,8 @@ export function isAbsoluteLine(lineCall: CallExpressionKw): boolean | Error {
 | 
			
		||||
        findKwArgAny([ARG_END_ABSOLUTE_X, ARG_END_ABSOLUTE_Y], lineCall) !==
 | 
			
		||||
        undefined
 | 
			
		||||
      )
 | 
			
		||||
    case 'startProfile':
 | 
			
		||||
      return true
 | 
			
		||||
  }
 | 
			
		||||
  return new Error(`Unknown sketch function ${name}`)
 | 
			
		||||
}
 | 
			
		||||
@ -3946,6 +4126,8 @@ export function getArgForEnd(lineCall: CallExpressionKw):
 | 
			
		||||
      return getAngledLineThatIntersects(lineCall)
 | 
			
		||||
    case 'arc':
 | 
			
		||||
      return getArc(lineCall)
 | 
			
		||||
    case 'startProfile':
 | 
			
		||||
      return getStartProfile(lineCall)
 | 
			
		||||
    case 'yLine':
 | 
			
		||||
    case 'xLine': {
 | 
			
		||||
      const arg = findKwArgAny(DETERMINING_ARGS, lineCall)
 | 
			
		||||
 | 
			
		||||
@ -1648,7 +1648,7 @@ function getTransformMapPathKw(
 | 
			
		||||
      }
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
  if (name === 'startProfile') {
 | 
			
		||||
  if (name === 'startProfile' || name === 'startSketchOn') {
 | 
			
		||||
    return false
 | 
			
		||||
  }
 | 
			
		||||
  const tooltip = fnNameToTooltip(allLabels(sketchFnExp), name)
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
import type { Node } from '@rust/kcl-lib/bindings/Node'
 | 
			
		||||
 | 
			
		||||
import type {
 | 
			
		||||
  ARG_AT,
 | 
			
		||||
  ARG_END_ABSOLUTE,
 | 
			
		||||
  ARG_END_ABSOLUTE_X,
 | 
			
		||||
  ARG_END_ABSOLUTE_Y,
 | 
			
		||||
@ -116,6 +117,7 @@ export type InputArgKeys =
 | 
			
		||||
  | 'p2'
 | 
			
		||||
  | 'p3'
 | 
			
		||||
  | 'end'
 | 
			
		||||
  | typeof ARG_AT
 | 
			
		||||
  | typeof ARG_INTERIOR_ABSOLUTE
 | 
			
		||||
  | typeof ARG_END_ABSOLUTE
 | 
			
		||||
  | typeof ARG_END_ABSOLUTE_X
 | 
			
		||||
 | 
			
		||||
@ -314,7 +314,6 @@ export type ModelingMachineEvent =
 | 
			
		||||
      type: 'Delete selection'
 | 
			
		||||
    }
 | 
			
		||||
  | { type: 'Sketch no face' }
 | 
			
		||||
  | { type: 'Toggle gui mode' }
 | 
			
		||||
  | { type: 'Cancel'; cleanup?: () => void }
 | 
			
		||||
  | { type: 'CancelSketch' }
 | 
			
		||||
  | {
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user