Skip to content

Slice 14 — Heatmap HM-1 (per-center viridis coloring) - #13

Merged
891458249 merged 6 commits into
mainfrom
slice-14-heatmap-hm1
Apr 22, 2026
Merged

Slice 14 — Heatmap HM-1 (per-center viridis coloring)#13
891458249 merged 6 commits into
mainfrom
slice-14-heatmap-hm1

Conversation

@891458249

@891458249 891458249 commented Apr 22, 2026

Copy link
Copy Markdown
Owner

Summary

Phase 2B second slice. Adds the first viewport heatmap mode — HM-1: per-center weight visualization — so artists can see at a glance which trained centers carry the largest weights for the current rig.

mRBFNode (kDependNode)         ← +const weights() accessor
    │  message
    ▼
mRBFShape (kLocatorNode)       ← +heatmapMode enum (Off / CenterWeights / PredictionField)
    │  drawdb classification
    ▼
mRBFDrawOverride               ← per-center colored spheres via 11-stop viridis LUT, cached on weights buffer ptr

Deliverables

  • Phase 1: additive RBFInterpolator::weights() const getter + 1 D-group test (138 → 139)
  • adapter_core: new color_mapping module (Maya-free) — HeatmapMode enum, map_scalar_to_color, compute_center_colors — + 8 F-group tests
  • mRBFNode: additive weights() accessor (Phase 2A logic untouched)
  • mRBFShape: new aHeatmapMode enum attribute (3 fields; Slice 13 attrs untouched)
  • mRBFDrawOverride: per-center color path with cache key on weights buffer pointer; Slice 13 supportedDrawAPIs/boundingBox/isBounded/classification untouched
  • smoke_viewport: heatmapMode round-trip (Off ↔ CenterWeights)

F-stop #5 — polynomial → LUT pivot

The original spec called for a 4th-order polynomial fit of viridis. The candidate coefficients failed F1/F2/F3 by 0.038–0.486 absolute per channel against the spec's own reference values (the polynomial fits a different curve, not viridis). Per the spec's prescribed recovery path, pivoted to an 11-stop piecewise-linear LUT with anchors at v=0 / v=0.5 / v=1 matching F1/F2/F3 reference values exactly. Single rebuild → 172/172.

Validation

Check Result
Adapter + Phase 1 (build-adapter, ctest) 172/172 passed
Maya 2022 plugin build (C:/SDK/Maya2022/devkitBase) 0 warn / 0 err
Maya 2025 plugin build (C:/SDK/Maya2025/devkitBase) 0 warn / 0 err
Maya 2022 × 4 smokes (hello/predict/train/viewport) all PASS
Maya 2025 × 4 smokes (hello/predict/train/viewport) all PASS
Phase 1 pure regression (build, ctest) 139/139 passed

Constraints respected

  • No Phase 1 kernel/solver/io_json behavioral changes (only additive const getter)
  • No Slice 10A/11/12 mRBFNode compute() / try_load() / train-cmd touches (only additive weights() accessor)
  • No Slice 13 mrbf_draw_override supportedDrawAPIs / boundingBox / isBounded / classification changes
  • No Slice 13 mrbf_shape pre-return MS::kSuccess line touches (new attribute appended at end of initialize())
  • No MRenderItem / custom shaders / playblast infra introduced
  • mRBFShape attribute set still strictly numeric + message + enum — no MFnTypedAttribute(kString), preserving the Slice 13 R-44 invariant

Tech-debt added

  • R-45: HeatmapMode::kPredictionField registered as enum field 2 but degrades to kOff in Slice 14; Slice 15 (HM-2) activates
  • T-19: per-frame min/max normalization makes color spread context-dependent
  • T-20: LUT precision 1e-2 at three anchors only; intermediate v values may drift ~0.05 vs matplotlib viridis

