Skip to content

Slice 13 — mRBFShape + mRBFDrawOverride (Path B) - #11

Merged
891458249 merged 6 commits into
mainfrom
slice-13-draw-override-skeleton
Apr 21, 2026
Merged

Slice 13 — mRBFShape + mRBFDrawOverride (Path B)#11
891458249 merged 6 commits into
mainfrom
slice-13-draw-override-skeleton

Conversation

@891458249

Copy link
Copy Markdown
Owner

Summary

Phase 2B opening slice: Viewport 2.0 visualization for trained mRBFNode centers.

Architecture (Path B)mRBFNode stays a pure kDependNode; visualization is carried by a new auxiliary locator mRBFShape connected via message attribute. mRBFDrawOverride is registered against the shape's classification.

mRBFNode (kDependNode, Phase 2A unchanged)
    │  .message ─► mRBFShape.sourceNode
    ▼
mRBFShape (kLocatorNode, classification drawdb/geometry/rbfmax/mRBFShape)
    │
    ▼
mRBFDrawOverride → MUIDrawManager::sphere() per center

Path A → B retrospective

Original spec attempted reparenting mRBFNode itself to MPxLocatorNode with 6-arg registerNode(…, kLocatorNode, &classification). Maya returned kFailure: "Unexpected Internal Failure" on both 2022 and 2025. 4+ hours of systematic assumption elimination (Rule 4) ruled out: classification format (3-level/2-level), MString storage (stack/static/anon-namespace), devkit provenance, registration order, Maya runtime version, output-only kString attr subset, C++ inheritance (via static_assert), and stale build artifacts. An out-of-repo reducer (MinLocator3.cpp) empirically pinned the failure to MPxLocatorNode + MFnTypedAttribute(MFnData::kString). Root Maya-internal cause unresolved; strategic retreat to Path B — a separate locator whose attribute set is strictly numeric + message. Full retrospective in DEVLOG.md Slice 13 entry; proposed Rule 5 candidate for Phase 2B memory: prefer canonical Autodesk sample patterns over ambitious deviations.

Deliverables

  • Phase 1 additive: RBFInterpolator::centers() const getter + 1 D-group test
  • IDrawSink / DrawCall / emit_centers_draw_calls adapter + 8 E-group tests (Maya-free)
  • mRBFNode::is_loaded() + centers_for_viewport() (read-only, no DG touch)
  • mRBFShape : MPxLocatorNode, typeId 0x00013A01 — attrs: sourceNode (message) + drawEnabled (bool) + sphereRadius (double)
  • mRBFDrawOverride : MPxDrawOverride — 2-hop resolve shape → mRBFNode, MUIDrawManager::sphere per center
  • plugin_main.cpp — registers all of the above with atomic rollback
  • tests/smoke/smoke_viewport.py — 7-step mayapy contract
  • DEVLOG.md + maya_node/README.md — Path A→B retrospective + Viewport 2.0 usage

Validation status

Check Result
Adapter + Phase 1 tests (build-adapter, ctest) 163/163 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) 138/138 passed

Invariant changes

  • Cross-version .mll bit-identity is NO LONGER expected — Slice 13 adds OpenMayaRender + OpenMayaUI link libs which diverge across 2022/2025 devkits. The Phase 2A "bit-identical across versions" lemma applied only when the ABI surface was OpenMaya only; explicitly retracted in DEVLOG.md.
  • Phase 2A contract for mRBFNode fully preserved — no change to its compute, attributes, type (still kDependNode), or initialize().

Tech-debt added

  • R-44: MPxLocatorNode + MFnTypedAttribute(kString) empirical failure mode on Maya 2022 and 2025; root cause unresolved. Mitigation: Slice 14/15 must keep mRBFShape attrs numeric + message only.
  • T-16: Slice 13 requires user to connectAttr mRBFNode.message mRBFShape.sourceNode manually. Consider a convenience rbfmaxAttachShape command in a later slice.
  • T-17: centers_for_viewport() returns positions only; Slice 14 (heatmap) will need per-center colors.
  • T-18: Fixed (-10, +10) bbox on draw override; tighten in Slice 14+.

