fix(frontend): camera shows sidelines and house above controls
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
parent
20c592d8ac
commit
2eb5760922
@ -31,11 +31,19 @@ export interface Renderer {
|
|||||||
|
|
||||||
export function createRenderer(canvas: HTMLCanvasElement): Renderer {
|
export function createRenderer(canvas: HTMLCanvasElement): Renderer {
|
||||||
const ctx = canvas.getContext('2d')!
|
const ctx = canvas.getContext('2d')!
|
||||||
// Default viewport: roughly 14 ft (≈ 4.27 m) wide, centered on the house.
|
// Fit full sheet width (5 m) with a little side padding so both sidelines show.
|
||||||
// Height is derived from the screen aspect ratio so the whole sheet is visible.
|
// Scale is primarily width-based, but also capped so house + backline fit above the HUD.
|
||||||
const viewportWidth = 14.0 / 3.28084
|
const SIDE_PADDING_M = 0.25
|
||||||
|
const viewportWidth = SHEET_WIDTH + SIDE_PADDING_M * 2
|
||||||
|
// Min world height: top of 12ft ring through backline + small pad.
|
||||||
|
const MIN_VIEW_HEIGHT_M =
|
||||||
|
BACK_LINE_Y - (HOUSE_CENTER.y - TWELVE_FT_RADIUS) + SIDE_PADDING_M
|
||||||
|
// Throw button (80) + hud padding (12×2) + small gap above controls.
|
||||||
|
const BOTTOM_UI_PX = 120
|
||||||
const viewportCenter = { x: 0, y: HOUSE_CENTER.y }
|
const viewportCenter = { x: 0, y: HOUSE_CENTER.y }
|
||||||
let viewportYOffset = 0
|
// Prefer backline at the bottom of the usable ice area (just above HUD).
|
||||||
|
// First setSize()/setViewYOffset clamps this to the valid minOffset.
|
||||||
|
let viewportYOffset = -(BACK_LINE_Y - HOUSE_CENTER.y)
|
||||||
|
|
||||||
const setSize = () => {
|
const setSize = () => {
|
||||||
const dpr = window.devicePixelRatio || 1
|
const dpr = window.devicePixelRatio || 1
|
||||||
@ -46,14 +54,29 @@ export function createRenderer(canvas: HTMLCanvasElement): Renderer {
|
|||||||
canvas.style.width = `${width}px`
|
canvas.style.width = `${width}px`
|
||||||
canvas.style.height = `${height}px`
|
canvas.style.height = `${height}px`
|
||||||
ctx.setTransform(dpr, 0, 0, dpr, 0, 0)
|
ctx.setTransform(dpr, 0, 0, dpr, 0, 0)
|
||||||
|
// Re-clamp so default framing keeps the backline just above the HUD.
|
||||||
|
setViewYOffset(viewportYOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Screen height available for ice (above bottom controls). */
|
||||||
|
const usableHeightPx = (): number => {
|
||||||
|
return Math.max(1, window.innerHeight - BOTTOM_UI_PX)
|
||||||
}
|
}
|
||||||
|
|
||||||
const scale = (): number => {
|
const scale = (): number => {
|
||||||
return window.innerWidth / viewportWidth
|
const byWidth = window.innerWidth / viewportWidth
|
||||||
|
const byHeight = usableHeightPx() / MIN_VIEW_HEIGHT_M
|
||||||
|
// Width fit for sidelines; height cap so house/backline stay on-screen on wide displays.
|
||||||
|
return Math.min(byWidth, byHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewportHeight = (): number => {
|
const viewportHeight = (): number => {
|
||||||
return window.innerHeight / scale()
|
return usableHeightPx() / scale()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Vertical center of the usable ice area (not the full window). */
|
||||||
|
const viewCenterY = (): number => {
|
||||||
|
return usableHeightPx() / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
const effectiveViewportCenter = () => ({
|
const effectiveViewportCenter = () => ({
|
||||||
@ -64,7 +87,7 @@ export function createRenderer(canvas: HTMLCanvasElement): Renderer {
|
|||||||
const worldToScreen = (x: number, y: number) => {
|
const worldToScreen = (x: number, y: number) => {
|
||||||
const s = scale()
|
const s = scale()
|
||||||
const cx = window.innerWidth / 2
|
const cx = window.innerWidth / 2
|
||||||
const cy = window.innerHeight / 2
|
const cy = viewCenterY()
|
||||||
const center = effectiveViewportCenter()
|
const center = effectiveViewportCenter()
|
||||||
return {
|
return {
|
||||||
x: cx + (x - center.x) * s,
|
x: cx + (x - center.x) * s,
|
||||||
@ -75,7 +98,7 @@ export function createRenderer(canvas: HTMLCanvasElement): Renderer {
|
|||||||
const screenToWorld = (sx: number, sy: number) => {
|
const screenToWorld = (sx: number, sy: number) => {
|
||||||
const s = scale()
|
const s = scale()
|
||||||
const cx = window.innerWidth / 2
|
const cx = window.innerWidth / 2
|
||||||
const cy = window.innerHeight / 2
|
const cy = viewCenterY()
|
||||||
const center = effectiveViewportCenter()
|
const center = effectiveViewportCenter()
|
||||||
return {
|
return {
|
||||||
x: (sx - cx) / s + center.x,
|
x: (sx - cx) / s + center.x,
|
||||||
@ -85,6 +108,8 @@ export function createRenderer(canvas: HTMLCanvasElement): Renderer {
|
|||||||
|
|
||||||
const clampViewYOffset = (): number => {
|
const clampViewYOffset = (): number => {
|
||||||
// Positive offset pans the view upward (toward the hog line).
|
// Positive offset pans the view upward (toward the hog line).
|
||||||
|
// When the usable viewport is tall enough, minOffset pins BACK_LINE_Y to the
|
||||||
|
// bottom of the usable area (just above the HUD), not under the controls.
|
||||||
const halfHeight = viewportHeight() / 2
|
const halfHeight = viewportHeight() / 2
|
||||||
const maxOffset = HOUSE_CENTER.y - halfHeight - HOG_LINE_Y
|
const maxOffset = HOUSE_CENTER.y - halfHeight - HOG_LINE_Y
|
||||||
const minOffset = -(BACK_LINE_Y - (HOUSE_CENTER.y + halfHeight))
|
const minOffset = -(BACK_LINE_Y - (HOUSE_CENTER.y + halfHeight))
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user