/- Topolei.Obs.Ctx =============== C2 of the categories-from-the-interface stack: **the observation category for peripherals.** Observations are typed peripheral configurations — they describe *what surface the rendering lands on*, not the rendering itself. Window dimensions, pixel format, eventually GPU adapter / mouse position / clock tick. None of these are transports in the cubical sense (cells-spec §1.7 calls them "sealed cells whose interior cannot be deformed from above"); but they do organise themselves into a category, and that category is what every transport in the cell calculus eventually has to live over. ## What this file proves We define `Ctx` as a record (the typed peripheral) and `resize` / `reformat` as the basic ops. We prove the *laws* that say these ops compose like morphisms in a category: - **Identity**: `c.resize c.width c.height = c`, `c.reformat c.pixelFmt = c` - **Idempotence on overwrite**: `(c.resize w₁ h₁).resize w₂ h₂ = c.resize w₂ h₂` (the second resize wins; the first is forgotten) - **Commutativity** of independent ops: `(c.resize w h).reformat fmt = (c.reformat fmt).resize w h` These laws *make `Ctx` a category* — objects are values of `Ctx`, morphisms are equivalence classes of resize/reformat sequences, and the laws above quotient the free monoid down to the right thing. We don't materialise the category as a Lean structure here because the laws-as-`@[simp]`-rewrites are enough for the reasoning we'll need; if H4 (horizontal lifts of transports along observation arrows) needs the explicit category, we add it then. ## What this file does NOT do - It does not define **the fibration** `p : Cells → Obs` that the cells-spec promises. That's C5, derived from C2 + C3. Once `Ctx` is in place, we lift `compileEMLPath` to take a `Ctx` explicitly instead of having `width`/`height`/format floating as bare arguments. - It does not define **arrows as a typed inductive** (the `Arrow : Ctx → Ctx → Type` form I sketched earlier). That would force us to either prove laws *up to a quotient* or use a higher-inductive type — overhead that buys nothing new at this layer. Instead, we use the *equational presentation*: ops + laws. When H4 needs to talk about "an arrow `f : c → c'`" abstractly, we'll add the typed inductive then. - It does not yet bridge to `Topolei.Selection.Selection`. A `WiredSelection` would pair `(c : Ctx) (s : Selection)` with a compatibility predicate; deferred until we know which compatibility actually matters for rendering. ## Reference Cells-spec §1.5 ("Rendering Context as a Cell"), §15.2 ("Presheaf of Potential Cells"). -/ namespace Topolei.Obs -- ── Pixel format ────────────────────────────────────────────────────────── -- -- The "type" of a framebuffer. Sealed-cell — its IEEE / sRGB / -- linear-RGB semantics are determined by the GPU's hardware spec, -- not by our calculus. We only carry it as a typed tag so -- rendering pipelines can refuse to bind into a context whose -- format they don't support. inductive PixelFormat where /-- 32-bit float per channel; no display clamp. Used by the `render_faithful` probe so CPU/GPU agreement isn't dominated by quantisation. -/ | rgbaF32 : PixelFormat /-- 8-bit per channel sRGB. Display surface; values clamp to [0, 1] and gamma-encode. Standard for live windows. -/ | rgbaSrgb : PixelFormat deriving Repr, DecidableEq, Inhabited -- ── Observation context ─────────────────────────────────────────────────── /-- An observation context: the typed peripheral configuration a render lands on. Objects of the observation category. -/ structure Ctx where width : Nat height : Nat pixelFmt : PixelFormat deriving Repr, DecidableEq, Inhabited namespace Ctx -- ── Operations (morphisms in the implicit category) ────────────────────── /-- Resize the observation surface. Width × height update. -/ def resize (c : Ctx) (w h : Nat) : Ctx := { c with width := w, height := h } /-- Change the pixel format. -/ def reformat (c : Ctx) (fmt : PixelFormat) : Ctx := { c with pixelFmt := fmt } -- ── Identity laws: ops with the current value are no-ops ───────────────── /-- Resizing a context to its current dimensions is the identity. -/ @[simp] theorem resize_self (c : Ctx) : c.resize c.width c.height = c := by rcases c with ⟨_, _, _⟩ rfl /-- Reformatting a context to its current format is the identity. -/ @[simp] theorem reformat_self (c : Ctx) : c.reformat c.pixelFmt = c := by rcases c with ⟨_, _, _⟩ rfl -- ── Idempotence on overwrite: the last value wins ──────────────────────── /-- Composed resizes collapse: only the outer dimensions matter. -/ @[simp] theorem resize_resize (c : Ctx) (w₁ h₁ w₂ h₂ : Nat) : (c.resize w₁ h₁).resize w₂ h₂ = c.resize w₂ h₂ := by rcases c with ⟨_, _, _⟩ rfl /-- Composed reformats collapse: only the outer format matters. -/ @[simp] theorem reformat_reformat (c : Ctx) (f₁ f₂ : PixelFormat) : (c.reformat f₁).reformat f₂ = c.reformat f₂ := by rcases c with ⟨_, _, _⟩ rfl -- ── Commutativity of independent ops ───────────────────────────────────── /-- Resize and reformat commute — they touch independent fields, so order doesn't matter. -/ @[simp] theorem resize_reformat (c : Ctx) (w h : Nat) (fmt : PixelFormat) : (c.resize w h).reformat fmt = (c.reformat fmt).resize w h := by rcases c with ⟨_, _, _⟩ rfl /-- Symmetric form: reformat-then-resize = resize-then-reformat. -/ theorem reformat_resize (c : Ctx) (fmt : PixelFormat) (w h : Nat) : (c.reformat fmt).resize w h = (c.resize w h).reformat fmt := (Ctx.resize_reformat c w h fmt).symm -- ── Read-back of the field updates ─────────────────────────────────────── -- These are field-update lemmas that downstream proofs will lean on. @[simp] theorem resize_width (c : Ctx) (w h : Nat) : (c.resize w h).width = w := by rcases c with ⟨_, _, _⟩ rfl @[simp] theorem resize_height (c : Ctx) (w h : Nat) : (c.resize w h).height = h := by rcases c with ⟨_, _, _⟩ rfl @[simp] theorem resize_pixelFmt (c : Ctx) (w h : Nat) : (c.resize w h).pixelFmt = c.pixelFmt := by rcases c with ⟨_, _, _⟩ rfl @[simp] theorem reformat_width (c : Ctx) (fmt : PixelFormat) : (c.reformat fmt).width = c.width := by rcases c with ⟨_, _, _⟩ rfl @[simp] theorem reformat_height (c : Ctx) (fmt : PixelFormat) : (c.reformat fmt).height = c.height := by rcases c with ⟨_, _, _⟩ rfl @[simp] theorem reformat_pixelFmt (c : Ctx) (fmt : PixelFormat) : (c.reformat fmt).pixelFmt = fmt := by rcases c with ⟨_, _, _⟩ rfl end Ctx -- ── Concrete demo (operational sanity check via `#eval`) ────────────────── /-- Default rendering context: 800×600, sRGB. -/ def defaultCtx : Ctx := { width := 800, height := 600, pixelFmt := PixelFormat.rgbaSrgb } #eval defaultCtx.width -- expected: 800 #eval defaultCtx.resize 1024 768 |>.width -- expected: 1024 #eval (defaultCtx.resize 1024 768).reformat PixelFormat.rgbaF32 |>.pixelFmt -- expected: PixelFormat.rgbaF32 #eval ((defaultCtx.resize 100 100).resize 200 200).width -- expected: 200 end Topolei.Obs