* feat(scene-graph)!: make a stroke a paint Stroke extends Fill, so a stroke carries the same paint vocabulary a fill does instead of a lone color. Every construction site now states a solid paint type, which keeps today's behaviour exactly; rendering, .fig conversion, and the stroke panel still read solid strokes only. copyFill is generic over the paint shape so copyStroke reuses it rather than repeating the deep copy of gradient stops, transforms and pattern fields. Groundwork for the gradient and image strokes in #797. * test(scene-graph): cover a stroke's nested paint data in copyStroke A stroke is a paint now, so its gradient stops and transform must copy as deeply as a fill's; the fixture was solid and proved only the color and dash pattern.
116 lines
3.6 KiB
TypeScript
116 lines
3.6 KiB
TypeScript
import { expect, test, useEditorSetupWithClear } from '#tests/e2e/fixtures'
|
|
|
|
const editor = useEditorSetupWithClear('/?test&no-chrome&no-rulers')
|
|
|
|
test('mixed vector regions keep open stroke segments', async () => {
|
|
await editor.page.evaluate(() => {
|
|
const store = window.openPencil?.getStore?.()
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
store.graph.createNode('VECTOR', store.state.currentPageId, {
|
|
name: 'Mixed region and open segment',
|
|
x: 160,
|
|
y: 110,
|
|
width: 300,
|
|
height: 160,
|
|
fills: [
|
|
{
|
|
type: 'SOLID',
|
|
color: { r: 0.23, g: 0.51, b: 0.96, a: 1 },
|
|
visible: true,
|
|
opacity: 1
|
|
}
|
|
],
|
|
strokes: [
|
|
{
|
|
type: 'SOLID',
|
|
color: { r: 0.95, g: 0.25, b: 0.15, a: 1 },
|
|
weight: 8,
|
|
opacity: 1,
|
|
visible: true,
|
|
align: 'CENTER'
|
|
}
|
|
],
|
|
vectorNetwork: {
|
|
vertices: [
|
|
{ x: 0, y: 0 },
|
|
{ x: 120, y: 0 },
|
|
{ x: 120, y: 120 },
|
|
{ x: 0, y: 120 },
|
|
{ x: 170, y: 60 },
|
|
{ x: 300, y: 60 }
|
|
],
|
|
segments: [
|
|
{ start: 0, end: 1, tangentStart: { x: 0, y: 0 }, tangentEnd: { x: 0, y: 0 } },
|
|
{ start: 1, end: 2, tangentStart: { x: 0, y: 0 }, tangentEnd: { x: 0, y: 0 } },
|
|
{ start: 2, end: 3, tangentStart: { x: 0, y: 0 }, tangentEnd: { x: 0, y: 0 } },
|
|
{ start: 3, end: 0, tangentStart: { x: 0, y: 0 }, tangentEnd: { x: 0, y: 0 } },
|
|
{ start: 4, end: 5, tangentStart: { x: 0, y: 0 }, tangentEnd: { x: 0, y: 0 } }
|
|
],
|
|
regions: [{ windingRule: 'NONZERO', loops: [[0, 1, 2, 3]] }]
|
|
}
|
|
})
|
|
store.clearSelection()
|
|
store.requestRender()
|
|
})
|
|
await editor.canvas.waitForRender()
|
|
editor.canvas.assertNoErrors()
|
|
const buffer = await editor.canvas.screenshotCanvasRegion()
|
|
expect(buffer).toMatchSnapshot('mixed-region-open-segment.png')
|
|
})
|
|
|
|
test('even-odd vector geometry preserves holes', async () => {
|
|
await editor.page.evaluate(() => {
|
|
const store = window.openPencil?.getStore?.()
|
|
if (!store) throw new Error('OpenPencil store not initialized')
|
|
const bytes: number[] = []
|
|
const pushFloat = (value: number) => {
|
|
const buffer = new ArrayBuffer(4)
|
|
new DataView(buffer).setFloat32(0, value, true)
|
|
bytes.push(...new Uint8Array(buffer))
|
|
}
|
|
const moveTo = (x: number, y: number) => {
|
|
bytes.push(1)
|
|
pushFloat(x)
|
|
pushFloat(y)
|
|
}
|
|
const lineTo = (x: number, y: number) => {
|
|
bytes.push(2)
|
|
pushFloat(x)
|
|
pushFloat(y)
|
|
}
|
|
const rectangle = (x: number, y: number, width: number, height: number) => {
|
|
moveTo(x, y)
|
|
lineTo(x + width, y)
|
|
lineTo(x + width, y + height)
|
|
lineTo(x, y + height)
|
|
bytes.push(0)
|
|
}
|
|
rectangle(0, 0, 240, 144)
|
|
rectangle(28, 28, 184, 88)
|
|
rectangle(48, 48, 144, 12)
|
|
|
|
store.graph.createNode('VECTOR', store.state.currentPageId, {
|
|
name: 'Even-odd card outline',
|
|
x: 160,
|
|
y: 110,
|
|
width: 240,
|
|
height: 144,
|
|
fills: [
|
|
{
|
|
type: 'SOLID',
|
|
color: { r: 0.23, g: 0.51, b: 0.96, a: 1 },
|
|
visible: true,
|
|
opacity: 1
|
|
}
|
|
],
|
|
fillGeometry: [{ windingRule: 'EVENODD', commandsBlob: new Uint8Array(bytes) }]
|
|
})
|
|
store.clearSelection()
|
|
store.requestRender()
|
|
})
|
|
await editor.canvas.waitForRender()
|
|
editor.canvas.assertNoErrors()
|
|
const buffer = await editor.canvas.screenshotCanvasRegion()
|
|
expect(buffer).toMatchSnapshot('even-odd-vector-holes.png')
|
|
})
|