Camera view frame, viewport clipping, overlay layer for editor symbols

- Camera assets get width/height properties; play mode renders only the
  camera frame (letterboxed, masked, culled) and nothing without a camera
- Remove demo arrow-key/camera-follow systems from play mode
- Clip world and gizmos to the viewport (selection frame no longer leaks
  over the left panel)
- Blur focused widget on pointerdown outside it so inspector edits commit
  before selection changes
- Separate objects/overlay layers so camera symbols always render on top
- Add tools/seed-demo.mjs for reproducible E2E runs

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
rootandClaude Fable 5.1 committed 2026-09-07 11:13:08 +02:00
1 parent 5a46124454
commit aaa8154adf
11 files changed
+307 -142

No files matched your search

+28
View File
@@ -0,0 +1,28 @@
import type { PropertyBag } from '../model/Properties';
import { defaultsFor } from './PropertySchema';
/** Axis-aligned rectangle in world units. */
export interface WorldRect {
left: number;
top: number;
right: number;
bottom: number;
}
/**
* Size of the world region a camera shows, in world units: its view size divided by its
* zoom. This is the frame drawn in edit mode and the only region rendered in play mode.
*/
export function cameraFrame(props: PropertyBag): { w: number; h: number } {
const d = defaultsFor('camera');
const zoom = Math.max(0.01, Number(props.zoom ?? d.zoom));
return { w: Number(props.width ?? d.width) / zoom, h: Number(props.height ?? d.height) / zoom };
}
export function rectAround(cx: number, cy: number, w: number, h: number): WorldRect {
return { left: cx - w / 2, top: cy - h / 2, right: cx + w / 2, bottom: cy + h / 2 };
}
export function rectsIntersect(a: WorldRect, b: WorldRect): boolean {
return a.left < b.right && a.right > b.left && a.top < b.bottom && a.bottom > b.top;
}