Test plan

  • Pre-flight Maya API grep (FitResult.weights, addField signatures across 2022/2025)

  • Adapter + Phase 1 ctest 172/172 (after F-stop Slice 08 — JSON I/O with schema v1 (v0.8.0) #5 LUT pivot)

  • Maya 2022 configure + build clean

  • Maya 2025 configure + build clean

  • Maya 2022 × 4 smokes exit 0 (incl. heatmapMode round-trip)

  • Maya 2025 × 4 smokes exit 0 (incl. heatmapMode round-trip)

  • Phase 1 pure regression ctest 139/139

  • Visual review — Maya 2022 kOff screenshot — 4 white spheres

    Maya 2022 Off

  • Visual review — Maya 2022 kCenterWeights screenshot — 3 deep-purple + 1 yellow (per-frame min/max maps tiny_rbf weight cluster)

    Maya 2022 CenterWeights

  • Visual review — Maya 2025 kOff screenshot — 4 white spheres

    Maya 2025 Off

  • Visual review — Maya 2025 kCenterWeights screenshot — 3 deep-purple + 1 yellow (cross-version match with Maya 2022)

    Maya 2025 CenterWeights

d891458249-rgb and others added 6 commits April 22, 2026 14:22
Phase 2B Slice 14 (HM-1) groundwork.  Mirror of Slice 13's centers()
getter: const reference to FitResult::weights, noexcept, returns a
0x0 matrix when !is_fitted().  Exposes the (N x M) weight matrix to
the Viewport 2.0 heatmap path so it can compute per-center L2 norms
without re-parsing the saved JSON or re-running the solver.

Phase 1 test count: 138 -> 139.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… + F-group tests)

Phase 2B Slice 14 (HM-1) — pure-C++14, Maya-free color-mapping
module.  Two surfaces:

* map_scalar_to_color(Scalar v01) -> 4-tuple float RGBA.  11-stop
  piecewise-linear viridis LUT with anchors at v=0/0.5/1 matching
  matplotlib reference values exactly.  Spec originally called for
  a quartic polynomial fit; pivoted to LUT after polynomial form
  failed F1/F2/F3 by ~0.4 absolute (see DEVLOG "F-stop #5").

* compute_center_colors(weights, out, n) — row L2 norm per center,
  per-frame min/max normalize, map through LUT.  NaN/Inf weight in
  any row falls back to white for that single center (others use
  finite-only min/max).  Degenerate "all weights equal" maps to the
  viridis start.

8 F-group tests cover both surfaces:
  F1/F2/F3 — viridis anchors at v=0/0.5/1
  F4/F5    — clamping below 0 / above 1
  F6       — alpha invariance across in/out-of-range inputs
  F7       — ascending norms map min->start max->end
  F8       — degenerate all-equal -> all colors equal & = viridis(0)

Random seed kSeedS14 = 0xF5BFADu reserved for future randomised
additions.  Adapter test count: 25 -> 33 (E + F merged into the
same target).  Total adapter+Phase 1 ctest: 172/172 green.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…s / PredictionField)

Phase 2B Slice 14 (HM-1) — adds the third (and currently last)
attribute on mRBFShape:

  heatmapMode (enum, default 0=Off, channel-box visible)
    0 = Off               (Slice 13 default behaviour)
    1 = Center Weights    (Slice 14 HM-1)
    2 = Prediction Field  (reserved for Slice 15; falls back to Off)

Field indices match the rbfmax::maya::HeatmapMode enum class so the
DrawOverride can cast the short to the enum type directly.  Slice 13
mRBFShape attributes (sourceNode, drawEnabled, sphereRadius) and
its Path B classification + initialize() flow are untouched; the new
block sits between addAttribute(aSphereRadius) and the trailing
return MS::kSuccess as the spec requires.

Strict invariant from Slice 13 Path A retrospective preserved:
mRBFShape MUST NOT carry any MFnTypedAttribute(MFnData::kString) —
the new attribute uses MFnEnumAttribute, which is unaffected by the
empirical kLocatorNode-typed-string failure mode (DEVLOG R-44).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…aching

