mirror of
https://github.com/Boof2015/prism.git
synced 2026-08-18 19:44:16 +02:00
centering
This commit is contained in:
@@ -151,13 +151,16 @@ export function resolveVUNeedleFaceLayout(
|
|||||||
const targetHeight = VU_NEEDLE_FACE_HEIGHT_CSS_PX * dpr
|
const targetHeight = VU_NEEDLE_FACE_HEIGHT_CSS_PX * dpr
|
||||||
const safeWidth = Math.max(0, Number.isFinite(canvasWidth) ? canvasWidth : 0)
|
const safeWidth = Math.max(0, Number.isFinite(canvasWidth) ? canvasWidth : 0)
|
||||||
const safeHeight = Math.max(0, Number.isFinite(canvasHeight) ? canvasHeight : 0)
|
const safeHeight = Math.max(0, Number.isFinite(canvasHeight) ? canvasHeight : 0)
|
||||||
const scale = Math.min(1, safeWidth / targetWidth, safeHeight / targetHeight)
|
const widthScale = safeWidth / targetWidth
|
||||||
|
const heightScale = safeHeight / targetHeight
|
||||||
|
const scale = Math.min(1, widthScale, heightScale)
|
||||||
const width = targetWidth * scale
|
const width = targetWidth * scale
|
||||||
const height = targetHeight * scale
|
const height = targetHeight * scale
|
||||||
|
const widthLimited = widthScale < Math.min(1, heightScale)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x: Math.max(0, (safeWidth - width) / 2),
|
x: Math.max(0, (safeWidth - width) / 2),
|
||||||
y: Math.max(0, safeHeight - height),
|
y: Math.max(0, widthLimited ? (safeHeight - height) / 2 : safeHeight - height),
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
scale,
|
scale,
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ export interface VectorscopeLayout {
|
|||||||
/**
|
/**
|
||||||
* Compute the center point and radius for a vectorscope mode.
|
* Compute the center point and radius for a vectorscope mode.
|
||||||
*
|
*
|
||||||
* Unipolar modes place the center at the bottom of the canvas so the
|
* Unipolar modes place the center at the bottom when height-limited. When
|
||||||
* semicircle/triangle fills the full vertical space.
|
* width-limited, the visible semicircle/triangle is centered vertically.
|
||||||
* Bipolar and Lissajous center in the canvas.
|
* Bipolar and Lissajous center in the canvas.
|
||||||
*/
|
*/
|
||||||
export function getVectorscopeLayout(
|
export function getVectorscopeLayout(
|
||||||
@@ -27,10 +27,14 @@ export function getVectorscopeLayout(
|
|||||||
const isUnipolar = mode === 'polar-unipolar' || mode === 'linear-unipolar'
|
const isUnipolar = mode === 'polar-unipolar' || mode === 'linear-unipolar'
|
||||||
|
|
||||||
if (isUnipolar) {
|
if (isUnipolar) {
|
||||||
// Center near the bottom; radius fills upward
|
|
||||||
const margin = height * 0.04
|
const margin = height * 0.04
|
||||||
const centerY = height - margin
|
const availableHeight = height - margin
|
||||||
const radius = Math.min(width / 2, height - margin) * 0.88
|
const halfWidth = width / 2
|
||||||
|
const widthLimited = halfWidth < availableHeight
|
||||||
|
const radius = Math.min(halfWidth, availableHeight) * 0.88
|
||||||
|
const centerY = widthLimited
|
||||||
|
? (height + radius) / 2
|
||||||
|
: availableHeight
|
||||||
return { centerX, centerY, radius }
|
return { centerX, centerY, radius }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3385,6 +3385,33 @@ test('Vectorscope uses the base layout radius for projection scale', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Vectorscope centers width-constrained unipolar artwork vertically', () => {
|
||||||
|
for (const mode of ['polar-unipolar', 'linear-unipolar'] as const) {
|
||||||
|
const layout = getVectorscopeLayout(120, 300, mode)
|
||||||
|
const artworkTop = layout.centerY - layout.radius
|
||||||
|
const artworkBottom = layout.centerY
|
||||||
|
|
||||||
|
assertAlmostEqual(
|
||||||
|
(artworkTop + artworkBottom) / 2,
|
||||||
|
150,
|
||||||
|
1e-12,
|
||||||
|
`${mode} artwork should center in a narrow canvas`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Vectorscope preserves height-limited and bipolar layout behavior', () => {
|
||||||
|
const wideUnipolar = getVectorscopeLayout(600, 200, 'polar-unipolar')
|
||||||
|
assertAlmostEqual(wideUnipolar.centerY, 192, 1e-12, 'wide unipolar layout should remain bottom-aligned')
|
||||||
|
assertAlmostEqual(wideUnipolar.radius, 192 * 0.88, 1e-12, 'wide unipolar radius should remain height-limited')
|
||||||
|
|
||||||
|
for (const mode of ['lissajous', 'polar-bipolar', 'linear-bipolar'] as const) {
|
||||||
|
const layout = getVectorscopeLayout(120, 300, mode)
|
||||||
|
assertAlmostEqual(layout.centerY, 150, 1e-12, `${mode} should remain centered`)
|
||||||
|
assertAlmostEqual(layout.radius, 54, 1e-12, `${mode} radius should remain unchanged`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
test('vectorscope adds a subtle dashed outer boundary without changing the base graph', () => {
|
test('vectorscope adds a subtle dashed outer boundary without changing the base graph', () => {
|
||||||
const lissajousRecorder = createFakeCanvasRecorder()
|
const lissajousRecorder = createFakeCanvasRecorder()
|
||||||
drawVectorscopeGridForMode(
|
drawVectorscopeGridForMode(
|
||||||
@@ -3566,12 +3593,23 @@ test('VUMeter needle face layout stays fixed instead of scaling up', () => {
|
|||||||
const retinaWide = resolveVUNeedleFaceLayout(2400, 1000, 2)
|
const retinaWide = resolveVUNeedleFaceLayout(2400, 1000, 2)
|
||||||
assert.equal(retinaWide.width, VU_NEEDLE_FACE_WIDTH_CSS_PX * 2)
|
assert.equal(retinaWide.width, VU_NEEDLE_FACE_WIDTH_CSS_PX * 2)
|
||||||
assert.equal(retinaWide.height, VU_NEEDLE_FACE_HEIGHT_CSS_PX * 2)
|
assert.equal(retinaWide.height, VU_NEEDLE_FACE_HEIGHT_CSS_PX * 2)
|
||||||
|
assert.equal(retinaWide.y, 1000 - VU_NEEDLE_FACE_HEIGHT_CSS_PX * 2)
|
||||||
assert.equal(retinaWide.scale, 1)
|
assert.equal(retinaWide.scale, 1)
|
||||||
|
|
||||||
const small = resolveVUNeedleFaceLayout(280, 180)
|
const small = resolveVUNeedleFaceLayout(280, 180)
|
||||||
assert.equal(small.width, 280)
|
assert.equal(small.width, 280)
|
||||||
assert.equal(small.height, 180)
|
assert.equal(small.height, 180)
|
||||||
assertAlmostEqual(small.scale, 0.5, 1e-12, 'fixed face should only shrink when the canvas is smaller')
|
assertAlmostEqual(small.scale, 0.5, 1e-12, 'fixed face should only shrink when the canvas is smaller')
|
||||||
|
|
||||||
|
const narrow = resolveVUNeedleFaceLayout(280, 360)
|
||||||
|
assert.equal(narrow.width, 280)
|
||||||
|
assert.equal(narrow.height, 180)
|
||||||
|
assert.equal(narrow.y, 90)
|
||||||
|
|
||||||
|
const retinaNarrow = resolveVUNeedleFaceLayout(560, 720, 2)
|
||||||
|
assert.equal(retinaNarrow.width, 560)
|
||||||
|
assert.equal(retinaNarrow.height, 360)
|
||||||
|
assert.equal(retinaNarrow.y, 180)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('VUMeter shared needle helpers switch between stereo needles and combined RMS', () => {
|
test('VUMeter shared needle helpers switch between stereo needles and combined RMS', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user