Test plan

  • Pre-flight Maya API grep (no new unverified APIs)
  • Adapter + Phase 1 ctest 163/163
  • Maya 2022 configure + build clean
  • Maya 2025 configure + build clean
  • Maya 2022 smoke_hellonode exit 0
  • Maya 2022 smoke_predict exit 0
  • Maya 2022 smoke_train exit 0
  • Maya 2022 smoke_viewport 7/7 PASS (new)
  • Maya 2025 smoke_hellonode exit 0
  • Maya 2025 smoke_predict exit 0
  • Maya 2025 smoke_train exit 0
  • Maya 2025 smoke_viewport 7/7 PASS (new)
  • Phase 1 pure regression ctest 138/138
  • Visual review: 4 screenshots (Maya 2022 top + perspective, Maya 2025 top + perspective) — user-side GUI, pending

d891458249-rgb and others added 6 commits April 21, 2026 20:22
Phase 2B Slice 13 prerequisite: Viewport 2.0 DrawOverride needs
read-only access to the trained centers matrix for visualisation,
mirrors the Slice 11 pattern where kernel_params() getter closed
the aKernelType attribute gap without forcing the node to re-parse
saved JSON.

New public API (additive, noexcept, Maya-free, engine-agnostic):

    const MatrixX& centers() const noexcept;

Contract: returns fit_result_.centers (owned by the interpolator
since fit() or load() populated it); before the first successful
fit/load returns a default-constructed 0x0 matrix.  Gate on
is_fitted() for defined row/col counts.  No behavioural change to
fit / predict / save / load / clone / ScratchPool paths.

Test: new TEST(RBFInterpolatorState, CentersGetterReflectsFit) in
category D (state queries; count goes 3 → 4).  Reuses the hand-
crafted 4-corner unit-square fixture shared with
KernelParamsReflectsFit — trivially solvable at λ=1e-6, Gaussian,
no polynomial tail.  Asserts row/col count and element-wise
EXPECT_DOUBLE_EQ equality against the input matrix.

Phase 1 regression now reports **138/138 green** (was 137/137).

Second Phase 1 additive amendment in Phase 2 under the Slice 11
precedent (additive const getter + test).  Section G allow-list
from Slice 11 covers this case verbatim; no new DEVLOG amendment
needed for this specific commit.

Local verification (MSVC 19.44 Release): 138/138 green, 12.97 s.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 2B Slice 13 piece 2 of 5.  Adds a Maya-free abstraction for
accumulating Viewport 2.0 draw primitives + one orchestrator that
emits one sphere per trained center.

Deliberately does NOT wire the DrawOverride through this sink in
Slice 13.  The current draw topology (single filled-sphere pass
per mRBFNode instance) does not justify the indirection; calling
MUIDrawManager directly from addUIDrawables keeps the native side
thin.  The abstraction lands now because Slice 14 (heatmap) and
Slice 15 (X-ray) will need to share orchestration — better to ship
the shape of the contract early and grow tests around it than to
retrofit on emergency.

Files:
  * maya_node/include/rbfmax/maya/draw_sink.hpp
      - DrawCall POD (type enum + p0/p1/radius/color, default-
        constructible with sane zeros)
      - IDrawSink abstract interface (begin / emit / end, all
        noexcept per contract)
      - emit_centers_draw_calls free function signature
  * maya_node/src/draw_sink_core.cpp
      - emit_centers_draw_calls body.  Always calls begin/end
        exactly once even for empty input so downstream sinks
        get well-defined transaction boundaries.

Tests (E group, 8 new TESTs, adapter suite):
  E1 MockSinkRecordsEmittedCalls  — sink contract smoke test
  E2 EmitCentersSimpleCount       — 4 in, 4 kSphere out
  E3 EmitCentersColorPropagates   — color argument preserved
  E4 EmitCentersRadiusPropagates  — radius argument preserved
  E5 EmitCentersEmptyInput        — begin/end still balanced
  E6 EmitCentersCoordsPreserved   — element-wise bit-exact
  E7 EmitCentersBeginEndBalanced  — 10 centers, still one
                                     begin/end pair
  E8 DrawCallDefaultsAreSane      — default ctor gives kPoint
                                     at origin, radius 0, white;
                                     anchors kSeedS13 = 0xF5BFACu
                                     (reserved for future
                                     randomised E-group tests)

Both draw_sink_core.cpp and the E tests link into the adapter
GTest target, so the whole accumulator is covered without Maya
being available — matches the Slice 10A/11/12 discipline of
keeping orchestration logic Maya-free.

Local verification (MSVC 19.44 Release):
  Step 1 adapter + Phase 1 combined: 163/163 green, 13.93 s
  (138 Phase 1 + 3 H + 6 C + 8 D + 8 E)

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