Phase 2B Slice 14 (HM-1) — wires the heatmap end to end.

Changes:
- mRBFNode: additive const weights() returning the interpolator's
  N x M weight matrix (or a static empty 0x0 when !is_loaded()).
  Phase 2A compute() / try_load() / attribute set zero-touch; this
  is purely a read accessor for the DrawOverride's color path.

- mRBFDrawOverride RbfDrawData: extended with center_colors vector,
  current heatmap_mode, and a 4-tuple cache key (weights buffer
  pointer + rows + cols + last cached mode).

- prepareForDraw: reads heatmapMode plug; for kCenterWeights
  computes (or reuses cached) per-center colors via
  compute_center_colors; kPredictionField gracefully degrades to
  kOff.  Cache key compares the underlying weights buffer pointer
  against the prior frame; pointer change (or shape change, or mode
  switch) triggers recompute.  Slice 13 supportedDrawAPIs /
  boundingBox / isBounded / classification untouched.

- addUIDrawables: switches to per-sphere setColor + sphere when
  center_colors is populated; otherwise keeps the Slice 13 single-
  color batch path.  Per Maya 2022/2025 docs, calling setColor
  inside the begin/endDrawable block before each sphere() is the
  documented idiom for per-primitive coloring.

- maya_node/CMakeLists.txt: src/color_mapping.cpp added to the
  plugin sources alongside the Slice 13 sources.

- smoke_viewport.py: round-trips heatmapMode through Off (0) and
  CenterWeights (1) after the Slice 13 drawEnabled / sphereRadius
  asserts.

Validation (8 smokes total, both Maya versions all PASS):
  Maya 2022 hellonode/predict/train/viewport: PASS
  Maya 2025 hellonode/predict/train/viewport: PASS

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
DEVLOG.md: ~140-line Slice 14 entry covering scope, deliverables,
12 design decisions, R-09 self-check (LUT precision, cache key
soundness), validation outcomes (172 / 0 / 8 / 139), the F-stop #5
record (polynomial -> LUT pivot rationale + recovery), and tech-
debt R-45/T-19/T-20.

maya_node/README.md: new "Heatmap mode (Slice 14 - HM-1)"
subsection inside the Slice 13 Viewport 2.0 section: enum table,
workflow snippet, implementation notes (LUT precision contract,
NaN fallback, per-frame normalization, cache key).

Visual review screenshots (4: Maya 2022 kOff + kCenterWeights;
Maya 2025 kOff + kCenterWeights) pending user-side GUI session and
will be attached to PR description on capture.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…+ 2025)

Captured during user-side GUI verification:
- maya2022_Off.png        — Maya 2022.5.1, heatmapMode=0 (4 white spheres)
- maya2022_CenterWts.png  — Maya 2022.5.1, heatmapMode=1 (3 deep-purple + 1 yellow)
- maya2025_Off.png        — Maya 2025.3,   heatmapMode=0 (4 white spheres)
- maya2025_CenterWts.png  — Maya 2025.3,   heatmapMode=1 (3 deep-purple + 1 yellow)

Color distribution observation: tiny_rbf fixture's 4 weight-norm
values cluster as {3 near min, 1 outlier}, which the per-frame
min/max normalization maps to viridis(0)=purple (3 centers) and
viridis(1)=yellow (1 center).  No teal-band centers because the
fixture lacks intermediate L2 norms.  Production rigs (50+ centers)
will exhibit the full purple->teal->yellow ramp.

Cross-version invariant verified visually: both Maya 2022 and Maya
2025 produce identical color assignments at identical positions.
This complements the deterministic adapter F-group tests by
confirming no Maya-side color management drift between versions.
@891458249
891458249 merged commit 9edffdd into main Apr 22, 2026
4 checks passed
@891458249
891458249 deleted the slice-14-heatmap-hm1 branch April 22, 2026 08:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants