104 lines
2.9 KiB
TypeScript
104 lines
2.9 KiB
TypeScript
import { describe, expect, mock, test } from 'bun:test'
|
|
|
|
import type { Fill, SceneNode } from '@open-pencil/scene-graph'
|
|
|
|
import { makeImageFillLocalMatrix, patternTileLayout } from '#core/canvas/fills'
|
|
import type { SkiaRenderer } from '#core/canvas/renderer'
|
|
|
|
function createRenderer() {
|
|
return {
|
|
ck: {
|
|
Matrix: {
|
|
identity: mock(() => ['identity']),
|
|
invert: mock((matrix) => ['invert', matrix]),
|
|
multiply: mock((...matrices) => ['multiply', ...matrices]),
|
|
scaled: mock((x, y) => ['scaled', x, y]),
|
|
translated: mock((x, y) => ['translated', x, y])
|
|
}
|
|
}
|
|
} as SkiaRenderer
|
|
}
|
|
|
|
const node = {
|
|
width: 120,
|
|
height: 80
|
|
} as SceneNode
|
|
|
|
describe('canvas pattern fills', () => {
|
|
test('uses scale, spacing, and alignment for rectangular pattern tiles', () => {
|
|
const layout = patternTileLayout(
|
|
{ width: 20, height: 10 } as SceneNode,
|
|
{
|
|
type: 'PATTERN',
|
|
scale: 2,
|
|
patternSpacing: { x: 0.25, y: 0.5 },
|
|
horizontalAlignment: 'CENTER',
|
|
verticalAlignment: 'END',
|
|
color: { r: 0, g: 0, b: 0, a: 1 },
|
|
opacity: 1,
|
|
visible: true
|
|
} as Fill
|
|
)
|
|
|
|
expect(layout).toEqual({
|
|
rect: { x: 0, y: 0, width: 50, height: 30 },
|
|
scale: 2,
|
|
positions: [{ x: -5, y: -10 }]
|
|
})
|
|
})
|
|
|
|
test('adds an offset source copy for hexagonal pattern tiles', () => {
|
|
const layout = patternTileLayout(
|
|
{ width: 20, height: 10 } as SceneNode,
|
|
{
|
|
type: 'PATTERN',
|
|
patternTileType: 'HORIZONTAL_HEXAGONAL',
|
|
patternSpacing: { x: 0, y: 0 },
|
|
color: { r: 0, g: 0, b: 0, a: 1 },
|
|
opacity: 1,
|
|
visible: true
|
|
} as Fill
|
|
)
|
|
|
|
expect(layout.positions).toEqual([
|
|
{ x: 0, y: 0 },
|
|
{ x: 10, y: 5 }
|
|
])
|
|
})
|
|
})
|
|
|
|
describe('canvas image fills', () => {
|
|
test('keeps untransformed tile fills in image pixel space', () => {
|
|
const renderer = createRenderer()
|
|
const fill = { type: 'IMAGE', imageScaleMode: 'TILE' } as Fill
|
|
|
|
const matrix = makeImageFillLocalMatrix(renderer, fill, node, 24, 16)
|
|
|
|
expect(matrix).toEqual(['identity'])
|
|
expect(renderer.ck.Matrix.identity).toHaveBeenCalled()
|
|
expect(renderer.ck.Matrix.scaled).not.toHaveBeenCalled()
|
|
})
|
|
|
|
test('uses the full imported affine transform for patterned image fills', () => {
|
|
const renderer = createRenderer()
|
|
const transform = { m00: 0.5, m01: 0.1, m02: 0.25, m10: -0.2, m11: 0.25, m12: 0.5 }
|
|
const fill = {
|
|
type: 'IMAGE',
|
|
imageScaleMode: 'TILE',
|
|
imageTransform: transform
|
|
} as Fill
|
|
|
|
const matrix = makeImageFillLocalMatrix(renderer, fill, node, 40, 40)
|
|
|
|
expect(renderer.ck.Matrix.invert).toHaveBeenCalledWith([
|
|
0.5, 0.1, 0.25, -0.2, 0.25, 0.5, 0, 0, 1
|
|
])
|
|
expect(matrix).toEqual([
|
|
'multiply',
|
|
['scaled', 120, 80],
|
|
['invert', [0.5, 0.1, 0.25, -0.2, 0.25, 0.5, 0, 0, 1]],
|
|
['scaled', 0.025, 0.025]
|
|
])
|
|
})
|
|
})
|