Projection Mapper — Cinema 4D plugin
Change log

Newest version first. Each entry summarises what was added or fixed. This file is
shipped at the root of every build ZIP and provides the release notes for the
GitLab Release (only the current tag's section is shown there).

================================================================================
v0.136.7 - macOS builds for every Cinema 4D version
================================================================================

-- Build -----------------------------------------------------------------------
- First release with notarized macOS builds: Cinema 4D R25, 2024, 2025 and
  2026, universal (Apple silicon and Intel), signed with the Developer ID.
  The macOS builds of v0.136.1 to v0.136.6 failed.

================================================================================
v0.136.6 - macOS builds for every Cinema 4D version
================================================================================

-- Build -----------------------------------------------------------------------
- First release with notarized macOS builds: Cinema 4D R25, 2024, 2025 and
  2026, universal (Apple silicon and Intel), signed with the Developer ID.
  The macOS builds of v0.136.1 to v0.136.5 failed.

================================================================================
v0.136.5 - macOS builds for every Cinema 4D version
================================================================================

-- Build -----------------------------------------------------------------------
- First release with macOS builds: Cinema 4D R25, 2024, 2025 and 2026,
  universal (Apple silicon and Intel), signed with the Developer ID and
  notarized. v0.136.1 to v0.136.4 were not published: their builds failed.
- The Cinema 4D 2026 Windows build is back; it had failed since the plugin
  was renamed to RemapEngine.

================================================================================
v0.136.4 - macOS builds for every Cinema 4D version
================================================================================

-- Build -----------------------------------------------------------------------
- First release with macOS builds: Cinema 4D R25, 2024, 2025 and 2026,
  universal (Apple silicon and Intel), signed with the Developer ID and
  notarized. v0.136.1 to v0.136.3 announced them, but their macOS builds
  failed and only the Windows zips were published.

================================================================================
v0.136.3 - The macOS builds are signed
================================================================================

-- Build -----------------------------------------------------------------------
- The macOS builds are signed with the Developer ID again. In v0.136.2 the build
  could not read the signing certificate, so no macOS zip was published.

================================================================================
v0.136.2 - The macOS builds ship
================================================================================

-- Build -----------------------------------------------------------------------
- The macOS builds announced in v0.136.1 now ship: R25, 2024, 2025 and 2026,
  universal, signed and notarized. v0.136.1 was not published because its
  macOS build script had lost its executable permission.

================================================================================
v0.136.1 - macOS builds for every Cinema 4D version
================================================================================

-- Build -----------------------------------------------------------------------
- macOS builds for Cinema 4D R25, 2024, 2025 and 2026, one per version as on
  Windows. Each is universal (Apple silicon and Intel), signed and notarized,
  and published next to the Windows zips when it succeeds.
- A macOS build that fails no longer holds up the Windows release.

================================================================================
v0.136.0 - The plugin is called RemapEngine
================================================================================

-- Installing ------------------------------------------------------------------
- The plugin folder is now RemapEngine (it was c4d-projector), and so is its
  entry in the Extensions menu. When updating, delete the old c4d-projector
  folder from the plugins folder: left beside the new one, Cinema 4D loads
  both. Preferences and saved scenes are not affected.

-- Menus -----------------------------------------------------------------------
- Custom Generator is renamed Custom Projector.
- View From Projector is removed, along with the Move view button and the
  View from projector entry of the Projector Manager. Looking through a
  projector from the Manager does the same job.
- Installation Report has its icon in the menu.
- Command names lose their trailing "...": Projector Catalog, Remap Licence,
  Projector Configuration, Installation Report.

-- Build -----------------------------------------------------------------------
- macOS: one universal build (Apple silicon and Intel) for Cinema 4D 2026,
  signed with the Developer ID and notarized.

================================================================================
v0.135.1 - Light gate as it was in v0.134.1
================================================================================

-- Viewport --------------------------------------------------------------------
- The light gate no longer turns its faces away from the camera, as v0.135.0
  made it do: seen from the front it stopped holding the beam, and the light
  spilled round the picture. It is hidden from behind the projector and
  drawn from the front, as before.

================================================================================
v0.135.0 - Light gate hidden from the front
================================================================================

-- Viewport --------------------------------------------------------------------
- The light gate in front of the lens is no longer drawn when the camera
  stands in front of the projector either: its faces turn away from the
  camera of the render view, whichever side of the gate it is on.

================================================================================
v0.134.1 - Cinema 4D R25 build restored
================================================================================

-- Under the hood --------------------------------------------------------------
- The texture RAM disk is created the way the R25 SDK allows: the R25 and S26
  package builds again. It was missing from v0.133.0 and v0.134.0.

================================================================================
v0.134.0 - Quick Config: series and models by brightness, N.C for unpublished figures
================================================================================

-- Quick Config ----------------------------------------------------------------
- The SERIES and MODEL lists are ordered by brightness, the strongest at the
  top. A series counts as its brightest model.
- A model that belongs to no series is no longer filed under its chassis
  colour, which made a "Black" series the brand does not have. It gets a row
  of its own at the top of the SERIES list.
- Specifications card: the lumens read ISO / ANSI, normal and eco. A model
  published in ISO only no longer shows an empty cell.
- A figure the manufacturer never published reads N.C instead of Unknown, and
  carries no unit.

-- Projector Search ------------------------------------------------------------
- N.C replaces Unknown in the cells and in the groups.

================================================================================
v0.133.0 - Viewport light intensity, Quad Pixel Drive, test card overlays
================================================================================

-- Viewport --------------------------------------------------------------------
- New group "Viewport", folded by default under the Configure button, with a
  Light Intensity Adjustment slider (0 to 100 %, 100 % by default). It dims
  the projector's picture in the view: a stack of four at full strength
  washed the card out. The beam's gradient fades by the same amount.
- It is a working comfort setting: the lux figures ignore it, and a projector
  shown as a light is rendered at full strength.
- Preferences: a dark ground behind the image circle and the throw-ratio
  gauge, with its own opacity. The preferences page is renamed Remap Engine.

-- Projector -------------------------------------------------------------------
- New Quad Pixel Drive box on the projector, unticked by default.
- A new projector starts with the distance in automatic and its projection
  axis armed.
- The distance to the target is set with the mouse in the calculation block:
  the projector slides along its axis.
- The width no longer stops in a gap between two lenses: it crosses to the
  next lens by itself, as the distance does.
- Picking a lens in the throw-ratio gauge sets the ratio to the middle of its
  bar.
- Changing the lens keeps the matrix inside the image circle; the stack's
  limit is drawn in orange.

-- Test card and slide ---------------------------------------------------------
- Free texts, images with alpha and the logo of the preferences can be laid
  over the test card.
- Sizes in pixels count the panel's pixels: the card no longer grows when a
  projector leaves quad pixel.
- The projected picture follows a change of texture without disturbing the
  viewport.
- The OpenBox follows the depth of the scene: no more light leaking around
  the picture.

-- Measuring -------------------------------------------------------------------
- Sonde: pixel size, display options, a single 15 cm model.
- Lux Meter: tool settings, pixel size in the panel.
- Spec tab: the frame and its reference, the commercial ratio, and the output
  on one line.

-- Search and configuration ----------------------------------------------------
- Projector Search: a plain word also finds by lens and by accessory.
- Quick Config: the series and model lists are held to the height of the
  screen.

-- Under the hood --------------------------------------------------------------
- Redshift gobos are written to a RAM disk, no longer under the preferences
  folder.
- The orphan sweep no longer deletes the materials of zones.

================================================================================
v0.132.0 - Projector Manager: compact rows, switches painted by dragging
================================================================================

-- Projector Manager -----------------------------------------------------------
- The rows are as tall as the Object Manager's: the list is set in the
  interface's own font, with smaller icons, and the title, caption and button
  bars are tightened to match. The same window shows about half as many
  projectors again.
- A switch can be painted down its column, as the Object Manager's dots are:
  click Active, Lamp, Lock or Solo, keep the button down and pull across the
  rows -- each one takes the state the click gave the first. Held against the
  top or bottom edge, the list scrolls under the cursor.
- Pulled across a group head, Active and Lamp are applied to all its members.
  A locked projector keeps its Active and Lamp switches, as it does under a
  click.
- The click and everything it painted are undone in one step.

================================================================================
v0.131.0 - Lens light loss, stacks centred on the rig, custom shift octagon
================================================================================

-- Lens light loss -------------------------------------------------------------
- New option, off by default: Preferences > Projector > Photometry > Apply the
  Lens Light Loss. A lens swallows a share of the projector's light, and not
  the same share at both ends of its zoom; the database records it for each
  projector and lens pairing, wide open and at the long end.
- Switched on, the loss at the throw ratio in use (a straight line between
  the two figures) is taken off everywhere the plugin counts lux: the Sonde,
  the Lux Meter, the Luma Map, and the Illuminance field -- as a read-out and
  as an input, where the width a requested lux gives now accounts for it.
- The Stack tab's "on screen" line names the loss: "50000 lm rated, less
  10.0 % in the lens at TR 1.43 = 45000 lm on screen."
- The Spec tab shows the two published figures and the loss at the current
  throw ratio. Projector Search gains a Luminosity Loss column: wide and
  tele, or a single figure in the middle when the two are the same.
- Switched off, nothing is taken off and none of these figures is shown. A
  pairing nobody measured loses nothing either way.

-- Lenses ----------------------------------------------------------------------
- The lenses designed for a body come first: in the Lens menu, in Projector
  Search, and when the automatic lens change has several that cover the
  throw ratio. In the viewport gauge they stand first within a group of
  lenses that start at the same ratio.

-- Aiming ----------------------------------------------------------------------
- The target is handled from the lens: moving the target, picking a lens or
  changing the model leaves the lens exit where it is, and the body moves
  behind it. Only a lens that changes by itself (automatic mode) keeps the
  bottom of the body in place and lets the lens slide along the beam.
- A lens that throws straight up or down (Epson ELPLX04H) projects its
  picture in landscape again; it was drawn in portrait.
- The "Free (unlink from target)" box has left the panel. A scene saved with
  it ticked keeps its free mode.

-- Stack -----------------------------------------------------------------------
- A stack never turns its machines: they all point as the rig does and only
  the lens shift brings their pictures together. There is no master machine
  any more -- the object sits at the centre of the rig and every machine
  shifts its share. The "This one sits" list and the vertical gap are gone.
- Turning a projector into a stack leaves the machine and its picture where
  they are. Older scenes are converted when they load.
- A machine the shift cannot bring onto the picture is refused, and the
  horizontal gap is held to what the shift can make up, each with a message.
  The rig's shift limit is drawn in light grey in the image circle.

-- Custom projector ------------------------------------------------------------
- Shift is in percent of the half picture, as on a database projector: 100 %
  moves the matrix by half its height or width. Older scenes are converted
  when they load; the picture does not move.
- The shift zone is the octagon of a lens data sheet, from the four Max
  Shift values: a limit is only reached with the other axis at rest.
  Defaults: Up / Down 100 %, Left / Right 50 %.

-- Preferences -----------------------------------------------------------------
- The Projector page is shorter and opens on Database. The viewport gadgets'
  positions and sizes are no longer fields; one button resets them all.

================================================================================
v0.130.1 - Top-down projection: the body stays put
================================================================================

-- Aiming ----------------------------------------------------------------------
- Replaces the near-vertical rule of v0.130.0. Within 20 degrees of the
  vertical the heading of the picture is frozen: the target can be dragged
  anywhere under (or over) the projector, with the mouse or by typing a
  coordinate, and the body only tilts to follow it. It no longer turns about
  the beam as the target goes round, and no longer squares itself to a world
  axis. The banking angle is the one thing that turns the picture there.
- From 40 degrees out the picture is level, as it always was. Between 20 and
  40 degrees the heading sweeps smoothly from the frozen one to the level
  one -- by nothing at all when the target leaves along the line it came in
  on, or straight across through the vertical, and by 90 degrees at most.
- A target moved straight across the point below the projector brings the
  body out tilted through the vertical, picture the other way up, like a hung
  projector. It stays that way until the target crosses back or the banking
  angle is set to 180 degrees.

================================================================================
v0.130.0 - Top-down projection, rig duplication
================================================================================

-- Aiming ----------------------------------------------------------------------
- A projector hung above its target no longer spins. Carrying it across the
  point right above the target used to turn it by 180 degrees at once, and
  sitting exactly on that point turned it again at every refresh.
- The picture stays level as before. Passing over the target, it simply comes
  out the other way up; it is upright again from 30 degrees off the vertical.
- Within 2 degrees of the vertical the picture squares itself to the nearest
  world axis (X or Z). The banking angle turns it from there.
- With the target handle armed, a target moved in one go (a coordinate typed
  in) is reached at once: the cone no longer lands beside it and swings back
  over the next few refreshes.

-- Duplication -----------------------------------------------------------------
- With the handle in the middle of the axis armed, Ctrl+drag duplicates the
  whole rig: the copy stays where the drag started, projector and target, and
  the original leaves with its target. The gizmo used to jump back onto the
  projector and move it alone.
- A Ctrl+drag or Shift+drag that starts on an arrow of the Move tool is locked
  to that axis. Anywhere else it stays free in the view plane.

================================================================================
v0.129.0 - Throw Ratio field, notched image circles, database update switch
================================================================================

-- Throw Ratio -----------------------------------------------------------------
- The Throw Ratio field opens on a single click. The caret goes where you
  click, a drag or Shift+arrows selects, Ctrl+A selects everything, and
  Ctrl+C / Ctrl+X / Ctrl+V copy, cut and paste. The track keeps its
  double-click.
- The field shows three decimals. Leaving it without typing changes nothing.
- When the range is narrow (a single lens), the scale is graduated in even
  steps instead of the few logarithmic marks it had.
- In distance mode, moving the target no longer leaves the throw ratio outside
  the range the lens reaches at the new distance: it is clamped as you drag.

-- Lens shift ------------------------------------------------------------------
- An image circle with a notch is now followed. On a PT-RQ32K with the
  ET-D3LEU100 the matrix slides along the tab it rests in, then gets its
  vertical shift back once it has left it; it used not to move at all.
- With a horizontal shift, the beam gate no longer cuts across the beam: no
  shadow on the slide, no light past the frame (seen on the ET-D75LE95).

-- Spec tab --------------------------------------------------------------------
- The image ratio is shown as published: 16/10 is no longer reduced to 8:5.
- A figure the manufacturer did not publish reads "N.C"; the "unknown" counter
  is gone.
- "Stack max" is now "Stack max with no frame".
- Rows are taller, so the dividing lines no longer run through the text.
- The sheet is laid out at its full height when a projector is created.

-- Projector Manager -----------------------------------------------------------
- Switching the lamp off also stops the projection of a camera-mapped
  projector.
- The total startup power and the average brightness are removed, along with
  the "Summary Figures" preference.

-- Preferences -----------------------------------------------------------------
- New "Database" group: "Update the Database at Startup", on by default.
  Switched off, the installed database is left alone until "Force Database
  Update" is used. A machine with no database still downloads one.
================================================================================
v0.128.0 - Projector Manager: drag into groups, frames cast shadows
================================================================================

-- Projector Manager -----------------------------------------------------------
- A projector can be dragged by its name and dropped on a group -- its header
  or one of its members -- to move it there. Dropped on a projector outside
  every group, or under the list, it leaves its group. When the dragged row
  is part of the selection, the whole selection moves.
- While dragging, the group under the cursor lights up, a label follows the
  cursor, and the list scrolls when the cursor is held against its top or
  bottom edge. A drop that would change nothing does nothing; the move is a
  single undo step.
- A single click now selects on release instead of on press.

-- Frames ----------------------------------------------------------------------
- A frame is no longer left out of the projection light: it is lit and casts
  shadows like any other object, in a stack too. The body and the lens of
  each machine still stay out of the beam.
================================================================================
v0.127.0 - Custom projector: drawn casing, presets, frame
================================================================================

-- Custom projector ------------------------------------------------------------
- The "Projector Generator" command is now "Custom Generator" and creates a
  custom projector. Search and Catalog still create projectors from the
  database.
- The custom body is a real casing: a box wearing a texture drawn for its
  size and lens, shared between identical customs. While a size, lens or tint
  is being edited the body turns plain white; the new texture settles half a
  second after the last change.
- The lens has a front ring, a rim and a recessed domed glass, for the three
  types (straight, 90, 180). The barrel wears LensBody; the glass wears
  LensOff at rest and LensOn while projecting, like a database lens.
- The Image Circle overlay of a custom projector can be grabbed: the shift
  travel is built from the four Max Shift values, the matrix stops on them,
  and the icon bar and the name are shown.
- "Save Preset..." writes every custom parameter (body, lens, matrix, lumens,
  shift limits, throw ratio, custom frame) to a JSON file; "Load Preset..."
  applies such a file, with an undo. Position and aim stay with the scene. An
  unusable file changes nothing and the message says why.
- New defaults: lens 16 cm wide and 15 cm long, Max Shift 25% in the four
  directions, frame tube 3 cm.
- The "Name" field is gone: the projector's name is its name in the Object
  Manager. The report shows "Custom projector" as the model.
- The custom frame follows the body: changing the width, height or depth of
  the projector resizes its frame, each axis keeping its proportion.
- Fixed: the four custom frame fields stayed hidden after choosing "Custom
  frame" in the Stack tab.

-- Projector Builder -----------------------------------------------------------
- The body wears the drawn texture for its size and lens, without the
  "RemapEngine" name. "Save Projector" also writes <name>.png next to
  <name>.json: the texture, to import in the database as the body material.

-- Projector Manager -----------------------------------------------------------
- A summary bar under the list adds up the active projectors: startup power
  (the peak the circuits must hold when the rig is switched on) and average
  lumens per machine, a stack counting all its machines. Projectors without
  a published figure are counted as "NC".
- New preference Projector > Projector Manager > Summary Figures: normal or
  eco lumens, ISO or ANSI.

-- Viewport --------------------------------------------------------------------
- The Image Circle cartouches and the Luma Map legend can be moved and
  resized: Ctrl+drag to move, the corner handle to scale, Escape to cancel.
  Position and scale are application preferences (Projector > Image Circle
  Overlay / Luma Map Legend).
- Fixed: the projection light's gizmo showed in the viewport for as long as
  a drag lasted.
- Fixed: after moving the target and undoing, the target and the gizmo were
  no longer in the same place.

-- Attribute Manager -----------------------------------------------------------
- With several projectors selected, the Throw Ratio gauge keeps its shape and
  shows "Mixed"; a ratio typed in the field is applied to every selected
  projector, each clamping it to its own range.
================================================================================
v0.126.0 - Frames from the database
================================================================================

-- Stack -----------------------------------------------------------------------
- A "Frame" dropdown opens the Stack tab, independent of the stack switch: a
  frame is drawn around a single machine too. It lists "No frame", "Custom
  frame", then every frame the database attaches to the model (accessories
  of type Frame). A frame the mounted lens collides with, or shadows, is
  listed with that warning.
- The custom frame (the box of tubes) is offered to every projector, from the
  database or custom. Its width, height, depth and tube diameter move from
  the Custom tab to the Stack tab, shown for that choice only, and are filled
  at 120% of the body when the choice is made. Scenes saved with the old
  "Enable Chassis" box keep their chassis.
- A status line under the dropdown says what the frame is: full or half
  frame, stacking height per unit, weight, built into the body, lens
  conflict -- or that the chosen frame does not fit the model any more.
- With a frame, the stack reads the FRAME's figures instead of the model's:
  the pitch is the frame's stacking height (the custom frame's height), the
  reported limits are the frame's, on a table and hung. No limit is
  enforced: a frame stacks as high as the user says. The pitch across a row
  is bounded by the frame's width.
- The frame is built from its 3D model with its own material, cloned for
  every machine of the rig, kept out of every beam and removed with the
  machine when the projector is cut.
- The "Pitch" field hides as soon as a stacking height is published by the
  frame, the custom frame or the datasheet.
================================================================================
v0.125.0 - Lens materials by convention
================================================================================

-- Materials -------------------------------------------------------------------
- A lens is dressed by the NAMES of the selections of its 3D file, not by
  rows recorded per model any more: the "LensOff" selection (the barrel)
  wears the shared material LensBody, the "LensOn" selection (the glass)
  wears LensOff at rest and LensOn while projecting. Any other selection
  wears nothing. A barrel could light up because of a wrongly filled zone
  row (PT-VMZ7ST): it cannot any more.
- A lens whose file does not follow that convention (no named selection, one
  material over all its faces, a procedural preset) wears LensBody over its
  whole mesh, else its own material, as before.
- A body and an accessory wear the material of their row over the whole mesh;
  the selections of their file are ignored.

-- Fixes -----------------------------------------------------------------------
- The body and the lens are placed at the position of their mesh, as the
  engine applies it.

-- Database --------------------------------------------------------------------
- common-motor-cpp 44bc0c6: the material convention (MaterialResolution),
  selection names exposed by the model decoder, ResolveAccessoryMaterials;
  schema 1.10, where the light loss of a lens moves onto its pairing with a
  projector (Relations.LuminosityLossW / LuminosityLossT) -- the engine
  carries an older database's lens figures onto its relations itself. The
  legacy Model3DMaterial table is still read for older builds but no longer
  decides anything.
================================================================================
v0.124.0 - Materials resolved by the engine
================================================================================

-- Materials -------------------------------------------------------------------
- Which lens a projector is built with, and which PbrMaterial its body and its
  lens wear (base coat, material zones, the shared LensBody, or nothing), is
  now decided by common-motor-cpp (ResolveProjectorMaterials) instead of the
  plugin's own copy of the rule. The back end's Material assigned page shows
  the same answer, so what it displays is what the plugin builds.
- The orphan material sweep asks the engine too: the materials it keeps and
  the materials the build creates cannot disagree any more.

-- Database --------------------------------------------------------------------
- common-motor-cpp b80da78: material resolution, and materials can be
  renamed in place (UpdatePbrMaterial).
================================================================================
v0.123.0 - Lenses light up without freezing
================================================================================

-- Fixes -----------------------------------------------------------------------
- Adding a projector from the catalogue or the search window, or changing its
  model with Configure, no longer freezes Cinema 4D. With the latest database
  a lens has its own at-rest glass material (LensOff): the plugin removed and
  recreated it in an endless loop. It also froze when the lens a projector is
  built with was not the one asked for (no relation with that projector).
- A lens with material zones wears its zones only: no whole-lens material
  under them any more.

-- Lens materials --------------------------------------------------------------
- Every lens wears LensBody, the barrel material all lenses share, instead of
  a material of its own.
- The glass of each projector has its own LensOn, in the projector's Body
  Colour (Display tab), while it projects.
- "Lens Luminosity" (Preferences) tunes LensBody and LensOff live, in every
  open scene.

================================================================================
v0.122.0 - Reads the 1.7 database
================================================================================

-- Database --------------------------------------------------------------------
- Reads projector databases up to schema 1.7: frames (stacking frames and how
  many machines they hold), frame types (full / half) and the conflicts between
  a lens and an accessory. A 1.6 database still loads, every frame keeping its
  type.
- Accessory columns that were never filled (required / default flags, width,
  height, depth) are no longer read.

-- Projector search ------------------------------------------------------------
- The accessory status column now says how the chosen lens gets along with the
  accessory -- "Shadow" or "Collides" -- instead of the Required / Default flags,
  which no catalogue ever filled.

================================================================================
v0.121.0 - Every machine of a stack projects
================================================================================

-- Camera mapping --------------------------------------------------------------
- A stack projects from every one of its machines: each gets its own camera,
  at its own lens, with its own tilt and shift, and paints its own picture.
  Cameras are added and removed with the machines -- switching the stack off
  or making it smaller leaves none behind.
- Switching "Projectors with Real Light" or clicking a projector now updates
  the view within a quarter of a second, by itself. It used to wait until the
  viewport was touched.
- "Projectors with Real Light (last clicked)" is a budget of LIGHTS: a stack
  in light mode takes one per machine. A projector whose machines do not all
  fit stays camera mapped even when clicked -- a 5-machine stack under a budget
  of 3 -- and the budget goes on to the ones clicked before it.

-- Stack -----------------------------------------------------------------------
- A machine tilted to converge (its lens shift ran out) now really aims at the
  frame. The tilt was taken as if the shift and the tilt added up on the
  screen; they add up in angle, so a steep rig aimed far below its target -- a
  machine 9.5 m above a screen 5 m away pointed 7 degrees too low. The tilt
  shown in the Stack tab and in the Installation Report is the one applied.
- The cone of each machine goes to the corners of the picture that machine
  really throws: a tilted machine draws its keystoned trapezoid, not the
  frame's rectangle.

================================================================================
v0.120.0 - A hundred projectors stay smooth
================================================================================

-- Viewport --------------------------------------------------------------------
- Projectors are now shown by camera mapping: each one paints its own test card
  (or its colour, or its picked image) onto every object inside its beam,
  through a camera-mapped texture tag, instead of a light shining through the
  slide. A viewport past fifty projectors used to freeze -- one light and one
  shadow map each; on the church scene it now runs at 74 fps with 30
  projectors and 26 fps with 100.
- The objects that receive a projector's picture are picked by its beam against
  each object's drawn box (deformers and cloners included): an object the beam
  touches is never missed. Nothing is recomputed while nothing moves, and
  moving a projector or its target updates the picture in the same frame.
- The last projectors you clicked keep the real light, slide and shadows -- 3
  by default. Clicking another one hands the light over to it, and the oldest
  goes back to camera mapping.
- While a projector keeps its light, "High Quality Transparency" is switched off
  in the views: with it on, one transparent slide hides every camera-mapped
  picture. A line in the console says so.
- The cameras and tags this needs are hidden, and belong to the plugin: they
  are removed when a projector's beam leaves an object, when the projector is
  deleted, and all at once when camera mapping is turned off.

-- Preferences -----------------------------------------------------------------
- New "Camera Mapping" group in Preferences > Projector: "Project by Camera
  Mapping" (on by default; off brings back a light per projector) and
  "Projectors with Real Light (last clicked)" (0 = every projector camera
  mapped).
- New "Projection Cone" group: "Beam Gradient" turns the translucent fill of
  the cone off, and "Beam Opacity (Lens -> Screen)" sets its opacity along the
  beam. The ramp is kept across restarts.

-- Projection cone -------------------------------------------------------------
- The frame the cone drew on its far plane is gone: it read as a panel standing
  across the beam ("Show Projected Screen" is there for a surface at that
  distance). The four faces of the fill are drawn far to near, so they no
  longer cut into each other.

-- Projector Manager -----------------------------------------------------------
- A row shows the projector's name, then its model and lens in grey -- each half
  left out when the name already says it. The small rendered view of each row
  is gone, and its column goes to the name.
- Double-click a row (or "Rename..." in its menu) to rename the projector in
  place. Moving the view to a projector stays one click away: the CAM column,
  the row menu and the Move view button.
- A projector that is cut (Active off) no longer builds its machine at all: a
  render showed the hardware standing there, projecting nothing. Its position
  and orientation are still read by the report.

-- 3D models -------------------------------------------------------------------
- Each zone of a model carries its own material: a lens's barrel and its front
  glass are no longer painted the same colour, and the glass lights up when the
  lamp is on.

-- Fixes -----------------------------------------------------------------------
- Switching the pivot between projector, middle and target no longer makes the
  projector's body -- or its picture -- jump for a frame.
- The Cinema 4D R25, 2024 and 2025 builds are back: v0.119.0 built on 2026 only.

================================================================================
v0.119.0 - The projection shows in Redshift
================================================================================

-- Redshift ---------------------------------------------------------------------
- A projector now projects its test card in Redshift: in a final render, in the
  RenderView IPR and in the viewport IPR (the Redshift menu of the view). It
  rendered black before -- Redshift does not carry the transparent slide of the
  standard beam as a coloured shadow. While Redshift is the document's render
  engine, each machine gets a Redshift spot light that carries the card as a
  texture, next to its standard light. The view goes on using the standard
  light and never sees the Redshift one; Redshift sees only its own. Switching
  the render engine switches the beam, both ways, with nothing to do by hand.
- Lens shift, stacks of several machines and the casings kept out of the beam
  behave as they do with the Standard renderer: the image lands on the same
  pixels in both engines. A change of the card or of the shift reaches a
  running IPR at once.
- The card texture is only written again when the picture changes -- not when a
  projector is moved -- and a second render of an unchanged scene writes
  nothing. The files live in a "remap_gobo" folder of the Cinema 4D preferences
  and are cleared at each session.
- Only a slide that carries the Test Pattern projects in Redshift for now. The
  projector bodies keep their standard materials, which Redshift converts.

-- Projection light ---------------------------------------------------------------
- Fixed: the projection light was visible in the viewport in a new document.
  Redshift is the default render engine of a new Cinema 4D 2026 document, and
  the plugin then built a Redshift light, which cannot be hidden the way the
  standard one is. The viewport light is now always the standard one.
- Fixed: a projector that does not emit (shutter closed, another machine soloed,
  lamp off) fell back to the old closed box around its lens for as long as the
  beam was cut. It keeps its collar.

-- Test card in a render ----------------------------------------------------------
- Fixed: in a Standard render, a projected card seen from far lost
  its thin strokes and its small texts, while the viewport showed them. The card
  now knows how much of itself one rendered pixel covers and averages over it.
- Most of what was left of the difference is the render's anti-aliasing:
  "Geometry" takes one sample per pixel. See the command below.

-- Commands -----------------------------------------------------------------------
- "Set Up Viewport For Projectors" is now "Set Up Viewport And Render". It also
  sets the render's anti-aliasing to Best, and raises the viewport's maximum
  number of lights to the number it already gave to shadows -- past that many
  projectors, the extra ones lit nothing in the view. The anti-aliasing change
  can be undone; the light ceiling is only ever raised, never lowered.

================================================================================
v0.118.0 - The test card is numbered
================================================================================

-- Test card -------------------------------------------------------------------
- The card's big label is now the projector's NAME as it reads in the Object
  Manager -- crews call a projector by its number, "1" or "VP 12", not by its
  reference. It sits at the centre, its ink 70 % of the card's height whatever
  the aspect ratio, and a long name is shrunk to fit the width. The height is
  a setting of the Test Pattern shader ("Height (% card height)").
