Slice 13 — mRBFShape + mRBFDrawOverride (Path B) - #11
Merged
Conversation
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
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>
8 tasks
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>
8 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 2B opening slice: Viewport 2.0 visualization for trained
mRBFNodecenters.Architecture (Path B) —
mRBFNodestays a purekDependNode; visualization is carried by a new auxiliary locatormRBFShapeconnected viamessageattribute.mRBFDrawOverrideis registered against the shape's classification.Path A → B retrospective
Original spec attempted reparenting
mRBFNodeitself toMPxLocatorNodewith 6-argregisterNode(…, kLocatorNode, &classification). Maya returnedkFailure: "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 (viastatic_assert), and stale build artifacts. An out-of-repo reducer (MinLocator3.cpp) empirically pinned the failure toMPxLocatorNode + 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 inDEVLOG.mdSlice 13 entry; proposed Rule 5 candidate for Phase 2B memory: prefer canonical Autodesk sample patterns over ambitious deviations.Deliverables
RBFInterpolator::centers() constgetter + 1 D-group testIDrawSink/DrawCall/emit_centers_draw_callsadapter + 8 E-group tests (Maya-free)mRBFNode::is_loaded()+centers_for_viewport()(read-only, no DG touch)mRBFShape : MPxLocatorNode, typeId0x00013A01— attrs:sourceNode(message) +drawEnabled(bool) +sphereRadius(double)mRBFDrawOverride : MPxDrawOverride— 2-hop resolve shape → mRBFNode,MUIDrawManager::sphereper centerplugin_main.cpp— registers all of the above with atomic rollbacktests/smoke/smoke_viewport.py— 7-step mayapy contractDEVLOG.md+maya_node/README.md— Path A→B retrospective + Viewport 2.0 usageValidation status
build-adapter, ctest)C:/SDK/Maya2022/devkitBase)C:/SDK/Maya2025/devkitBase)build, ctest)Invariant changes
.mllbit-identity is NO LONGER expected — Slice 13 addsOpenMayaRender+OpenMayaUIlink libs which diverge across 2022/2025 devkits. The Phase 2A "bit-identical across versions" lemma applied only when the ABI surface wasOpenMayaonly; explicitly retracted inDEVLOG.md.mRBFNodefully preserved — no change to its compute, attributes, type (stillkDependNode), orinitialize().Tech-debt added
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.connectAttr mRBFNode.message mRBFShape.sourceNodemanually. Consider a conveniencerbfmaxAttachShapecommand in a later slice.centers_for_viewport()returns positions only; Slice 14 (heatmap) will need per-center colors.(-10, +10)bbox on draw override; tighten in Slice 14+.Test plan