Fix: Opposite adjacent edge selection (#3896)

* fix opposite adjacent edge selection

* make test more robust
This commit is contained in:
Kurt Hutten
2024-09-17 05:38:58 +10:00
committed by GitHub
parent 0ff820d4da
commit 8d48c17395
3 changed files with 214 additions and 19 deletions

View File

@ -441,6 +441,34 @@ export async function getUtils(page: Page, test_?: typeof test) {
} }
return maxDiff return maxDiff
}, },
getPixelRGBs: async (
coords: { x: number; y: number },
radius: number
): Promise<[number, number, number][]> => {
const buffer = await page.screenshot({
fullPage: true,
})
const screenshot = await PNG.sync.read(buffer)
const pixMultiplier: number = await page.evaluate(
'window.devicePixelRatio'
)
const allCords: [number, number][] = [[coords.x, coords.y]]
for (let i = 1; i < radius; i++) {
allCords.push([coords.x + i, coords.y])
allCords.push([coords.x - i, coords.y])
allCords.push([coords.x, coords.y + i])
allCords.push([coords.x, coords.y - i])
}
return allCords.map(([x, y]) => {
const index =
(screenshot.width * y * pixMultiplier + x * pixMultiplier) * 4 // rbga is 4 channels
return [
screenshot.data[index],
screenshot.data[index + 1],
screenshot.data[index + 2],
]
})
},
doAndWaitForImageDiff: (fn: () => Promise<unknown>, diffCount = 200) => doAndWaitForImageDiff: (fn: () => Promise<unknown>, diffCount = 200) =>
new Promise<boolean>((resolve) => { new Promise<boolean>((resolve) => {
;(async () => { ;(async () => {

View File

@ -528,11 +528,22 @@ const sketch002 = startSketchOn(launderExtrudeThroughVar, seg02)
await page.waitForTimeout(100) await page.waitForTimeout(100)
await u.closeDebugPanel() await u.closeDebugPanel()
const extrusionTop: Coords2d = [800, 240] const extrusionTopCap: Coords2d = [800, 240]
const flatExtrusionFace: Coords2d = [960, 160] const flatExtrusionFace: Coords2d = [960, 160]
const arc: Coords2d = [840, 160] const tangentialArcTo: Coords2d = [840, 160]
const close: Coords2d = [720, 200] const close: Coords2d = [720, 200]
const nothing: Coords2d = [600, 200] const nothing: Coords2d = [600, 200]
const closeEdge: Coords2d = [744, 233]
const closeAdjacentEdge: Coords2d = [688, 123]
const closeOppositeEdge: Coords2d = [687, 169]
const tangentialArcEdge: Coords2d = [811, 142]
const tangentialArcOppositeEdge: Coords2d = [820, 180]
const tangentialArcAdjacentEdge: Coords2d = [893, 165]
const straightSegmentEdge: Coords2d = [819, 369]
const straightSegmentOppositeEdge: Coords2d = [635, 394]
const straightSegmentAdjacentEdge: Coords2d = [679, 329]
await page.mouse.move(nothing[0], nothing[1]) await page.mouse.move(nothing[0], nothing[1])
await page.mouse.click(nothing[0], nothing[1]) await page.mouse.click(nothing[0], nothing[1])
@ -540,26 +551,141 @@ const sketch002 = startSketchOn(launderExtrudeThroughVar, seg02)
await expect(page.getByTestId('hover-highlight')).not.toBeVisible() await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
await page.waitForTimeout(200) await page.waitForTimeout(200)
await page.mouse.move(extrusionTop[0], extrusionTop[1]) const checkCodeAtHoverPosition = async (
await expect(page.getByTestId('hover-highlight').first()).toBeVisible() name = '',
await page.mouse.move(nothing[0], nothing[1]) coord: Coords2d,
await expect(page.getByTestId('hover-highlight').first()).not.toBeVisible() highlightCode: string,
activeLine = highlightCode
) => {
await test.step(`test selection for: ${name}`, async () => {
const highlightedLocator = page.getByTestId('hover-highlight')
const activeLineLocator = page.locator('.cm-activeLine')
await page.mouse.move(arc[0], arc[1]) await test.step(`hover should highlight correct code`, async () => {
await expect(page.getByTestId('hover-highlight').first()).toBeVisible() await page.mouse.move(coord[0], coord[1])
await page.mouse.move(nothing[0], nothing[1]) await expect(highlightedLocator.first()).toBeVisible()
await expect(page.getByTestId('hover-highlight').first()).not.toBeVisible() await expect
.poll(async () => {
const textContents = await highlightedLocator.allTextContents()
return textContents.join('').replace(/\s+/g, '')
})
.toBe(highlightCode)
await page.mouse.move(nothing[0], nothing[1])
})
await test.step(`click should put the cursor in the right place`, async () => {
await expect(highlightedLocator.first()).not.toBeVisible()
await page.mouse.click(coord[0], coord[1])
await expect
.poll(async () => {
const activeLines = await activeLineLocator.allInnerTexts()
return activeLines.join('')
})
.toContain(activeLine)
// check pixels near the click location are yellow
})
await test.step(`check the engine agrees with selections`, async () => {
// ultimately the only way we know if the engine agrees with the selection from the FE
// perspective is if it highlights the pixels near where we clicked yellow.
await expect
.poll(async () => {
const RGBs = await u.getPixelRGBs({ x: coord[0], y: coord[1] }, 3)
for (const rgb of RGBs) {
const [r, g, b] = rgb
const RGAverage = (r + g) / 2
const isRedGreenSameIsh = Math.abs(r - g) < 3
const isBlueLessThanRG = RGAverage - b > 45
const isYellowy = isRedGreenSameIsh && isBlueLessThanRG
if (isYellowy) return true
}
return false
})
.toBeTruthy()
await page.mouse.click(nothing[0], nothing[1])
})
})
}
await page.mouse.move(close[0], close[1]) await checkCodeAtHoverPosition(
await expect(page.getByTestId('hover-highlight').first()).toBeVisible() 'extrusionTopCap',
await page.mouse.move(nothing[0], nothing[1]) extrusionTopCap,
await expect(page.getByTestId('hover-highlight').first()).not.toBeVisible() 'startProfileAt([20,0],%)',
'startProfileAt([20, 0], %)'
)
await checkCodeAtHoverPosition(
'flatExtrusionFace',
flatExtrusionFace,
`angledLineThatIntersects({angle:3.14,intersectTag:a,offset:0},%)extrude(5+7,%)`,
'}, %)'
)
await page.mouse.move(flatExtrusionFace[0], flatExtrusionFace[1]) await checkCodeAtHoverPosition(
await expect(page.getByTestId('hover-highlight')).toHaveCount(6) // multiple lines 'tangentialArcTo',
await page.mouse.move(nothing[0], nothing[1]) tangentialArcTo,
await page.waitForTimeout(100) 'tangentialArcTo([13.14+0,13.14],%)extrude(5+7,%)',
await expect(page.getByTestId('hover-highlight').first()).not.toBeVisible() 'tangentialArcTo([13.14 + 0, 13.14], %)'
)
await checkCodeAtHoverPosition(
'tangentialArcEdge',
tangentialArcEdge,
`tangentialArcTo([13.14+0,13.14],%)`,
'tangentialArcTo([13.14 + 0, 13.14], %)'
)
await checkCodeAtHoverPosition(
'tangentialArcOppositeEdge',
tangentialArcOppositeEdge,
`tangentialArcTo([13.14+0,13.14],%)`,
'tangentialArcTo([13.14 + 0, 13.14], %)'
)
await checkCodeAtHoverPosition(
'tangentialArcAdjacentEdge',
tangentialArcAdjacentEdge,
`tangentialArcTo([13.14+0,13.14],%)`,
'tangentialArcTo([13.14 + 0, 13.14], %)'
)
await checkCodeAtHoverPosition(
'close',
close,
'close(%)extrude(5+7,%)',
'close(%)'
)
await checkCodeAtHoverPosition(
'closeEdge',
closeEdge,
`close(%)`,
'close(%)'
)
await checkCodeAtHoverPosition(
'closeAdjacentEdge',
closeAdjacentEdge,
`close(%)`,
'close(%)'
)
await checkCodeAtHoverPosition(
'closeOppositeEdge',
closeOppositeEdge,
`close(%)`,
'close(%)'
)
await checkCodeAtHoverPosition(
'straightSegmentEdge',
straightSegmentEdge,
`angledLineToY({angle:30,to:11.14},%)`,
'angledLineToY({ angle: 30, to: 11.14 }, %)'
)
await checkCodeAtHoverPosition(
'straightSegmentOppositeEdge',
straightSegmentOppositeEdge,
`angledLineToY({angle:30,to:11.14},%)`,
'angledLineToY({ angle: 30, to: 11.14 }, %)'
)
await checkCodeAtHoverPosition(
'straightSegmentAdjancentEdge',
straightSegmentAdjacentEdge,
`angledLineToY({angle:30,to:11.14},%)`,
'angledLineToY({ angle: 30, to: 11.14 }, %)'
)
}) })
test("Extrude button should be disabled if there's no extrudable geometry when nothing is selected", async ({ test("Extrude button should be disabled if there's no extrudable geometry when nothing is selected", async ({
page, page,

View File

@ -52,6 +52,7 @@ export type Selection = {
| 'end-cap' | 'end-cap'
| 'point' | 'point'
| 'edge' | 'edge'
| 'adjacent-edge'
| 'line' | 'line'
| 'arc' | 'arc'
| 'all' | 'all'
@ -146,6 +147,15 @@ export async function getEventForSelectWithPoint({
engineCommandManager.artifactGraph engineCommandManager.artifactGraph
) )
if (err(codeRef)) return null if (err(codeRef)) return null
if (_artifact?.subType === 'adjacent') {
return {
type: 'Set selection',
data: {
selectionType: 'singleCodeCursor',
selection: { range: codeRef.range, type: 'adjacent-edge' },
},
}
}
return { return {
type: 'Set selection', type: 'Set selection',
data: { data: {
@ -557,6 +567,37 @@ function codeToIdSelections(
} }
return return
} }
if (type === 'edge' && entry.artifact.type === 'segment') {
const edges = getArtifactsOfTypes(
{ keys: entry.artifact.edgeIds, types: ['extrudeEdge'] },
engineCommandManager.artifactGraph
)
const edge = [...edges].find(
([_, edge]) => edge.type === 'extrudeEdge'
)
if (!edge) return
bestCandidate = {
artifact: edge[1],
selection: { type, range, ...rest },
id: edge[0],
}
}
if (type === 'adjacent-edge' && entry.artifact.type === 'segment') {
const edges = getArtifactsOfTypes(
{ keys: entry.artifact.edgeIds, types: ['extrudeEdge'] },
engineCommandManager.artifactGraph
)
const edge = [...edges].find(
([_, edge]) =>
edge.type === 'extrudeEdge' && edge.subType === 'adjacent'
)
if (!edge) return
bestCandidate = {
artifact: edge[1],
selection: { type, range, ...rest },
id: edge[0],
}
}
if ( if (
(type === 'end-cap' || type === 'start-cap') && (type === 'end-cap' || type === 'start-cap') &&
entry.artifact.type === 'path' entry.artifact.type === 'path'