- The projector's model reference is shown behind the name, dimmed, unless the
  name already spells it out (an auto-named projector's does). The lens moves
  to the small bottom row, between the resolution and the format.
- Every text on the card is drawn as two veils: a dark one for the letters and
  a lighter one for a heavy rim around them. Whatever body colour a projector
  is given, its texts come out as a darker and a lighter shade of it -- a red
  body gets a dark-red "1" with a pink rim, with no colour to match by hand.
  The switch is "Texts in Shades of the Body Colour" in the shader's Texts tab;
  off, the shared colour and outline colour apply as before. The name, the
  model and the lens each have their own opacity.
- Scenes saved by earlier versions are brought to this layout when opened. A
  text whose position or size was set by hand keeps it.
- Renaming a projector now updates its card, and at once. It did not update it
  at all before: the card and the "Diapositive_<name>" material kept the old
  name until something else happened to refresh them.
- The viewport texture is asked for as soon as the card changes, instead of
  whenever Cinema 4D next rebuilt it -- seconds later, or at the next click.
  What follows a rename is now the bake of that texture, about a second for a
  4K card; the card itself is only rewritten when something on it changed, and
  the texts already drawn are kept rather than drawn again.

================================================================================
v0.117.0 - A lens that only shifts one way can be shifted
================================================================================

-- Lens shift ------------------------------------------------------------------
- Fixed: on some lenses the shift could barely be moved, by the fields or by
  dragging the matrix in the image circle. The PT-RS30K with the ET-D75LE90 is
  the case that showed it: Panasonic gives that pair no upward shift at all
  and cuts the top corners of the range, so what the lens can reach is a small
  wedge hanging under the zero position -- V from 0 to -8 %, H up to 9.14 % at
  the bottom. Every limit in the plugin was worked out from the zero shift, on
  the assumption that zero lies inside the range. Here it is the very tip of
  it. A typed value bounced back unchanged, and the matrix only moved when it
  was dropped exactly inside the wedge. The range is now found wherever it
  lies. Lenses whose range surrounds zero behave exactly as before.
- Dragging the matrix against the edge of the range stops the axis that is
  blocked and lets the other one keep following the hand: pull down past the
  bottom of the range and V stops while H stays under the cursor. It used to
  slide H back toward the centre, more or less depending on the angle of the
  gesture. Along a cut corner the matrix slides down the slope.
- When the whole travel of a lens is only a few pixels tall in the overlay --
  2 pixels of vertical travel on the pair above -- the drag is geared down, so
  the travel takes a few centimetres of hand movement and a value in the
  middle of it can actually be reached. Ordinary lenses stay one to one under
  the cursor.
- The overlay is drawn on whole pixels, symmetrically about the zero shift.
  The same shift to the left and to the right used to be drawn a pixel apart:
  on a travel of three pixels, +9.14 % looked like it went further than
  -9.14 %.
- Stack: a rig on such a lens is no longer reported as unable to converge
  when its machines do fit inside the range.

-- Projector Manager -----------------------------------------------------------
- The two switches at the top of a row do what their names say. The lamp (the
  shutter) takes the light away AND the translucent fill of the cone and the
  veil on the projected screen, which were painted as light by a machine that
  no longer emits any; the edges, the far rectangle, the markers and the gauge
  stay, because a projector with its lamp off is still aimed and still framed.
  Active (the green notch) is a full mute: the machine itself leaves the
  viewport. It is still built, so the report, the body pick and a render keep
  seeing the hardware.

-- Installation Report ---------------------------------------------------------
- The placement table states an orientation: the H / P / B the Coordinates
  manager shows, for the rig and for each machine of it. A machine that had to
  be angled because its shift ran out states its OWN angle, in red.
- Angles are printed the short way round: -173,6 rather than +186,4.
- "Throw" is now "Distance": that column holds a distance, and a throw ratio
  is a ratio. Units moved into the headers and "Illuminance" became "Lux", so
  nothing on the page wraps any more.
- Fixed: each machine of a rig is printed where the viewport draws it. Its
  position and its aim were worked out by arithmetic on the rig's figures in
  the wrong frame: on a turned and pitched rig a machine printed 20 cm from
  its casing, and an angled one printed the wrong way round.
- The legend under a red figure says what is true: the figure is the end of
  the shift's travel, and the machine is angled for the REST.

-- Misc ------------------------------------------------------------------------
- The Sonde no longer appears in the Create menu or the Commander. It is
  placed by the Lux Meter tool, not by hand; scenes holding one are untouched.

================================================================================
v0.116.0 - The report is the document, and every machine states its shift
================================================================================

-- Installation Report ---------------------------------------------------------
- The window IS the document. There is no editing pane beside the page any
  more: a title, a section heading, a paragraph and a caption are typed where
  they are printed, a block is added from the bar between two blocks, and the
  options of the block you pick appear beside it. What you arrange is the
  layout the PDF is made of -- one layout, rather than a form and a preview
  that drift apart.
- Where the pages break is drawn on the sheet, whether the break was asked for
  or the text simply ran out of room. A continuous page hides that, and hiding
  it is how a table ends up split in two without anyone noticing until the PDF.
- A section can be made to start on a new page, and a new section can be put at
  the top of the document as well as at the end, with or without a page of its
  own.
- The cover can be removed, and brought back from the bar it leaves behind: a
  report that is one appendix does not want a title page in front of it.
- Every machine of a rig has its own line in the placement table -- where that
  casing hangs and what it is dialled to -- under the single image they all
  make. The line above them says how many machines the rig is, so folding the
  per-machine rows away ("Expand stacks") never loses that fact.
- A figure the lens could not deliver is printed in red, and the table says
  underneath what the red means: the machine is angled for the rest, so its
  picture lands as a trapezoid and not a rectangle. Only the axis that ran out
  is red -- the tilt is one angle, but only one of the two shifts is the one
  the optics could not give. "Flag shifts out of lens range" takes the red away
  and the legend with it.
- Fixed: a table set to the rigs page came back as the power budget the next
  time the scene was opened.

-- Stack -----------------------------------------------------------------------
- The Stack tab lists the rig machine by machine: which slot each one sits in,
  how far it hangs from the one you placed, the shift it is dialled to, and the
  angle it takes when that shift runs out.
- Fixed: those figures were what a SLOT DEMANDED, not what a lens can reach.
  The tab assembled a partial database record with no image circle, so nothing
  was ever clamped and a two-high rig could report "V -260.79 %" -- a setting
  that exists on no machine. A rig is now resolved in one place, image circle
  included, and the tab and the report both read it from there.

================================================================================
v0.115.0 - The throw ratio gauge takes the mouse
================================================================================

-- Viewport --------------------------------------------------------------------
- The lens gauge pinned to the edge of the viewport is a control now, not a
  picture. Drag the white marker and it sets the throw ratio; click a lens bar
  and it selects that lens. Until this release it could tell you that a lens
  covered the ratio you wanted and leave you to go and find it in the Attribute
  Manager.
- Because a lens is a CONSEQUENCE of the ratio in auto mode, clicking a bar only
  moves the ratio when it has to: it is pushed to the nearest edge of that
  lens's window when it sits outside, and left exactly where it is when it does
  not. Clicking the lens you are already using does not move the image.
- The marker writes the ratio where the ratio is an input. In Distance mode it
  is written straight; in Screen Width mode it is traded against the width at
  the distance the projector stands at -- the same bargain the image-size handle
  already strikes. In Pixel and Illuminance mode the ratio is derived from end
  to end, so the gauge stays a read-out there and says so: its marker is drawn
  grey and thin, and a click falls through to the scene behind it.
- Every graduation now rules a line across the whole stack. A mark at the edge
  of the scale says where a graduation falls; a rule says where a BAR ENDS
  relative to it, which is the only question this gauge is ever asked. The marks
  have also moved to the gutter side, against the numbers that name them.
- The gauge is narrower: bars of four pixels instead of six, the gaps tightened
  with them, and the panel's padding trimmed -- about a fifth of the width, at
  the same lens count.
- It moves and resizes like any floating gadget: Ctrl+drag the panel to move it,
  Ctrl+drag the corner grip to change its length and the thickness of its bars.
  Where it was left is remembered with the application preferences, in fractions
  of the viewport, so it holds its place when a view is resized.

-- Preferences -----------------------------------------------------------------
- A "Throw Ratio Gauge" section on the Projector page: Orientation (a vertical
  column against a side, or a horizontal stack of bars along an edge), Lens Bar
  Thickness and Length. "Reset Position and Size" puts the gadget back on its
  default edge -- its position has no field of its own, since only a drag can
  express it.
- Fixed: the page decided a field's GUI from its RANGE, so a value stored 0 to 1
  was always handed back as a percentage. Harmless while every such field was a
  luminosity; an orientation cycle lives in 0..1 too, and came out empty.

================================================================================
v0.114.0 - Every machine in the frame throws its own light
================================================================================

-- Stack -----------------------------------------------------------------------
- Every machine of a stacked frame is a machine now. Until this release the
  extra units were empty casings, and the frame's light was faked by making the
  one real light N times brighter: what the extra machines did could be counted
  but never seen. Each unit carries its own light, its own beam and its own
  slide, so a frame lights the scene by adding up, the way the real one does.
  The cost is real as well -- N machines mean N shadow maps.
- A frame can be a grid. "Piled up" and "Side by side" are two counts, and the
  rig is their product: 2 x 1 is the ordinary stack, 2 x 2 a four-machine grid,
  1 x 3 a row on a truss. Every machine still throws the SAME picture -- that is
  what stacking is -- so a grid is one image, and as many times the light as
  there are machines in it.
- "Frame layout" is gone. It chose which direction the single count applied to;
  with two counts, the rig states its own shape.
- When the lens shift cannot span the frame, the other machines are tilted to
  converge, instead of the rig being declared impossible. The Stack tab used to
  say the units could not share one image, and the viewport drew a clean stack
  regardless. They are now angled the way a crew angles them in the frame, and
  the keystone that buys is on screen: an angled projector lays a trapezoid over
  a rectangle, and that is worth seeing before the trucks are loaded. The
  projector you placed keeps its exact aim; the others carry the correction, so
  the distortion grows with the distance from it.
- Two clearances instead of one. "Vertical gap" adds to the pitch up the frame,
  "Horizontal gap" to the pitch along the row: what a stacking frame puts
  between two decks is not what separates two frames standing side by side. A
  scene saved with the old single gap keeps it as the vertical one.
- "Alignment loss" is gone. The extra machines used to be taxed a few percent
  for a convergence that is never perfect at the pixel, and the whole photometry
  of a rig -- the lux read-out, the Sonde, the Luma Map, the screen width a
  requested lux resolves to -- rode on a figure that was invented rather than
  measured. N machines are worth N times the rated output.
- The pitch field appears only when it is the figure actually used. The database
  publishes a stacking height for most models and that value always won; the
  field sat beside it greyed out, which still reads as a blank waiting to be
  filled in one day. It is hidden for those models now.
- Neither count is capped. The model's stack limit is reported in the Stack tab
  rather than enforced.
- The beams of the other machines are drawn with the same gradient as this one's
  instead of four bare lines. They are the same machine; they look it now.

-- Viewport --------------------------------------------------------------------
- The Rotate tool turns the whole projection axis. A projector could not be
  rotated by hand at all before: a hidden Protection tag locked rotation,
  because the aiming system owned it and any turn was undone on the next pass --
  so the only way to swing a projector was to move its target. The turn is now
  read as an input and applied to both ends of the lens-to-target axis, around
  whichever handle is armed: nothing armed, the target orbits the projector; the
  target armed, the projector orbits it; the rig handle, both ends swing
  together. Throw distance, throw ratio and the mounted lens are untouched.
  Scale stays locked -- nothing in the optics survives a scaled projector.
- Projectors saved before this release carry that locked tag, and since it is
  hidden nobody could unlock it by hand: it is normalised when the scene is
  opened and when a projector is inserted. A projector you lock deliberately is
  still aimed correctly and stays unturnable by hand.

================================================================================
v0.113.0 - The axis has a middle to grab
================================================================================

-- Viewport --------------------------------------------------------------------
- A new handle sits halfway along the white line between the projector and its
  target. It takes the two as one rigid object: with the Move tool it slides the
  whole rig, throw distance, ratio and aim untouched; with the Rotate tool it
  swings the entire axis about that middle point. Until now the only way to
  reposition a projector without disturbing its aim was to hold Shift while
  dragging the body, and there was no way at all to turn the pair together.
- The toolbar over the image circle has five icons instead of three, ordered the
  way the rig runs, from the machine out to the picture it throws: projector,
  axis centre, target, projection size, banking. The first two are new -- the
  projector icon parks the gizmo back on the body, which previously needed a
  click on the body itself.

================================================================================
v0.112.0 - The projection axis holds its line
================================================================================

-- Projector -------------------------------------------------------------------
- Changing lens no longer moves the projection axis across itself. The axis is a
  line, and a line is unchanged by sliding its origin ALONG it: the optics may
  still recoil forwards or backwards, but they no longer drift sideways or
  vertically, and the image stays where it was aimed.
- Which part of the projector absorbs the swap now follows from that, instead of
  being a pivot mode to choose. A lens on the same optical axis only changes how
  far the optics stick out, so the body does not move at all. A lens that sits
  off the axis would push the beam aside, so the body slides instead and the
  beam holds its line.
- Fixed: an automatic lens switch in the middle of a target drag could step the
  axis sideways -- close to a metre when the new lens was a bent one, whose
  light exits at ninety degrees to the body. The correction that holds the axis
  was measuring the previous lens in the new lens's frame, so for a bent lens it
  cancelled the wrong displacement.
- In auto-lens mode, the lens in use stays in the Lens menu even when the throw
  ratio has left its window. The windows are evaluated at the current screen
  width, so in SW, pixel-size and lux mode they slide while the value is being
  edited; a ratio landing between two windows emptied the menu down to "(auto)"
  alone, and a lens reference of nothing resolves to the projector's default
  relation -- a lens nobody picked, bent where the one in use was straight.

================================================================================
v0.111.0 - The body colour lights the beam
================================================================================

-- Display ---------------------------------------------------------------------
- The test pattern has no background colour of its own any more: its ground is
  the projector's Body Colour. The Pattern background row is gone from the
  Display tab, and the beam and the projected screen take the Body Colour too.
- In auto-lens mode the cone's edges keep the active lens's colour -- the one of
  its column in the throw-ratio gauge -- while the translucent beam stays in the
  colour the projector throws.
- Spec is now the last tab, after Display.

-- Projector Manager -----------------------------------------------------------
- The S column is gone: the test pattern can no longer be switched off from the
  manager.
- The colour column always shows each projector's Body Colour, and clicking it
  changes the Body Colour exactly as the Display tab does.

-- Attribute Manager -----------------------------------------------------------
- The calculation block's mode buttons and input field, and the Throw Ratio
  field, no longer draw their text over their frame.

================================================================================
v0.110.0 - The installation has a report
================================================================================

-- Installation Report ---------------------------------------------------------
- New command, Installation Report...: builds a PDF report of the installation
  from the scene. A header (title, venue, client, dates, version, summary), then
  sections holding four kinds of block:
  - Text: a paragraph.
  - Render frames: one to three frames rendered from the scene -- top view,
    current viewport, a scene camera or a projector's own point of view -- in
    four layouts, with a caption. "From projector" gives the frame exactly the
    projector's native aspect.
  - All VPs view: one frame per projector, each at its projector's aspect, one
    to three to a row.
  - Table: filled from the scene and the projector database -- projector
    inventory, placement (position, tilt, distance, image, illuminance),
    projection surfaces by Projector Manager group, and power budget.
- A projector's frame chooses whether its projectors show the test pattern or
  their content (the slide image, or plain white light when the test pattern is
  all they have), and whether it lights its projector alone or with the rest of
  the rig. The scene itself is never changed: every render works on a copy.
- Two render presets: Viewport, a quick clean draft, and Realistic, the scene's
  own render settings. Frames render one at a time from Render all or Create
  render, and a Viewport frame is stopped after two minutes rather than holding
  Cinema 4D.
- The A4 preview is the PDF page for page: the same layout, the same line
  breaks, the same pagination. Export PDF writes it with the rendered pictures.
- The report is saved with the scene. Sections fold in the editor to keep long
  reports manageable, and Demo content fills a complete example.

================================================================================
v0.109.0 - The screen has a surface
================================================================================

-- Display ---------------------------------------------------------------------
- The Projected Screen is no longer an empty outline: it is filled, at 30 %
  opacity, with the colour the projector throws -- the Test Pattern's
  background while the slide is on, the Projector Manager's colour when it is
  off, the same rule as the cone. The image plane reads as a surface and the
  scene behind it stays visible.

-- Fixes -----------------------------------------------------------------------
- The Projected Screen hid behind the Projection Cone: with both shown, the
  cone's translucent faces masked the screen, which only appeared once the cone
  was switched off. The screen now shows through the cone.

================================================================================
v0.108.1 - The projector stays on its base
================================================================================

-- Fixes -----------------------------------------------------------------------
- Projector Pivot is again the pivot, and the only one. Since 0.105.0 a new
  projector quietly started in Optical Pivot while the selector was hidden, so
  a lens swap kept the optics in place and swung the body around them. When the
  lens changes now -- picked from the list, switched automatically by the zoom
  or by moving the projector, or brought in with another model -- the projector
  stays on its base, the optics move, and the image stays on the target.
- Scenes saved with Optical Pivot switch to Projector Pivot when they are
  opened: with the selector hidden there is no other pivot left to choose.
- Every input mode holds the base. In L mode, a zoom that switched the lens
  used to hold the optics instead (since 0.102.0); the base holds there too
  now, so the measured lens-to-target distance follows the new lens.
- Dragging the projector in SW, Pixel Size or Lux mode: when the automatic lens
  switch fires mid-drag, the base stays under the mouse and the optics move,
  instead of the body jumping.
- A scene reopened with the target handle still armed: a lens swap holds the
  base as well, where it used to hold the optics.

================================================================================
v0.108.0 - The custom projector builds its own rig
================================================================================

-- The Projector Builder replaces the Projector Stack object -------------------
- The Projector Stack object had nothing left to do once stacking became a
  property of the projector (0.107.0). Its place goes to the Projector Builder:
  a box for the body (width, height, depth, lens offset) and the lens generator,
  with the same optics as part 3 of the custom projector.
- A Save Folder field and two buttons, Save Projector and Save Lens, write
  <name>.json straight into that folder, replacing a file of the same name --
  saving again updates the preset. Characters a file system refuses become "_".
  Each preset shows its Last Save path.
- No folder, a folder that does not exist, an empty name or a file that cannot
  be written: the button writes nothing and says what is missing and what to do.
- In the database, a model can now name a preset instead of a mesh: the body is
  built as a box and/or the lens as the generator. Either half can be a preset
  while the other is a glTF.

-- Custom projector: a chassis, and a stack as big as you like ----------------
- "4 - Chassis (Frame)" now builds something. Ticked, the projector gets a Frame
  around its body -- width, height and depth from the group, set to 120% of the
  body when you tick it -- and a new Diameter field sets the thickness of its
  tubes (9 cm by default). Like the rest of the casing, the chassis never cuts
  the beam nor shadows the picture.
- A custom stack has no ceiling: as many units as you type. Database models keep
  their manufacturer limit, or 32 when the datasheet is silent.
- With the chassis on, every unit of the stack carries its own copy, and the
  least pitch between two units is the frame -- its height piled up, its width
  side by side -- not the body. A larger typed pitch still wins, and the extra
  gap is added on top.
- A projector switched from a database model to custom no longer keeps that
  model's stack limit or pitch.
- Three groups that did nothing are gone: Stack Mode (the Stack tab owns it),
  Light (the Projector Manager owns it) and Projected Image (the Display tab
  owns it). Projected Info becomes group 5.

-- Display tab: what is in the gate -------------------------------------------
- Two rows at the top of the Test Pattern group. "Show Slide" takes the slide
  out of the projector or puts it back; scenes saved before this version keep
  theirs. Without a slide the beam draws in the lamp's white.
- "Image" puts a picture on the slide in place of the test card. Clear the field
  and the card comes back exactly as it was, hand-tweaked settings included.
- The card's own rows grey out whenever they project nothing -- slide removed or
  an image in place. Show Slide counts in the tab header, Hide all turns it off
  and Reset tab puts it back.

-- The Frame object, rebuilt in C++ -------------------------------------------
- The Frame had disappeared from Cinema 4D, and its old version returned an
  empty object anyway. It is back in C++ as a box of tubes: the twelve edges
  of a width x height x depth box, each a square tube with chamfered edges,
  with a closed mesh and outward normals.
- It is hidden from the object menu, like the OpenBox and the Lens: it is what
  the custom projector's chassis is built from, not an object to place by hand.

================================================================================
v0.107.0 - A stack is a property, not an object
================================================================================

-- Stacking moves onto the projector, in its own "Stack" tab -------------------
- A stack is several identical machines rigged in one frame, aimed at the same
  screen, whose images sit on top of each other. That is ONE optical path with
  several lamps behind it -- the crew spends its alignment time making sure of
  exactly that -- so it is now a property of a projector rather than an object
  that clones projectors. One node, one cone, one gizmo, one set of optics.
- The new Stack tab opens on one switch, "Stack this projector". Off -- the
  default -- the projector is exactly what the database says it is and nothing
  below is read. On, the tab holds: how many machines are in the frame, which
  slot this one occupies (bottom / middle / top), whether the frame is piled up
  or set side by side, the pitch between two units, an extra gap, and what the
  alignment costs in light.
- The count starts at TWO, because a frame does. One machine on its own is not
  a stack of one -- it is the switch turned off -- so the number never has to
  double as an on/off as well.
- The count stops where the manufacturer stops: the model's stack limit from the
  database caps the field. Where the datasheet is silent, the field stays open
  rather than inventing a limit.
- The pitch comes from the database (the model's stacking height); the manual
  field is a fallback, and it greys out as soon as the database has the figure.
  With the switch off every row below it greys out too -- there is no frame, no
  pitch and no alignment to lose.

-- What the tab tells you ------------------------------------------------------
- Three read-out lines, all consequences of the three values you type: what is
  rigged ("3 machines piled up, 0.42 m apart, datasheet pitch"), what comes out
  of it ("3 x 31500 lm, -5% alignment = 91350 lm on screen") and what it weighs
  and draws ("78 kg, 3300 W at 220 V in the frame").
- The light figure is honest in both directions. The alignment loss is applied to
  the EXTRA machines only, so one unit always reads the model's rated output and
  switching a stack off can never change a scene's photometry. And the line says
  out loud that the black level adds up along with the white: a stack makes the
  picture brighter, not deeper.

-- The lens shift now knows there is a frame above and below it ----------------
- In a stack, the reachable lens shift is no longer this projector's alone. Every
  machine of the frame has to put its picture on the SAME rectangle, and a unit
  one slot higher can only do that by shifting its image down by the physical
  offset -- the pitch, counted in image widths. Shift the bottom machine of a
  two-high frame down and you are asking the one above it to shift down twice as
  far; past a point its lens simply cannot.
- So the limit tightens by itself as soon as the frame grows: the drag in the
  image-circle overlay, the sliders and the typed values all stop where the WHOLE
  rig can still converge, not where this one lens could. Nothing new to learn --
  the wall is just in the right place now.
- And the shift follows on its own when the frame changes under it: switch the
  stack on, add a machine, widen the frame or change the screen width, and a
  shift that has become unreachable walks back to the nearest position the rig
  can hold, instead of quietly describing a convergence that cannot happen.
- The reference position is the frame BALANCED on the image circle, with the
  travel left over shared between the top and the bottom machine -- where a crew
  ends up anyway, and the only place from which "does this rig fit?" is the right
  question to ask.
- When it does not fit at all -- the frame spans more than the lens shift can --
  the Stack tab says so in plain words instead of letting the scene show a
  convergence that no real rig could reach.

-- The stack lights the scene, and every instrument agrees ---------------------
- The extra units are drawn as casings -- body and lens, with their materials --
  around this projector, along its own up (or right) axis, so a tilted rig stays
  square. They carry no light, no camera and no cone of their own, and they are
  excluded from the projection light exactly like the projector's own casing, so
  the machine below never shadows an upward-shifted image.
- The brightness is multiplied on the single projection light rather than
  duplicated across N of them: the beams are superimposed, so the contributions
  add exactly, and the scene keeps one shadow map per projector instead of N.
- The lux read-out, the Sonde, the Lux Meter and the Luma Map all read the same
  rig flux as the render light. A 2x stack reads twice the lux, and asking for a
  given lux resolves to the wider screen a stack can actually cover.
- The projector node keeps its place whatever the count: raising "Units" grows
  the frame around the machine you placed instead of moving it.

================================================================================
v0.106.0 - The name gets its own band
================================================================================

-- The image circle block, in one piece ----------------------------------------
- The projector name now sits on a dark band that runs the full width of the
  block's frame, continuous with the icon row under it. It used to sit on a
  plate cut to the length of the name, which left the top-right corner of the
  block -- above the banking icon -- a different shade from the rest of the row.
- The three icons spread from one edge of the frame to the other instead of
  clustering in the middle: the width the icons do not use goes into the gaps
  between them.

-- The name is drawn by the plugin, not by the HUD -----------------------------
- The name is rasterised by the shared engine (the same code that draws the test
  card's labels, over the DejaVu Sans the plugin already ships) and blitted as a
  texture. Cinema 4D's HUD text draws its own backdrop behind the string, which
  nothing underneath can cover, and its metrics belong to the host; drawing the
  row ourselves means the background of the name IS the background of the block,
  and the name comes out the same in Cinema 4D, in Blender and on the website.
- The band is snapped to whole pixels, so the glyphs stay crisp instead of being
  resampled between two of them.
- A name too long for the block keeps its END -- the model number and the lens,
  which is what tells two projectors apart -- at a size that stays readable,
  rather than shrinking until nothing can be read.

================================================================================
v0.105.1 - The beam fades, it does not turn grey
================================================================================

-- Fixes -----------------------------------------------------------------------
- The projection cone now fades by real transparency: it keeps the colour the
  projector emits all the way to the projection surface, growing clearer as it
  goes. It used to fade toward a fixed light grey, which showed as a pale grey
  wash at the far end on a dark viewport and painted grey over any object seen
  through the beam.

================================================================================
v0.105.0 - See the beam
================================================================================

-- The projection cone, filled -------------------------------------------------
- The projection cone now fills with a translucent gradient along its length:
  densest at the lens and fading to nothing at the projection surface, so the
  beam reads as thrown light rather than a bare wireframe. The crisp cone edges
  stay drawn on top.
- The cone takes the colour the projector actually emits. With the slide on,
  that is the Test Pattern's background colour (the mire's ground). With the
  slide switched off in the Projector Manager, it is the flat colour the
  manager emits for that projector.

-- Which version is running ----------------------------------------------------
- The plugin's version -- the release tag it was built from -- is now compiled
  into the binary and shown in the License window, so a glance tells you which
  build is loaded. It is filled in at build time from the git tag (CI, or a
  local `git describe`), and reads "dev" for an untagged local build.

-- Fixes -----------------------------------------------------------------------
- A projector created from the Catalog or the Search window now starts in the
  box-origin pivot mode, matching a projector added by hand.

================================================================================
v0.104.0 - One row, one thing on screen
================================================================================

-- The Display tab, rebuilt ----------------------------------------------------
- The tab now reads like the drawing it commands: one row per thing that gets
  drawn, and on that row its switch, its colour, its size, and a plain-word
  hint saying which line of the picture it is ("the beam", "image rectangle",
  "zoom limits"). It was a flat list of checkboxes followed by the Test
  Pattern block, where an element's colour lived three groups away from the
  checkbox that turned it on.
- Three sections, in the order the eye meets them: what lands in the scene
  (cone, projected screen, image circle and shift, lens change markers), what
  you can grab with the mouse (target icons, banking handle, bottom-right
  icon), and what is printed on the slide (the test card).
- The header says how much of the projector is currently drawn -- "14 of 15
  drawn" -- next to one button that turns everything off, or back on: its
  label says which way it will go.
- New Reset tab button at the foot of the tab: the display options, the casing
  colour and the whole test card go back to what a new projector shows.
- The test card's rows carry their colour and their size inline, so what draws
  a line and how wide it is are read together instead of hunted for.

================================================================================
v0.103.0 - A coat of paint
================================================================================

-- Projector casing colour -----------------------------------------------------
- New "Body Colour" swatch in a projector's Display tab. The database body
  textures are greyscale, so this colour is MULTIPLIED into them -- a real
  multiply on the black-and-white texture -- to recolour the casing per
  projector, on both the colour channel and the self-lit luminance channel.
  White leaves the texture exactly as before.
- The colour is baked into the Projector DB Texture shader, so it tints the body
  identically under scene light and in its self-lit glow, without disturbing the
  "Projector Luminosity" balance.
- The projector's Object-Manager icon takes the same colour when a Body Colour is
  set, and returns to the default icon when it is white.
- Each distinct colour keys its own material, so two projectors of the same model
  stay independent; the scene hook reclaims the materials a colour change leaves
  behind once nothing references them anymore.

-- Throw Ratio slider: editable field ------------------------------------------
- The Throw Ratio slider gains an editable numeric field, so the TR can be typed
  directly instead of only scrubbed.

-- Fixes -----------------------------------------------------------------------
- Restored fixed focal lengths that were missing from the lens drop-down list.

================================================================================
v0.102.0 - Two projectors, one brighter picture
================================================================================

-- New object: Projector Stack (brightness stacking) ---------------------------
- A new generator that stacks several identical projectors on the same screen so
  their images overlap and the brightness adds up -- the "stacking" technique
  from live projection. Drop ONE projector inside a Projector Stack object and it
  becomes the template; the stack emits 2..N copies of it.
- The number of stackable units and the physical spacing between them come from
  the database per model (StackMaxWNF / StackHeightWNF); when the model has no
  spacing set, a manual "Spacing (fallback)" field is used, plus an optional
  "Extra Gap". "Master Position" (bottom / middle / top) chooses which unit is
  the reference the others converge onto.
- Convergence uses lens shift up to the lens's maximum (the image circle) to
  cover the offset with NO keystone, then tilts (rotates) only for the residual
  the shift cannot reach -- so a lens with enough shift range converges with no
  distortion at all.
- Because every unit is a real projector, the stacked brightness shows up at
  render AND in the measurement tools: the Lux Meter, the Sonde and the Luma Map
  now count the stack's units, so a 2x stack reads ~2x the lux on screen.

-- New icon set for every menu entry -------------------------------------------
- The plugin's commands, objects, tool and shaders now share one hand-authored
  icon family instead of showing up as bare text or the default plug. One flat
  style, one palette (a lavender ramp with a warm accent for "light / active"):
  Projector Generator, Projector Stack, Sonde, Lux Meter, Projector Catalog,
  Projector Search, Force Database Update, Remap Licence, Projector
  Configuration, View From Projector, Projector Manager, Set Up Viewport, and
  the Test Pattern and Projector DB Texture shaders.
- Projector Stack gets its own icon (it used to borrow the generator's), and the
  two shaders and the "Set Up Viewport" command, which had no icon at all, now
  have one.
- The icons ship as PNGs in res/icons and are loaded at registration; the
  original ten keep their drawn-in-binary fallback if a file cannot be found.

-- The Throw Ratio slider is now logarithmic, with a printed scale -------------
- A projector's lenses do not spread their throw ratios evenly: a wide lens
  covers a narrow low-TR window while a tele stretches over a broad high one,
  so on the old linear slider every short-throw ratio was crushed into the
  first few pixels. The Throw Ratio control is now a drawn LOGARITHMIC slider:
  every ratio gets the same relative room, with a 1-2-3-5-7 graduation printed
  under the track so the reading stays legible. Drag the knob or double-click
  to type a value (editable in Distance / "L" mode, a read-out elsewhere, as
  before).
- Under the track it paints one band per lens, spanning that lens's reachable
  throw window and coloured by its throw ratio on a cold->warm spectrum (blue
  short-throw, green in the middle, red tele) -- the SAME colours the viewport
  gauge and the projection cone now use, so all three read as one instrument.
  The active lens is framed in white and its exact ratio marked by the knob
  line; the header names it (e.g. ET-D3LES250). A single-lens body zooms the
  axis to that lens.
- The graduation prints the two axis ends plus 1-2-3-5-7 log ticks, with the
  decade values (1, 10) brighter.
- The slider fills the full panel width (no more "TR" row label -- the widget
  draws its own caption). In auto mode it shows every lens and the knob rides
  the whole projector's throw range; when a lens is locked it shows just that
  one lens, zoomed to fill the width.
- The viewport availability gauge is logarithmic too, and carries its own TR
  graduation down the side, so both places agree at a glance.

================================================================================
v0.101.2 - Four versions that never reached you
================================================================================

-- Cinema 4D R25 build, and the four releases it held back ---------------------
- Nothing has shipped since v0.98.2 on 3 September. Four tags in a row --
  0.99.0, 0.100.0, 0.101.0 and 0.101.1 -- built cleanly on the 2024, 2025 and
  2026 SDKs and stopped on R25, and a release is only created once all four
  binaries exist. The work below was finished, tagged, and never delivered.
- Two R25-only const-correctness gaps were behind it: BaseDraw::GetDataInstance
  (fixed after 0.99.0) and GeListNode::GetDocument, which R25 declares
  non-const while 2024 and up added a const overload. The test pattern relay
  added in 0.100.0 reads it through a const object, so it compiled on three
  SDKs out of four.
- This build was compiled against R25 locally before the tag was placed.

-- What this release finally delivers ------------------------------------------
- v0.101.1 -- Lux probes and the Luma Map read the beam where it really falls
  on shifted lenses; a projector's own casing no longer shadows its own light.
- v0.101.0 -- The viewport tool icons redrawn, the bar above the image circle,
  two new preferences.
- v0.100.0 -- Projector materials created with the projector, a Projector page
  in the Preferences, the mire's controls on the projector, Move tool
  modifiers.
- v0.99.0 -- The viewport light leak with several projectors, the OpenBox
  collar, catalog fixes.
- Each of those has its own section below, unchanged.

================================================================================
v0.101.1 - The probes measure where the light really is
================================================================================

-- Lux probes on lenses that project off the optical axis ----------------------
- A Sonde standing in the projected image reported "No projector lights this
  point" whenever the lens carries a built-in optical offset -- the whole
  ultra-short-throw family, an ET-D75LE95 among them. The measured beam was
  the vertical MIRROR of the drawn one: the lens shift was applied upwards on
  screen and downwards in the illuminance maths. A probe only read light where
  no light fell, and the optical axis itself, which such a lens leaves dark,
  was treated as the centre of the image.
- Ordinary lenses were affected too, in proportion to their vertical shift; at
  shift zero the two agreed exactly, which is how this went unseen.
- The Luma Map painted its heat map with the same inverted shift, so it too
  coloured the mirror image of the beam. Both now follow the projection.

-- Projectors that throw backwards or sideways ---------------------------------
- A 90 or 180 degree lens leaves the light at the very edge of the body, so the
  beam grazes the projector it belongs to. The shadow ray counted that body as
  an obstacle and every probe in the beam read darkness. A projector's own
  casing no longer shadows its own light; its OpenBox walls, and every other
  projector and object in the scene, still do.

================================================================================
v0.101.0 - The viewport icons, redrawn
================================================================================

-- New tool icons in the viewport ---------------------------------------------
- Projection size, Target and Banking were redrawn from the design system: a
  dashed image frame with a diagonal double arrow, a crosshair whose filled
  centre dot IS the target point, and the image frame rolled off the dashed
  axis it turns around.
- They are white at rest and turn accent blue on a dark plate while their
  handle is active, instead of the old amber artwork tinted by Cinema 4D's
  selection colour.
- The three handle icons floating in the viewport are now all the same size.
  They were 40 / 20 / 24 pixels, a hierarchy that does not exist: the
  projection-size one looked like a different class of control.
- The disc behind a handle icon is opaque, so the projection cone's rays stop
  behind the icon instead of crossing the glyph.

-- The bar above the image circle ---------------------------------------------
- The projector name moved to the top of the block and the icon bar sits
  against the circle it belongs to.
- The bar spans the width of the image circle and follows it when the grid
  shrinks the circles; the icons themselves are capped, and the leftover width
  goes into the gaps.
- The whole block -- name, bar and circle -- is enclosed in a grey frame.
- The line joining the target to the banking knob is drawn through whatever
  geometry stands between them, so the pair still reads as one control.

-- Two new preferences --------------------------------------------------------
- Edit > Preferences > Projector gained a Viewport group: Handle Icon Size
  (default 28 px) for the icons floating in the viewport, and Overlay Icon Size
  (default 22 px) for the bar above the image circle. Both apply on the next
  redraw and are saved with the application preferences.

================================================================================
v0.100.0 - Dressed before you see it
================================================================================

-- Projector materials exist the moment the projector does --------------------
- A projector created from the Catalog or Projector Search (or a script, or a
  paste) showed a gray body and a black slide until it was moved. Two causes,
  both fixed: material creation now runs in the INITIAL execution pass, before
  the generator builds -- and when that pass lands on the asynchronous redraw
  thread (the normal case right after a command creates an object), the work
  is handed to the main thread and the viewport repainted, so the projector
  arrives fully dressed with no user action.
- Materials from older documents are adopted on sight and brought in line.

-- "Projector" page in the Preferences ----------------------------------------
- Two sliders under Edit > Preferences > Projector: Projector Luminosity
  (default 30%) and Lens Luminosity (default 10%). The body material's
  luminance is a black base with the DB texture ADDed at the projector
  strength; the lens material's luminance brightness is the lens value.
- Moving a slider retunes every projector material in the document live, and
  Reset to Default returns to 30% / 10%.

-- Material Manager stays clean -----------------------------------------------
- The generated materials (body, lens, slide) are hidden from the Material
  Manager. They still render through their texture tags.

-- The mire's controls, on the projector --------------------------------------
- The Display tab gained Test Pattern and Test Pattern Colours groups: line
  width, grid step, arrows, circle skip, the eight layer switches and all the
  colours, relayed straight to the slide material's Test Pattern shader. The
  panel always shows what the mire actually draws; text and format fields
  stay on the shader (the projector pushes those itself).

-- Viewport setup -------------------------------------------------------------
- Set Up Viewport For Projectors also raises viewport supersampling to 2x2
  (raise-only: a viewport already higher is left alone).

-- Move tool, with modifiers --------------------------------------------------
- Ctrl-dragging a projector's body duplicates it; Shift-dragging takes the
  world-fixed target along with the move. Both win over the Move tool's axis
  gizmo, so the drag starts where you clicked.

================================================================================
v0.99.0 - The mask was never the problem
================================================================================

-- Viewport light leak, with two or more projectors ---------------------------
- A second projector made the first one's beam bleed straight through its own
  enclosure. Alone, each was spotless. The geometry was byte for byte identical
  in both cases -- nothing in the enclosure depends on how many projectors
  exist -- and the RENDER was clean throughout.
- An enclosure does not block light by being opaque. It blocks by being in the
  VIEWPORT'S SHADOW MAP. With the viewport's shadow type at Opaque, Cinema 4D
  merges what its lights cast into a single shadow, so two masked lights cannot
  each keep their own mask. Colored Layered keeps them apart, which is exactly
  what a room full of projectors is.
- There is a second door to the same failure: Cinema 4D caps how many lights get
  shadowed at all (Preferences > OpenGL). A projector past that ceiling gets no
  mask whatsoever.
- New command, "Set Up Viewport For Projectors": shadows to Colored Layered, at
  high quality, with a 2048 map -- what that map has to resolve is not a
  building but a small hole seen from the lamp -- and the shadow-casting light
  ceiling raised to cover every projector in the document, floor of 30, saved so
  it does not bite again on the next launch.
- ON DEMAND and never automatically. These are display settings you may have
  chosen deliberately. The command says in the console what it changed, and the
  menu entry ticks when there is nothing left to do.

-- Projector casing (OpenBox): a collar instead of a box ----------------------
- The casing is no longer a five-walled box around the lens. It is a collar: the
  annulus between the projector's frustum and the light's cone, which is exactly
  the region that overshoots, and nothing else.
- Why a mask is needed at all: a projector's frustum is OFF-AXIS and a spot is
  symmetric, so the cone has to contain the frustum and therefore overshoots. A
  ray leaving the lamp outside the frustum never CROSSES the frustum's own
  planes -- it runs alongside them -- so nothing lying on those planes can stop
  it. The only place to catch it is across its path.
- What makes the collar small is that its corners sit at the lamp's own RADIUS
  rather than on a plane. A flat blocker 5 cm along the axis is 48 cm away at 84
  degrees off it, and would have to be nearly a metre wide to cover the same
  cone.
- Measured on a church with three projectors and real lens shifts:
      box      10 / 14 / 10 polygons    27.9 x 26.7 x 12.0 cm    719 lit pixels
      collar    5 /  5 /  5 polygons    22.4 x  9.1 x  4.3 cm    719 lit pixels
  Identical light through, a tenth of the bounding volume. Swept across the
  lens's whole shift travel the longest side stays between 21.7 and 22.9 cm.
- It then sits HALF WAY IN by default, which halves those figures again to
  11.2 x 4.6 x 2.2 cm. The collar only has to cover the same DIRECTIONS as the
  cone, so pulling it towards the lamp shrinks it in proportion, and the slide
  rides in on the same fraction because the picture is a pure scaling from the
  apex -- a slide half the size at half the distance projects the same image.
  What stops it going further is the viewport shadow map's near plane: a blocker
  inside that plane is not in the map and occludes nothing.
- The box remains as the fallback, taken whenever the cone cannot produce four
  finite corners.

-- Catalog --------------------------------------------------------------------
- The catalog now finds what Projector Search finds. Same query, same results:
  its filter matches label, reference, model, brand AND series ("32" brings up
  the whole PT-RQ32K-Series), and projectors without a registered lens are no
  longer hidden -- they build without a lens, as they do from the search and in
  the Blender catalog.

================================================================================
v0.98.2 - The shroud steps back from the lamp
================================================================================

-- Projector casing (OpenBox) -----------------------------------------------
- The light leak on a short-throw lens with a large optical-axis offset
  (Panasonic PT-RQ45K + ET-D75LE95, throw ratio 0.36) is fixed. The projection
  came out as a white sheet as wide as the whole cone with the image floating
  above it -- the same picture 0.97.1 had cured, back again in 0.98.x.
- It was never the shape of the enclosure. Casting 12 000 rays from the lamp
  against the mesh finds ZERO escaping: the box is watertight and its hole is
  the exact cross section of the frustum. The software renderer projects one
  clean rectangle. What fails is the VIEWPORT: its OpenGL shadow map has a near
  plane, and geometry closer to the lamp than that plane is simply not in the
  map, so it occludes nothing at all.
- 0.98.0 had reduced the enclosure's size factor from 10 to 2 for a more
  natural-looking box. At 2, the aperture of a 0.36 throw ratio sits 0.72 cm
  from the lamp -- under that plane -- and the lower half of the beam left
  without ever crossing the slide.
- The factor is now derived rather than chosen: it clears 5 cm around the lamp
  (OPENBOX_LAMP_CLEARANCE * max(2, 1 / throw ratio)). For every throw ratio
  from 0.5 up this gives back the 10 that 0.97.1 shipped; only short lenses,
  whose aperture would otherwise sit on top of the lamp, grow beyond it
  (13.9 cm on the ET-D75LE95). Nothing optical changes: the box scales about
  the lamp, so the beam does not move by a hair.
- Measured against a wall filmed head on, the halo as a share of the lit area
  went from 2.1 % in an 8 m room and 90.7 % in a 200 m scene, to 0.0 % in both.
  Checked over 11 projector/lens pairs (PT-RQ45K, PT-RQ35K, PT-MZ882B; throw
  ratios 0.36 to 7.4) at two scene sizes.
- Note for the record: only a lens whose optical axis is far off the image
  shows this at all. On a centred lens the smallest cone containing the image is
  barely wider than the image, so even a shadow that fails completely spills
  almost nothing.
- Side effect: the enclosure is now visible next to the lens, roughly half the
  width of the casing. It reads as a light shroud, and it is what 0.97.1 drew.

================================================================================
v0.98.1 - The test card learns to read
================================================================================
(v0.98.0 carried these same changes but its tag build failed on CI -- the engine
text source was not wired into the R25 project-tool build; v0.98.1 is the fix.)


-- Test Pattern (mire) text -------------------------------------------------
- The mire's labels are now rasterised by the ENGINE (common-motor-cpp, via
  stb_truetype) instead of C4D's GeClipMap. GeClipMap silently capped glyph
  rendering above ~120 px -- it drew only the bottom of large letters, so a
  label cut in half as soon as its size was raised. The engine renders crisply
  at any size, with exact ink metrics, and the very same call will feed the
  Blender port and a future web version of the tool (deterministic, WASM-ready).
  The plugin ships DejaVuSans.ttf in its resources for this.
- Text size is now a percentage of the image WIDTH (100 % = full width); it
  never overflows -- a string too wide for the card shrinks just enough to fit.
- Text position X/Y are percentages that move the label across its on-card
  travel: 0 % flush to the left/top edge, 100 % flush to the right/bottom. The
  label always stays fully on the card.
- The font, colour, opacity (now shown as a %), outline width and outline colour
  are SHARED by all four fields (Projector Name / Lens / Resolution / Format);
  each field keeps only its own show/text/position/size. The old per-field
  background rectangle is gone -- the text reads directly over the pattern,
  legible thanks to its outline.
- Sensible default layout: name top-left (large), lens top-right, resolution
  bottom-left, format bottom-right.
- The mire now shows the projector's REAL database name -- the model alone,
  without the brand -- and the real lens name, rather than the (renameable)
  object name; it falls back to the object name for custom projectors.
- New "Circle Skip" control (default 1): draw a concentric circle every N grid
  lines instead of every one.
- The "Show ..." element toggles are laid out in three columns.

-- Projector object ---------------------------------------------------------
- The projector casing texture is also fed into the material's Luminance
  channel, so the projector body is always visible (self-lit) even where no
  scene light reaches it.
- Removed the forced "Flat" viewport Display tag from the body and the lens.

-- Build --------------------------------------------------------------------
- ccache is used automatically when installed (no-op otherwise), so rebuilding
  after a one-file change is near-instant; the cache is shared across worktrees.

================================================================================
v0.97.1 - One rectangle, and nothing else
================================================================================

-- Projector casing (OpenBox) -----------------------------------------------
- On a short-throw lens with a large optical-axis offset the projection came out
  as a bright band with the image floating detached above it, and the rest of the
  room lit. The enclosure introduced in 0.97.0 was doing two wrong things at
  once: its snout kept the APERTURE's cross-section all the way back to a third
  of the way to the aperture, where the beam is only a third as wide and (with
  lens shift) a third as high off the axis, so the snout floor cut straight
  through the middle of the beam; and what it did let past was a band the beam
  never owned. Measured on a Panasonic PT-RQ45K + ET-D75LE95 (throw ratio 0.36,
  optical axis 0.46 image-widths below the image).
- The enclosure is now a closed box straddling the light whose front face
  carries a hole exactly the size of the output footprint. Every ray through the
  hole is a ray of the projection frustum, by construction, and every other ray
  meets a wall -- there is nothing left to tune. A long lens, whose aperture
  sits metres in front of the light, gets a snout for the remaining distance,
  and that snout is a frustum of the beam, so it can never clip it either.
- The slide (diapo) is still the last polygon of the mesh, so its material,
  selection and UVW mapping are unchanged.

-- Projection light ---------------------------------------------------------
- The cone of the projection spot is now measured from the four corners of the
  image instead of from the throw ratio alone. The old formula is only correct
  for a lens on the optical axis: tilt the cone onto a shifted image and the
  corners move in the tilted frame. On the ET-D75LE95 it came out 2 deg too
  NARROW horizontally -- the bottom corners of the projection were cut off --
  and 11 deg too WIDE vertically, throwing light 93 deg off axis for an image
  that ends at 65. A lens with no shift gets exactly the same cone as before.

================================================================================
v0.97.0 - The projection light stays in its box
================================================================================

-- Projector casing (OpenBox) -----------------------------------------------
- The projection light is an omni sitting at the box origin, and the OpenBox
  that masks it used to have its back wall flush with the lamp, so everything
  the lamp threw backward or sideways leaked out. The OpenBox is now a
  light-tight enclosure: a large closed box straddles the lamp (back + four
  sides), a chamfer narrows it down to the projection footprint, and a snout
  carries the beam to the front. Only the front window -- sized to the cone, so
  the projected image is never clipped -- stays open.
- The lens shift is carried by the snout and absorbed by the chamfer; the slide
  (diapo) is still the last polygon of the mesh, so its material, selection and
  UVW mapping are unchanged.

================================================================================
v0.96.0 - The licence window watches the database
================================================================================

-- Licence window -----------------------------------------------------------
- The licence window (Extensions > Licence) now shows the projector database
  version on this machine next to the one the server has, so a waiting update is
  visible at a glance instead of guessed at. The check reads only the small
  server manifest -- nothing is downloaded -- and runs off the window's thread,
  the same way the sign-in does, so the panel never freezes.
- A Force Database Update button sits right there too, running the exact same
  command as Extensions > Force Database Update and then re-reading the two
  versions so the panel reflects what just landed.
- When the server refuses the check with a 403, the panel says what it means and
  what to do: the licence session has gone stale, sign out and back in to
  refresh it. That refusal is what the tightened download security now returns
  once a token expires -- previously it surfaced only as an opaque download
  failure with no hint that re-signing fixes it.

================================================================================
v0.95.0 - Every projector on one list
================================================================================

-- Projector Manager ---------------------------------------------------------
- A new dockable panel (Extensions > Projector Manager) lists every projector in
  the scene, one row each, with what that projector actually sees: the row's
  first cell is a render through its own lens, so you can tell where a projector
  is aimed without flying the viewport there.
- Three switches per row, and they are independent. ACTIVE cuts the projector
  entirely -- no light, no slide, and no cone left in the viewport. LAMP is the
  one you use while you work: no light and nothing projected onto the geometry,
  but the cone stays drawn, because a projector with its lamp off is still aimed
  and still framed. SLIDE off keeps the projector lit and puts a flat colour of
  your choosing through it instead of the test card.
- LOCK makes a row read-only. SOLO isolates one projector: every other one is
  muted for as long as it is on, and the mute is worked out rather than typed,
  so a projector pasted in while a solo is running arrives muted too.
- Groups. Put a selection into a named group, open and close it, reorder it,
  rename it. The group row summarises its members in the same three columns: the
  mark when they all agree, a dash when they do not. Membership is a name the
  projector carries, so a projector pasted into another scene lands in the same
  group there; the order and what is open live in the document.
- The camera column looks through a projector and back out again -- which is
  also what puts the render settings on that projector's native resolution. The
  Move view button does the other thing: it brings your own viewport camera to
  the projector's viewpoint and leaves the render settings alone.
- Three more columns, and they are handles rather than switches -- the same
  three things the icons in the viewport do, done from the row. Press the target
  cell and drag: the projector arms its target (the move gizmo goes onto it) and
  the target then follows your cursor in the view, live -- a row aims the
  projector without flying the viewport there. A plain click just arms it, as
  before. Drag the image width sideways and the image resizes, through the same
  clamp AND the same code path the viewport handle uses, so it resizes whatever
  the calc block's active input is and the lens never promises a size it cannot
  reach. Drag the banking and the projector rolls; double click it and
  the roll goes back to zero. The three cells carry the viewport's OWN handle
  icons, the same PNG files, so the mark for a thing is one mark in this plugin
  and not two. They are loaded once for the whole plugin now instead of once per
  projector, which a scene of seventy-eight of them was doing.
- Every cell that shows nothing when it is off shows a grey ghost of its mark
  under the cursor: an empty cell that does something has to say so. The camera
  cell follows the same rule and only carries its mark on the projector the
  viewport is actually looking through. Both figures are the projector's own parameters,
  shown as they stand: the row and the viewport are never two versions of the
  same number. Banking is greyed on a projector in free mode, where it is
  manual, exactly as the Attribute Manager greys it.
- The rendered views are made one at a time, only for rows on show, and only
  when the projector they belong to has moved or changed. A scene where one of
  them costs more than a moment switches the column off and says so rather than
  making the panel unusable.

-- Fixes ---------------------------------------------------------------------
- The Configure... button no longer crashes Cinema 4D. Two unrelated helpers
  were both named SpecCell, which collapsed onto one memory layout and corrupted
  the heap the moment the panel filled its specification card.
- The manager's image-size handle now actually resizes. It went through the
  screen-width path that only applies when the calc block's active input is the
  screen width, so in any other input mode the drag did nothing; it now takes
  the same buteed route the viewport handle does and works in every mode.
- Cut all now cuts whenever anything is still throwing light, and only brings
  the projectors back once the scene is already dark. It was deciding on the raw
  Active flag, which let a click switch everything back on instead of cutting.

================================================================================
v0.94.0 - Which projector, and how they differ
================================================================================

-- The projector search is a search again ------------------------------------
- The window used to ask for a shot: image width and height, throw distance,
  target nits, a weight limit and a power limit -- and it ranked the library
  against them. That question belongs to the scene. There the projector already
  stands, the screen is where it is, and the Lux Meter answers against the real
  geometry instead of against six figures typed a second time. Answered here it
  was a second opinion nobody could check, and the two could disagree without
  either saying why. The mode is gone.
- What is left is what a database can actually answer: which bodies exist, and
  in what way they differ. Two tabs, one query, one set of pins.

-- Results: a tree cut the way you read it -----------------------------------
- The matches are grouped into levels you name -- brand, then resolution, out
  of the box -- and each level is a chip you can drop. Any attribute a projector
  has can be a level, including two bands, brightness and power, for the figures
  that read better as ranges than as values.
- The selected model's figures run down a column on the right, with every lens
  it takes beside them. Each lens is drawn against the span the whole body
  covers, so a pair of numbers stops being a pair of numbers: you can see at a
  glance which lens sits where in the projector's reach, and how much of that
  reach it uses.
- Brand and brightness are picked rather than typed, from a combo and two
  sliders. They are not a second store: each writes its own token into the query
  and reads itself back from it, so the chip under the field takes a brand off
  and the combo follows.

-- Compare: the spec is the row, the projector is the column -----------------
- Pin as many models as you like and read a spec along its row. The winning
  column is marked on every row that has a winner -- more lumens, less weight,
  more stacking -- and rows every model answers the same way can be hidden
  altogether, which is what "Differences only" does.
- The last row is the one no figure can give on its own: how much of the throw
  range each body covers, all of them drawn to one scale.

-- One table of attributes, four menus ---------------------------------------
- The columns of the result table, the levels the tree is cut into, the key it
  is sorted on and the rows of the comparison all name a field from the same
  table of twenty-seven. A field can no longer be offered in one place and be
  missing from another -- which is exactly what the old five-value sort enum and
  eight-value group enum did to each other, and to the twenty-odd fields neither
  of them named.
- Right click the result table for the columns: every spec, the ones on show
  marked, with "add all" and "remove all" at the top. Columns are dragged to
  width, the model column sizes itself and never scrolls, and what does not fit
  beside it scrolls sideways.

-- The window looks like the panel it lives in -------------------------------
- Rounded corners on the tabs and on the frame around the results, and the open
  tab is joined to the panel it opens rather than sitting above it.
- The four buttons are drawn rather than borrowed from the system, and they
  light under the cursor.
- A colour bug that had been there since the window was written: every recessed
  surface -- the caption strips, the group rows, the scrollbar gutters -- was
  mixed towards "the opposite of the text colour", which on the default theme
  lands within a thousandth of the background itself. They had all been coming
  out the colour of the background, at any ratio. Headings now read as headings.

-- The licence token no longer sits in the clear -----------------------------
- The licence token carries the AES key of the projector database. Written to
  licence.json in the clear, any licensee could read it and decrypt the database
  offline. It now goes through the OS vault -- DPAPI on Windows. An existing
  licence.json migrates silently and one way; a machine with no vault falls back
  to the plain file, which is never worse than before.

================================================================================
v0.93.0 - What you asked for, and what the optics will give
================================================================================

-- The calculation block answers in three rows -------------------------------
- One cell was serving as both the question and the answer, and every argument
  the block has caused came from that. Type a 50 m screen width in front of a
  target 12.07 m away through a 0.3579 lens and the field came back reading
  33.72 -- correct, and written straight over the request. The 50 was gone, the
  reason for the 33.72 was never given, and backing the projector off gave
  nothing back because nothing remembered 50 m was what was wanted.
- The block now carries three rows. INPUT is what was asked for, and only the
  active mode's column has one. FROM SCENE is what the scene measures, which is
  the distance to the target. COMPUTED is what actually comes out, in all four
  columns. Two cells per column, never more.
- When the request is reachable the rows say the same thing and the widget reads
  the way it always did. When they diverge the COMPUTED row lights up and gives
  the two numbers that were missing: 33.72 m from here, or 17.90 m of clearance
  for the 50 m. Which of those to do is the user's call, and now it can be made.
- The row names moved to a strip on the left. With three rows, a caption above
  every value cost more height than the values it labelled, and the same word
  repeated four times across a row said nothing the first one did not.

-- An input is never corrected ------------------------------------------------
- The requested width has its own place now, separate from the width in force
  that all the geometry reads. Nothing overwrites it: not the butee, not a lens
  change, not a target drag.
- Because the butee is applied to the REQUEST on every evaluation rather than
  once on entry, the image now grows as the projector backs away -- and stops
  at the width that was asked for. Before, the width stayed at whatever the
  first butee had left it at, and the request was already lost.
- Same rule for the pixel size and the lux: in their own input mode their field
  is no longer recomputed from the width, so it keeps exactly what was typed.
  If the optics cannot deliver it at this distance, the COMPUTED row says so
  instead of quietly editing the entry.
- The image-size handle writes the request as it drags. It is direct
  manipulation and it cannot be dragged past the stop, so what is on screen IS
  the request -- without this the stored request would take the handle back on
  the very next recalculation.

-- L mode: the scene is the input, and it is read on time ----------------------
- L mode had no input anywhere. The distance cannot be typed -- it is wherever
  the target stands -- so the INPUT row sat empty from end to end and the block
  looked like nothing was driving it. The SCENE is the input in L mode, and it
  is the FROM SCENE row that now carries the mark. Which cell drives and which
  cell can be written to are two different questions, and only L mode ever had
  a different answer to them.
- The measured distance reached the optics one evaluation late. It was published
  to the container only AFTER the recalculation had read it, so every pass was
  served the previous pass's distance. A live drag re-evaluates frame by frame
  and hides that; one discrete change -- swapping a lens, typing a ratio --
  simply left the readout wrong until something else triggered a pass. Measured
  on a PT-RQ45K walked across its whole lens range, the distance now matches at
  every step instead of trailing by one.
- The distance still moves when the lens changes and the target has not: it is
  measured from the lens's optical exit, and in Projector Pivot mode a swap
  holds the chassis while the optics glide. Across the PT-RQ45K catalogue that
  is 14.7 cm, exactly the travel of the object itself. The readout is right; it
  is the exit that moved.

-- Between two lenses, only the projector is left ------------------------------
- When no lens covers the current throw ratio, the projector now shows nothing
  but its own body: no cone, no lens, no slide, no light. An image cannot form
  there, and drawing one said it could.
- All of that was already written and none of it could fire, because the throw
  ratio stored on the object was no longer the projector's own. It was written
  once, right after the butee, and never again -- so once the measured distance
  was folded in, the stored value kept whatever the butee had left while the
  screen width moved out from under it. Measured on a PT-RQ45K: the width swept
  0.9 m to 8.7 m while the stored ratio sat at 1.67 the whole way. Everything
  reading it -- the automatic lens choice, the coverage count, the red wire, the
  "Out of range" status -- was reading a ratio the projector did not have.
- The ratio is a CONSEQUENCE of the distance and the width, so the object now
  carries the consequence, and the coverage count is recomputed against it in
  the same pass: the assembly runs immediately after and reads that count to
  decide what to build.
- The rule "does this lens cover this ratio" had three copies that had drifted
  apart; one of them still ignored the fixed-focal tolerance. There is one now.

-- Fixed focal lengths are reachable in automatic mode ------------------------
- A fixed lens has a throw-ratio window of zero width. A ratio dragged with the
  mouse never lands on it to the last float, so it never entered, and the
  hysteresis then held the current lens for good: the ET-D3LEU101 and the
  ET-D75LE95 could not be reached in automatic mode at all, and with them the
  widest image the projector can make.
- Half-open windows already had a 2% relative tolerance. Degenerate ones now get
  the same, in both places that decide coverage -- the automatic selection and
  the cone's own test -- so the two can never disagree about what is covered.

================================================================================
v0.92.1 - The keyboard is enough, and the optics stop disagreeing
================================================================================

-- The catalogue: the arrows choose, Enter adds --------------------------------
- The dialog opened the cursor in the search field and stopped there: choosing
  anything other than what the mouse was pointing at meant letting go of the
  keyboard. It now does what Blender's own search popup does, which is the
  gesture everyone already knows -- three letters, Enter.
- The up and down arrows move the highlight and scroll it back into view without
  taking the focus off the field, so left and right still walk through the text
  and the next letter typed still lands in the search.
- The first row is highlighted after every filtering, so typing then validating
  is enough, and the dialog never waits on a selection nobody can see.

-- Ten image circles to a row --------------------------------------------------
- The viewport grid counted how many 76-pixel circles fitted across the frame
  and wrapped as soon as that was exceeded: six or seven to a row in a viewport
  flanked by Cinema 4D's usual panels. The count leads now and the size follows
  -- a row fills up to ten circles whatever the frame's width, and the circle
  shrinks so the ten fit.
- The one guard is the smallest circle still readable: if the frame is too narrow
  for ten at that size, the row holds what it can and the grid climbs. The old
  calculation could let a row spill past the right edge of the frame.
- The horizontal gap goes from 18 to 6 pixels: a cell already reserves the room
  for the widest image circle in the catalogue, so the gap has only to keep two
  neighbours from touching. Every pixel spent there is a pixel of circle when ten
  share a row.

-- The Sonde readout sits below its bottom-left corner -------------------------
- It was drawn to the right of the probe's origin, where the move gizmo passes as
  soon as the probe is selected: the arrows ran through the text. It now sits 30
  pixels under the bottom-left corner of the probe's screen footprint, never more
  than 140 pixels from that origin, on a translucent rounded plate with a lead
  line pointing back at the measured point -- the same placement as the Blender
  overlay, to the pixel.

-- The automatic name follows the selection again ------------------------------
- A projector's name stopped tracking the selection as soon as it differed from
  the composed name by one character. The ".52" suffix Cinema 4D gives a copy was
  enough, and so is a prefix of the user's own: "Panasonic PT-RQ50K -
  ET-D3QS400.52" kept its lens after a lens change.
- The remembered name is cut back into its two halves -- "<brand> <model>" and
  "<lens>" -- and each is looked for in the current name and replaced at every
  occurrence. What surrounds a half does not count; one character of difference
  does: ET-D3QS401 is not ET-D3QS400 and stays put. A name that says the lens
  twice means it twice. A name that carries neither half is the user's own and is
  never rewritten.
- Opening a scene renames nothing: what is remembered is the canonical name of
  the selection, so both halves already match as long as the selection does not
  move.
- A lens the database cannot name at that instant no longer erases the one
  already in the name, and a remembered name with no recognisable separator no
  longer has its model half substituted while a lens is selected.

-- Off-centre lenses point where they actually point ---------------------------
- The runtime baked the BoxOrigin/Dir nulls with each null's own rotation. Three
  models carry it the wrong way round (ET-C1U200, ET-D3LEU100/101) and their
  optical exit landed behind the body -- 41 cm behind the front face on the
  C1U200, with the beam starting inside the chassis. The quarter turn between
  the nulls' frame and the mesh's is the same for the whole database, so it is a
  constant now. 62 of the 66 models are unchanged.
- The relations already carried CenterLensX/Y, the lens's own optical axis:
  loaded by the engine, read by nobody. On the ET-C1U200 that is 0.78 image
  heights -- the offset the manufacturer builds into the lens. It now reaches
  the geometry: OpenBox frustum, look-through camera film offset, viewport cone,
  screen-width handle, Sonde cone.
- The image-circle overlay keeps the raw shift: it is the diagram of the shift
  mechanism, not a physical view. Its limits stay the manufacturer's (+/-18% in
  H, 0 to +20% in V) and the user starts at 0 without ever seeing the 78%.

-- The cone, the gadget and the target agree on one distance -------------------
- The viewport cone ran to sw * trMax when that was further than the screen, and
  painted there a rectangle no lens can fill: 58 m wide at 65 m for an
  ET-D3QW300 that stops at 38 m. It stops at the projection surface now.
- Its half-width was computed from the distance to the TARGET rather than the
  optical distance. The beam angle is optical -- the image is sw wide at
  dist = sw * tr -- so whenever the two diverged the cone lied, twice too wide
  or down to a thread, while the slide itself stayed right.
- Typing a value in the calculation block marked nothing dirty (only a mode
  change did), so the geometry waited for a rebuild that never came.
- The "from scene" distance was derived (l = tr * sw) AFTER the ratio was
  clamped, so the field published a distance that does not exist: 22.49 m shown
  for a target at 28 m, as soon as the ratio was parked in a gap between two
  lenses. It is measured now. When the clamp bites, what gives way depends on
  the mode: with the lens locked the image width yields; in automatic the ratio
  is free to leave the window -- that is what triggers a lens change -- so the
  requested width holds.

-- Also in this release --------------------------------------------------------
- The R25 build fix written up under 0.91.1, which never got a tag of its own.
  v0.88.0 and v0.91.0 both stopped at the same R25 failure, and release-plugin
  publishes nothing without all four binaries -- so this is the first tag to
  reach customers since v0.87.0.
- One more R25-only build error, reported by the pipeline of v0.92.0 and fixed
  here. R25 declares BaseObject::GetMp() and GetRad() non-const; 2024 made them
  const. The Sonde's readout reads the probe's bounding box through a const
  pointer, so it compiled on three SDKs out of four -- and v0.92.0, having built
  2024, 2025 and 2026 and nothing else, published nothing, exactly like the two
  tags before it.

================================================================================
v0.91.1 - Build fix: the Attribute Manager gadgets are 2024 and up
================================================================================

- v0.88.0 gave the Attribute Manager two data types of its own so that one
  widget could own a whole grid. They are declared with CustomDataTypeT, written
  and read through GeData's typed SetCustomDataType / GetCustomDataType<>, and
  registered with CUSTOMDATATYPE_INFO_NO_ALIASTRANS. All three arrived with the
  2024 SDK. On R25 the base class is the bare CustomDataType and the GeData
  accessors take an explicit type ID, so that build failed with forty errors --
  "undefined base class", "does not take 1 arguments" -- while 2024, 2025 and
  2026 went green. Both tags posted since -- v0.88.0 and v0.91.0 -- stopped
  there: release-plugin needs all four binaries before it will publish, so
  neither produced a release at all.
- The shim sits in am_gadgets.h behind the same MAXON_API_ABI_VERSION < 100000
  gate the rest of the plugin already uses for R25: the missing base class, the
  flag as a no-op (it is a copy-on-write hint, and the bit 2024 uses is
  unassigned on R25 -- setting it would be a guess), and one pair of helpers for
  the accessors. Every read and write of the two types goes through that pair,
  so the alternative -- the same #if repeated at eight call sites -- cannot
  come apart one site at a time.
- All four SDKs were built locally before this commit. R25, 2024 and 2025 with
  the project tool, 2026 with cmake: four binaries, no errors.

================================================================================
v0.91.0 - One panel to configure, tabs that hide on their own
================================================================================

-- Fewer tabs: everything you edit lives in Projector Parameters ----------------
- The object's parameters were spread across the forced "Object" tab, a separate
  Projector Parameters tab, and a Calculation Method tab. They are now one panel:
  the Configure button, the projector/lens pickers, the formula toggle and the
  Calculation Method block, in that order. Cinema forces its first tab to the name
  "Object" and will not let a plugin rename it, so that tab is simply emptied --
  it carries no visible parameter and disappears on its own.
- The Lens and Projector Pivot dropdowns share one grid now, so their labels and
  their menus line up in the same two columns instead of drifting apart.

-- The Calculation Method reads and edits in place ------------------------------
- The method gadget is a slider you can drag, with hover chevrons to step the
  value and a double-click to type one in -- no more separate field underneath to
  change what the gadget already shows. The value is centred in each column with
  the chevrons in the side margins, and the block spans the full panel width.

-- Icons on the generator, the Sonde, and the Lux Meter ------------------------
- The Projector generator, the Sonde and the Lux Meter tool were the last plugins
  showing as bare text among icons. They now draw their own, like the commands did
  since 0.89.0.

-- Configure: only the toggles that still mean something -----------------------
- Optics and Calculations lost their visibility toggles when their sections moved
  into the always-visible merged tab, so the dialog no longer offers them. What
  remains are the two tabs that genuinely come and go -- Display and Spec (the
  former "Info", renamed here and everywhere from a single place). Display is now
  the last tab of the list, and the Throw Ratio slider sits directly under its
  "Use Formulas" toggle.

-- Hiding one of Display / Spec no longer hides both ---------------------------
- Turning off either tab from Configure hid both, irreversibly. The Apply path
  re-read the eye toggles through GetBool on their state ids; those are custom
  drawn widgets, not checkboxes, and the read landed on an unrelated gadget that
  answered "off", forcing whichever tab you had not just touched back to hidden.
  The snapshot no longer reconstructs the eye states from widgets -- they are kept
  as you set them -- so each tab hides and shows on its own.

================================================================================
v0.90.0 - The lens list reads like the optics
================================================================================

-- The lenses are ordered by throw ratio, not by name ---------------------------
- The Attribute Manager sorted a projector's lenses alphabetically, which on the
  Panasonic references means Standard, Tele, Ultra, Wide -- S before T before U
  before W. On a PT-MZ882B the ultra-short ET-ELU20 (0.33:1) sat fourth, below a
  1.61:1 standard. The list now runs by increasing throw ratio, so it reads
  Ultra, Wide, Standard, Tele: the order the Search window and the Blender
  version have always shown.
- The commercial ratios are the sort key rather than the ratios the formulas give
  at the current screen width. Those move as you drag, and a menu that reorders
  itself under the cursor would be worse than one sorted a hair off.
- A lens whose ratios the manufacturer never published has nothing to sort on and
  goes to the end of the list, by name.

-- The object name keeps up with the lens auto mode picks -----------------------
- Changing the projector or the lens from the Attribute Manager or from Quick
  Config has renamed the object since 0.56.0. Auto mode also changes the lens on
  its own -- while the target or the image-size handle is being dragged and the
  throw ratio crosses a lens boundary -- and that path writes the new lens
  straight onto the object without sending a parameter message, so the name kept
  a lens the projector no longer had.
- The name is reconciled with the database selection once per evaluation pass
  now, in the scene hook. That catches every path at once instead of the ones we
  remembered to instrument.
- It costs nothing when nothing moved: the selection a name was composed from is
  remembered next to it, and the check ends there. Only a changed selection
  reaches the database.
- A projector renamed by hand is still left alone. So is one saved before this
  feature existed: opening an old scene renames nothing.

-- The image circle is smaller, and in the corner --------------------------------
- The overlay disc was drawn from a 100-pixel span and floated 80 pixels above
  the bottom edge and 24 in from the left. The span is 76 now, 12 in from the
  left and 30 up -- flush into the bottom-left corner, keeping only what Cinema
  4D's own viewport HUD line needs. The icon bar and the projector name still sit
  above the circle.
- Several projectors showing their circle at once still tile bottom-left, as many
  per row as the frame width takes.
- Two overlays could run into each other, and so could the names above them. The
  grid sized its cells at 1.2 times the span while the image circle the database
  gives runs from 1.0 to 1.7 matrix widths depending on the lens -- so on a
  generous circle the drawing was wider than the cell holding it. Cells are sized
  on 1.7 now, the widest the catalogue has.

-- A probe reads lux and distance, and stops there -------------------------------
- Every row of the probe readout ended with the projector's name, and with it the
  lens, since a projector object is named "<brand> <projector> - <lens>". It was
  the widest thing in the panel, for something already on screen. A row is the
  illuminance and the throw distance now.
- The Lux Meter tool draws the same panel and reads the same way.

================================================================================
v0.89.0 - Icons on the commands, and the application's own blue
================================================================================

-- The commands have icons -----------------------------------------------------
- Projector Catalog, Projector Search, Force Database Update, Remap Licence,
  Projector Configuration and View From Projector all registered with no icon at
  all, so they sat in the menus as bare text between entries that had one. Each
  now carries its own.
- They are drawn in code rather than shipped as files, for the same reason the
  panels are: a file can go missing in packaging, and the icon folder is
  resolved at runtime from a path that has already found nothing once. An icon
  compiled into the plugin cannot be lost on the way to a machine.
- Rasterised at four times the size and filtered down, so the edges are smooth
  at 32 pixels rather than the staircase a direct draw gives.

-- The accent is Cinema 4D's blue, not the website's violet ---------------------
- Every drawn surface carried Nocturne's #9184d9, which is the web front end's
  accent. It was never this window's: a violet edge on a Cinema 4D panel reads
  as something pasted into the application. The accent is now #545a99, the
  interface highlight Cinema 4D itself paints active tabs and pressed buttons
  with, so the panels read as part of the host.
- Thin work -- hairlines, the monogram, accented text -- takes the same colour
  one step up, because at one pixel on a dark ground the base accent is a smudge.
- The projector search window had carried a blue of its own, close to the brand
  and never equal to it. It is the same colour as everything else now.
- The web front end keeps the violet. The divergence is the point: one accent
  belongs to the product, the other to the application hosting it.

-- Custom mode counted fields that were not on screen ---------------------------
- A straight lens has no L2, a non-180 lens has no L3, and neither has a
  rotation -- those rows are never built. Closing the panel still reported them
  as edited, because reading a Cinema 4D gadget that does not exist SUCCEEDS and
  hands back zero rather than failing, so each one looked like a value changed
  to 0 against the figure still stored on the object.
- Which fields the current lens type actually builds was written out inside the
  snapshot and nowhere else. It is one function now, asked by everything that
  needs it -- the third defect this month traced to the same shape of omission.

-- Configure: the Info group was missing ---------------------------------------
- The Attribute Manager's Info tab could be hidden by nothing: the Configure
  panel listed Optics, Calculations and Display, and stopped there. It has an
  eye of its own now, remembered with the rest.

-- Configure: Apply, and what counts as a change -------------------------------
- Apply then Close asked "Close without applying 8 changes?" with nothing left
  to apply. The eyes became drawn areas rather than checkboxes in 0.88.0, and
  reading a gadget that no longer exists returns false rather than failing --
  so all of them counted as changed, for ever. The count is taken from the same
  state the eyes are drawn from now.
- A shift limit typed by hand is normalised before it is stored and was compared
  before being normalised, so certain values never stopped counting either.

-- Configure: "Show all" stays on its side of the panel ------------------------
- "Show all" reached across both modes: used in Custom it also opened and closed
  the four database groups, and the other way round. Worse, it was changing eyes
  the panel was not showing, which then counted as pending changes nobody could
  see -- the likeliest source of the phantom counts above.
- The test that says which eye belongs to which mode was written out by hand in
  one place and implied in three others, which is how the Info eye came to be
  missed by it on the day it was added. There is one of them now.

================================================================================
v0.88.0 - The Attribute Manager draws itself
================================================================================

-- The Info tab is a datasheet, not a list ------------------------------------
- Twenty-four label/value rows became one drawn sheet: a header with the model,
  brand, series and reference, then imaging, luminosity, physical and lens in a
  two-column grid. It says the same things in a fifth of the height.
- A figure the manufacturer never published reads "Unknown" at the dimmest step
  and loses its weight, so it is legible without ever being mistaken for a
  measurement. The header counts them once, rather than leaving you to discover
  the gaps cell by cell.
- Selecting several projectors that are not the same model says so, instead of
  showing the first one's datasheet as if it spoke for all of them.
- Two figures were wrong on the old rows and are fixed here: the chip size
  printed an unpublished value as -1.00", and dimensions were metres formatted
  as centimetres.

-- Calculation Method: one widget, one grid -----------------------------------
- The tab row and the four values were separate widgets. A tab strip sizes its
  tabs to their labels while a layout group sizes its columns to their content,
  so the two rows could never line up -- and the value cells carried no label at
  all, which left their position as the only way to tell them apart. They are
  now a single drawn widget on one grid, aligned by construction.
- Each column states its role: the one you type into, the distance measured from
  the scene, and the ones derived from them. In distance mode nothing is
  editable, which was true before and is now visible.
- The value being typed stays a native field, so units, arrows, undo and
  animation keep working exactly as they did.
- The numbers stopped losing their unit off the end of a narrow column:
  "85.364 m..." and "7755.5 l..." are "85.36 mm" and "7756 lux".

-- The Configure panel ---------------------------------------------------------
- An eye marks what the Attribute Manager will show. The square checkbox used to
  mean two different things -- hiding a row and setting a value -- and now keeps
  only the second.
- Database mode shows the specifications of the machine you picked. It used to
  be two dropdowns and nothing else: you chose a model by name and had to close
  the window to find out what it was.
- A drawn header band carries the bound projector, its mode and the size of the
  selection.
- Apply counts what is waiting: "Apply (3)". Close no longer discards pending
  edits without a word.
- Sections are named rather than numbered; the numbering ran 1, 2, 3, 4, 5, 8, 9
  and the gaps were visible. The stack section stopped shipping the placeholder
  text "(to be defined)".
- Every numeric field in custom mode has the arrows the resolution rows had.

-- Build -----------------------------------------------------------------------
- Restored the ad-hoc macOS signing step, which a Cinema 4D SDK refresh had
  silently reverted along with the rest of the vendored cmake file. macOS builds
  had been shipping unsigned. The block now carries a marker so the next refresh
  shows it in the diff.

================================================================================
v0.87.1 - Signing in connects, and does so at once
================================================================================

-- The licence window reaches the server -------------------------------------
- Signing in no longer fails with "SSL server verification failed". The client
  now checks the server certificate against the operating system's trust store
  (the system bundle on macOS, the system store on Windows) instead of a path
  that, inside Cinema 4D, resolved to no certificates at all -- the server and
  its certificate were valid the whole time.

-- ...and without the long wait ----------------------------------------------
- Signing in used to freeze for five to twenty seconds after the click. The
  work itself takes under a second; the wait was the window relying on its own
  timer to notice the answer, a timer Cinema 4D only services when the event
  loop happens to wake. The background thread now wakes the window the instant
  it is done, so the result appears at once.
- Each request also asks the server to close the connection when it answers.
  That removes a separate stall where the encrypted connection's shutdown
  waited on a keep-alive server that never volunteered to hang up.

================================================================================
v0.87.0 - A column for the tech, and specifications that admit what they do not know
================================================================================

-- A specification nobody published says so ----------------------------------
- The database now spells "the manufacturer does not publish this" as NULL,
  and the dialog reads it back as "Unknown" instead of inventing a figure.
  The stat tiles were the worst of it: Grouped() prints the absolute value,
  so an unpublished brightness came out as "1 lm ISO" and an unpublished
  weight as "-1.0 kg", both of which read as measurements.
- Max inclination words each axis on its own. One may be documented and the
  other not, and the line used to print "V -1" beside a real figure.
- A numeric filter is no longer satisfied by a figure that does not exist:
  "weight<20" used to list every projector whose weight is simply missing
  from the database, right at the top of the results.

-- The tech reads from the left --------------------------------------------
- The chip and lamp were set against the right edge of the model column, so
  every row started them wherever its own name happened to end. They have a
  column of their own now, left-aligned under the TECH heading like every
  other column in the table, and the model name takes the width left over.

-- The Specs tab stops answering the lens question ---------------------------
- The "lens for your throw" card is gone from it. The Lenses tab asks the shot
  and judges every compatible lens against it, so the card was the same
  question answered twice and a shorter answer at that -- two answers that
  would have drifted apart the next time one of them changed.

-- Compare holds whatever is pinned -----------------------------------------
- The four-model ceiling is gone. Columns keep a readable minimum width and
  the table scrolls sideways when they no longer fit, with the row labels
  painted over the columns that pass under them so the figures stay readable
  against what they are figures of.
- The pinned list scrolls too, rather than running off its panel.
- "best" no longer eats the value it is marking: the number is fitted first
  and the marker takes what is left of the column. A column too narrow for
  both still says which one won -- in the accent, rather than in a word.

================================================================================
v0.86.0 - The Lenses tab answers the shot
================================================================================

-- Which lens do I need? -----------------------------------------------------
- The Lenses tab of the detail panel now carries the two numbers the question
  actually needs -- image width and throw distance -- with the ratio they come
  to underneath. It is the same shot the Fit-the-shot page works from, so a
  distance typed here re-ranks that page too.
- Every lens is judged against that ratio and says so in its own column: too
  wide, at min, fits, at max, too long. The list is ordered by throw ratio
  rather than default-first, so top to bottom it reads too wide -> fits ->
  too long; the default lens keeps its own mark.
- The Specs tab says "too wide" as well, where it used to say "too short" --
  one word for one situation, whichever panel it is read in.
- The panel's heading and its three tabs moved into an area of their own, so
  the fields can sit between the tabs and the list where the design puts them:
  a text field cannot be drawn inside a user area.

-- Everything the record holds -----------------------------------------------
- With the shot fields gone from it, the specification list has the room for
  the rest of the database: ANSI brightness, both eco figures, eco and strike
  power, the chip's second dimension, the stacking frame's height and the
  commercial series.
- A line the model has no figure for is dropped instead of printed as a dash:
  a panel of dashes reads as a broken database rather than as a gap.

-- The shot diagram was losing its top edge ----------------------------------
- Its box was sized before the type grew, so the screen it draws ran off the
  top and the "screen 5.00 m" label sat on top of it. Each label has a band of
  its own now and the cone takes what is left, both measured from the font
  rather than fixed -- the next change of size cannot bring the fault back.

-- Near misses ---------------------------------------------------------------
- Listed again by default. The band that opens them says plainly where the
  answers stop, so they no longer read as part of the list above them.

================================================================================
v0.85.0 - The type scale, and columns that line up
================================================================================

-- One size up, everywhere ---------------------------------------------------
- FONT_STANDARD is the interface's base size, and the design sets its body
  three points above it, so the whole window was a size down from the mockup at
  once. The body is FONT_BIG now, which puts the captions back at the base size
  and the headings at FONT_HUGE: roughly the design's 10 / 13 / 18 instead of
  10 / 11 / 14.
- The number columns were re-measured against the bigger face, and no wider
  than their widest real value: every pixel taken there comes off the column
  that has to hold a sentence.

-- The chip and lamp have a column ------------------------------------------
- They were drawn straight after the model name, so each row started them at a
  different x and the eye read a staircase instead of a table. The name is set
  against the left edge of the column and the chip and lamp against the right,
  so both sides are flush and the ragged gap falls between them -- where it
  says nothing. A TECH heading marks the column.
- Nothing is cut to make that work: only a name long enough to reach the far
  side takes room away, and only then does the tech give way.

-- Label right, value left --------------------------------------------------
- The specifications ran label hard left and value hard right, so a short pair
  left a hand's width of nothing in between and the eye had to travel back to
  pair them up. Both halves are set against a common centre now, and the column
  of labels reads as one thing. Same for the row headings in Compare.

================================================================================
v0.84.2 - Build fix: the two new type sizes are 2025 and up
================================================================================

- v0.84.0 reached for FONT_HUGE and FONT_SMALL_BOLD. They arrived with the 2025
  SDK; on R25 and 2024 the enum stops at FONT_BIG_BOLD, and those two builds
  failed on "identifier not declared" while 2025 and 2026 went green. Guarded
  behind MAXON_API_ABI_VERSION, the way the rest of the plugin already handles
  SDK differences: R25 and 2024 lose a type size, not the plugin.
- Both sides of the guard are compiled locally now before a tag goes out. A
  green build here is a green build on 2026 and says nothing about R25.

================================================================================
v0.84.1 - Brighter text
================================================================================

- The window took its text colour straight from the theme, which is a
  comfortable grey for a manager full of small labels and too quiet for a
  reading surface. It is now lifted halfway towards the extreme, and the four
  secondary tones with it.
- Lifted AWAY FROM THE BACKGROUND rather than towards white: on a light theme
  that means darker text, not a washed out one. And stopped halfway, because
  the extreme glares.

================================================================================
v0.84.0 - Readable, and only what fits
================================================================================

-- The window reads at a glance ----------------------------------------------
- Cinema 4D has more than the three faces the drawn window was using. FONT_HUGE
  is +7 points and FONT_SMALL_BOLD is a point smaller and bold, which is what
  the design sets its 19px headings and its 10px all-caps captions at. The
  selected projector's name is now the size it is in the design, and every
  caption -- PIN / MODEL / LUMENS, BRIGHTNESS, THROW RATIO, PINNED FROM THE
  LIST -- is small, bold and upper case rather than body text in capitals.
- The model name is bold on every row, not only on the selected one: it is what
  the row is about. So is the brightness, in the list and on the fit cards.
- The secondary tones were too far down -- 0.86 / 0.70 / 0.54 of the way from
  the background to the text reads as grey on grey. They are 0.93 / 0.82 / 0.68
  now. A caption has to be readable to be worth drawing.

-- Fit the shot answers the question it was asked ----------------------------
- Only the projectors that fit are listed. The near misses were mixed in with
  them, dimmed, and dimming is not a separation: the list had to be re-read to
  find where the answers stopped.
- "Show the near misses" at the top right brings them back, under a band across
  the full width that says how many follow. They keep their tag and their
  reason.

================================================================================
v0.83.0 - Pin from where you are judging
================================================================================

- Fit the shot has a Pin box on every card, in the same place and the same
  shape as the one in the List tab. Deciding which projectors are worth lining
  up side by side happens while reading the cards that judge them, so having to
  go back to the List tab to tick them was a trip for nothing.
- A pinned card carries the same tint and outline a pinned row does, so what
  Compare holds is legible from either tab.
- Toggling a pin no longer sends the fit list back to the top.

================================================================================
v0.82.0 - Projector Search, drawn
================================================================================

The window is no longer a stack of Cinema 4D gadgets. Everything below the
query field is a GeUserArea that draws itself and reads its own clicks: a table
whose rows carry two type sizes and a state, filter chips you take off by
clicking them, the shot drawn as a shot, cards with the lens range under them.
Only what has to be typed stayed a native gadget -- the query, the six shot
numbers, the three buttons.

-- One window, three modes ---------------------------------------------------
- List, Fit the shot and Compare, sharing one search, one selection, one set of
  pins and one shot. The mode bar carries live counts, and the shot it all
  hangs on is written along the right of it with an Edit that jumps there.

-- The search box takes a query, not a form ----------------------------------
- The two rows of combo boxes and spin fields are gone. What replaces them is
  the field itself: "brand=Panasonic lumens>25000 weight<80" reads the way it
  is meant. "=", ":", ">", "<", ">=" and "<=" all work, "a-b" is a range, a
  quoted value keeps its spaces, and anything without a key still matches the
  brand, label, reference and model name.
- Queries written before this grammar still mean what they meant: ":" keeps its
  historical reading, so a bare "lumens:5000" is still "at least 5000".
- Lens conditions (lens, lenstype, throw, fvalue) must be satisfied by a SINGLE
  compatible lens, as before -- asking for a short-throw zoom under f/2.8 finds
  projectors that have one lens doing both, not one lens each.
- Every condition becomes a chip under the field, in words: "lumens > 25 000
  lm", "weight < 80 kg". Clicking one takes that term back out of the query. A
  half-typed term filters nothing and shows no chip, so the list does not empty
  itself while you type.

-- The results table ---------------------------------------------------------
- Model, brand, chip and lamp on one line, the numbers right-aligned in their
  own columns, group headers on a recessed band, the selected row on an accent
  wash with a bar down its left edge.
- A Pin column at the far left. Pinned rows keep a tint of their own, and they
  are what the Compare mode compares.
- The column headings sort; the "Sort / Group" line opens a menu with both.
- The list scrolls itself rather than living in a Cinema 4D scroll group: a
  scroll group adopts its content's minimum size, and 78 rows asked for a
  window 2 200 pixels tall.

-- The projector, on the right -----------------------------------------------
- Four tiles for the numbers one opens the panel for -- brightness, weight,
  resolution, power -- then the rest as label/value rows.
- Specs / Lenses / Accessories as a segmented control carrying its own counts.
- The Lenses tab draws every compatible lens with its throw window as a bar on
  a scale shared by the whole projector, and the shot's own ratio as a tick
  across them. Which lens reaches this shot stops being arithmetic.
- Under the specifications, the one line that answers "and which lens, then":
  the lens that covers the shot's throw ratio, its range, and whether it fits.

-- Fit the shot --------------------------------------------------------------
- A new mode. The shot is described once -- image width and height, throw
  distance, target nits, optional weight and power ceilings -- and drawn: the
  screen, the beam, the distance.
- The lumens the shot needs come from the engine's own photometry read
  backwards. Illuminance.h computes lux = lumens / beam area, so this asks for
  the flux that puts the wanted nits on a matte white screen at gain 1.0. A
  projector that fits here is one the Lux Meter agrees with in the scene.
- The matches come back as cards, ranked by headroom, each with the lens that
  reaches the ratio, where in that lens's zoom the shot falls, and how much
  light is left over. The best one is outlined and says so.
- A near miss keeps its place, dimmed, and says why: "1 500 lm short of 600
  nits on 5.00 m", "no lens reaches 1.70 - closest 1.35", "over the weight
  limit by 12.0 kg". It can be hidden from the toggle at the top right.

-- Compare -------------------------------------------------------------------
- Up to four pinned models, spec against spec, the best value in each row
  marked -- and marked on none of them when they all carry the same number.
- The last two rows are the ones that matter for the job in hand: the lens each
  would wear for this shot, drawn to its own scale, and whether it fits.

-- Double click adds it ------------------------------------------------------
- Double clicking a projector adds it to the scene with its default lens.
  Double clicking a row of the Lenses tab adds it with THAT lens. Double
  clicking a card in Fit the shot adds it with the lens that reaches the ratio.
  The Add to Scene button still does what it did.

-- Colours come from the theme -----------------------------------------------
- Only the accent is fixed. Everything else is mixed from the theme's own
  background and text, in two directions -- towards the text for what comes
  forward, away from it for what recedes -- so the window sits on a light theme
  as well as on a dark one. The old fixed green / yellow / amber / red on the
  Lumens and Weight columns read as a warning in one and as noise in the other.

-- Fixed ---------------------------------------------------------------------
- Dimensions were printed in centimetres from a value stored in metres, so a
  110 cm projector read "1 cm". (projector_generator.cpp had it right; this
  window did not.) The width / height / depth search keywords are documented as
  metres for the same reason.
- Brightness and power figures are grouped ("40 000 lm"), like every other
  number in the window.

-- Also carried by this tag --------------------------------------------------
- Projector Catalog lost its menu flyout. Brand > Serie > Model > Lens was four
  levels deep to reach a model, and a menu cannot carry a search field. It is
  one window now: a search field, every projector in the database as a list,
  and a click that adds the projector with its default lens -- the same shape
  as the Blender add-on's Projector Catalog. Catalog is the "I already know the
  model" path; Projector Search is everything else.
- The v0.81.0 section below never got a tag of its own, so the licence window's
  rework ships here too.

================================================================================
v0.81.0 - The licence window says who you are
================================================================================

-- The account is the subject of the window, and now looks like it ------------
- The account and the plan were two static texts in the same body as every
  label around them, when they are the two things one opens this window to
  read. They are now drawn in a card of their own: the address in the big bold
  face, the plan as an accent chip on the right. AddStaticText cannot change
  type size; a user area can.
- A long address drops a type size before it is cut short, and is cut at the
  end rather than being allowed to push the plan chip off the card.
- Signed out, the card keeps the remembered address, muted: it says which
  account the prefilled field is about to sign in.

-- One deadline, in one place -------------------------------------------------
- "Sign in again within 12 days to extend the offline window" was written twice
  over: once in days by the status message, once as a date by a line of its own
  underneath. The status message is now just "Licence active.", and the
  deadline is the detail line inside the state box, under the headline it
  belongs to -- one block, one place to look.
- The box holds two texts from now on: the headline is the last thing that
  happened (a state, a result, an error), the detail underneath is the deadline,
  which stays put while results come and go above it.
- Both areas tint their ground from the Cinema 4D theme's own background colour
  instead of painting the bare one, so the state reads as a block on a light
  theme as well as a dark one.

-- Two things the form got wrong ----------------------------------------------
- "Email address" was cut short: its label was fixed at 96 pixels, which is
  narrower than the words on any theme whose interface font is a little wider
  than the default.
- The password note was split across two static texts, because a static text
  does not wrap and the first wording ran past the window. It is one shortened
  sentence on one line now.

- Nothing changed in what the window does: the same three gestures, the same
  calls, the same enforcement in the compiled library.

================================================================================
v0.80.1 - v0.80.0 delivered nothing
================================================================================

- The four SDK builds of v0.80.0 all failed before compiling a single file, on
  the source parity guard: DatabasePack.cpp arrived in common-motor-cpp with the
  compressed-database work, and the project-tool build lists every source by
  hand. Nothing was published, so the fix goes out as a new tag rather than by
  moving the old one.
- projectdefinition.txt now carries the four pieces the cmake builds get from
  target_compile_definitions: DatabasePack.cpp and miniz.c in Include.Win and
  Include.OSX, the miniz include directory, and MINIZ_NO_ZLIB_COMPATIBLE_NAMES
  -- without which miniz defines uncompress and crc32 as globals, in a plugin
  loaded into a Cinema 4D that carries its own zlib.
- No change to what the plugin does. v0.80.0's release notes still describe it.

================================================================================
v0.80.0 - The database arrives compressed
================================================================================

-- Compressed under the encryption, because that is the only place it works ---
- The database travels as `projectors.db.enc`, and that file is AES ciphertext:
  nothing in front of it can shrink it. Transport compression gains zero bytes
  on ciphertext. The only place compression pays is INSIDE the encryption, and
  that only works if the plugin knows how to inflate what it receives.
- The engine now reads a container: "RMPZ", a format version, the uncompressed
  size, then a deflate stream. An explicit header rather than a guess at the
  bytes -- a SQLite file and a compressed one can be told apart by sniffing, but
  getting that wrong hands the catalogue a database made of noise, and that is
  not a question to settle on a hunch.
- On the current-schema database this is roughly a quarter of the bytes,
  downloaded and on disk both. The weight is the 3D geometry, stored as JSON
  text, which compresses by about 84%; the textures barely move, being PNG
  already.

-- Read at the door, not at the download -------------------------------------
- The inflate happens where the database is opened, not where it is installed.
  The file outlives its download by months and is reopened every session,
  including by the lazy paths that fetch geometry and textures. A decision taken
  once at install time would have to be remembered somewhere -- and a plugin
  rolled back to a build that cannot inflate would read that remembered answer
  and hand back geometry made of deflate.

-- The plugin says what it can read, on every request ------------------------
- The updater announces `X-Remap-Accept-Payload-Encoding: deflate`, on the
  manifest request as well as the download. The service compresses only for
  callers that ask; the manifest announces the hash of the copy THAT caller
  would receive, so asking on one request and not the other would fail the
  comparison every time and be reported as a corrupt download.
- A plugin that does not ask keeps receiving exactly what it received before.
  That is what protects every build already installed.

-- Engine ---------------------------------------------------------------------
- common-motor-cpp moves to the commit carrying `DatabasePack` and the vendored
  miniz codec. miniz is compiled with MINIZ_NO_ZLIB_COMPATIBLE_NAMES: without
  it, it defines `uncompress` and `crc32` as plain globals, and this plugin is
  loaded into Cinema 4D, which carries its own zlib.

================================================================================
v0.79.0 - Signing in downloads the database, and says so
================================================================================

-- The licence window fetches the database, in front of you ------------------
- Cinema 4D starts signed out, so the startup database check bows out: no
  licence, nothing to open, nothing downloaded. Signing in was the moment that
  could be fixed, and it already triggered a download -- on a detached thread
  that told nobody. 18 MB went by behind the words "Signed in", and a failure
  came back later as a message box with no visible link to the click.
- The download now runs from the licence window and reports in the same box the
  sign-in answered in: "Downloading the projector database...", then the version
  that landed, or why it did not. A first install that fails is shown in red -
  the catalogue is empty; a refresh that fails is shown in amber - the previous
  database still works.
- One entry point for "get the database into shape" (SyncProjectorDatabase),
  shared by the startup check and the window, with a guard so the two cannot
  download over each other when someone signs in while the check is running.

-- The window says which licence, and until when -----------------------------
- Two new lines under the account: the plan (Trial / Active subscription /
  Payment overdue) and the deadline - "Sign in again within 12 days (works
  offline until 2026-09-08)". The number of days answers the question people
  actually ask; the date is what they can write in a calendar.
