Skip to content

Releases: LoveDaisy/Lumice

v4.7.4

Choose a tag to compare

@github-actions github-actions released this 19 Oct 13:08
943184d

⚠️ Breaking Changes

  • LUMICE_API_VERSION now lives in a new leaf header lumice_api_version.h, not lumice_base.h (#509). The macro moved out of the wide-inclusion base header — a value bump used to invalidate the compile cache of every TU in lumice_base.h's includer closure, and now only the TUs that textually use the macro recompile — and its per-version change history moved with it. Code that read LUMICE_API_VERSION after including only lumice_base.h no longer compiles: add #include "lumice_api_version.h". The macro's meaning is unchanged, and the 453 → 454 value bump is not part of this move — it is #508's surface addition below, recorded in the new header's history.

CLI

  • Added: Lumice raypath survey (#508, #511, #512). A target-free census face of the raypath analysis: given one crystal entry and one path (--crystal 1 --path 1-2), it routes the path's class at the algebra layer and emits one JSON document (schema lumice.path-survey, schema_version 4) — the configurational analysis of that class, without choosing a sky point, sampling, or warm state. A class whose every member carries the identity certificate collapses to a single source image at the sun and the analysis stops there; any other member verdict ships the structural facts with named reasons and leaves the deeper object kinds unevaluated. --literal analyses the requested face sequence as written instead of the physical P|B|D class; --lambda pins the wavelength; --budget bounds the request. Internal analysis deepening since the first cut (the full-dimensional delta machine, the chromatic tint leg) is part of the same output.
  • Changed: -v and -d are now observable (#502, #503, #506). The engine's only VERBOSE log site had become unreachable on both production consumption paths, so -v ran silently no matter what it was given; the dead site is gone and both flags now print a parse-time level line (-v arms VERBOSE, -d arms DEBUG), and --help describes -v as the verbose level instead of implying a deeper trace.

GUI

  • Added: an entry bar in the Edit Entry modal (#497). The crystal's participation toggle and Weight slider now sit at the top of the Edit Entry modal, buffered like the modal's other fields (nothing writes through until OK), so re-enabling a disabled crystal or retuning its weight no longer requires closing the modal to reach the card. The card keeps both controls.
  • Changed: the Axis tab is a one-gesture editor (#495, #496, #513). The preset name is a label the user chooses, never re-derived from the parameters: clicking one of the six label chips sets the label and loads that preset's parameters (remembered per crystal for the session); the old Apply-preset combo is gone — a chip click does that job, and a Reset row reloads the current label's definition without touching the label, the memory, or the view. Applying parameters no longer resets the preview view, and parameters drifting outside a preset's usual range light a mismatch badge, with an exact "currently reads as" tooltip, instead of silently renaming the axis.
  • Fixed: the analysis list's crystal numbers now match the crystal cards (#494). The list showed scene-insertion-order ids (C0, C1, …) where the cards show pool ids (#0, #1, …); the two diverged after dragging a card within its layer or deleting one, and the analysis list then described the wrong crystal. Both now speak the card numbering.
  • Fixed: the lens border colour is gated under print mode (#504). tone=print lays annotation ink by density and never reads hue, but the lens border — an annotation by construction — still took its colour from the screen setting and could print in colour. It now joins the same gate as the horizon, grid, sun circles, distance rings and markers, and stays editable under screen.

Engine C API

  • Added: LUMICE_API_VERSION 453 → 454 — the path survey surface (#508). LUMICE_AnalyzePathSurvey, LUMICE_PathSurveyToJson and LUMICE_PathSurveyDestroy, the LUMICE_PathSurveyRequest struct, the opaque LUMICE_PathSurvey handle and the two budget constants (LUMICE_PATH_SURVEY_MAX_BUDGET_MS, LUMICE_PATH_SURVEY_DEFAULT_BUDGET_MS) publish the same survey the CLI subcommand prints: one schema-4 document per request, serialized engine-side once. A pure append — nothing existing changed. The library is still not part of the download packages.

What's Changed

  • docs(raypath): align analysis docs with the current product state by @LoveDaisy in #492
  • test(gui): restore shared window basis at case entry with a leak sentinel by @LoveDaisy in #493
  • fix(gui): align analysis list crystal numbers with card IDs by @LoveDaisy in #494
  • feat(gui): sticky user-chosen axis label, decoupled from preset apply by @LoveDaisy in #495
  • feat(gui): Apply preset is a pure parameter shortcut, label and view untouched by @LoveDaisy in #496
  • feat(gui): entry bar in Edit Entry — weight and participation toggle in the modal by @LoveDaisy in #497
  • docs: organize documentation into topic directories by @LoveDaisy in #498
  • docs: reconcile guides and architecture with current implementation by @LoveDaisy in #500
  • Fix dump_xyz_stats.py: consume current C API contract via CLI npy export by @LoveDaisy in #501
  • fix(cli): describe -v as verbose level in help text by @LoveDaisy in #502
  • fix(cli): describe -v as verbose level in help text, pin -v/-d level mapping by @LoveDaisy in #503
  • fix(gui): gate lens border colour under print mode (join NotUnderPrintMode family) by @LoveDaisy in #504
  • chore(source-comment-consistency): rewrite five drift groups of src/ comments by @LoveDaisy in #505
  • feat(cli): make -v/-d observable — drop dead VERBOSE site, add parse-time level lines by @LoveDaisy in #506
  • test/e2e cost trims + shadow bench probe (697 PR1: restore work headroom, instrument the budget) by @LoveDaisy in #507
  • refactor(capi): move LUMICE_API_VERSION to a leaf header by @LoveDaisy in #509
  • feat(raypath): survey CLI skeleton + C14 smoke slice (schema4) by @LoveDaisy in #508
  • chore(test): restore the endpoint-bisection registry entry dropped in #507 by @LoveDaisy in #510
  • feat(raypath): survey C01 full-dimensional delta machine, radial probe and chromatic layer v1 by @LoveDaisy in #511
  • feat(raypath): survey C15 slice — weight_tint chromatic leg (smoke trilogy finale) by @LoveDaisy in #512
  • GUI: axis chips switch label and parameters, Reset row replaces apply combo by @LoveDaisy in #513
  • ci: per-miss build budget — adaptive limit + absolute backstop (registry v3) by @LoveDaisy in #514
  • docs: slim root AGENTS.md to constitution + route map (983→396 lines) by @LoveDaisy in #515
  • chore(test): track li-parity fixtures in Git LFS by @LoveDaisy in #517
  • ci: per-miss budget on all four legs + envelope-derived outer bounds by @LoveDaisy in #518
  • refactor: single authority for the pi/vec3 families (census ledger item 2) by @LoveDaisy in #516
  • feat(ci): test-TU cost declaration gate — fifth diff-scoped checker by @LoveDaisy in #519
  • chore(release): cut 4.7.4 by @LoveDaisy in #520

Full Changelog: v4.7.3...v4.7.4

v4.7.3

Choose a tag to compare

@github-actions github-actions released this 12 Oct 23:44
5b26788

⚠️ Breaking Changes

  • Lumice raypath --report now emits schema 3 (#475, #477, #478; LUMICE_API_VERSION
    452 → 453). The target-free feature report serializes as schema_version: 3 where 4.7.2
    emitted 2, and a reader written for the schema-2 document must reject a v3 document (the
    engine's own reader does) — update any script that parses --report JSON to accept schema 3.
    The change is the report's shape, not just its number: where schema 2 was a per-feature list
    with Monte-Carlo evidence, schema 3 emits structural objects — a scope block, a support
    block carrying the delta-axis partition with its completeness certificate, the per-wavelength
    onset table and the restricted family curve, per-feature blocks, unattributed_structures,
    and mc_evidence demoted to a corroboration field. The request surface (target schema 1, the
    fiber walk, --warm) is unchanged.

CLI

  • Changed: config fields that silently did nothing now warn at startup (#476). A config that
    declares a filter nothing binds, places a filter key where nothing reads it (on the scene or
    a scattering layer, instead of per entry), or commits an empty render list used to be
    accepted without a trace; the engine now logs a warning naming the no-op. Every such config
    still renders byte-identically — the change is the warning, not new behavior.

GUI

  • Added: Look At target locking (#483). The camera row is now Reset → Look At → Lock.
    The Look At combo points the camera at a named direction in one click; with Lock checked,
    the camera keeps following that direction as scene values such as sun altitude change, without
    a Run. Editing Elevation or Azimuth, or dragging the preview to rotate it, turns Lock
    off and keeps that manual view; changing Roll, FOV, or the preview zoom does not. The
    selected target is retained, so re-checking Lock resumes following it. Reset, a lens
    change, or a document New/Open/Revert turns Lock off (document changes also reset the target
    to Sun); full-sky lenses disable both controls.
  • Changed: the Sun diameter field accepts 0 — a point source (#486). The GUI's sun-diameter
    editor clamped the value at a 0.1° floor, so a point-source sun (diameter 0, which the
    engine's default scene uses and the config file has always accepted) could not be entered
    from the panel. The floor is gone; the tooltip names what 0 means.
  • Fixed: the Look At row aligns with the panel's control grid (#487, #489). The Reset /
    Look At / Lock row drew its three controls at mismatched heights and off the grid the
    right panel's other rows follow; all three now share one full-height row that ends on the
    Lock checkbox, its right edge on the same line the FOV input ends on.

Rendered output (GUI + CLI)

  • Fixed: dual-fisheye fold-boundary energy loss (#480). The dual-fisheye projection passed
    the fold-boundary direction through a float32 rounding that pushed rays out of the projection
    and let the bounds check silently drop them — roughly 18.9% of a dual-fisheye frame's energy.
    The boundary is now clamped after the floor, so the dropped rays render; the projection's
    shared path is fixed once, for both the CLI and the GUI.

liblumice_analytic

  • Added: LUMICE_ANALYTIC_API_VERSION 7 → 8 — the u-S² field layer (#477). Eight new calls
    (TraceBoundaryLoop, TraceWeightKinks, ClassifyCriticalStructure, PartitionDeviationAxis,
    TraceWavelengthCriticalTable, TraceRestrictedFamilyCurve, DiagnoseChromatic,
    DiagnoseClassTint) with their result structs, Release functions and twelve enumerations
    publish the deviation-field geometry of one fixed face sequence: kind-1/2/3 critical structure,
    the delta-axis partition with its escape regimes, per-wavelength onset tables, the restricted
    family curve, and the two-index chromatic verdicts. Nothing existing changed. The kernels are
    the C++ port of Lumice Integral's field layer, pinned against it by parity fixtures; the
    library is still not part of the download packages.

What's Changed

  • feat(raypath): schema3 measure layer (task-661) by @LoveDaisy in #475
  • fix(config): warn on the two silent no-op config shapes by @LoveDaisy in #476
  • feat(analytic): schema3 geometry layer port (u-S2 field, partition, walks, focusing/chromatic, contour, API v8) by @LoveDaisy in #477
  • feat(raypath): schema3 report - state machines, support block, unattributed, mc_evidence, schema_version=3 by @LoveDaisy in #478
  • Share-domain calibration: lens landing semantics + dual fold-boundary defect + cone cross-check tool (task-669) by @LoveDaisy in #479
  • fix(raypath): clamp dual-fisheye fold-boundary pixels into canvas (task-670) by @LoveDaisy in #480
  • Clarify mixed-corner tolerance consumer comment by @LoveDaisy in #481
  • ci: reduce repeated raypath test work and govern cumulative duration by @LoveDaisy in #482
  • feat(gui): add Look At target locking with manual takeover by @LoveDaisy in #483
  • chore(release): cut 4.7.3 by @LoveDaisy in #484
  • docs: migrate repo name ice_halo_sim to Lumice by @LoveDaisy in #485
  • fix(gui): allow sun diameter 0 (point source) in the GUI domain by @LoveDaisy in #486
  • fix(gui): align the View Look At row to the panel's control grid by @LoveDaisy in #487
  • fix(ci): fix ccache seed audit false positive (one write per miss) by @LoveDaisy in #488
  • Fix: revert right panel to 300px and end the Look At row on the Lock square by @LoveDaisy in #489
  • chore(release): recut 4.7.3 by @LoveDaisy in #491

Full Changelog: v4.7.2...v4.7.3

v4.7.2

Choose a tag to compare

@github-actions github-actions released this 07 Oct 11:34
215a850

CLI

  • Added: the Lumice raypath subcommand (#445) — the physics behind one raypath, without
    the UI. Lumice analyze lists a scene's raypaths; Lumice raypath --crystal <n> --path <p>
    takes one of them and reports what the light does through that one crystal: fiber components
    at a sky point (seed search, then the fiber walk — the level set on the sun-direction sphere),
    per-point detail (crystal pose, the sun as seen inside the crystal, segment directions,
    per-interface energy), a grid over the sun-direction sphere, and a reach field that splits
    an empty result three ways — a degenerate point-mass path, a geometrically unreachable
    target, and a target not excluded but not found (most often because no finite crystal admits
    the passage). --warm re-reads a previous run's JSON and densifies from its seeds. The result
    is serialized once in the engine (target schema 1, doc/raypath-cli-output.md; user-manual
    section in en/zh). This is the analysis half of the raypath feature: the GUI workspace it
    feeds is still being designed, and the JSON grows by appended fields only.
  • Added: Lumice raypath --report — target-free feature diagnostics (#464, #468). Instead
    of following one chosen path, --report asks which halo features the document's actual
    spectrum, crystal shape and orientation fold can support, and answers per candidate with a
    three-tier verdict — actual (confirmed by sampled evidence), candidate (plausible but
    under-evidenced), or unfinished with its reason — alongside per-wavelength finite-crystal
    A·T estimates, convergence evidence, positions and explicit coverage limits. A combined
    numerical budget bounds the work (--budget-ms, default 15 s, max 120 s;
    --max-evaluations; --max-field-evaluations); a run that exhausts it still emits its
    partial report marked partial, and a report that fails to parse leaves stdout empty.
    Serialization is report schema 2 (doc/raypath-cli-output.md §7); the target/fiber/--warm
    contracts above are unchanged.

GUI

  • Fixed: window geometry at fractional Windows display scales (#465). Match Background
    computed its window size from an unscaled minimum, so at a fractional scale the system
    imposed its own scaled minimum instead — changing the preview's aspect ratio and leaving
    blank bands above and below the image. Startup, aspect presets and scale changes now share
    one geometry model (current monitor work area, actual decoration sizes, scaled minimums);
    programmatic resizes settle as bounded transactions that expire unconfirmed results rather
    than act on them; and a preview area that cannot represent the requested aspect is clamped
    and reported from the actual area.
  • Changed: import diagnostics live in one Load Notice (#466). The warnings raised while
    loading a document are consolidated into a single load-time notice, and a preview restored
    from cache states what its texture mode can and cannot show (recommending Run then Save)
    instead of leaving the limitation implicit. Cached previews are kept; nothing reruns behind
    the user's back.

Rendered output (GUI + CLI)

  • Fixed: sporadic light-leak points on Parry-oriented scenes (#472). Single bright pixels
    near azimuth 160–162°, altitude ≈0° (beta-reported, no multi-scattering). CPU and Metal
    judged whether a spawned child ray had left the crystal implicitly: the face it was born on
    could win the min-t race at t ≈ 0⁺ and slip past a guard whose epsilon has the wrong
    dimension for grazing children (t = δ/denom, so denom → 0 blows a ~1 ulp plane residual past
    any fixed ε) — the child then re-hit its own birth face, TIR'd back inside and exited
    unphysically. Each event carries ~1e-5 of the ray's energy but is visible because
    grazing-incidence Fresnel reflection is near-total. Children are now classified at birth by
    the pure sign of the source denominator, in CPU and Metal (CUDA already had the semantics;
    the three backends now share one shape), which also removes the edge-birth phantom family.
  • Fixed: the channel_br display mode no longer subtracts the sky into the diagnostic
    (#466). The B−R difference used to include the configured sky background — an empty blue
    sky rendered white, which is background, not signal. Both the CLI's and the GUI's B−R output
    now diagnose halos only; the post-gamma formula, gain, exposure, masking and annotations are
    unchanged, so a scene whose sky contributes no channel difference renders as before.

Engine C API

  • Added: LUMICE_API_VERSION 449 → 452 (#445, #464, #468) — new functions only; nothing
    existing changed shape or meaning. v450 adds LUMICE_AnalyzeSinglePath /
    LUMICE_SinglePathResultToJson / LUMICE_SinglePathResultDestroy — the single-path result
    is serialized once in the engine, and the CLI and the future GUI read the same JSON — plus
    LUMICE_SetLogLevel(NULL) to set the engine-wide log level. v451 adds the
    path-feature-report entry points with their
    LUMICE_PATH_FEATURE_REPORT_MAX_SAMPLE_COUNT / ..._MAX_WAVELENGTH_COUNT bounds. v452 adds
    a report-request suffix carrying budgets, observation, layer and symmetry bits; requests in
    the old prefix form are accepted with the new defaults.
  • Changed: the single lumice.h is gone; the C API is six capability headers (#453) —
    lumice_{base,scene,render,editor,engine,raypath}.h, one bridge file per header, same
    functions and values: the split left the export tables byte-identical and moved neither API
    version. The engine and testapi shared libraries additionally export the analytic capability
    (lumice_analytic_core.h), and each packaged library's export surface is declared once in
    cmake/export_surfaces.cmake and checked in CI and at packaging time. The headers still do
    not ship in the download packages, so this reaches source-level consumers only.

liblumice_analytic

  • Added: LUMICE_ANALYTIC_API_VERSION 1 → 7 (#444, #446, #468) — the analytic library's
    surface grew to two modules plus diagnostic numerics: module A
    (LUMICE_ANALYTIC_EvaluatePath, TraceFiber / TraceFiberBatch with per-pose diagnostics,
    DiscoverComponents), module B (LUMICE_ANALYTIC_BandSum — bounded sums over sampled
    poses, rank-0 point-mass branch included), and the v7 diagnostics (batch diagnostic
    evaluation, source-event brackets, weighted-field values, fixed-observation colour bands).
    The kernels are pinned against Lumice Integral by replayed parity fixtures. Consumers build
    against the install tree with find_package(LumiceAnalytic 7); the library is not yet part
    of the download packages.

What's Changed

  • ci: add top-level permissions: contents: read to workflows by @LoveDaisy in #443
  • liblumice_analytic module A v0: EvaluatePath, TraceFiber, DiscoverComponents + LI parity by @LoveDaisy in #444
  • Lumice raypath: single-path analysis module, lumice.h v4.50 entry, D4 verification by @LoveDaisy in #445
  • liblumice_analytic wave 2: per-pose diagnostics (API 5), band sum module B (API 6), 93 LI parity fixtures by @LoveDaisy in #446
  • docs(analytic): three kinds of critical lines; LI 31c682e parity fixtures (94) by @LoveDaisy in #452
  • Organize the C API and engine by capability by @LoveDaisy in #453
  • Split c_api_scene.cpp into bridge, encoder and decoder by @LoveDaisy in #455
  • scrum ci-time-governance (WIP): build-time landing by @LoveDaisy in #451
  • ci: continue time-governance calibration by @LoveDaisy in #462
  • docs(ci): close time-governance acceptance by @LoveDaisy in #463
  • Add target-free raypath feature diagnostics and scientific acceptance by @LoveDaisy in #464
  • fix(gui): keep background aspect ratios consistent across UI scales by @LoveDaisy in #465
  • fix(render): exclude sky background from B-R diagnostic by @LoveDaisy in #466
  • fix(testing): isolate local slow pytest performance pool by @LoveDaisy in #467
  • feat(raypath): phase-1 raypath feature diagnostics (scrum-649) by @LoveDaisy in #468
  • refactor(raypath): module design cleanup — role-based naming, detail/ demote, JSON single-owner by @LoveDaisy in #469
  • fix(sky-direction): resolve convention-mismatch investigation — call sites proven correct, defense lines landed by @LoveDaisy in #470
  • docs(cases): add raypath analysis scenario-case corpus index by @LoveDaisy in #471
  • fix(core): classify outward children at birth in CPU/Metal slab traversal by @LoveDaisy in #472
  • docs(corpus): C12 tint multi-caliber + C06 LI closed-form anchor by @LoveDaisy in #473
  • chore(release): cut 4.7.2 by @LoveDaisy in #474

Full Changelog: v4.7.1...v4.7.2

v4.7.1

Choose a tag to compare

@github-actions github-actions released this 28 Sep 11:17
01b9dd5

Added

  • C API: LUMICE_API_VERSION 446 → 449 (#429, #430, #436) — new functions only, nothing
    existing changed. LUMICE_GetCrystalSymmetry / LUMICE_CrystalSymmetry: which of the P/B/D
    symmetry elements a crystal's shape actually has (a prism with unequal alternating face
    distances has a three-fold axis, not a six-fold one). LUMICE_IsPApplicable /
    LUMICE_IsBApplicable: the orientation-ensemble counterparts of the existing
    LUMICE_IsDApplicable. LUMICE_CouldCrystalHaveFace / LUMICE_CouldFilterMatchFace: whether a
    filter naming a face can ever match through it on this crystal (the latter with the filter's own
    P/B/D taken into account). LUMICE_ExpandRaypathClass with its LUMICE_SYMMETRY_SEMANTICS_* /
    LUMICE_MAX_RAYPATH_CLASS_MEMBERS constants: the members of a raypath's label or physical
    class, which Exclude this raypath uses to write a filter that removes exactly one analysis
    row's members.

Changed

  • The raypath-analysis list now groups rows by physical symmetry class; a filter's P/B/D
    keeps its previous meaning
    (#429, #430, #431, #436). The analysis list used to assume every
    prism and pyramid has full six-fold symmetry regardless of the configured shape or orientation
    distribution — on a prism with face_distance: [1, d, 1, d, 1, d] (d != 1), which really has
    only a three-fold axis, P merged a near-face path with a far-face path that differ in energy by
    2.4×. The list now intersects P/B/D with what the crystal's shape (LUMICE_GetCrystalSymmetry)
    and its orientation ensemble (LUMICE_IsPApplicable / LUMICE_IsBApplicable) actually admit,
    so a row that used to merge such paths can now split into several; the filter editor shows a
    hint beside the P/B/D checkboxes when a tick would merge physically inequivalent paths on the
    bound crystal (e.g. a Parry arc's lit 3-5 with its dark 4-6). A filter's own P/B/D matching
    behavior is unchanged from v4.7.0
    — it stays a label equivalence (P: any 60° face relabeling;
    B: 1↔2 and upper/lower cone swap) whatever the crystal's shape or orientation, exactly as
    before this release; only the analysis list's grouping and the editor's hints are new. See
    doc/raypath-symmetry.md §1.1/§2a for the two meanings of the same P/B/D bits.
  • sim.ray_allocation's Settings editor is a two-option combo instead of a bare checkbox
    (#434). The row's label was only the key path, so nothing said whether checked meant adaptive
    or proportional; it is now an explicit adaptive / proportional combo. The underlying field
    and the .lmc / JSON / C API wire formats are unchanged.
  • Channel B−R display mode's contrast is 4× stronger (#439). gray = clamp(0.5 + 2·(B−R), 0, 1) on the displayed sRGB channels, up from 0.5 + 0.5·(B−R), matching a researcher's
    Photoshop channel-mixer workflow (measured slope 1.998 on their reference image; this project's
    formula now fits at 1.941, was 0.484). Purely a diagnostic display transform — no ABI or struct
    change, no config key change — but a saved channel_br screenshot or a config with
    display_mode: channel_br now renders visibly steeper: |B−R| >= 0.25 clips to pure
    black/white where it did not before.

Fixed

  • The adaptive ray-allocation mode no longer starves a filtered crystal whose output is small
    in total but concentrated in one visible arc
    (#435). Example: a layer with a randomly oriented
    column and a Parry-oriented column filtered to 3-5, equal proportions. Adaptive (Neyman)
    allocation gave the filtered entry so few rays that the Parry arc came out 2.1–2.6× noisier than
    under proportional, visibly grainier than the rest of the image. Each entry's allocation now
    floors at half its proportional share (q_i >= p_norm_i / 2), which brings the arc back to
    1.1–1.2× the proportional noise; the price on that scene is about 11% more noise on the random
    column's halos, which the floor takes rays from. Scenes where no entry hits the floor allocate
    exactly as before. The starvation dates from when adaptive was introduced (v4.6.0), not from
    this release.
  • Duplicating a crystal card that carries a filter no longer blanks the whole preview after a
    finished run
    (#440). Any edit that structurally changes the committed scene (Duplicate on a
    filtered entry is one way to trigger it) used to zero the displayed frame's intensity
    immediately, on the assumption a new render would follow within its live-edit debounce window —
    which does not hold once a run has already finished, so the preview stayed black until the next
    Run. The old frame now stays on screen until the new one actually replaces it; the
    "unsaved changes" cue is the existing top-bar Modified indicator and Revert, not a
    blanked preview.

What's Changed

  • docs(raypath-analysis): expand §5.1 into the post-selection three-feature plan by @LoveDaisy in #426
  • docs(raypath-analysis): compute location moves per module maturity by @LoveDaisy in #427
  • docs(raypath-analysis): Analyze as the second product core (§5.1.8) by @LoveDaisy in #428
  • fix(core): reduce raypaths only under the symmetry the crystal shape admits by @LoveDaisy in #429
  • fix(core): apply P/B raypath symmetry only where the orientation ensemble admits it by @LoveDaisy in #430
  • fix(core): device filter reduction follows the crystal's shape and ensemble symmetry by @LoveDaisy in #431
  • docs: finalize the Analyze workspace layout (raypath-analysis §5.1.8) by @LoveDaisy in #432
  • docs: repository roles for Lumice / Lumice Integral, and the shared library's first consumer by @LoveDaisy in #433
  • gui: sim.ray_allocation Settings editor becomes an adaptive/proportional combo by @LoveDaisy in #434
  • core: relative floor in adaptive ray allocation stops starving a filtered arc by @LoveDaisy in #435
  • core: a filter's P/B/D is a label equivalence again; the analysis list keeps physical symmetry by @LoveDaisy in #436
  • docs: state which symmetry meaning each repo's computation uses by @LoveDaisy in #437
  • Channel B-R display mode: raise gain from 0.5 to 2 by @LoveDaisy in #439
  • GUI: a struct-hard edit no longer blanks the preview by @LoveDaisy in #440
  • Publish-ready shared library foundation: liblumice_analytic, per-library export lists, packaging by @LoveDaisy in #441
  • chore(release): cut 4.7.1 by @LoveDaisy in #442

Full Changelog: v4.7.0...v4.7.1

v4.7.0

Choose a tag to compare

@github-actions github-actions released this 26 Sep 03:58
e4adc5b

Added

  • The GUI is DPI-aware, with a user scale preference (#398). Until now the whole UI was drawn
    at physical pixels regardless of monitor scaling — on a 150% Windows display, at 13 px instead
    of the intended ~19 px (matched to the OS title bar: the same window went from 1600×980 to
    2400×1470 physical pixels). A single UiPx() / UiPxI() outlet now converts every layout
    constant (~90 literals) through a monitor tier (read at startup, tracked across screens) and a
    new user tier: Settings › app gets a discrete scale combo (75 / 100 / 125 / 150 / 200%),
    stored as app.ui_scale_multiplier in user_defaults.json and applied immediately.
    ui_scale == 1 renders byte-identical to 4.6.1. Changing the scale re-centres the Settings /
    Analysis / Colors windows at their design size, so a larger window cannot end up off-screen.
  • A search box over the raypath analysis list (#399). Filters the list by the raw raypath
    text — the same text the CSV and the log print, not the on-screen arrow glyph; energy shares,
    the cumulative column and the selection keep their meaning, it only hides rows.
  • Right-click Copy (and Copy all) on read-only text (#399), across the analysis list, the Log
    panel, the filter editor's expansion preview, and a new "Copy as text" button on the Summary
    page.
  • The analysis list remembers what you have excluded (#400). Pressing Exclude this
    raypath
    used to make the row vanish on the next Analyze, with no way to see what had already
    been taken out. An excluded raypath now stays in the list as a greyed row (was X%, the share
    it last had), with Include again to undo it. A hand-written filter the greyed-row detector
    cannot read as a plain raypath token is not shown this way, by design.
  • Cards can be reordered by dragging, and Duplicate lands next to the original (#405). The
    card list previously supported only append and delete; a single new primitive
    (MoveEntryWithinLayer) now backs both a drag handle on the card thumbnail (click-to-edit now
    fires on release, so a drag no longer opens the editor) and Duplicate, which inserts
    directly below the source card instead of at the end of the layer. Every position-based
    reference — the open Edit Entry popup, the analysis panel's pick-link mode — is remapped
    through the same compensation the delete path already used.
  • Continue: add more rays to a finished render without starting over (#406). A new
    Continue button next to Run traces another ray_num worth of rays into the
    accumulation a completed run already has — the XYZ planes, ray/energy totals and both exposure
    anchors (relative and absolute) carry over unchanged, so the picture only gets less noisy,
    not different. The new rays are a fresh random stream, never a replay, on all three backends.
    Continue is disabled, with a tooltip, when there is no finished render to continue, an analysis
    session is active, or the document has changed in a way that needs a full re-simulation.
  • Globe lens: the far side of the sphere shows through, fading like fog (#407, #416, #424). A
    new Back fade slider (Settings, under the Globe lens; JSON globe_back_fade, default 0 —
    today's camera-facing-hemisphere-only image) adds the far hemisphere's light on top of the near
    side, weighted by exponential fog with no hard cutoff (exp(-depth/fade)); the slider runs
    0–2.0. The four line families (grid, horizon, angular distance, view distance) fade the same
    way and are drawn under the near-side lines; markers and text labels stay near-side only.
    Every direction — light and lines alike — is clipped by its own visible / front, not by
    the near point sharing its pixel, so an "upper" view correctly shows a faded far side even
    where the near point itself is excluded. fade <= 0, or a full-sphere visible, renders
    byte-identical to before this feature.
  • A "Channel B−R" display mode: is this spot bluer or redder? (#410, #419). A small segmented
    control in the preview's top-right corner (moved there from a Settings row after users did not
    find it) switches between the normal image and a grey-level map of
    clamp(0.5 + 0.5·(B−R), 0, 1) on the displayed sRGB — the same subtraction a researcher used to
    do by hand in Photoshop. Purely a display transform: switching it, or EV, never re-simulates,
    and the export / screenshot paths match the screen exactly. It has no effect under print tone
    (greyed out there), suppresses the raypath-colour composite and the background photo while
    active, and a non-default ray_color triggers a warning. JSON key render.display_mode
    ("normal" / "channel_br").
  • Pick an angular-distance ring on the preview, instead of typing a number (#421). Both ring
    families (sun-centred, and lens-centre-referenced) gain a Pick button: a live ring follows
    the cursor with its angle read out, a left click adds it to that family's list (turning the
    family's line display on if it was off), Esc or a right-click cancels. A location with no
    direction — outside the lens circle, or clipped by visible / front — shows no preview ring
    and adds nothing on click.
  • A screenshot export options popup (#422). Save › Screenshot... now opens a popup,
    pre-filled from what is currently on screen, before the file dialog: each overlay family's line
    and label, and the display mode (Normal or Channel B−R, greyed out where the uploaded texture
    cannot render it honestly), can be turned off for that one export — never turned on beyond what
    the panel already shows, and never written back to the document.
  • The Edit Entry modal's Axis page highlights the active preset and summarises the crystal
    (#418). The active preset button (Custom included) now fills with the accent colour instead of
    leaving the choice to be read off the thumbnail alone; a new summary line under the preview
    (<preset> · zenith <dist> · <type> · <height scalars…>) updates live as the edit buffers
    change.
  • C API: LUMICE_API_VERSION 443 → 446 (#406, #407, #410). LUMICE_ContinueRender(server, infinite, additional_ray_num) (v4.44) is a pure append, no struct change — the Continue
    feature above. ABI (v4.45): LUMICE_RenderParam gains a trailing globe_back_fade
    (sizeof 6452 → 6456) — the Globe far-side parameter above; 0, the zero-initialised value, is
    the pre-4.7.0 image. ABI (v4.46): LUMICE_RenderParam gains a trailing display_mode
    after it (sizeof 6456 → 6460) — LUMICE_DISPLAY_MODE_NORMAL (0, the default) /
    _CHANNEL_BR, the display mode above. Both struct growths require recompiling against the new
    header; neither moves an existing field's offset or changes its meaning, and an
    unmodified / zero-initialised caller reads the same image as before.

Changed

  • The filter editor's last OR row can be deleted (#399). It used to grey out the row's x, a
    rule copied from the crystal card where an empty list really is illegal; a filter list may
    legitimately be empty, and deleting the last row now goes through the same invariant that
    already refills a blank one.
  • Tab in the crystal editor's shape tables walks column-major (#399). Tab now walks the Value
    column down every row before moving to the Spread column, matching how face distances are
    actually edited; Shift+Tab reverses.
  • Settings and Summary windows keep the height you drag them to (#399). Both become
    "semi-variable" under a newly documented sizing policy (doc/gui-visual-language.md): width
    pinned per window, height free until the user drags it and then remembered for the session;
    Summary still auto-fits its content until that first drag.
  • The Edit Entry popup has two shapes: Compact and Expanded (#401). A side-by-side tab layout
    and a three-column prototype are gone; Compact (unchanged — the stacked layout almost
    everyone already used) keeps the halo preview visible while editing, and the new Expanded
    shows the crystal, axis and filter sections all at once, two columns, no tabs — chosen by the
    owner from a measured comparison of all four candidate shapes. See ⚠️ Breaking Changes for what
    happens to a document that had chosen the removed shape.
  • The Edit Entry popup's height can be dragged, and its Expanded headers line up (#403). It
    was fixed-height (AlwaysAutoResize); it now follows the window-sizing policy above — width
    pinned, height free until dragged, then kept for the session — and a too-short work area no
    longer pushes the OK button out of reach. Expanded's Crystal and Filter section headers,
    previously aligned by coincidence, are now derived from one height and asserted to agree. All
    three section headings switch to the standard SeparatorText style used elsewhere in the GUI.
  • The top bar's buttons are grouped and sized consistently, and Continue gets its own colour
    (#417, #420). Revert, Run / Stop / Continue, and New / Open / Save each share one height and
    one width per group, with a proper separator between groups instead of a "/" character;
    Continue is now violet, distinct from every other action colour. The always-reserved Revert
    slot — previously a hole in the middle of the bar when nothing was unsaved — has moved to the
    trailing end, next to the right-panel collapse chevron.
  • CPU multi-worker throughput no longer falls off a cliff as workers are added, and Linux's
    automatic worker count is uncapped
    (#412). The queue handoff between simulation workers and
    the consumer was tied to the 128-ray physics batch; under load, the resulting synchronization
    rate created a runaway feedback loop that made adding workers past 10 counter-productive on
    Linux, which is why the automatic count (num_workers = 0) was capped there. Workers now
    aggregate batches locally before handing them off, decoupling the two; the automatic count on
    Linux is now the full physical co...
Read more

v4.6.1

Choose a tag to compare

@github-actions github-actions released this 21 Sep 04:24
f70cdd2

Added

  • Raypath analysis from the command line: Lumice analyze (#369, #394). The analysis the
    4.6.0 GUI window runs is now a CLI subcommand that writes the same CSV the window's Export CSV
    produces (one CSV builder, shared by both — a unit test pins the two byte-identical). The config
    is the scene and the flags are the request, so no config field is added: --roi sky|frame|cone,
    with --center alt,az + --radius deg for a cone and --render-id for a frame; --symmetry
    (read-time, default PBD); --rays / --seed / --workers; --csv <path> for a file, else
    stdout (progress goes to stderr, so 2>/dev/null leaves the CSV alone). An infinite budget
    rewrites the file periodically and a Ctrl-C ends the run with one complete final write and exit
    0. The pass runs on the CPU whatever --backend says, as in the GUI. New in the same cycle:
    --chain-capacity <N> sizes the run's chain record, which until now was one compiled-in
    constant (16384, calibrated on the GUI's multi-scatter scenes) that a single-crystal
    --symmetry none scene overflowed silently into the other row — a hexagonal prism alone has
    29 212 distinct chains through 8 faces, so its 8-face set (0.84 % of the energy on the reference
    scene) was never listed. 0 keeps the default; the ceiling is 2²⁰, and the cost is memory,
    about 220 bytes per chain per worker. Chains with an adjacent repeated face (grazing-edge
    numerical noise) are documented as a known shape rather than filtered out. Guide:
    doc/user-manual/06-raypath-analysis.md and the CLI quickstart.
  • Lumice render --format npy: the accumulator as float32, with a metadata sidecar (#379).
    Each renderer is written as a .npy (H×W×3 float32 XYZ, C order — a hand-written v1.0 header,
    no new dependency) plus a same-name .json carrying what a consumer needs to interpret it:
    normalization, emitted_energy, total_pixels, intensity_factor, axis_solid_angle,
    anchor_l99_sky, ev_mode, sim_ray_num, seed, renderer_id, width / height,
    LUMICE_API_VERSION and more. The values are the same unexposed snapshot the 8-bit image is
    baked from — no background, no gamma, no clamp. --raw-normalization raw|normalized (or the new
    top-level config key raw_export.normalization; the flag wins) chooses between the physical
    landed energy (default) and the absolute-mode exposure scale at EV 0; normalized with a zero
    emitted_energy skips that renderer with a warning instead of mislabelling the file.
    doc/configuration.md states the units, the conversions to radiance and to the 8-bit image, and
    the fact that normalized uses the absolute-mode scale regardless of the renderer's ev_mode.
  • render --seed <N> (#370): the same fixed-seed option analyze has, for a byte-identical
    re-run of a render. benchmark rejects it with its own message — a fixed seed collapses the
    server to one worker, which would defeat the throughput measurement.
  • A Summary window: the whole configuration on one page, for a screenshot (#396). A new
    Summary button in the Top Bar, beside Colors and Analysis, opens a read-only,
    non-modal window laying the current document out for sharing — the version that drew it, the sun
    and spectrum, the simulation budget, the lens / view / exposure / mode rows, the Settings-only
    fields, and per layer two tables, Crystals (enabled, weight, name and type, the three axis
    distributions each led by the preset it classifies as, the filter as the card summarises it) and
    Shape (the editor's own columns for prism and pyramid), cross-referenced by row number.
    Distributions print in one notation, letter centre(spread) with the panel's own two numbers.
    1200 px wide, two columns, so a typical document fits a 1280×900 screen without scrolling. It
    prints what the panels show, not what the simulator is asked for internally: a linear lens
    reads linear, and nothing about your machine (overlays, background photo, panel layout) is on
    the page. No export button of its own — take the picture with the operating system's screenshot
    tool. Guide: doc/user-manual/07-config-summary.md.
  • Look At gains a Horizon series (#393). Four level bearings relative to the sun —
    Toward sun / Sun +90° / Away from sun / Sun −90° — at elevation 0, for turning
    the camera to a fixed bearing without dragging the Azimuth slider. The existing Sun-side
    horizon
    entry is folded in as Toward sun. Presets still set only azimuth and elevation;
    roll and fov are untouched.
  • The product version is shown everywhere, from one source (#391). The GUI window is titled
    Lumice <version>; Lumice --version prints it and exits 0; the first log line of a GUI or CLI
    run names it; a saved .lmc carries it as app_version beside schema_version (ignored on
    load, never a difference row in Settings). A build that is not a tagged release carries a -dev
    suffix, so a locally built binary never passes for a release.
  • C API: LUMICE_API_VERSION 439 → 443 (#369, #375, #391, #394). Three pure appends, with no
    struct change: LUMICE_SceneGetRenderer(scene, index, out) (v4.40) reads a renderer back with
    the engine's defaults applied — the inverse of SceneAddRenderer, addressed by array position,
    not .id; LUMICE_GetEngineIsaLevel() (v4.41) answers the ISA tier of the engine actually
    loaded (baseline / x86-64-v3 / x86-64-v4 / native), which is also what the
    [BENCHMARK] JSON's isa key now reports instead of the shell's compile-time macro;
    LUMICE_GetVersionString() (v4.42) is the product version above. ABI (v4.43):
    LUMICE_RaypathAnalysisRequest gains a trailing chain_capacity (sizeof 88 → 96, recompile
    against the new header); 0 — what a zero-initialised request, and the GUI, send — is
    byte-for-byte the old request, 1 … LUMICE_MAX_RAYPATH_CHAIN_CAPACITY (2²⁰) sizes the record,
    anything else is LUMICE_ERR_INVALID_VALUE. Two documented semantics moved without a version
    bump: LUMICE_GetBackendFallbackFlag now also reports a GPU session that fell back because the
    run's config had no renderer, more renderers than the backend serves, or a compatibility miss
    (#372); and LUMICE_ServerConfig.num_workers = 0 now means the full logical core count on
    Windows (see the performance entry below) while Linux / macOS keep the physical count capped at
    10 (#382).

Changed

  • Engine and GUI log lines go to stderr; stdout carries only product output (#369, #370). The
    CLI's console sink used to write its log lines to stdout, mixed in with Saved: / Stats: /
    [BENCHMARK] / ColorClassSignal: and now the analysis CSV; those product lines stay on stdout
    and everything logged moves to stderr, so Lumice … 2>/dev/null yields clean output and a
    script that parsed stdout for one of the product lines no longer has to skip log lines. The GUI's
    own console sink moves the same way. A script that captured log lines from stdout needs
    2>&1.
  • A saved .lmc reopens as the picture you saved, and stays re-exposable (#381, #384). The
    texture section now stores the unexposed linear XYZ energy the live preview is built from — as
    float16 with one global scale, zlib-compressed (format v6) — instead of the exposed, clipped
    8-bit sRGB preview of v4; on reopen it goes up through the live path's own shader branch, so
    EV, ev_mode, sky / paper colour and print mode all work on a reopened document exactly as
    after a Run, and the reopened frame is byte-identical to the screenshot taken before saving
    (the live preview is quantised through the same codec before upload, which is what makes that
    hold by construction; GPU texture memory halves as a side effect). Files are larger than v4's
    baked PNG — about 1.4 / 8.5 / 9.9 MB on the three beta scenes at the default resolution,
    versus 0.2 / 5.0 / 5.3 MB before — because they now hold the dynamic range the PNG had clipped
    away. v5 (the same content as float32, an intermediate format never released) and every v≤4 file
    open unchanged; a file saved by this version cannot be opened by 4.6.0 or earlier.
  • Release packages: one executable per entry point, plus two internal engine libraries (#375).
    4.6.0 shipped each Linux / Windows executable twice behind a launcher (Lumice.baseline /
    Lumice.x86-64-v4, .baseline.exe / .x86-64-v3.exe). Those files are gone. Windows x64
    now has Lumice.exe / LumiceGUI.exe plus lumice-engine.baseline.dll and
    lumice-engine.x86-64-v3.dll; each shell probes CPUID at startup and delay-loads the matching
    DLL from its own directory by absolute path (never PATH or cwd), --isa=baseline|x86-64-v3
    overrides. Linux x64 has Lumice / LumiceGUI plus lib/liblumice.so and
    lib/glibc-hwcaps/x86-64-v4/liblumice.so; glibc's own loader (≥ 2.33) picks the AVX-512 copy
    with no application code. The engine library is an internal component — its ABI changes freely
    between versions, lumice.h does not ship with it. Measured on the reference box: the DLL
    boundary itself costs nothing (100 % / 98 % single / multi-worker against a static build of the
    same tier); the tier gain is 2.31× single- and 1.56× 16-worker on Windows, 2.03× on Linux. Two
    accepted costs: a machine without AVX2 runs the
    baseline engine 10–14 % slower than 4.6.0's static baseline executable (the export table rules
    out link-time optimisation for that DLL), and the Windows zip unpacks to 21 MB. macOS and Linux
    ARM64 packages are unchanged.
  • A config with several render entries now runs on the GPU (#372). Metal and CUDA used to
    serve one renderer only; a multi-lens config on a GPU-preferred window or --backend fell back
    to the legacy CPU path, silently. Both backends now accumulate up to four renderers per session
    in one pass — a two-renderer run reaches 88–95 % of a single-renderer run's through...
Read more

v4.6.0

Choose a tag to compare

@github-actions github-actions released this 13 Sep 17:34
fd1bb1c

Added

  • Raypath Analysis: a window that says which raypaths make the light, and how much each
    contributes
    (#347, #349, #359, #360, #361, #366). A new Analysis button in the Top Bar, next
    to Colors, opens an independent, non-modal window. Press Analyze and a dedicated,
    non-rendering pass groups every outgoing ray by its complete raypath — root to exit, across
    scattering layers, 3-5 for one crystal, C1(3-5) where a layer holds several, layers joined
    root-first by an arrow — and lists each group's share of the total energy, sorted descending,
    with a running Cumulative % and a +/- noise estimate per row. Three regions: Whole
    sky
    , In frame (rays that land inside the picture as currently framed) and Point — a
    cone around a marker drawn on the preview itself, placed by dragging it or by Pick on
    preview
    , that follows the view every frame; its Radius slider re-sums the result at display
    time, so one Analyze click serves a whole sweep of radii. P / B / D regroup the list on hand
    without re-running, the same symmetries the filter editor uses. Select a row and press Exclude
    this raypath
    to turn it into a filter on the crystal, then Run to see the picture without it;
    Export CSV writes the list as displayed. The run has its own Infinite rays / Rays(M)
    budget, ends only on that budget or on Stop (which keeps what accumulated), and holds a fixed
    number of distinct raypaths — a greyed other row at the bottom carries whatever did not fit,
    with a reported error bound. Analyze needs the document to have shown a picture once but not to
    match it: a list describing a document you have since edited, Run or Reverted is marked stale in
    the window rather than cleared. The pass always runs on the CPU, on every core: a Metal- or
    CUDA-preferred window keeps a standing CPU worker pool that costs nothing while idle. It follows
    the Adaptive ray allocation setting below, so a rare raypath on a low-proportion crystal gets
    a far less noisy row (10× on the reference three-crystal scene); for that reason the table shows
    no ray count. Guide: doc/user-manual/06-raypath-analysis.md.
  • The C API gains the analysis run (#347, #349, #360; LUMICE_API_VERSION 428 → 437).
    LUMICE_StartRaypathAnalysis(server, scene, request) starts a pass on a scene handle directly
    (no prior LUMICE_CommitScene), with LUMICE_RaypathAnalysisRequest naming the region
    (LUMICE_RAYPATH_ROI_FULL_SKY / _IN_FRAME / _CONE) and the run's own infinite / ray_num
    budget; LUMICE_FrameGetRaypathAnalysisInfo / LUMICE_FrameGetRaypathAnalysis read the result
    back off a LUMICE_ResultFrame under a symmetry bit set (LUMICE_RAYPATH_SYMMETRY_P|B|D), as
    LUMICE_RaypathHistogramEntry rows (chain, display string, energy, count, per-ring energy for
    a cone, error_bound) plus what the bounded record could not keep as rows (other_energy,
    other_count, truncated_chain_count, max_row_error). Alongside: LUMICE_UnprojectPixel /
    LUMICE_ProjectDirection (pixel ↔ direction under a LUMICE_AnnotationView) and
    LUMICE_GetActiveBackend. ABI: LUMICE_SimLifecycleResult gains a trailing session_kind
    (LUMICE_SESSION_RENDER / _ANALYSIS; sizeof 16 → 24, recompile) — the server's own record of
    what kind of run the current session is, so a client predicting the commit's consumer-reuse
    decision no longer keeps a shadow copy. LUMICE_ServerConfig.num_workers is no longer ignored on
    the GPU route: it now sizes the CPU analysis pool (0 = physical cores, capped). A non-zero
    sim_seed collapses the pool to one worker, so a fixed-seed analysis is bit-identical across
    sessions and across preferred_backend.
  • Adaptive ray allocation across a layer's crystals (#348, #364, #366). A new
    scene.ray_allocation config field — "proportional" (today's behaviour) or "adaptive" —
    decouples how many rays a crystal entry is dealt from how much energy it carries. Under
    adaptive the engine deals a layer's rays by a Neyman allocation measured online from the run's
    own batches (q_i ∝ p_i·√E[e²]_i, recomputed every batch on every backend, CPU, Metal and CUDA
    alike) and weights each ray by p_i/q_i, so the expected picture and emitted_energy are
    unchanged while the per-pixel noise is minimised. The case it was built for: three prisms each
    behind a single-raypath filter_in at proportions 100 / 100 / 0.2, where the rare path's 120°
    halo skirt used to be built from ~1/2600 the hits of the 98° halo and read as pure grain — its
    relative pixel noise drops ≈10× at the same ray count, the 144° ring is untouched, total landed
    energy within +0.15 %. Throughput cost on a single-crystal scene: −0.2 % Metal, −1.7 % CUDA.
    In the GUI it is a document field that new documents default to adaptive; the switch
    lives in Settings (not on the Simulation panel — after trying many configurations the
    algorithm behaves stably enough not to need a panel toggle), and an .lmc saved before the
    field existed, or a core JSON imported without it, lands on the GUI default. The CLI default
    stays proportional, so every existing config renders as before. C API:
    LUMICE_SceneSetRayAllocation(scene, LUMICE_RAY_ALLOCATION_*) (LUMICE_API_VERSION 437 → 438, a
    pure append — no struct or ABI change).
  • A fifth overlay family: circles of constant angular distance from the lens centre (#357,
    #362, #363, #365). The axis-referenced twin of the sun-centred circles: a framing and calibration
    aid that stays put on the canvas while the view is turned — the fov/2 circle must be inscribed in
    the short edge, so it also checks a lens setting at a glance. Config, .lmc and exported JSON
    carry it as grid.view_dist / grid.view_dist_line (default on) / grid.view_dist_label
    (default off), shaped exactly like the angular_dist three; absent keys leave the family off,
    so a document written before this release renders unchanged. The CLI composites it between the
    sun circles and the horizon outline. In the GUI both ring families share one collapsed
    Angular Distance from... section under the Overlay table, one row each — Sun and Lens
    Center
    — with colour, Line, Label and an angle-list fold apiece; new documents start the
    Lens Center list at {22, 46, 90} (an empty default made "works" and "broken" look the same),
    and the fold's presets are split by family (Sun {9, 22, 28, 46}, Lens Center {22, 46, 90}).
  • LUMICE_RenderParam gains view_dist[] / view_dist_count / view_dist_line /
    view_dist_label; LUMICE_AnnotationRequest gains view_dist_deg / view_dist_count
    (#357;
    LUMICE_API_VERSION 438 → 439, both appended at the end of their struct — LUMICE_RenderParam
    sizeof 4904 → 6452, LUMICE_AnnotationRequest 128 → 144, recompile against the new header; both
    structs now carry an exact-size static_assert). Labels come back with kind
    LUMICE_ANNOTATION_VIEW_DIST (4). Same zero-initialisation caveat as the other three line
    switches: the JSON default of view_dist_line is on, so a zeroed struct asks for no lines even
    with a full angle list. There is deliberately no reference-direction field — the centre is the
    view's own forward, independent of lens_shift by construction.

Changed

  • Release packages use the CPU and GPU they land on, without moving the support floor (#346).
    One download, the choice made on your machine. Linux x64 ships every executable twice —
    Lumice.baseline (x86-64-v1, as before) and Lumice.x86-64-v4 (AVX-512) — behind a CPUID
    launcher installed under the unchanged names Lumice / LumiceGUI, which picks the variant the
    CPU supports (--isa=<level> overrides); measured 1.99× on a Zen 5 desktop. Windows x64 does
    the same with .baseline.exe (MSVC) and .x86-64-v3.exe (clang-cl, AVX2+FMA — MSVC gains nothing
    from /arch, clang-cl gains 2.23×, and v4 adds nothing on top), covering every Haswell+/Zen1+
    desktop; on a machine without AVX the launcher falls back to baseline. The CUDA fatbin now
    carries native sm_120 (RTX 50 series), so first launch no longer JIT-compiles from PTX
    (1.87 s → 0.22 s); the PTX floor stays compute_61. macOS and Linux ARM64 packages are unchanged.
  • The GUI's ray budget reaches 10¹¹ rays (#354). The Rays(M) slider — on the Simulation panel
    and in the analysis window — now spans 0.1 … 100 000 million on a logarithmic track, so the low
    end still resolves to 0.1 M while the top reaches 100 000 M; the input box takes any value
    directly. Display format changes from one decimal to six significant digits. .lmc and exported
    JSON fields are unchanged.
  • A lens with neither fov nor f now runs with a per-type default instead of exiting (#355).
    The docs always promised a default; the CLI exited with code 4. It now uses 90° (30° for globe),
    logs a warning naming the default, and renders. Nothing to do for a config that states either
    field. While there, doc/configuration.md now says what the code always did: lens.fov is the
    angle across the short edge of the frame, not the diagonal.
  • The Overlay panel's Angular Dist. row moves into a collapsed section, and Reference Points
    gets an All row
    (#362, #363, #365). The main Overlay table keeps three rows (horizon, elevation,
    longitude); the sun circles now sit as the Sun row of the new Angular Distance from...
    section beside the Lens Center row, collapsed on a new document (its open state is saved with the
    document). In Reference Points the header-bar All / None / ⋯ buttons are gone; the
    marker table's first row is an All family row in the same six columns — Line / Label are
    tri-states derived from the six entries (a square when mixed; click turns all on unless all are
    already on), Alpha binds the shared marker alpha, the f...
Read more

v4.5.1

Choose a tag to compare

@github-actions github-actions released this 11 Sep 00:16
38aff9c

Added

  • Print mode: ink laid on paper, for a halo on a light background (#337, #343). A white or
    pale background used to erase the halo entirely. Light is added to the background and clamps
    at white, so with a white sky every pixel was at the ceiling before any ray energy arrived —
    while the grid and overlay lines, which are blended rather than added, stayed perfectly visible,
    so the result read as a failed simulation rather than a colour choice. No foreground colour can
    fix that under an additive operator, so the fix is a second operator. render.tone chooses it:
    screen (the default, and byte-for-byte what every existing config rendered) or print, under
    which accumulated radiance becomes a neutral ink density laid on render.paper (a new field,
    default white): D = 11·log10(1+e), out = paper·10^(-D), so a white page can go dark. Print is
    greyscale by construction — hue is given up on purpose, since "ink absorbs the complementary
    colour" would make white-light features such as parhelic circles vanish on white paper — so a
    print reads like a drawing and its arcs are told apart by position and shape. In the GUI the
    Display panel's Mode switch selects it and the ground-colour row becomes Paper Color. Because
    ink owns the colour channel under print, four things that colour pixels are mutually exclusive
    with it and say so instead of silently doing nothing — the background photo overlay, raypath
    colouring, ray_color, and per-family annotation colours: the CLI logs one warning per field it
    will not read, and the GUI greys the control with the reason. Overlay text labels are ink too
    (#343 closed the one path — the GUI's ImGui-drawn labels — the first cut missed, so under Print
    every label was still drawn in its family's hue beside a black line). A contrast-headroom notice
    guards the degenerate end of each operator through one shared predicate: a screen background
    within a few 8-bit levels of white, or a print paper within a few of black, both render a blank
    picture from a configuration that looks reasonable — the CLI warns at commit, the GUI shows an
    inline notice with a one-click repair (Switch to Print / Reset paper to white). .lmc and
    exported JSON round-trip both fields.
    ABI: LUMICE_RenderParam gains trailing tone / paper[3] (LUMICE_API_VERSION 426 → 427,
    appended so every existing field keeps its offset; sizeof grows, recompile against the new
    header). A zero-initialized tone is LUMICE_TONE_SCREEN, the operator this API has always
    used — but paper's JSON default is white while a zeroed struct names black paper, which under
    print is an all-black page. Set it, or go through JSON.
  • An eyedropper for Sky Color that samples the background photo (#334). Next to the Sky Color
    swatch, a pipette enters a picking mode in which the preview shows the photo alone — render,
    auxiliary lines and lens border are suppressed, so what you see is what you get — and a colour
    chip follows the cursor until you click. Sampling the sky of the photo is the point: the photo
    blend is background·(1−α) + render·α, so the closer background is to the photo's own sky
    colour, the less the photo is washed out and the more the halo comes through as pure increment.
    The button is greyed, with the reason, while the photo is hidden or none is loaded.
  • A config can now ask for a grid family's numbers without its lines. (#323) The parallels, the
    meridians and the sun's angular-distance circles each gain a line switch of their own —
    grid.elevation_line, grid.longitude_line, grid.angular_dist_line, all defaulting to true, so
    every existing config renders exactly as before. Previously only the horizon could be asked for
    text without a line; for the other three, "do not draw me" could only be said by removing their
    angles, which removed their labels too. The GUI's exported config follows suit: turning grid or
    circle lines off while leaving their labels on used to drop the numbers from the CLI
    render with no warning, and now exports exactly what the preview shows.
  • LUMICE_RenderParam gains elevation_line / longitude_line / angular_dist_line (#323)
    (LUMICE_API_VERSION 425 → 426, appended at the end of the struct — recompile against the new
    header). Note the defaults run the other way from every other annotation flag: the JSON default is
    on, so a zero-initialized struct asks for no lines even when it carries a full angle list. Set
    the three fields, or go through JSON.
  • LUMICE_ConvertMillerIndexToWedgeAngle — one place to ask what Miller indices mean. (#336)
    New C API function returning the wedge angle and a verdict on the indices themselves: valid, "no
    cone this side", "not enough indices yet" (so a UI can call it on every keystroke without owning a
    rule for when a row is finished), or invalid, with the offending slot named where one slot is at
    fault. No LUMICE_API_VERSION change — a new function, no struct or ABI change.
  • The wedge-angle dropdown takes custom Miller indices. (#336) Below the four built-in presets
    the Crystal editor's Upper A / Lower A dropdowns now carry a row of index boxes in the same
    {h,k,i,l} notation the preset labels use, so a face the table does not offer — {3,0,-3,1}, say
    — can be asked for by name instead of converted to degrees by hand. The angle updates as you type
    and is only written when you press Apply, so a triple being typed through never lands
    half-finished. i is shown, not typed: it is -(h+k) by definition, and deriving it means the
    four numbers can never contradict each other. Indices that name no buildable face are refused with
    the reason spelled out and Apply greyed — a non-zero k (a second-order pyramidal face this
    crystal model cannot express), a negative index, an h:l ratio outside the buildable range, and h = 0, which says "no pyramidal cap on this side" and belongs in the pyramid height rather than in
    an angle. Nothing is stored: the dropdown writes the angle and no Miller indices enter the
    document.
  • The Settings panel keeps the wedge angles you use. (#336) A new region under Settings >
    Presets saves crystal faces by their Miller indices, and everything saved there is offered in the
    Crystal editor's Upper A / Lower A dropdowns from then on, across restarts. The add row is the
    same {h,k,i,l} control the dropdown carries, refusing the same triples for the same stated
    reasons. Entries are stored as indices rather than as degrees, so a saved face keeps naming the
    same face if the ice constants are ever corrected; a preset has no name of its own for the same
    reason — the indices are the name. The four built-in presets are unaffected and cannot be deleted:
    your list is added to them, and a triple they already cover is not saved twice. Nothing is written
    until you press Save, and closing the panel discards the change. A file edited by hand can hold a
    face that names no buildable cone; the panel shows that row with a warning and a live delete
    button rather than hiding it, and loading one says which entries it dropped and why.

Changed

  • The CLI no longer rounds every render up to a whole second (#324). The output loop used to
    wait out its 1 s poll interval before checking whether the simulation had finished, so a
    100-ray render and a 6,000,000-ray one both took 1.05 s and every render's wall time was a
    whole-second staircase. It now checks first and waits only if there is something to wait for;
    a 20k-ray render measures 1.02 s → 0.04 s, and the results file and stats are written no more
    often than before. (The test suite does not get proportionally faster — under a parallel run the
    waits already overlapped — so this is a latency change for a person at the terminal, not a CI
    one.)
  • The Display panel's Rendering rows are regrouped and renamed (#338). The rows now read
    Resolution ‖ EV Anchor / EV / its read-out ‖ Mode / ground colour / warning, so the EV
    read-out sits under the value it reports instead of two rows away. The ground-colour row is one
    control with two faces — Sky Color (with the eyedropper) under Screen, Paper Color under
    Print — because the renderer reads only one of the two fields in each mode, so one of two
    permanently shown controls was always dead; both fields keep their own values, .lmc keys,
    Settings rows and Revert baseline, and switching modes back and forth changes neither. Three
    labels: Tone → Mode, the ev_mode selector's Mode → EV Anchor (the same word as
    anchor_l99_sky), Paper → Paper Color.
  • Importing a core/CLI config into the GUI now warns about the keys the GUI deliberately does
    not support
    (#328). A non-zero sun.azimuth, render[0].lens_shift or scene.geom_clock in
    an imported JSON used to be dropped without a word — a hand-written azimuth: 30 rotated the
    whole picture by 30° in the CLI and by nothing in the GUI, with no hint why. All three now
    raise the same import-warning popup the reader already uses for a complex filter it cannot hold.
    This is a boundary, not a gap: the GUI fixes the sun's azimuth at 0 and the CLI keeps the
    freedom; the change is that the boundary now speaks.
  • The "layer produces no rays" notice reads the weights that actually reach the engine (#320).
    It used to fire only when every crystal in a layer was excluded; a layer whose crystals were all
    included with Weight 0 is the same engine configuration byte for byte and was not reported.
    The notice now covers both, and names both controls.
  • upper_indices / lower_indices are now judged rather than partly ignored. (#336) These
    arrays used to be read only when they held exactly three entries, and only entries 0 and 2 were
    looked at, so three kinds of mistake passed silently: a four-index array — [1,0,-1,1], the
    notation us...
Read more

v4.5.0

Choose a tag to compare

@github-actions github-actions released this 06 Sep 15:35
c04ad13

Added

  • Lens border ring for fisheye projections (#283). Overlay gains a Lens Border row (colour +
    toggle, off by default) that draws the projection's own image-circle boundary — equal-area,
    equidistant, orthographic and all four dual-fisheye variants — so a halo that does not fill the
    frame no longer looks indistinguishable from the black surround outside the lens's valid domain.
    linear, single-lens fisheye_stereographic, rectangular and globe are unaffected: the first
    three already fill the frame at every reachable field of view, and globe is excluded as a
    product choice despite having a bounded image circle of its own.
  • GUI background colour reaches the picture (#286). The preview, the three PNG exports
    (screenshot / dual-fisheye / equirectangular) and the frame baked into a saved .lmc now all
    paint the configured background colour behind the halo — previously only the CLI did, so the GUI
    colour picker moved and nothing on screen changed. The colour is composited additively in linear
    RGB before the sRGB transfer curve, which makes a pixel carrying no halo energy render as exactly
    the sRGB triple the picker showed; it is painted only where the lens actually images sky, so the
    black surround outside a fisheye's image circle stays black.
  • Raypath-colour (composite) display honours the background colour (#286). With raypath
    colouring on, the picture now carries the same background as with it off — previously the
    composite was baked server-side with a black surround, so toggling colouring changed the
    background out from under the user.
  • Two new C API entry points for the background colour (#286, ABI additions, non-breaking):
    LUMICE_SetCompositeBackground(server, background_linear), a display-time push shaped exactly
    like LUMICE_SetCompositeExposure (no epoch bump, no re-simulation; all-zero is an algebraic
    no-op, so a caller that never calls it sees byte-identical composites), and
    LUMICE_XyzToSrgbUint8WithBackground(...), the existing LUMICE_XyzToSrgbUint8 with an additive
    linear-RGB background composited before the final clamp and gamma.
  • ev_mode (#287, #299) — a first-class choice between two exposure anchors, reaching core
    RenderConfig, the C API, .lmc documents and CLI JSON. "absolute" anchors to the energy the
    light source EMITTED, so two renders at the same EV are directly comparable. "relative" (the
    DEFAULT) anchors to the P99 radiance of a fixed, full-sky reference buffer built for the scene —
    the same anchor the GUI preview has always used — so it does not depend on which view, lens,
    visible clip or output resolution a particular render asks for; ray_num still co-determines
    it, since the anchor is a statistic over the accumulated simulation. A config with no ev_mode
    key, or a misspelled value, renders relative. On the composite (raypath-colour) path the same
    field selects between the participating-pixel self-anchor and the mono path's absolute scale,
    sharing one scalar rather than re-deriving it.
    ABI: LUMICE_RenderParam gains a trailing int ev_mode (LUMICE_EV_MODE_RELATIVE = 0,
    LUMICE_EV_MODE_ABSOLUTE = 1); LUMICE_API_VERSION moves 415 -> 416 (the same step also removes
    opacity, below). See Breaking Changes below for what selecting each mode does to an existing
    config's brightness.
    The GUI's own auto-EV anchor calculation moves into core alongside this, as two new pure C API
    functions with no ABI-breaking half: LUMICE_ComputeP99Y (the P99 statistic itself, with an
    optional coarse-grid downsample) and LUMICE_ComputeEvAuto (the stops-and-clamp arithmetic on
    top of it) — so the GUI and any other consumer share one implementation of the anchor instead of
    each carrying their own copy.
  • LUMICE_RawXyzResult.emitted_energy (#287, C API, ABI addition): the total spectral energy
    the light source emitted into a snapshot — the quantity absolute mode normalizes by. A raw
    total, unlike the neighbouring per-pixel snapshot_intensity, and a different measurement, not a
    rescaling of it, since one counts what went in and the other what landed. A consumer reproduces
    the renderer's own absolute scale as intensity_factor * kNormScale * total_pixels / emitted_energy. Occupies pre-existing alignment padding, so sizeof(LUMICE_RawXyzResult) is
    unchanged.
  • Core-side annotation layer: the CLI renderer can now draw every auxiliary line and marker the
    GUI preview draws, from one shared implementation instead of two (#292, #305). A render[] entry
    can ask for parallels (grid.elevation), meridians (grid.longitude, new), circles of angular
    distance from the sun (grid.angular_dist, renamed from grid.central — see Breaking Changes), a
    front-hemisphere clip (front, new), text labels for each of those families
    (horizon_label / grid_label / angular_dist_label, new), and — generalizing the existing
    zenith/nadir ring into a named list — up to six sky-direction markers (grid.markers: zenith,
    nadir, sun, subsun, anthelion, antisolar), each independently switched, coloured and drawn, with a
    matching [Look At ▾] view preset in the GUI's View group. All of this previously existed in the
    GUI only, or not at all in this generalized form; an exported config now reproduces it. New C API:
    LUMICE_ComputeAnnotationOverlay / LUMICE_ReleaseAnnotationOverlay (pure, side-effect-free
    geometry and label anchors for one view), LUMICE_ResolveAnnotationMarkerDirection (a named
    marker as a world direction, for pointing the camera at it rather than finding its canvas
    position), and LUMICE_ResolveSunHorizonDirection (the sun's azimuth carried down to the
    horizon, for the same View-preset use).
    ABI: a series of LUMICE_RenderParam field additions across this arc — elevation_grid
    becoming rendered, longitude_grid, zenith_nadir, front, the three *_label switches, and
    markers / markers_count / markers_opacity / markers_radius_px — every one appended so
    existing fields keep their offsets, and every one opt-in: a zero-initialized struct draws none of
    them, matching what a config with no grid object already rendered. zenith_nadir and the
    .lmc overlay_zenith_nadir_* keys keep working unchanged; where both zenith_nadir and a
    non-empty markers list are present, markers wins. LUMICE_API_VERSION moves 416 -> 421 for
    the render[] fields above (#292); the marker generalization (#305) later resumes this same
    counter at 423 (after the two LUMICE_RawXyzResult additions below take it there), moving it
    423 -> 424 for the underlying named-direction table (LUMICE_AnnotationMarkerPoint and the
    LUMICE_ANNOTATION_MARKER_* ids, all appended at each struct's physical end) and 424 -> 425 for
    markers[] itself — 425 in total, matching HEAD.
  • LUMICE_RawXyzResult.anchor_l99_sky and .axis_solid_angle (#299, C API, ABI additions):
    the two quantities a consumer needs to reproduce the relative-mode scale itself —
    anchor_l99_sky is the P99 radiance of the fixed full-sky reference buffer (identical on every
    row of one call), and axis_solid_angle is the solid angle this renderer's own on-axis pixel
    subtends: scale = intensity_factor * TargetWhiteToLinear(135) / (axis_solid_angle * anchor_l99_sky). anchor_l99_sky grows the struct (64 -> 72 bytes) — a caller that was not
    recompiled hands the API a shorter buffer; axis_solid_angle then lands in the padding that
    growth leaves behind, so it does not grow the struct a second time. LUMICE_API_VERSION moves
    421 -> 422 for the first field, 422 -> 423 for the second (the annotation-layer entry above
    continues the count from here to 425).
  • CLI --workers N, and a matching GUI personal default (#314). Both front ends previously left
    worker count at its zero-initialized automatic default; --workers now lets a user pick it
    explicitly (rejected outright, not silently clamped, if it is not a positive integer), and the
    GUI Settings panel can save a preferred value the same way it saves other personal defaults. See
    Breaking Changes below for the new automatic default this doesn't override.
  • GUI "Use GPU" can be saved as a personal default (#311). Previously the checkbox reset to
    legacy CPU on every launch even after an explicit choice; it can now be saved via Settings like
    other personal defaults. The factory default is unchanged (CPU), and a saved GPU preference on a
    machine with no GPU backend available is silently ignored by the existing runtime fallback.

Changed

  • Cylinder crystal height can go as low as 1e-4, down from 0.01 (#301) — the GUI slider's
    floor was far above core's actual h > 1e-5 acceptance, so this range was reachable by editing a
    config by hand but not from the slider. Several sliders' display formats are also corrected
    (axis fields, sun.diameter) where the digit shown did not change finely enough to track the
    slider's own step size, making the control look frozen mid-drag.
  • Legacy CPU backend throughput improved via reduced allocation churn (#303). A per-batch
    working buffer was reallocated on every batch; it is now reused for the worker's lifetime,
    measured to cut large allocations from roughly 17,000 to about a dozen in one run. Measured
    throughput gain ranges from +2.4% to +159.5% across two machines, worker counts and scenarios
    (largest at 16 workers on a 16-core machine); peak memory dropped 57-61% in the same runs. Output
    is bit-identical — no GetUniform() call sites were added or removed.
  • The renderer-count-exceeded error names the actual count and the limit (#294). Loading a
    fifth render[] entry used to fail with a bare error code 3; the CLI now reports config has 5 "render" entries, exceeding the limit of 4. The limit itself (4) is unchanged.

⚠️ Breaking Changes

  • **`LUMICE_RenderParam:...
Read more

v4.4.3

Choose a tag to compare

@github-actions github-actions released this 30 Aug 09:58

What's Changed

  • test: retire five dead observation channels and one lying marker by @LoveDaisy in #264
  • fix(gui,core): 以 core 为参照收敛「静默替用户挑一个语义」的加载分支 by @LoveDaisy in #265
  • docs: make a completeness claim carry the same burden of proof as adding code by @LoveDaisy in #266
  • docs: 远程验证文档按「角色 / 主机绑定」分层,并写入新参照机 recipe by @LoveDaisy in #267
  • fix(test): 把 setenv/unsetenv 的 #ifdef 收敛成一个有名字的 helper by @LoveDaisy in #268
  • fix(build): 让 CMP0091 真正生效,Windows 发布产物链接静态 CRT by @LoveDaisy in #269
  • docs(gui): 落盘视觉语言与布局架构两份 GUI 蓝图 by @LoveDaisy in #270
  • feat(gui): 落地 GUI 视觉语言——单一 owner、比例字体、量化节奏、调色板与语义色 by @LoveDaisy in #271
  • fix(gui): 底图纹理改用 mipmap + trilinear,修缩小显示时的欠采样混叠 by @LoveDaisy in #274
  • feat(gui): 底图可平移缩放,让裁剪过的照片能与仿真结果对齐 by @LoveDaisy in #275
  • feat(gui): 晶体卡新增「参与仿真」toggle,替代把权重拖到 0 by @LoveDaisy in #276
  • feat(gui): Overlay 辅助线组改为 6 列表格形态 by @LoveDaisy in #277
  • fix(core+gui): 全球面快路径补 roll 旋转对称条件,修滑条端点浮点漂移导致的采样路静默切换 by @LoveDaisy in #278
  • refactor(gui): 颜色收口——色槽补齐 58/58 + 调用点裸字面量逐处 disposition by @LoveDaisy in #279
  • fix(gui): Overlay 表格人工验收三条修复 by @LoveDaisy in #280
  • fix(gui): 行末标签列左缘对齐 + 间距收敛为单一 owner by @LoveDaisy in #281
  • 晶体卡片 layout 重排 + 晶体身份可寻址 + Colors 面板编号/失效态 by @LoveDaisy in #282

Full Changelog: v4.4.2...v4.4.3