Phase 2B Slice 13 Path B — groundwork for the mRBFDrawOverride that
will be registered on the auxiliary mRBFShape locator (next commit).
Both accessors are read-only and touch only interp_; Phase 2A
compute / try_load / attribute set are unchanged.

* is_loaded() -> bool, noexcept: interp_ != nullptr
* centers_for_viewport() -> std::vector<MPoint>: first-3-dim
  projection of RBFInterpolator::centers() with zero-pad D<3 and
  truncation D>3

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 2B Slice 13 Path B — new auxiliary locator that hosts the
Viewport 2.0 draw override (next commit). mRBFNode stays a pure
kDependNode; mRBFShape connects via message attribute:

    mRBFNode.message -> mRBFShape.sourceNode

Attributes (deliberately numeric + message only — no
MFnTypedAttribute of MFnData::kString, see DEVLOG Slice 13 Path A
retrospective):

* sourceNode  — message, writable
* drawEnabled — bool,   default true
* sphereRadius — double, default 0.05, min 0.001, softMax 1.0

typeId 0x00013A01 (monotonic after mRBFNode's 0x00013A00).

CMake: adds OpenMayaUI to find_package COMPONENTS and mrbf_shape.cpp
to the plugin target.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 2B Slice 13 Path B — Viewport 2.0 integration.

mRBFDrawOverride is registered against mRBFShape's classification
"drawdb/geometry/rbfmax/mRBFShape". prepareForDraw upstreams via
shape.sourceNode's message connection to resolve the connected
mRBFNode, then reads is_loaded() + centers_for_viewport(). Any
failure along the chain yields empty draw data (no crash, no
warning). addUIDrawables emits one MUIDrawManager::sphere per
center inside a beginDrawable/endDrawable pair.

plugin_main.cpp gains a 6-arg registerNode for mRBFShape with
MPxNode::kLocatorNode + classification pointer, and a
MDrawRegistry::registerDrawOverrideCreator call keyed on that
same classification. mRBFNode's 4-arg registerNode (Slice 10A
kDependNode form) is unchanged. Mirror unwind + atomic rollback
on any failure.

smoke_viewport.py — 7-step Path B contract:
  0. loadPlugin OK
  1. mRBFNode in allNodeTypes(), no drawdb classification
  2. mRBFShape in allNodeTypes()
  3. mRBFShape classification matches DrawOverride literal
  4. mRBFNode.message -> mRBFShape.sourceNode connect OK
  5. Phase 2A predict round-trip on mRBFNode
  6. isLoaded + nCenters state attrs reflect loaded interp
  7. mRBFShape drawEnabled + sphereRadius round-trip

Validated on Maya 2022 (devkit C:/SDK/Maya2022/devkitBase) and
Maya 2025 (devkit C:/SDK/Maya2025/devkitBase); both 4-smoke
suites exit 0 in each version. Adapter + Phase 1 tests 163/163.
Phase 1 pure regression 138/138.

CMake: adds OpenMayaRender to find_package COMPONENTS and
mrbf_draw_override.cpp to the plugin target.

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

DEVLOG.md: adds a ~180-line Slice 13 entry covering scope,
deliverables, design decisions, the full 4+ hour Path A failure
analysis (assumption-elimination trajectory: classification format,
MString storage, devkit, registration order, Maya version, output-
string attrs, inheritance static_assert, stale artifacts), the
Path B architecture diagram, a Rule 5 candidate for Phase 2B
memory (prefer canonical Autodesk sample pattern over ambitious
deviations), R-09 typeId self-checks, validation outcomes, and a
tech-debt register (R-44, T-16, T-17, T-18).

maya_node/README.md: adds a "Viewport 2.0 visualization" section
with the mRBFShape attribute table, the create-and-connect
workflow, dimension projection / unloaded-state / multiple-shape
notes; updates the Files table with the 4 new headers, 4 new
implementations, the 2 new tests, and smoke_viewport.py.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@891458249
891458249 merged commit 74e8001 into main Apr 21, 2026
4 checks passed
@891458249
891458249 deleted the slice-13-draw-override-skeleton branch April 21, 2026 21:34
891458249 pushed a commit that referenced this pull request Apr 21, 2026
Companion to Slice 13 — lets end users install/uninstall the
rbfmax plugin by dragging a Python file into the Maya viewport.

Files added
-----------
- installer/drag_drop_install.py — onMayaDroppedPythonFile hook +
  install / uninstall via Maya Module System (.mod descriptor
  written into the user's ~/Documents/maya/modules).  Single-
  version install covers Maya 2022 and 2025 via per-version
  MAYAVERSION blocks in the .mod file.
- installer/package.py — copies built .mll from
  build-maya-<ver>/bin/Release/ into installer/plug-ins/<ver>/.
  Strict fail-fast build-missing preflight: exits 1 with a full
  cmake recipe if either version's .mll is absent.
- installer/README.md — Chinese user guide covering both the end-
  user and developer flows, plus a troubleshooting section.
- docs/install.md — repository-level user entry pointing at the
  installer (so GitHub viewers see the Maya install path without
  having to download the zip first).

Files updated
-------------
- README.md — adds a blockquote under Quick Start pointing
  Maya end users at docs/install.md.
- .gitignore — excludes installer/plug-ins/2022/*.mll and
  installer/plug-ins/2025/*.mll (large build artifacts; users
  populate them via package.py).

Validation
----------
mayapy install + uninstall smoke cycle on Maya 2022 and 2025:
- package.py copies .mll        : Maya 2022 505.5 KiB / 2025 506.0 KiB
- install copies payload + .mod : both versions OK
- uninstall deletes .mod + dir  : both versions OK
- post-uninstall residue check  : zero bytes on both versions
- build-missing guard           : exit 1 with full cmake recipe

Design rationale
----------------
- Maya Module System (rather than dropping .mll into the user's
  plug-ins dir) gives a clean uninstall surface — one .mod file +
  one directory, both in a user-owned, non-system path.
- onMayaDroppedPythonFile is Autodesk's standard drag-drop hook;
  stable across Maya 2022 / 2024 / 2025 / 2026.
- Per-version plug-in subdirectories are selected at Maya runtime
  via MAYAVERSION blocks in the .mod file — one install script
  covers all supported versions, no user choice needed.
- Scene node purge (deletes mRBFNode + mRBFShape) precedes
  unloadPlugin during uninstall; Maya refuses unload otherwise.

Stacked on top of Slice 13 — must merge after PR #11.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
891458249 pushed a commit that referenced this pull request Apr 22, 2026
Companion to Slice 13 — lets end users install/uninstall the
rbfmax plugin by dragging a Python file into the Maya viewport.

Files added
-----------
- installer/drag_drop_install.py — onMayaDroppedPythonFile hook +
  install / uninstall via Maya Module System (.mod descriptor
  written into the user's ~/Documents/maya/modules).  Single-
  version install covers Maya 2022 and 2025 via per-version
  MAYAVERSION blocks in the .mod file.
- installer/package.py — copies built .mll from
  build-maya-<ver>/bin/Release/ into installer/plug-ins/<ver>/.
  Strict fail-fast build-missing preflight: exits 1 with a full
  cmake recipe if either version's .mll is absent.
- installer/README.md — Chinese user guide covering both the end-
  user and developer flows, plus a troubleshooting section.
- docs/install.md — repository-level user entry pointing at the
  installer (so GitHub viewers see the Maya install path without
  having to download the zip first).

Files updated
-------------
- README.md — adds a blockquote under Quick Start pointing
  Maya end users at docs/install.md.
- .gitignore — excludes installer/plug-ins/2022/*.mll and
  installer/plug-ins/2025/*.mll (large build artifacts; users
  populate them via package.py).

Validation
----------
mayapy install + uninstall smoke cycle on Maya 2022 and 2025:
- package.py copies .mll        : Maya 2022 505.5 KiB / 2025 506.0 KiB
- install copies payload + .mod : both versions OK
- uninstall deletes .mod + dir  : both versions OK
- post-uninstall residue check  : zero bytes on both versions
- build-missing guard           : exit 1 with full cmake recipe

Design rationale
----------------
- Maya Module System (rather than dropping .mll into the user's
  plug-ins dir) gives a clean uninstall surface — one .mod file +
  one directory, both in a user-owned, non-system path.
- onMayaDroppedPythonFile is Autodesk's standard drag-drop hook;
  stable across Maya 2022 / 2024 / 2025 / 2026.
- Per-version plug-in subdirectories are selected at Maya runtime
  via MAYAVERSION blocks in the .mod file — one install script
  covers all supported versions, no user choice needed.
- Scene node purge (deletes mRBFNode + mRBFShape) precedes
  unloadPlugin during uninstall; Maya refuses unload otherwise.

Stacked on top of Slice 13 — must merge after PR #11.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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