- Shown only while the licence verifies. In every other state those figures
  come from a token that was just refused, and the message already says what
  to do about it.

-- The window is in English --------------------------------------------------
- Labels, buttons and messages of the licence window and of the licence store,
  in line with the rest of the product. The Blender extension got the same
  wording on the same day, deliberately: two plugins that drifted on "what does
  an expired licence say" would give the same customer two answers.

================================================================================
v0.78.0 — La base se telecharge, elle ne se livre plus
================================================================================

-- Le plugin ne transporte plus la base de projecteurs -----------------------
- Les paquets R25, 2024 et 2025 emportaient 18 Mo de base chiffree que le
  plugin est cense aller chercher lui-meme. Ils passent de ~21 Mo a ~4 Mo, au
  niveau du paquet 2026 qui, lui, avait toujours ete correct.
- Ce n'etait pas qu'une question de poids : une base embarquee devient
  illisible des la rotation suivante de la cle de chiffrement. Le moteur ne
  sait plus l'ouvrir, le catalogue est vide, et rien ne l'explique.
- Deux chemins d'empaquetage cohabitent (project-tool pour R25/2024/2025, cmake
  pour 2026) et ils divergeaient depuis des semaines sans que personne le voie.
  La CI verifie desormais l'archive elle-meme : aucun .db ni .enc ne peut plus
  y entrer, par l'un ou l'autre chemin.

-- Les zips ne disent plus << cpp >> -----------------------------------------
- c4d-plugin_v0.78.0_cpp_2025_0_1-win.zip devient
  c4d-plugin_v0.78.0_2025-win.zip. L'ancien nom venait du dossier du SDK Maxon
  tronque : exact et illisible, puisque << cpp >> designe le SDK C++ et non le
  langage, et que << 2025_0_1 >> est une version de SDK, pas de Cinema 4D.
- Les quatre etiquettes sont r25, 2024, 2025, 2026. La correspondance est une
  table explicite : un SDK inconnu fait echouer le build la ou on l'ajoute,
  au lieu de produire en silence un nom que la CI attendra en vain plus loin.

-- La base vient du service de licences, plus du CDN --------------------------
- Le plugin va chercher la base sur https://remapengine.duckdns.org, derriere
  son jeton de licence, et non plus directement sur le CDN. Les BINAIRES de
  plugin, eux, continuent d'arriver du CDN : ils ne valent rien seuls, et faire
  transiter des megaoctets pour proteger un zip serait du cout sans benefice.
- Ce que ca change : le service remet a chaque compte une copie de la base
  chiffree avec SA cle, de sorte qu'une cle qui refait surface designe un compte
  au lieu de ne designer personne. Actif cote serveur depuis le 2026-08-27,
  publie en meme temps que cette version -- il n'y avait pas encore
  d'utilisateurs, donc pas de flotte a migrer.
- Consequence de deploiement : cette version exige un front end qui expose
  /api/downloads/db. Deployer le service AVANT de publier ce plugin.
- Les deux constantes XOR-voilees qui portaient l'adresse du CDN ont disparu
  avec leur dernier usage. La CI continue de verifier que << remapcdn >>
  n'apparait pas dans le binaire : ce n'est plus un voile a maintenir, c'est
  une garde contre la reintroduction d'un acces direct.

-- Le premier demarrage telecharge la base -----------------------------------
- Le controle de mise a jour au demarrage abandonnait quand la base etait
  absente. C'etait coherent tant qu'elle partait dans le zip ; depuis qu'elle
  n'y est plus, ce meme abandon livrait un plugin sans base et sans moyen d'en
  obtenir une, sauf a trouver << Force Database Update >> dans un menu.
  Il la telecharge maintenant, une fois, et la met en cache.
- Le telechargement est aussi declenche juste apres une connexion reussie. Au
  tout premier lancement il n'existe aucun jeton, donc le controle du demarrage
  s'etait deja retire faute de licence : sans ce second point d'entree, il
  aurait fallu redemarrer Cinema 4D pour obtenir une base apres s'etre connecte.
- Un echec le dit dans une fenetre, avec la raison et la marche a suivre,
  plutot que dans la console.

================================================================================
v0.77.1 — Le plugin exige une licence
================================================================================

-- La base ne s'ouvre plus sans licence --------------------------------------
- Le plugin refuse d'ouvrir la base de projecteurs tant qu'une licence valide
  n'a pas ete presentee. La base EST le produit (geometrie, optique, modele
  d'eclairement), donc la verrouiller verrouille tout.
- Le refus vit dans la bibliotheque compilee, pas dans une couche au-dessus :
  contourner le controle revient a se passer du moteur.
- Nouveau menu << Licence Remap... >> : se connecter, voir l'etat, se
  deconnecter. Rien d'autre. La saisie du mot de passe est masquee
  (EDITTEXT_PASSWORD) et le mot de passe n'est jamais conserve : il sert a
  obtenir le jeton de licence, puis il est oublie.
- La fenetre hors ligne n'est pas un compteur : c'est la duree de vie du jeton,
  14 jours, reemise a chaque demarrage connecte. Etre hors ligne ne bloque
  jamais en soi ; l'etre au-dela de la fenetre bloque. Le demarrage ne touche
  pas au reseau ; le rafraichissement se fait en arriere-plan.
- Une licence tenue par une autre machine peut etre deplacee, apres
  confirmation : l'autre poste la perd.

-- La fenetre de licence ------------------------------------------------------
- Les commandes du plugin s'eteignent quand la licence manque, au lieu de
  s'ouvrir puis d'echouer. Un menu grise dit ce qui se passe avant qu'on
  clique ; un menu qui repond par une erreur le dit apres.
- La fenetre de connexion reste vivante pendant l'appel reseau : le travail se
  fait en arriere-plan et la fenetre se rafraichit sur son Timer, plutot que
  de figer Cinema 4D le temps d'un aller-retour au serveur.
- Dialogue redessine aux couleurs du produit, bandeau trace au trait plutot
  qu'en image -- il reste net a toutes les echelles d'interface.

-- La base est chiffree sur le disque, et le reste ---------------------------
- La base de projecteurs est livree chiffree (AES-256) et n'existe JAMAIS en
  clair sur le disque : le moteur la dechiffre en memoire, cle en main, a
  l'ouverture. La cle n'est compilee nulle part -- elle arrive dans le jeton
  de licence signe, par utilisateur et par machine (chiffrement a enveloppe).
  Copier la base et le jeton sur un autre poste ne donne rien : le jeton ne
  verifie pas ailleurs que sur la machine pour laquelle il a ete emis.
- Les mises a jour en ligne suivent : le fichier chiffre du CDN s'installe
  TEL QUEL, verifie par son empreinte (payload_hash du manifest). L'updater
  ne dechiffre plus jamais rien -- il n'a pas de cle, c'est le principe.
- L'ancienne cle AES compilee dans les versions precedentes a ete RETIREE et
  la cle de la base a tourne : les fichiers publies ne sont plus lisibles avec
  ce qu'un binaire v0.76 contient. La CI verifie sur chaque artefact que ces
  octets n'y reviennent jamais, et que l'adresse du CDN n'apparait pas en
  clair (voilee dans le binaire).
- L'ancien sous-module de donnees common-motor (module Python historique) est
  supprime : la base chiffree est versionnee directement (LFS), et le moteur
  est common-motor-cpp seul.
- La verification de mise a jour ne se lance que si une licence est active :
  sans licence le produit est ferme, telecharger une base qu'on ne peut pas
  ouvrir ne serait que de la bande passante depensee.

-- Limites assumees ----------------------------------------------------------
- Le voile sur l'adresse du CDN est un voile, pas un secret : le trafic reseau
  la revele. Ce qui protege la base est sa cle, pas son adresse.
- La charge du jeton est signee mais pas chiffree : un licencie peut en
  extraire la cle de SA base. Inherent -- le client legitime doit pouvoir
  dechiffrer. Ce qui est garanti : on ne peut ni fabriquer un jeton, ni s'en
  servir sur une autre machine.

================================================================================
v0.76.2 — Nouvelle adresse du CDN (mise a jour REQUISE)
================================================================================

-- Le CDN a son propre nom d'hote -------------------------------------------
- La base de projecteurs est desormais servie depuis remapcdn.duckdns.org, un
  nom dedie qui ne depend d'aucun autre service de la machine. Le jour ou le
  CDN part chez un vrai diffuseur, l'adresse se repointe en DNS sans qu'une
  nouvelle version du plugin soit necessaire — ce qui est precisement le
  probleme que cette version-ci corrige.
- L'ancienne adresse, boiteakk.duckdns.org/projectors-db, a ete retiree le
  2026-08-25 et repond maintenant 410 Gone.

-- Consequence : les versions anterieures ne se mettent plus a jour -----------
- Tout plugin en v0.76.1 ou anterieure interroge l'ancienne adresse et ne
  recoit donc plus aucune mise a jour de la base. La base deja telechargee
  continue de fonctionner normalement, hors ligne : rien n'est perdu, mais
  plus rien n'arrive.
- Installer cette version retablit les mises a jour. La base locale est
  conservee, aucune autre action n'est necessaire.

-- Verifie avant publication -------------------------------------------------
- Manifeste, base complete et deltas repondent sur la nouvelle adresse, en
  canal prod comme en canal test, et un delta reel se telecharge.

================================================================================
v0.76.1 — Projector Pivot: SW-mode fixes (target drag, mode switch, lens swap)
================================================================================

-- SW mode + Projector Pivot: the projector no longer dances ------------------
- Dragging the target: the projector BASE is now the world-fixed end for the
  whole drag (frozen when the target handle is grabbed). The body pivots on
  its base to track the target and auto lens switches glide the optics
  sideways instead of kicking the body -- no more dance/recoil.
- Switching Projector Pivot <-> Optical Pivot no longer moves the stored
  target (the old handler re-captured it at the nominal distance along the
  gizmo axis, which teleported it -- sometimes to the OPPOSITE side of the
  projector when the target gizmo was still selected). A mode switch now
  also settles the nominal/world drift so the projected screen re-seats on
  the target immediately, without having to nudge it.
- Swapping the lens in the SW-family input modes now holds the DISTANCE to
  the target (the L-mode invariant): if the picked lens cannot reach
  TR = distance/SW, the image size (SW) is traded through the shared throw
  butee instead of letting the projected screen teleport to the nominal
  distance far off the target -- what a real lens swap does to the image on
  a wall. Lenses that cover the distance leave SW untouched.
- Pose solver for bent-lens swaps: more fixed-point iterations for the large
  (~90 degrees) direction changes of elbowed lenses.
- Known minor issue (to revisit): a small glitch remains right after a lens
  swap while the target handle is still armed.

================================================================================
v0.76.0 — Projector Pivot + mire text plates
================================================================================

-- Projector Pivot: the ID_PROJ_MODE option finally works ---------------------
- The option (renamed "Projector Pivot", 2 entries) now really selects what
  stays world-fixed when the lens or projector is swapped:
  * Optical Pivot: the optics (BoxOrigin) and the image hold their position,
    the body recoils through the lens geometry.
  * Projector Pivot: the projector stays ON ITS BASE (bottom-centre of the
    body bounding box, world-anchored) and PIVOTS AROUND IT so the beam keeps
    hitting the stored target -- like re-aiming a real installed projector
    without moving it. The image stays on target, the optics glide.
  The gizmo/axis always stays at the optics in both modes, and every optical
  formula keeps measuring from the BoxOrigin.
- Auto-lens selection gained hysteresis: when the throw ratio sits in the gap
  between two lens windows, the current lens is kept (its clamp pins TR at
  the window edge) instead of re-picking the nearest lens on every tick --
  this removed an endless bent<->straight flip-flop in L mode.
- The old unconditional compensation (body kept fixed in EVERY mode, which
  made the option inert) is gone; the Projector Pivot pose is solved
  world-anchored and history-free, so lens swap round trips return exactly
  (no more creeping/"dancing" projector).

-- Test card texts: translucent white plate + black outline ------------------
- Every text field of the mire now sits on a backing plate: white at 85 %
  opacity by default, padded around the glyph block, with its own
  show/colour/opacity settings per field. The text outline now defaults to
  2 px black. Keeps the labels readable over the busy card; the Blender
  readouts share the same style.

================================================================================
v0.75.1 — Parametric test card / mire (issue #45)
================================================================================

-- Test Pattern shader: full redesign to the issue #45 design spec ------------
- The mire is now defined in the projector's NATIVE PIXEL space and follows
  the agreed chart: 25% grey ground, magenta 1 px frame (always topmost)
  and centre axes, white 100 px grid justified on the axes (no centred
  cell), blue corner-to-corner diagonals, and concentric circles stepped on
  the grid pitch cycling red/green/blue out to the corners.
- Arrows at every line end, pointing outward and white-outlined: blue
  60 px arrows at the four axis ends and at the four corners (along the
  diagonals, drawn in front), magenta half-size arrows at every grid-line
  end. Height is adjustable (proportions and the 2:1 large/small ratio are
  fixed); the 1 px outline width is its own setting.
- Line width, grid step and every colour are editable; straight strokes
  snap to the native pixel grid so a 1 px line renders as exactly one
  crisp pixel at native resolution.
- Four text fields — projector name, lens, resolution ("1920 x 1200") and
  format ("16:10") — each with its own show/position/size/font/colour/
  opacity/outline settings. Name, lens and the format values are pushed
  automatically from the projector's DB selection (the lens label is new);
  custom projectors without a DB lens simply hide the lens text.
- The first-generation presets, SMPTE bars, greyscale ramp, checker,
  sub-grid, ruler and corner squares are retired.
- The card's geometry is computed by the shared engine (common-motor-cpp),
  so the Blender addon projects the exact same mire.

================================================================================
v0.74.0 — Projector series (DB schema 1.3), shift as % of the half matrix
================================================================================

-- Matrix shift readout: percent of the HALF matrix on each axis (issue #46) --
- The Attribute Manager vertical shift was shown as a percentage of the FULL
  matrix height (raw value / aspect): 45% V for the RQ50K + S400 at max
  shift. Projector spec sheets express each axis as a percentage of its own
  HALF matrix instead -- a floor-standing projector that puts the bottom
  edge of the image on the horizon reads 100% V. The vertical display scale
  is now 2 x resX/resY, so the RQ50K + S400 reads 32% H / 90% V at max
  shift, matching the spec sheet values. Display only: the stored raw
  values, the image-circle clamp and the cone/camera/OpenBox geometry are
  untouched, so existing scenes are unaffected.
- The FINAL clamped value snaps to the display precision (2 decimals of a
  percent), truncated TOWARD ZERO: an image-circle wall stored rounded in
  the DB (0.501 instead of 0.50097656) puts the geometric butee at
  90.0089% -- the stored value now snaps to exactly 90.00, never 90.01, so
  the reachable shift can never read above the spec-sheet maximum. The AM
  display truncates the same way, covering legacy scenes whose stored
  value predates the snap.

-- Projector series (DB schema 1.3) -------------------------------------------
- The database now groups projectors by commercial series (per-brand lookup
  table, e.g. Panasonic "PT-RQ"; schema 1.3, additive — older databases keep
  loading, the writer migrates them on open).
- Catalog flyout: the Extensions menu now reads Brand > Serie > Model > Lens;
  models without a series stay directly under their brand, after the serie
  submenus.
- Projector Search: new "Series" entry in the results "Group by" combo
  (headers read "Brand - Serie") and a new serie: search keyword; plain-word
  searches match the series name too.
- Series are assigned via the backend import (template 3.2, field IdSerie =
  series name, created on the fly within the brand).

================================================================================
v0.73.3 — Auto-lens gap snap, bare casing when no lens fits, viewport polish
================================================================================

-- L mode: per-lens TR windows now use the distance formulas ------------------
- Every consumer of the per-lens throw-ratio windows (availability gauge,
  lens auto-select, gap snap, TR slider bounds, locked-lens range, lens
  dropdown filter) evaluated the LW_SW / LT_SW formulas at the current SW in
  every input mode. In L mode SW = L/TR moves with the slider, so all the
  windows moved too: the gauge columns danced, the auto-select churned
  between lenses on a plain TR drag, and each spurious switch shifted the
  cone apex and target sideways (lens-origin compensation) -- the target
  visibly drifted in X/Z while only TR was edited.
- In L mode those windows are now evaluated with the LW_L / LT_L formulas at
  the held distance (ID_L), which is exactly what the L-mode TR clamp always
  used: stable rails, stable auto-select boundaries, and the target stays
  put. Other modes keep the SW formulas at the current SW.

-- Auto mode: TR/distance snap to the nearest lens window ---------------------
- In auto-lens mode the committed throw ratio can no longer rest in a gap
  BETWEEN two lens windows: it now sticks to the edge of the active lens's
  window and jumps to the nearest lens once the input crosses closer to it,
  so the derived distance (L = TR x SW) snaps between reachable values --
  the same blocked-then-jump feel L mode always had from its per-lens
  formula clamp. The lens coverage count is recomputed against the final
  snapped value so cone colour, gauge and status agree it is reachable.

-- Auto mode: no matching lens = bare casing ----------------------------------
- When no lens covers the current throw ratio (e.g. the target is dragged
  into a gap or beyond the set's range), the projector is now assembled
  WITHOUT the lens, the OpenBox slide plane and the projection light:
  nothing pretends an image can form. Everything remounts as soon as a lens
  window covers the ratio again. Locked mode keeps its lens mounted (the red
  cone signals out-of-range there).

-- Locked lens: red cone honoured the wrong TR range --------------------------
- With "Use Formulas for TR Range" enabled, the in-range test behind the wire
  colour still compared TR against the raw commercial ratios, so the cone
  turned red on values the formula range (and the clamp) legitimately allow.
  The test now runs after the clamp, against the same resolved range, and
  also refreshes on SW / formula-toggle / lens changes.

-- Viewport ---------------------------------------------------------------------
- The projected screen marker is a hollow white rectangle at the screen
  distance again (was four corner ticks).
- The image-circle overlay now also draws the matrix home position (zero
  shift) as a dark grey rectangle under the pink shifted one.
- Fixed two per-frame small memory leaks in the viewport draw path.

================================================================================
v0.73.2 — L mode: cone width exact at the zoom extremes (clamp-order fix)
================================================================================
- At the TR slider's butees the cone drew slightly narrower (~1%) than the
  projected diapo and the look-through camera, and the derived distance L
  drifted by the same amount on every slider edit held at the extreme. Cause:
  SW was derived from the TR clamped by the LW_L/LT_L formula window, then the
  global range clamp (commercial ratios, which differ from the formula window
  by the lens-offset term: 2.00 vs 1.99 wide, 3.41 vs 3.44 tele on the
  ET-D3QT500) tightened TR afterwards, leaving SW on the stale ratio.
- SW is now re-derived from the FINAL clamped TR, so the optical invariant
  L = TR x SW holds exactly at every slider position and the cone, diapo and
  camera agree at the extremes too. L no longer drifts.

================================================================================
v0.73.1 — "Info" datasheet tab, lens-shift overhaul, L-mode throw-ratio fix
================================================================================

-- L mode: the throw ratio drives the image again (formula regression) --------
- Fixes a regression introduced by v0.72.2: once the LW_L / LT_L formulas
  actually evaluated, L mode fed their result straight into the image width.
  Those formulas actually return the THROW RATIO at the lens's wide / tele
  zoom extremes for a given distance -- the code misread them as an SW in
  meters (a ~100x bogus width), so SW snapped to a nonsense value, editing TR
  no longer resized the projection cone, and the cone stopped meeting the
  edges of the projected diapo.
- L mode now keeps the optical invariant SW = L / TR: editing TR resizes the
  image at the held distance, and cone and diapo agree again at every ratio.
  The LW_L / LT_L formulas are used as what they are -- the reachable
  throw-ratio window at the current distance -- clamping the requested TR at
  the lens's zoom limits (their large-L limit is the commercial ratio; the
  affine term models the lens offset).

-- New "Info" tab: the full projector datasheet -------------------------------
- New read-only "Info" tab on the projector object, populated straight from the
  database record for the selected projector (and lens). It gathers everything
  the catalogue knows in one place, grouped into:
  * Identification — brand, model, reference.
  * Imaging / Technology — chip technology (DLP / LCoS type), lamp type
    (LASER / UHP), native resolution (with the XPR-doubled resolution when the
    panel has Quad Pixel Drive), image ratio, chip size, refresh rate, contrast.
  * Luminosity — Normal and Eco lumens, each in ISO and ANSI.
  * Physical / Electrical — dimensions, weight, chassis colour, lens mounting,
    power draw at 220 V (Normal / Eco / Strike), max V/H inclination, stack max.
  * Lens (Selected) — reference, model, type and F-value range of the mounted
    optic; it updates when you switch lenses.
- The tab appears only in Database mode (hidden for Custom projectors and while
  no projector is selected). Every field falls back to "-" when the underlying
  column is empty, so nothing ever reads as a blank or stale value.
- Chip-technology and lamp-type labels are resolved from their lookup tables, not
  shown as raw ids.

-- Lens shift: correct direction, spec-convention %, exact IC limits ----------
- Vertical shift direction fixed: dragging the matrix UP in the image-circle
  overlay now shifts the projected image UP (previously it went down). The sign
  is flipped in lockstep on all three consumers — the viewport cone, the
  look-through camera's film offset and the OpenBox projected slide (DB and
  custom assemblers) — so the cone, the render and the projected geometry stay
  consistent. Horizontal is unchanged.
- Shift percentages now follow the projector-spec convention. Horizontal is shown
  as a percentage of the HALF image width (x2), vertical as a percentage of the
  FULL image height (raw / aspect). On the PT-RQ50K / ET-D3QT500 the max reads
  32.0% horizontal and 45.0% vertical (was 16% / 23.7%). This is display-only:
  the stored value, the clamp and all optics keep using the raw IC-normalised
  shift, so nothing about the geometry changes.
- The maximum shift now equals the true geometric edge of the image circle. The
  clamp applied a 0.002 inset to the matrix RECTANGLE (halfW/halfH - margin)
  instead of to the image-circle polygon, which let the matrix centre overshoot
  the real boundary by 0.2% of the matrix width (16.2% instead of the exact
  16.0% = 0.66 - 0.5 horizontally). The inset is removed (margin = 0); a genuine
  safety margin, if ever needed, must inset the polygon, not shrink the rectangle.

================================================================================
v0.72.2 — "Use Formulas for TR Range" actually applies the formulas
================================================================================
- The projector/lens throw-ratio formulas are now evaluated. The formula parser
  was substituting the obsolete RMH/RMV variable names, but the database formulas
  use MH/MV (the raw MatrixH/MatrixV resolution) — so every formula silently
  failed and the TR range always fell back to the commercial ratios, in every
  mode.
- With the toggle ON (locked lens), the TR slider bounds now follow the LW_SW /
  LT_SW formulas at the current image size; OFF keeps the commercial ratios. The
  auto-mode global envelope, the projection-cone markers and the screen-width
  butée go through the same formulas too.
- Formulas are fed the raw MatrixH/MatrixV (not the reduced aspect ratio), and
  toggling the checkbox refreshes the slider bounds immediately.

================================================================================
v0.72.0 — Projector body texture loaded straight from the database
================================================================================
- New "Projector DB Texture" channel shader (ID 1069828): the projector body
  albedo is now sampled directly from the database texture BLOB, decoded in
  memory at render time. The texture is never written to disk, so it cannot leak
  as a loose file, and the .c4d stores only a hidden integer key — not a file
  path. This removes the stale-path "black projector" failure mode for good
  (superseding the v0.69.0 temp-dir workaround).
- Old documents self-heal: on rebuild, a projector whose body still carries the
  legacy on-disk bitmap is upgraded to the new shader automatically.
- Graceful fallback: if the database (or that texture) is unavailable at render
  time — e.g. a net-render node without the DB — the body falls back to the
  material's constant albedo colour instead of rendering black.

================================================================================
v0.71.0 — Render-matched look-through camera + "View From Projector"
================================================================================
- Looking through a projector now renders EXACTLY its view:
    * Camera focal length is driven by the throw ratio (horizontal FOV = the
      projection cone), aperture fixed at 36 mm. Works in DB and custom mode, no
      chip data needed.
    * Lens shift is mapped to the camera film offset: X maps 1:1, Y is scaled by
      matrixH/matrixV (width->height conversion) — both keep the shift's sign.
    * Render settings auto-sync to the active projector's native resolution
      (XRES/YRES + film aspect) while looking through it, and are restored to the
      user's originals when switching to another camera. Originals are stored in
      the render data, so they survive document save/reload.
- New command "View From Projector" (ID 1069815): moves the active viewport camera
  to the selected projector's viewpoint. If the view is already a projector (a
  locked camera), the default editor camera is used instead.

================================================================================
v0.70.0 — Image-circle shift overlay fixes
================================================================================
- Fix DB-mode IC overlay drawn upside-down (Y-flip centralised in IcScreenTransform).
- Double-click the matrix rectangle to reset H/V shift to 0 (recentre).
- CI matrix shift no longer clobbers the active 3D handle.
- AM H/V shift sliders clamp to the image circle and follow its arc when blocked.

================================================================================
v0.69.0 — Fix the "black projector"
================================================================================
- DB PBR textures are extracted to Cinema 4D's own temp directory (Application
  TEMP_DIR, "remap" subfolder) instead of the OS temp dir the renderer could not
  resolve. The projector body loads its albedo again.

================================================================================
v0.68.0 — Look-through camera + same-tick rebuild
================================================================================
- Projector look-through camera handles: centered layout, screen-axis drag, locked
  camera.
- Same-tick projector rebuild: removes the one-frame lag when dragging the projector
  or target with the Move tool.
- Look-through camera and projection light hidden in the editor from
  Execute@GENERATOR+1 (out of Draw), applied to the finalised cache every pass.

================================================================================
v0.67.0 — Luma Map post effect (ID 1069770)
================================================================================
- Illuminance heat map of the projectors, driven from Render Settings > Effects and
  toggled per viewport via Options > Post Effects: per pixel in the rendered image,
  adaptive OpenGL overlay in the editor, gradient gadget, min/max or auto scale,
  opacity, optional shadow rays.
- Also restores the R25 build (three 2024+-only API uses fixed).

================================================================================
v0.66.0 — Exclude body/lens from the projection light
================================================================================
- Projector body and lens excluded from the projection light (EXCLUDE mode,
  LIGHT_EXCLUSION_LIST) so the casing no longer blocks the beam or casts a shadow
  into the projected image.

================================================================================
v0.65.0 — Persist projector world-state
================================================================================
- Target, lens anchor, active handle and pivot persisted via Read/Write/CopyTo:
  position and aim kept on reload and duplicate; slide mire shows the advertised
  aspect ratio with XPR-doubled resolution.

================================================================================
v0.64.0 — One slide material per projector
================================================================================
- Unique Diapositive_<name> material carrying the projector's name on each slide,
  cloned on duplicate, renamed with the projector; legacy shared materials adopted.

================================================================================
v0.63.0 — True photometric lux (lm/m²)
================================================================================
- True lux everywhere: Attribute Manager ID_LUX, Sonde probe and Lux Meter tool;
  common-motor illuminance standardised on metres.

================================================================================
v0.62.0 — Sonde lux probe
================================================================================
- Scene object + click-to-place via the Lux Meter, shared common-motor illuminance
  (Blender-ready), dual-resolution mesh, aperture-bounded near-field lux.

================================================================================
v0.61.0 — 4K slide preview, light hide, material at creation
================================================================================
- Slide material: 4K preview + refraction index 1.0; Test Pattern defaults show
  name/resolution/ratio.
- Slide material created up-front at projector creation (menu + Catalog/Search).
- Projection light hidden in the editor (still renders + casts shadows).

================================================================================
v0.60.x — CI + auto cone fixes + per-lens gauge
================================================================================
- v0.60.3: CI, tagged releases built from main/dev (cpp branch retired).
- v0.60.1: cone hidden outside the lens range; lens auto-switch on move.
- v0.60.0: per-lens availability gauge (TR columns on the right edge, white line at
  current TR); cone/markers coloured by the active lens.

================================================================================
v0.59.0 — Image-size butée + continuous auto-switch
================================================================================
- Throw-ratio range as a single source of truth (mode + "Use Formulas" aware),
  shared by SW butée, TR clamp, slider bounds and cone markers; auto mode:
  continuous resize + lens auto-switch; keyboard entry validated.

================================================================================
v0.58.0 — Shared in-memory ProjectorDatabase
================================================================================
- Shared in-memory projector database + background lens-mesh preload for throw-ratio
  auto mode.

================================================================================
v0.55.0 – v0.57.1 — Handle/overlay polish, multi-selection matrix drag
================================================================================
- v0.57.1: darker, firmer viewport icon backdrop.
- v0.57.0: projector name above the IC circle in multi-selection.
- v0.56.0: IC matrix viewport drag for multi-selected projectors.
- v0.55.0: size handle stays on the target plane, +Z aim every pass, banking as a
  horizontal drag, lens<->target line, name HUD.

================================================================================
v0.54.0 — Projector banking
================================================================================
- Banking as a persistent parameter rolled inside the shared look-at (AimZAxis);
  RADIAL circle/knob handle, 45° snap, double-click reset; jump-free free-target
  mode; hidden Protection tag (rotation locked, position free).

================================================================================
v0.47.25 – v0.53.1 — C++ foundations (aiming, search, updater, drag)
================================================================================
- v0.53.0: database updater present in all four Cinema 4D builds
  (R25/2024/2025/2026), static OpenSSL 3, hard build failure if absent.
- v0.52.0: Projector Search revamp (lens/accessory trees, specs grid, field:value
  search).
- v0.51.0: aim delegated to common-motor-cpp + Projector Search command (ID 1069548).
- v0.50.0: interaction overhaul (body grab zone, free 3D target drag, live cone);
  textures moved to res/images.
- v0.49.0: C++ target-aiming system (world target, +Z convention).
- v0.48.0: schema version warning, per-parameter visibility toggles, Force Database
  Update.
- v0.47.25: CDN URL uses the /prod prefix, update pipeline validated end-to-end.

(Older history: see the annotated Git tags in the repository.)
