Skip to content

feat: Refactored the vneinteraction to use quaternion based implementation. Remove Follow manipulator and Eular Orbit Behavior. Also, minor refactoring to organize the code.#37

Open
ajeetsinghyadav wants to merge 25 commits intomainfrom
VNE_VNEINTERACTION_QUAT_IMPL
Open

feat: Refactored the vneinteraction to use quaternion based implementation. Remove Follow manipulator and Eular Orbit Behavior. Also, minor refactoring to organize the code.#37
ajeetsinghyadav wants to merge 25 commits intomainfrom
VNE_VNEINTERACTION_QUAT_IMPL

Conversation

@ajeetsinghyadav
Copy link
Copy Markdown
Member

@ajeetsinghyadav ajeetsinghyadav commented Apr 7, 2026

  • Changed the creation date from April 2026 to March 2026 in multiple header and implementation files.
  • Enabled autodoc for several files to ensure they are included in the documentation generation process.
  • Updated author information consistently across all modified files.

Description

Release notes: Use a Conventional Commits–style PR title (e.g. feat: add X, fix: resolve Y, docs: update Z) so release-please can include this change in the changelog. If you squash-merge, use the PR title as the commit message.

Checklist

  • Project builds (e.g. cmake -B build/shared and cmake --build build/shared, or platform script with -l shared/-l static).
  • Tests pass (e.g. ctest --test-dir build/shared or script -a test).
  • Code is formatted (e.g. run clang-format as configured for this repo); CI clang-format will check.
  • Docs updated if you changed behavior or public API.

Additional notes

Summary by CodeRabbit

  • New Features

    • New navigation factory: NavTrackball rig and navigation trackball rotation options; controllers expose rotation-mode and orbit-animation controls.
  • Changed

    • Interaction types consolidated into a single public header; camera state and manipulators unified around quaternion-based virtual trackball; examples/docs updated to favor trackball flows.
  • Removed

    • Follow controller/manipulator and legacy per-file type headers (action/state/binding) removed.
  • Tests

    • Tests updated to reflect trackball defaults and removed follow/orbit-specific suites.

- Renamed and refactored OrbitCameraState to TrackballCameraState for improved clarity in camera manipulation.
- Updated logging functions to reflect changes in state structure, including quaternion orientation instead of Euler angles.
- Adjusted example code to demonstrate the new TrackballCameraState functionality, ensuring consistent behavior in camera state save/restore operations.
- Modified FreeLookManipulator and OrbitalCameraManipulator to utilize quaternion-based orientation, enhancing stability and performance during interactions.
- Updated tests to validate the new camera state management and ensure compatibility with existing functionalities.
- Adjusted logging output in the camera state save/restore example for better readability by formatting quaternion output.
- Cleaned up code formatting in `camera_state.h`, `interaction_types.h`, and `free_look_manipulator.cpp` for consistency and clarity.
- Enhanced comments in various files to improve understanding of camera manipulation functionalities and interactions.
… related files

- Deleted `euler_orbit_strategy.h`, `orbit_behavior.cpp`, and `orbit_behavior.h` as they are no longer needed.
- Updated `trackball_strategy.h` to remove dependencies on the deleted orbit behavior.
- Refactored `OrbitalCameraManipulator` to exclusively use `TrackballStrategy` for rotation handling.
- Removed rotation mode management from `Inspect3DController` and related logic.
- Cleaned up `FollowController` and `FollowManipulator` implementations by removing unused code and ensuring proper camera manipulation.
- Adjusted comments and documentation to reflect the removal of Euler-based rotation handling.
- Commented out tests in `orbital_camera_manipulator_test.cpp` and `manipulator_regression_test.cpp` to temporarily disable them.
- Deleted the `orbit_behavior_test.cpp` file as it is no longer needed following recent refactoring of camera manipulation strategies.
- Revised `README.md` files for examples to reflect changes in controller functionalities and clarify usage.
- Updated `01_library_info` to include new controller defaults and trackball projection modes.
- Enhanced `02_medical_3d_inspect` to focus on trackball projection variants and inertia settings.
- Simplified `05_robotic_simulator` by removing the FollowController, emphasizing Inspect3D and Navigation3D functionalities with moving pivots.
- Adjusted example code to improve clarity and consistency in logging and comments across various files.
- Removed references to deprecated FollowController in tests and examples, streamlining the codebase.
…applications

- Streamlined logging statements in `01_example.cpp`, `05_example.cpp`, and `07_example.cpp` for better readability and uniformity.
- Consolidated multi-line expressions into single lines where appropriate to enhance clarity.
- Updated comments and formatting in `orbital_camera_manipulator_test.cpp` to maintain consistency across test cases.
…nipulator

- Added `makeNavTrackball` method to `CameraRig` for creating a navigation rig with virtual trackball mouse look.
- Enhanced `FreeLookManipulator` to support trackball rotation mode, allowing for more intuitive camera control.
- Updated `Navigation3DController` to manage rotation modes, including the new trackball option.
- Added tests to validate the functionality of the navigation trackball mode in both `CameraRig` and `Navigation3DController`.
- Merged type headers `camera_action.h`, `camera_state.h`, and `input_binding.h` into a single `interaction_types.h` for better organization and accessibility.
- Removed obsolete headers to streamline the codebase and reduce dependencies.
- Updated documentation and examples to reflect the new structure and ensure compatibility with the consolidated types.
- Introduced `interaction_utils.h` and `interaction_utils.cpp` to replace the functionality of the removed `camera_math.h` and `view_math.h`, providing internal helpers for camera manipulators.
…e autodoc

- Changed the creation date from April 2026 to March 2026 in multiple header and implementation files.
- Enabled autodoc for several files to ensure they are included in the documentation generation process.
- Updated author information consistently across all modified files.
Copilot AI review requested due to automatic review settings April 7, 2026 21:42
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 7, 2026

📝 Walkthrough

Walkthrough

Consolidates interaction type headers into interaction_types.h; removes FollowController/FollowManipulator and Euler-based orbit strategy; migrates manipulators (Orbital/FreeLook) from Euler to quaternion/trackball models; replaces internal math (camera_math/view_math) with interaction_utils; updates build, examples, and tests to match these API and implementation changes.

Changes

Cohort / File(s) Summary
Type Consolidation
include/vertexnova/interaction/interaction_types.h, CHANGELOG.md, docs/vertexnova/interaction/interaction.md
Merged camera_action.h, camera_state.h, input_binding.h into a single public interaction_types.h defining GestureAction/CameraActionType, CameraCommandPayload, state structs, input binding types, and new enums (FreeLookRotationMode, etc.). Documentation updated.
Removed Public Headers
include/vertexnova/interaction/camera_action.h, .../camera_state.h, .../input_binding.h, .../follow_controller.h, .../follow_manipulator.h
Deleted three shim/type headers and two follow-related public headers (controller + manipulator). Consumers must include interaction_types.h and remove Follow usage.
Orbital Manipulator & Rotation Strategy
include/vertexnova/interaction/orbital_camera_manipulator.h, src/vertexnova/interaction/orbital_camera_manipulator.cpp, src/vertexnova/interaction/detail/*
Removed strategy/behavior abstraction and Euler orbit implementations (IRotationStrategy, EulerOrbitStrategy, OrbitBehavior, trackball_strategy.h); converted OrbitalCameraManipulator to a quaternion virtual-trackball model; added orientation/get/set, fit/view animation controls, and orbit-animation toggles.
FreeLook Manipulator Migration
include/vertexnova/interaction/free_look_manipulator.h, src/vertexnova/interaction/free_look_manipulator.cpp
Switched FreeLook internals to quaternion orientation, added orientation accessors and rotation-mode/trackball-projection APIs, and changed ctor/dtor/move declarations to explicit definitions.
Internal Math -> interaction_utils
src/vertexnova/interaction/interaction_utils.h, .../interaction_utils.cpp, removed src/.../camera_math.h
Removed camera_math.h and introduced interaction_utils with reference-frame, mouse/NDC, unproject, and scaleTrackballQuaternion helpers; updated callers to include interaction_utils.h.
CameraRig / Factory Changes
include/vertexnova/interaction/camera_rig.h, src/.../camera_rig.cpp
Removed makeOrbit()/makeFollow() factories; added makeNavTrackball() (FreeLook configured for trackball); makeTrackball() retained but no longer explicitly set Euler/trackball mode.
Controller API Adjustments
include/vertexnova/interaction/inspect_3d_controller.h, navigation_3d_controller.h, src/.../*.cpp
Removed Inspect3D rotation-mode API; added orbit-animation enable API; added Navigation3DController rotation-mode get/set forwarding to underlying FreeLookManipulator.
Build & Install Changes
src/CMakeLists.txt, tests/CMakeLists.txt, deps submodule pointer
Build now compiles interaction_utils.cpp and drops follow/orbit-behavior sources; public header install list updated to remove deleted headers; deps submodule pointer updated.
Examples & READMEs
examples/** (01,02,05,06,07,08 and READMEs)
Examples updated to remove FollowController and Euler-orbit flows, use trackball factories/modes, switch to quaternion state save/restore, and reflect new projection/animation APIs.
Tests
tests/*
Removed follow/orbit-behavior tests; updated many tests to use makeTrackball/makeNavTrackball, quaternion-based state and new enums; added Navigation3DController rotation-mode tests and new orbital animation tests; several test headers updated for metadata.
Docs & Diagrams
docs/vertexnova/interaction/interaction.md, docs/.../component.drawio, header docs across include/vertexnova/interaction/*
Documentation and component diagram updated to reflect consolidated types, replaced camera_math/view_math with interaction_utils, and removed Follow and Euler-oriented wording in APIs and examples.
Misc Internal Metadata
multiple src/... headers and cpp files
Updated file header metadata (Created/Autodoc) and small implementation tweaks (e.g., InputMapper pinch zoom sign change, minor refactors).

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Poem

🐰 I hopped through headers, neat and spry,

Gathered types beneath one sky.
Euler tucked to rest at last,
Quaternions spin our cameras fast.
No more following — inspect and fly! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.82% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: refactoring to quaternion-based implementation, removing Follow manipulator and Euler Orbit Behavior, plus code organization.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch VNE_VNEINTERACTION_QUAT_IMPL

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

  • Refactors 3D orbit interaction to a quaternion/virtual-trackball-only implementation and removes legacy Euler-orbit and Follow manipulator/controller paths.
  • Consolidates public interaction type headers into interaction_types.h and merges internal math helpers into interaction_utils.*.
  • Updates tests/examples/docs to reflect the new APIs and removed features.

Changes:

  • Orbital rotation: remove Euler/strategy abstraction; implement internal quaternion trackball rotation state in OrbitalCameraManipulator.
  • API cleanup: remove camera_action.h, camera_state.h, input_binding.h, Follow*, and add quaternion-based state/accessors + Navigation3DController::set/getRotationMode.
  • Documentation/tests/examples updated to the new type surface and trackball-only behavior.

Reviewed changes

Copilot reviewed 78 out of 78 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tests/trackball_behavior_test.cpp Adds file header/autodoc + brief test doc comment.
tests/ortho_2d_manipulator_test.cpp Adds file header/autodoc + brief test doc comment.
tests/ortho_2d_controller_test.cpp Adds file header/autodoc + brief test doc comment.
tests/orbital_camera_manipulator_test.cpp Updates rotate gesture payload ordering; leaves some rotation-mode tests commented out.
tests/orbit_behavior_test.cpp Removes Euler OrbitBehavior unit tests (feature removed).
tests/navigation_3d_controller_test.cpp Adds tests for new Navigation3DController rotation-mode API.
tests/manipulator_regression_test.cpp Switches to interaction_utils.h; removes legacy Euler-orbit regression; adds a now-empty trackball regression test (commented out).
tests/inspect_3d_controller_test.cpp Updates defaults test to trackball projection; removes rotation-mode assertions.
tests/input_mapper_test.cpp Adds file header/autodoc + brief test doc comment.
tests/free_look_manipulator_test.cpp Renames tests/docs to orientation/quaternion terminology.
tests/follow_manipulator_test.cpp Removes FollowManipulator tests (feature removed).
tests/follow_controller_test.cpp Removes FollowController tests (feature removed).
tests/controller_move_safety_test.cpp Removes FollowController move-safety coverage (feature removed).
tests/CMakeLists.txt Removes deleted test sources from build.
tests/camera_rig_test.cpp Updates rig factory usage to trackball + adds makeNavTrackball test.
src/vertexnova/interaction/version.cpp Enables autodoc in file header.
src/vertexnova/interaction/ortho_2d_manipulator.cpp Enables autodoc in file header.
src/vertexnova/interaction/ortho_2d_controller.cpp Enables autodoc in file header.
src/vertexnova/interaction/orbital_camera_manipulator.cpp Replaces strategy-based orbit rotation with internal quaternion trackball rotation state + inertia.
src/vertexnova/interaction/navigation_3d_controller.cpp Adds set/getRotationMode delegating to FreeLookManipulator.
src/vertexnova/interaction/interaction_utils.h Merges former internal math headers into one internal helper header.
src/vertexnova/interaction/interaction_utils.cpp Implements API-dependent helpers; replaces view_math include.
src/vertexnova/interaction/inspect_3d_controller.cpp Removes rotation-mode state and API usage; defaults to trackball-only orbit manipulator.
src/vertexnova/interaction/input_mapper.cpp Enables autodoc in file header.
src/vertexnova/interaction/input_event_translator.h Enables autodoc in file header (internal header).
src/vertexnova/interaction/input_event_translator.cpp Enables autodoc in file header.
src/vertexnova/interaction/free_look_manipulator.cpp Moves FreeLook to quaternion orientation + adds optional trackball mouse-look mode.
src/vertexnova/interaction/follow_manipulator.cpp Removes FollowManipulator implementation (feature removed).
src/vertexnova/interaction/follow_controller.cpp Removes FollowController implementation (feature removed).
src/vertexnova/interaction/detail/trackball_strategy.h Removes strategy abstraction header (no longer used).
src/vertexnova/interaction/detail/trackball_behavior.h Updates documentation to reflect its narrowed role.
src/vertexnova/interaction/detail/trackball_behavior.cpp Switches to interaction_utils.h; minor cleanups.
src/vertexnova/interaction/detail/rotation_strategy.h Removes strategy interface header (no longer used).
src/vertexnova/interaction/detail/orbit_behavior.h Removes Euler OrbitBehavior header (feature removed).
src/vertexnova/interaction/detail/orbit_behavior.cpp Removes Euler OrbitBehavior implementation (feature removed).
src/vertexnova/interaction/detail/euler_orbit_strategy.h Removes Euler strategy header (no longer used).
src/vertexnova/interaction/camera_rig.cpp Removes makeOrbit/makeFollow; simplifies makeTrackball; adds makeNavTrackball.
src/vertexnova/interaction/camera_math.h Removes legacy internal math header (replaced by interaction_utils).
src/vertexnova/interaction/camera_manipulator_base.cpp Switches internal include from view_math to interaction_utils.
src/vertexnova/interaction/camera_controller_impl.h Enables autodoc in file header (internal header).
src/CMakeLists.txt Removes deleted sources; adds interaction_utils.cpp; removes deleted public headers.
include/vertexnova/interaction/ortho_2d_manipulator.h Minor header metadata formatting.
include/vertexnova/interaction/ortho_2d_controller.h Minor header metadata formatting.
include/vertexnova/interaction/orbital_camera_manipulator.h Removes rotation-mode API; adds quaternion orientation API; updates docs.
include/vertexnova/interaction/navigation_3d_controller.h Exposes set/getRotationMode in public API.
include/vertexnova/interaction/interaction.h Umbrella header no longer includes Follow*; updates type-surface description.
include/vertexnova/interaction/interaction_types.h Consolidates actions/state/bindings/enums into one header; adds FreeLookRotationMode.
include/vertexnova/interaction/inspect_3d_controller.h Removes rotation-mode API from public controller contract; updates docs to trackball default.
include/vertexnova/interaction/input_binding.h Removes shim header (consolidated into interaction_types.h).
include/vertexnova/interaction/free_look_manipulator.h Moves to quaternion orientation API + adds trackball mouse-look mode.
include/vertexnova/interaction/follow_manipulator.h Removes FollowManipulator public header (feature removed).
include/vertexnova/interaction/follow_controller.h Removes FollowController public header (feature removed).
include/vertexnova/interaction/camera_state.h Removes shim header (consolidated into interaction_types.h).
include/vertexnova/interaction/camera_rig.h Removes makeOrbit/makeFollow; adds makeNavTrackball; updates docs.
include/vertexnova/interaction/camera_manipulator.h Updates docs to remove FollowManipulator mention and Euler-orbit wording.
include/vertexnova/interaction/camera_manipulator_base.h Minor header metadata formatting.
include/vertexnova/interaction/camera_controller.h Updates docs to remove FollowController mention.
include/vertexnova/interaction/camera_action.h Removes shim header (consolidated into interaction_types.h).
examples/README.md Updates example descriptions to remove Follow and Euler-orbit references.
examples/08_camera_state_save_restore/README.md Updates to reference interaction_types.h instead of removed camera_state.h.
examples/08_camera_state_save_restore/main.cpp Updates top-level commentary to match consolidated types.
examples/08_camera_state_save_restore/08_example.cpp Switches orbit bookmark sample to TrackballCameraState + quaternion restore.
examples/07_camera_rig_composition/README.md Updates factory-method list and hybrid-rig explanation for trackball-only.
examples/07_camera_rig_composition/main.cpp Updates example header comments for removed factories.
examples/07_camera_rig_composition/07_example.cpp Removes orbit/follow factory usage; uses trackball + eRim projection rebuild example.
examples/06_custom_input_bindings/06_example.cpp Switches to interaction_types.h; uses makeTrackball where needed.
examples/05_robotic_simulator/README.md Removes Follow section; replaces with moving pivot + orbit damping.
examples/05_robotic_simulator/main.cpp Updates example overview to remove FollowController.
examples/05_robotic_simulator/05_example.h Updates example description to remove FollowController.
examples/05_robotic_simulator/05_example.cpp Removes FollowController usage; demonstrates moving pivot and damping.
examples/02_medical_3d_inspect/README.md Updates to trackball-only orbit + projection modes section.
examples/02_medical_3d_inspect/02_example.cpp Removes rotation-mode switching; focuses on projection modes.
examples/01_library_info/README.md Updates to remove FollowController and OrbitalRotationMode references.
examples/01_library_info/01_example.cpp Removes FollowController logging; updates Inspect defaults output and rig factory list.
docs/vertexnova/interaction/interaction.md Updates swimlane/type-surface text to reflect consolidated types and interaction_utils.
docs/vertexnova/interaction/diagrams/component.drawio Updates public-type and math-helper labels (diagram content partially still references removed components).
CHANGELOG.md Adds notes about consolidated headers/internal math merge, but unreleased notes still include now-inaccurate statements about OrbitBehavior/Follow*.
Comments suppressed due to low confidence (1)

include/vertexnova/interaction/orbital_camera_manipulator.h:147

  • This comment still references an "internal Euler path" for top/bottom pitch limits, but Euler orbit support was removed. Consider rewording to describe the actual trackball/quaternion implementation and the chosen polar-limit constants (±89°) without referring to Euler.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
include/vertexnova/interaction/camera_rig.h (1)

128-138: ⚠️ Potential issue | 🟠 Major

Keep a deprecated makeOrbit() bridge before hard-removing the old preset API.

Lines 128-138 replace a public factory entry point with makeTrackball(). That means existing integrations stop compiling even though the preset still maps to the same conceptual “orbit/trackball” use case. Please keep makeOrbit() as a deprecated forwarding alias for at least one release, and document the makeFollow() removal in migration notes instead of making it a silent source break.

Possible compatibility shim
     /** Trackball rig: OrbitalCameraManipulator with quaternion virtual-trackball rotation. */
     static CameraRig makeTrackball();
+    [[deprecated("Use makeTrackball()")]]
+    static CameraRig makeOrbit() { return makeTrackball(); }

As per coding guidelines, "Avoid surprising API changes; keep interfaces stable in C++ code."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@include/vertexnova/interaction/camera_rig.h` around lines 128 - 138, Existing
API removed silently: add a deprecated bridge named makeOrbit() that forwards to
the new makeTrackball() to preserve source compatibility; specifically, declare
static CameraRig makeOrbit() in the same header alongside makeTrackball() and
mark it as deprecated (e.g., [[deprecated]] or PROJECT_DEPRECATED macro) with a
short comment pointing users to use makeTrackball() and to the migration notes
explaining makeFollow() removal, so existing callers continue to compile for at
least one release.
examples/08_camera_state_save_restore/main.cpp (1)

5-18: ⚠️ Potential issue | 🟡 Minor

Update the example banner to match the new quaternion state API.

The comment block still advertises OrbitCameraState, a yaw/pitch-based FreeCameraState, and a non-exported OrbitalInteractionState, but this PR replaces those with quaternion-based state types in interaction_types.h. Anyone using this example as migration guidance will get the wrong public surface.

Doc fix
- *   - OrbitCameraState  — save/restore Euler orbit (COI, distance, yaw, pitch, up)
- *   - TrackballCameraState — save/restore quaternion trackball (COI, distance, quat, up)
- *   - FreeCameraState   — save/restore free-look (position, yaw, pitch, up_hint)
+ *   - TrackballCameraState — save/restore quaternion orbit/trackball state
+ *                            (COI, distance, orientation, world_up)
+ *   - FreeCameraState   — save/restore free-look state (position, orientation)
  *   - Typical "undo/bookmark" pattern: save before interaction, restore to jump back
  *   - Reading live state from OrbitalCameraManipulator fields
  *   - Reading live state from FreeLookManipulator fields
  *   - FreeLookInputState — querying held-key snapshot (diagnostic use)
- *   - OrbitalInteractionState — not exported as snapshot; shown via manipulator API
+ *   - OrbitalInteractionState — drag/pan interaction snapshot
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/08_camera_state_save_restore/main.cpp` around lines 5 - 18, Update
the example banner to reflect the new quaternion-based camera state API: remove
references to the old OrbitCameraState, yaw/pitch FreeCameraState and the
non-exported OrbitalInteractionState, and instead describe the actual exported
quaternion state types present in interaction_types.h (e.g., the
quaternion-based trackball state and the quaternion-based free-look state) —
update the bullet list under "Demonstrates:" to mention TrackballCameraState
(quaternion orbit/trackball), the new quaternion-based free-look state type, and
that interaction snapshot/serialize is the application's responsibility so
readers consult interaction_types.h for exact struct names and fields.
include/vertexnova/interaction/camera_controller.h (1)

67-68: ⚠️ Potential issue | 🟡 Minor

Remove stale follow reference from onUpdate docs.

ICameraController::onUpdate still documents “follow smoothing” even though follow controller/manipulator were removed in this PR. Please update this line to avoid implying unsupported behavior.

As per coding guidelines, public C++ headers should keep behavior documentation clear and unsurprising.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@include/vertexnova/interaction/camera_controller.h` around lines 67 - 68, The
onUpdate documentation for ICameraController still mentions "follow smoothing"
which is no longer supported; update the comment for ICameraController::onUpdate
to remove the stale "follow smoothing" phrase and instead document only the
active behaviors (e.g., inertia and damping) and the delta_time parameter so the
header's public docs reflect current behavior.
include/vertexnova/interaction/camera_manipulator.h (1)

15-16: ⚠️ Potential issue | 🟡 Minor

Top-level manipulator brief still mentions removed follow mode.

The @brief still says “orbit, fly, ortho, follow, …” while follow support was removed. Please sync this brief with the new supported set.

As per coding guidelines, public APIs should avoid surprising or misleading interface documentation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@include/vertexnova/interaction/camera_manipulator.h` around lines 15 - 16,
Update the `@brief` for ICameraManipulator to remove the outdated "follow" mode
and reflect only the currently supported modes (e.g., "orbit, fly, ortho, …");
locate the ICameraManipulator declaration in camera_manipulator.h and edit the
brief line so it no longer mentions "follow" and matches the actual supported
manipulators.
docs/vertexnova/interaction/diagrams/component.drawio (1)

17-50: ⚠️ Potential issue | 🟡 Minor

Diagram update is incomplete after follow/euler removal.

You updated interaction_types.h and interaction_utils, but the same diagram still lists removed components (FollowController, FollowManipulator, EulerOrbitStrategy, OrbitBehavior). Please update these labels so the diagram matches the current architecture.

Suggested drawio value updates
- <mxCell id="controllers_pub" value="Inspect3DController&#10;Navigation3DController&#10;Ortho2DController&#10;FollowController" ...>
+ <mxCell id="controllers_pub" value="Inspect3DController&#10;Navigation3DController&#10;Ortho2DController" ...>

- <mxCell id="manipulators_pub" value="OrbitalCameraManipulator&#10;FreeLookManipulator&#10;Ortho2DManipulator&#10;FollowManipulator&#10;CameraManipulatorBase" ...>
+ <mxCell id="manipulators_pub" value="OrbitalCameraManipulator&#10;FreeLookManipulator&#10;Ortho2DManipulator&#10;CameraManipulatorBase" ...>

- <mxCell id="strategies" value="detail/IRotationStrategy&#10;detail/EulerOrbitStrategy&#10;detail/TrackballStrategy" ...>
+ <mxCell id="strategies" value="detail/IRotationStrategy&#10;detail/TrackballStrategy" ...>

- <mxCell id="behaviors" value="detail/OrbitBehavior&#10;detail/TrackballBehavior" ...>
+ <mxCell id="behaviors" value="detail/TrackballBehavior" ...>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/vertexnova/interaction/diagrams/component.drawio` around lines 17 - 50,
The diagram still lists removed components (FollowController, FollowManipulator,
EulerOrbitStrategy, OrbitBehavior) and must be updated to reflect the current
code; open the component.drawio shapes with ids/controllers named
controllers_pub, manipulators_pub, strategies and behaviors and remove the
obsolete labels (FollowController, FollowManipulator, EulerOrbitStrategy,
OrbitBehavior) and replace them with the current components present in
interaction_types.h and interaction_utils (or simply remove those entries if no
direct replacement exists), ensuring the values for interaction_types_h,
controllers_pub, manipulators_pub, strategies, and behaviors match the updated
source (interaction_types.h and interaction_utils).
docs/vertexnova/interaction/interaction.md (1)

140-140: ⚠️ Potential issue | 🟡 Minor

Remove references to deleted factory methods makeOrbit() and makeFollow().

The documentation lists makeOrbit() and makeFollow() as factory methods, but these no longer exist in the codebase. Only makeTrackball(), makeFps(), makeFly(), and makeOrtho2D() are implemented.

📝 Suggested fix
-- **Factories** — `makeOrbit()`, `makeTrackball()`, `makeFps()`, `makeFly()`, `makeOrtho2D()`, `makeFollow()` build rigs with a single default manipulator; you can still `addManipulator` for custom stacks.
+- **Factories** — `makeTrackball()`, `makeFps()`, `makeFly()`, `makeOrtho2D()` build rigs with a single default manipulator; you can still `addManipulator` for custom stacks.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/vertexnova/interaction/interaction.md` at line 140, Update the docs line
that lists factory functions by removing the deleted makeOrbit() and
makeFollow() entries and only listing the implemented factories:
makeTrackball(), makeFps(), makeFly(), and makeOrtho2D(); keep the note that you
can still call addManipulator for custom stacks and ensure the sentence
references the same symbols (makeTrackball, makeFps, makeFly, makeOrtho2D,
addManipulator) so readers can find the valid API.
🧹 Nitpick comments (3)
examples/06_custom_input_bindings/06_example.cpp (1)

5-7: Remove the duplicate interaction_types.h include.

The same header is included on Lines 5 and 7. #pragma once makes it harmless, but it adds noise to an example that is supposed to show the minimal wiring.

Cleanup
 `#include` "vertexnova/interaction/camera_rig.h"
 `#include` "vertexnova/interaction/inspect_3d_controller.h"
-#include "vertexnova/interaction/interaction_types.h"
 `#include` "vertexnova/interaction/input_mapper.h"
 `#include` "vertexnova/interaction/interaction_types.h"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/06_custom_input_bindings/06_example.cpp` around lines 5 - 7, Remove
the duplicate include of "interaction_types.h" in the example: keep a single
`#include` "vertexnova/interaction/interaction_types.h" alongside the `#include`
"vertexnova/interaction/input_mapper.h" in 06_example.cpp so the example is
minimal and non-redundant.
tests/free_look_manipulator_test.cpp (1)

225-256: Consider documenting the negative delta-time test intent.

The test at lines 245-246 passes both zero and negative delta time to onUpdate(). While this appears intentional for robustness testing, a brief inline comment explaining the expected behavior for negative dt would improve maintainability.

📝 Suggested documentation addition
     b.markAnglesDirty();

+    // Zero and negative dt should still trigger orientation sync (no movement, but state is updated).
     b.onUpdate(0.0);
     b.onUpdate(-1.0);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/free_look_manipulator_test.cpp` around lines 225 - 256, Add a brief
inline comment in the test
FreeLookManipulator.OnUpdateZeroDeltaTimeStillSyncsOrientationFromCamera
explaining why onUpdate is called with zero and negative dt (e.g., to assert
that ensureAnglesSynced()/syncOrientationFromCamera still run and that negative
dt should be treated as no-op for movement or used for robustness), placing the
comment immediately above the b.onUpdate(0.0); b.onUpdate(-1.0); calls near the
markAnglesDirty() usage so future readers understand the intent of the negative
dt check.
src/vertexnova/interaction/free_look_manipulator.cpp (1)

43-63: Consider extracting shared quaternion scaling utility.

This scaleTrackballDeltaQuaternion implementation is identical to OrbitalTrackballRotation::scaleQuaternion in orbital_camera_manipulator.cpp. Consider moving this to interaction_utils.h to eliminate duplication.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/vertexnova/interaction/free_look_manipulator.cpp` around lines 43 - 63,
The function scaleTrackballDeltaQuaternion duplicates
OrbitalTrackballRotation::scaleQuaternion; extract the shared logic into a
single utility (e.g., a free function named scaleQuaternion or
scaleTrackballQuaternion) declared in interaction_utils.h and defined in the
corresponding .cpp, then replace both scaleTrackballDeltaQuaternion and
OrbitalTrackballRotation::scaleQuaternion bodies to call that utility; ensure
the new utility preserves the existing behavior and noexcept/[[nodiscard]]
semantics and uses the same vne::math::Quatf API so callers need only include
interaction_utils.h.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@examples/07_camera_rig_composition/README.md`:
- Around line 17-24: The fenced diagram block in README.md lacks a language tag
causing markdownlint MD040; update the triple-backtick fence around the
CameraRig diagram to include a language identifier (for example use "text") so
the block becomes ```text and keeps the same content; this targets the fenced
code block that contains "CameraRig", "OrbitalCameraManipulator", and
"FreeLookManipulator" so the linter stops warning.

In `@include/vertexnova/interaction/free_look_manipulator.h`:
- Around line 138-140: getOrientation() currently returns the cached
orientation_ inline and can bypass the dirty-sync path controlled by
orientation_dirty_ (toggled by markAnglesDirty()), so change getOrientation() to
be an out-of-line method that first performs the same "if (orientation_dirty_)
syncAnglesToOrientation()" (or whatever existing resync routine is used
elsewhere) before returning orientation_; update the header to declare
getOrientation() non-inline and implement it to clear orientation_dirty_ after
resync to ensure external camera changes are reflected immediately.

In `@include/vertexnova/interaction/interaction_types.h`:
- Around line 224-228: The documentation for TouchPinch::scale is inverted;
update the comment on the TouchPinch struct (symbol: TouchPinch, member: scale)
so it reads that values >1 zoom out and values <1 zoom in (matching the rest of
the API and Example 06), and scan/update any adjacent
documentation/comments/examples (e.g., Example 06) that referenced the old
direction to keep them consistent.
- Around line 15-30: Create thin forwarding headers for the old public headers
(e.g., camera_action.h, camera_state.h, input_binding.h) that simply include
interaction_types.h and re-export the old type names as deprecated aliases; for
each removed symbol referenced downstream (e.g., OrbitCameraState,
CameraActionType, CameraCommandPayload, GestureAction, TrackballCameraState,
FreeCameraState, FreeLookInputState, OrbitalInteractionState, InputRule,
MouseBinding, KeyBinding) add a using typedef or inline alias in those
forwarding headers annotated with the compiler deprecation attribute or a
static_assert-friendly deprecation macro, so existing code continues to compile
while emitting deprecation warnings and guiding users to include
interaction_types.h instead.

In `@src/vertexnova/interaction/orbital_camera_manipulator.cpp`:
- Around line 628-631: The aspect ratio calculation can divide by zero if
viewport().height is zero; update the block in orbital_camera_manipulator.cpp
(around perspCamera(), fov_y_rad, fov_x_rad) to guard against zero height by
using a safe denominator or fallback: compute a safeHeight =
std::max(viewport().height, tiny_positive_value) or branch if viewport().height
== 0 and then compute aspect = std::max(viewport().width / safeHeight,
CameraManipulatorBase::kMinOrthoExtent) (or simply set aspect =
CameraManipulatorBase::kMinOrthoExtent when height is zero) before computing
fov_x_rad so division by zero cannot occur.

In `@tests/manipulator_regression_test.cpp`:
- Around line 300-324: This test, OrbitalCamera_HorizontalDragMatchesEulerSign,
currently has its body commented out and provides no verification; either
re-enable and run the original checks against the quaternion-based
implementation or remove/disable the test to avoid false coverage. To fix:
restore the commented setup and actions for
vne::interaction::OrbitalCameraManipulator (construct b, call
b.setRotationMode(vne::interaction::OrbitalRotationMode::eTrackball),
b.setCamera(persp), b.onResize(...)), recreate the CameraCommandPayload moves
and call b.onAction(...) for eBeginRotate/eRotateDelta/eEndRotate, then compute
pos_before/pos_after from persp->getPosition() and re-add the
EXPECT_LT(pos_after.x(), pos_before.x()) assertion; alternatively, if the
behavior is intentionally pending, replace the test with
TEST(ManipulatorRegression,
DISABLED_OrbitalCamera_HorizontalDragMatchesEulerSign) to mark it as not
executed until the quaternion implementation is ready.

In `@tests/orbital_camera_manipulator_test.cpp`:
- Around line 29-41: Remove the commented-out legacy tests and add active unit
assertions that reflect the current API and defaults for
OrbitalCameraManipulator: instantiate vne::interaction::OrbitalCameraManipulator
and assert current default rotation mode via getRotationMode(), default pivot
via getPivotMode(), that getOrbitDistance() and getZoomSpeed() are > 0, and test
trackball inertia/rotation behavior through the current public methods (e.g.,
setRotationMode()/applyRotation()/simulateInertia() or their renamed
equivalents) to verify inertia is applied when trackball mode is active; update
or replace the two additional commented blocks at lines ~150-151 and ~181-182 in
the same way.

---

Outside diff comments:
In `@docs/vertexnova/interaction/diagrams/component.drawio`:
- Around line 17-50: The diagram still lists removed components
(FollowController, FollowManipulator, EulerOrbitStrategy, OrbitBehavior) and
must be updated to reflect the current code; open the component.drawio shapes
with ids/controllers named controllers_pub, manipulators_pub, strategies and
behaviors and remove the obsolete labels (FollowController, FollowManipulator,
EulerOrbitStrategy, OrbitBehavior) and replace them with the current components
present in interaction_types.h and interaction_utils (or simply remove those
entries if no direct replacement exists), ensuring the values for
interaction_types_h, controllers_pub, manipulators_pub, strategies, and
behaviors match the updated source (interaction_types.h and interaction_utils).

In `@docs/vertexnova/interaction/interaction.md`:
- Line 140: Update the docs line that lists factory functions by removing the
deleted makeOrbit() and makeFollow() entries and only listing the implemented
factories: makeTrackball(), makeFps(), makeFly(), and makeOrtho2D(); keep the
note that you can still call addManipulator for custom stacks and ensure the
sentence references the same symbols (makeTrackball, makeFps, makeFly,
makeOrtho2D, addManipulator) so readers can find the valid API.

In `@examples/08_camera_state_save_restore/main.cpp`:
- Around line 5-18: Update the example banner to reflect the new
quaternion-based camera state API: remove references to the old
OrbitCameraState, yaw/pitch FreeCameraState and the non-exported
OrbitalInteractionState, and instead describe the actual exported quaternion
state types present in interaction_types.h (e.g., the quaternion-based trackball
state and the quaternion-based free-look state) — update the bullet list under
"Demonstrates:" to mention TrackballCameraState (quaternion orbit/trackball),
the new quaternion-based free-look state type, and that interaction
snapshot/serialize is the application's responsibility so readers consult
interaction_types.h for exact struct names and fields.

In `@include/vertexnova/interaction/camera_controller.h`:
- Around line 67-68: The onUpdate documentation for ICameraController still
mentions "follow smoothing" which is no longer supported; update the comment for
ICameraController::onUpdate to remove the stale "follow smoothing" phrase and
instead document only the active behaviors (e.g., inertia and damping) and the
delta_time parameter so the header's public docs reflect current behavior.

In `@include/vertexnova/interaction/camera_manipulator.h`:
- Around line 15-16: Update the `@brief` for ICameraManipulator to remove the
outdated "follow" mode and reflect only the currently supported modes (e.g.,
"orbit, fly, ortho, …"); locate the ICameraManipulator declaration in
camera_manipulator.h and edit the brief line so it no longer mentions "follow"
and matches the actual supported manipulators.

In `@include/vertexnova/interaction/camera_rig.h`:
- Around line 128-138: Existing API removed silently: add a deprecated bridge
named makeOrbit() that forwards to the new makeTrackball() to preserve source
compatibility; specifically, declare static CameraRig makeOrbit() in the same
header alongside makeTrackball() and mark it as deprecated (e.g., [[deprecated]]
or PROJECT_DEPRECATED macro) with a short comment pointing users to use
makeTrackball() and to the migration notes explaining makeFollow() removal, so
existing callers continue to compile for at least one release.

---

Nitpick comments:
In `@examples/06_custom_input_bindings/06_example.cpp`:
- Around line 5-7: Remove the duplicate include of "interaction_types.h" in the
example: keep a single `#include` "vertexnova/interaction/interaction_types.h"
alongside the `#include` "vertexnova/interaction/input_mapper.h" in 06_example.cpp
so the example is minimal and non-redundant.

In `@src/vertexnova/interaction/free_look_manipulator.cpp`:
- Around line 43-63: The function scaleTrackballDeltaQuaternion duplicates
OrbitalTrackballRotation::scaleQuaternion; extract the shared logic into a
single utility (e.g., a free function named scaleQuaternion or
scaleTrackballQuaternion) declared in interaction_utils.h and defined in the
corresponding .cpp, then replace both scaleTrackballDeltaQuaternion and
OrbitalTrackballRotation::scaleQuaternion bodies to call that utility; ensure
the new utility preserves the existing behavior and noexcept/[[nodiscard]]
semantics and uses the same vne::math::Quatf API so callers need only include
interaction_utils.h.

In `@tests/free_look_manipulator_test.cpp`:
- Around line 225-256: Add a brief inline comment in the test
FreeLookManipulator.OnUpdateZeroDeltaTimeStillSyncsOrientationFromCamera
explaining why onUpdate is called with zero and negative dt (e.g., to assert
that ensureAnglesSynced()/syncOrientationFromCamera still run and that negative
dt should be treated as no-op for movement or used for robustness), placing the
comment immediately above the b.onUpdate(0.0); b.onUpdate(-1.0); calls near the
markAnglesDirty() usage so future readers understand the intent of the negative
dt check.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 40bacf8e-76d0-416a-b642-5e2bdf3de339

📥 Commits

Reviewing files that changed from the base of the PR and between cffb985 and 67e8b35.

📒 Files selected for processing (78)
  • CHANGELOG.md
  • deps/internal/vnescene
  • docs/vertexnova/interaction/diagrams/component.drawio
  • docs/vertexnova/interaction/interaction.md
  • examples/01_library_info/01_example.cpp
  • examples/01_library_info/README.md
  • examples/02_medical_3d_inspect/02_example.cpp
  • examples/02_medical_3d_inspect/README.md
  • examples/05_robotic_simulator/05_example.cpp
  • examples/05_robotic_simulator/05_example.h
  • examples/05_robotic_simulator/README.md
  • examples/05_robotic_simulator/main.cpp
  • examples/06_custom_input_bindings/06_example.cpp
  • examples/07_camera_rig_composition/07_example.cpp
  • examples/07_camera_rig_composition/README.md
  • examples/07_camera_rig_composition/main.cpp
  • examples/08_camera_state_save_restore/08_example.cpp
  • examples/08_camera_state_save_restore/README.md
  • examples/08_camera_state_save_restore/main.cpp
  • examples/README.md
  • include/vertexnova/interaction/camera_action.h
  • include/vertexnova/interaction/camera_controller.h
  • include/vertexnova/interaction/camera_manipulator.h
  • include/vertexnova/interaction/camera_manipulator_base.h
  • include/vertexnova/interaction/camera_rig.h
  • include/vertexnova/interaction/camera_state.h
  • include/vertexnova/interaction/follow_controller.h
  • include/vertexnova/interaction/follow_manipulator.h
  • include/vertexnova/interaction/free_look_manipulator.h
  • include/vertexnova/interaction/input_binding.h
  • include/vertexnova/interaction/inspect_3d_controller.h
  • include/vertexnova/interaction/interaction.h
  • include/vertexnova/interaction/interaction_types.h
  • include/vertexnova/interaction/navigation_3d_controller.h
  • include/vertexnova/interaction/orbital_camera_manipulator.h
  • include/vertexnova/interaction/ortho_2d_controller.h
  • include/vertexnova/interaction/ortho_2d_manipulator.h
  • src/CMakeLists.txt
  • src/vertexnova/interaction/camera_controller_impl.h
  • src/vertexnova/interaction/camera_manipulator_base.cpp
  • src/vertexnova/interaction/camera_math.h
  • src/vertexnova/interaction/camera_rig.cpp
  • src/vertexnova/interaction/detail/euler_orbit_strategy.h
  • src/vertexnova/interaction/detail/orbit_behavior.cpp
  • src/vertexnova/interaction/detail/orbit_behavior.h
  • src/vertexnova/interaction/detail/rotation_strategy.h
  • src/vertexnova/interaction/detail/trackball_behavior.cpp
  • src/vertexnova/interaction/detail/trackball_behavior.h
  • src/vertexnova/interaction/detail/trackball_strategy.h
  • src/vertexnova/interaction/follow_controller.cpp
  • src/vertexnova/interaction/follow_manipulator.cpp
  • src/vertexnova/interaction/free_look_manipulator.cpp
  • src/vertexnova/interaction/input_event_translator.cpp
  • src/vertexnova/interaction/input_event_translator.h
  • src/vertexnova/interaction/input_mapper.cpp
  • src/vertexnova/interaction/inspect_3d_controller.cpp
  • src/vertexnova/interaction/interaction_utils.cpp
  • src/vertexnova/interaction/interaction_utils.h
  • src/vertexnova/interaction/navigation_3d_controller.cpp
  • src/vertexnova/interaction/orbital_camera_manipulator.cpp
  • src/vertexnova/interaction/ortho_2d_controller.cpp
  • src/vertexnova/interaction/ortho_2d_manipulator.cpp
  • src/vertexnova/interaction/version.cpp
  • tests/CMakeLists.txt
  • tests/camera_rig_test.cpp
  • tests/controller_move_safety_test.cpp
  • tests/follow_controller_test.cpp
  • tests/follow_manipulator_test.cpp
  • tests/free_look_manipulator_test.cpp
  • tests/input_mapper_test.cpp
  • tests/inspect_3d_controller_test.cpp
  • tests/manipulator_regression_test.cpp
  • tests/navigation_3d_controller_test.cpp
  • tests/orbit_behavior_test.cpp
  • tests/orbital_camera_manipulator_test.cpp
  • tests/ortho_2d_controller_test.cpp
  • tests/ortho_2d_manipulator_test.cpp
  • tests/trackball_behavior_test.cpp
💤 Files with no reviewable changes (17)
  • include/vertexnova/interaction/camera_action.h
  • tests/CMakeLists.txt
  • tests/orbit_behavior_test.cpp
  • include/vertexnova/interaction/follow_manipulator.h
  • src/vertexnova/interaction/detail/orbit_behavior.cpp
  • tests/follow_manipulator_test.cpp
  • src/vertexnova/interaction/detail/euler_orbit_strategy.h
  • src/vertexnova/interaction/follow_controller.cpp
  • include/vertexnova/interaction/input_binding.h
  • include/vertexnova/interaction/camera_state.h
  • src/vertexnova/interaction/follow_manipulator.cpp
  • src/vertexnova/interaction/camera_math.h
  • include/vertexnova/interaction/follow_controller.h
  • src/vertexnova/interaction/detail/orbit_behavior.h
  • src/vertexnova/interaction/detail/rotation_strategy.h
  • tests/follow_controller_test.cpp
  • src/vertexnova/interaction/detail/trackball_strategy.h

@ajeetsinghyadav ajeetsinghyadav self-assigned this Apr 7, 2026
@ajeetsinghyadav ajeetsinghyadav added the enhancement New feature or request label Apr 7, 2026
…eraManipulator tests

- Improved the documentation for TrackballCameraState to clarify its purpose and components.
- Reinstated and enhanced tests for OrbitalCameraManipulator, including checks for default values and trackball rotation scale.
- Removed commented-out code in tests to improve readability and maintainability.
…lator functionality

- Enhanced descriptions of the OrbitalCameraManipulator, emphasizing quaternion virtual trackball implementation and its internal inertia handling.
- Revised interface and implementation sections to reflect changes in strategy and behavior components, specifically regarding the use of TrackballBehavior for screen mapping.
- Updated diagrams to accurately represent the current structure and functionality of the interaction library.
- Added comments to explain the purpose of testing both zero and negative delta time in the onUpdate method, ensuring that angle synchronization occurs before movement integration.
…aternion

- Moved the scaleTrackballDeltaQuaternion function to interaction_utils.cpp and renamed it to scaleTrackballQuaternion for better clarity and reusability.
- Updated references in FreeLookManipulator and OrbitalCameraManipulator to use the new function, ensuring consistent quaternion scaling behavior across manipulators.
- Removed unnecessary blank lines and improved code formatting in the scaleTrackballQuaternion function for better readability.
- Ensured consistent quaternion axis calculation by simplifying the axis definition without altering functionality.
- Updated aspect ratio calculations in fitToAABB to ensure correct handling of viewport dimensions, preventing potential division by zero.
- Enhanced getWorldUnitsPerPixel method to use a local variable for viewport height, improving readability and maintainability.
- Updated comments in 06_example.cpp to specify that the pinch scale is used in the same multiplicative sense as the scroll zoom factor.
- Enhanced documentation in input_mapper.h and interaction_types.h to clarify the relationship between pinch scale and zoom factor, ensuring better understanding of the touch pinch gesture's functionality.
- Adjusted the implementation in input_mapper.cpp to correctly use the pinch scale as the zoom factor, aligning with the updated documentation.
…e documentation

- Updated getOrientation method to ensure angle synchronization before returning the camera-to-world orientation.
- Introduced a constant for pitch clamping to improve stability in pitch calculations.
- Adjusted README.md to specify code block language for better clarity in documentation.
- Updated pitch limits in OrbitalCameraManipulator to prevent singularity issues during top/bottom views.
…bitalCameraManipulator

- Added `setOrbitAnimationEnabled` method to `Inspect3DController` to toggle animation for fit and view-direction transitions.
- Enhanced `OrbitalCameraManipulator` to support disabling animation, allowing for instant fit and view-direction changes.
- Updated documentation to reflect new functionality and usage examples for animated view presets.
- Added tests to verify the behavior of orbit animation toggling and its impact on fit and view-direction methods.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/vertexnova/interaction/orbital_camera_manipulator.cpp (1)

800-809: ⚠️ Potential issue | 🟠 Major

Resync the cached quaternion after setLandmark().

This method retargets the attached camera and recomputes orbit_distance_, but it leaves orbital_rot_->orientation on the old view. The next basis-driven call (getOrientation(), setOrbitDistance(), orthographic fitToAABB(), view-direction animation, etc.) can then jump because it starts from stale state.

Minimal fix
 void OrbitalCameraManipulator::setLandmark(const vne::math::Vec3f& world_pos) noexcept {
     anim_->stop();
     coi_world_ = world_pos;
     pivot_mode_ = OrbitPivotMode::eFixed;
     if (camera_) {
         orbit_distance_ = std::max((camera_->getPosition() - coi_world_).length(), kMinOrbitDistance);
         camera_->setTarget(coi_world_);
         camera_->updateMatrices();
+        onPivotChanged();
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/vertexnova/interaction/orbital_camera_manipulator.cpp` around lines 800 -
809, OrbitalCameraManipulator::setLandmark currently updates coi_world_,
orbit_distance_ and the camera target but leaves the cached quaternion
(orbital_rot_->orientation) stale; after anim_->stop(), after you call
camera_->setTarget(coi_world_) and camera_->updateMatrices(), resynchronize the
cached orientation by assigning orbital_rot_->orientation from the camera's
current orientation (e.g. from camera_->getOrientation() or by building the
quaternion from camera_->getViewBasis()/getOrientation() and normalize it) so
subsequent calls like getOrientation(), setOrbitDistance(), fitToAABB(), or any
view-direction animations start from the correct, up-to-date rotation state.
Ensure you update the same orbital_rot_ object used elsewhere (not a new local)
and preserve pivot_mode_/coi_world_/orbit_distance_ logic as-is.
src/vertexnova/interaction/free_look_manipulator.cpp (1)

104-113: ⚠️ Potential issue | 🟠 Major

setCamera() discards an orientation configured before attachment.

Line 279 stores the quaternion even when camera_ is null, but Lines 111-112 immediately replace that state from camera_->getOrientation() on attach. The new setter is therefore a silent no-op for “configure first, attach camera later” flows. Either preserve a pending orientation across setCamera(), or reject the no-camera case up front like setYawPitchDegrees() does.

As per coding guidelines, "Avoid surprising API changes; keep interfaces stable in C++ code."

Also applies to: 278-285

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/vertexnova/interaction/free_look_manipulator.cpp` around lines 104 - 113,
FreeLookManipulator::setCamera currently overwrites any previously configured
pending orientation when attaching a camera because it always calls
syncOrientationFromCamera() if camera_ is non-null; preserve a pending
orientation or reject the preconfigured case: modify setCamera (which calls
CameraManipulatorBase::setCamera) so that if a stored pending orientation exists
(e.g., set earlier while camera_ was null) you do not call
syncOrientationFromCamera() and you keep orientation_dirty_ as set by the prior
setter, otherwise (no pending orientation) continue to sync from camera_;
alternatively mirror setYawPitchDegrees() behavior by validating/rejecting the
no-camera case up-front. Ensure the logic references camera_,
orientation_dirty_, syncOrientationFromCamera(), and the pending orientation
storage used by the prior setters.
🧹 Nitpick comments (1)
include/vertexnova/interaction/orbital_camera_manipulator.h (1)

154-157: Document the input contract of setOrientation().

This new public API does not say whether non-unit quaternions are normalized internally or whether zero-length input is invalid. A one-line precondition/result note here would make the new surface much less surprising for callers.

As per coding guidelines, "Ensure public APIs have brief doc comments if behavior isn't obvious in C++ code."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@include/vertexnova/interaction/orbital_camera_manipulator.h` around lines 154
- 157, The setter setOrientation lacks a contract for non-unit or zero-length
quaternions; update its doc comment to state whether inputs are normalized and
how zero-length is handled. Specifically, document that setOrientation(const
vne::math::Quatf& rotation) noexcept will normalize non-unit quaternions before
applying them, treat a zero-length quaternion as the identity rotation (or
alternatively assert/throw if you prefer a stricter policy—pick one consistent
with project style), and clear rotation inertia and update the camera
accordingly; reference setOrientation and getOrientation in the comment so
callers know expected behavior and the resulting orientation value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/vertexnova/interaction/interaction.md`:
- Around line 35-42: The docs still list removed types—remove all references to
FollowManipulator and FollowController from the grouping table and any other
sections that mention them (e.g., the "Manipulators" and "Controllers" lists and
any later mentions of follow workflows); update the table row entries so only
existing symbols remain (e.g., keep OrbitalCameraManipulator,
FreeLookManipulator, Ortho2DManipulator under Manipulators and
Inspect3DController, Navigation3DController, Ortho2DController under
Controllers) and remove any explanatory text that describes follow-specific
behavior or usage (references to FollowManipulator, FollowController, or
"follow" workflows) so the documentation no longer presents stale API guidance.

In `@src/vertexnova/interaction/free_look_manipulator.cpp`:
- Around line 42-43: The constant kPitchAsinSinAbsMax (used by clampFpsPitch) is
too low (0.999f) so asin clamps to ≈87.4° and never reaches the intended 89°
guard; fix by raising the threshold to match sin(89°) (e.g., 0.99985f or
0.9999f) or even better compute it as the largest float < 1.0 via
std::nextafter(1.0f, 0.0f) and use that value in clampFpsPitch (and any other
uses in the 237-257 range) so over-pitch detection/correction behaves as
intended.
- Around line 259-270: getYawDegrees() and getPitchDegrees() read orientation_
indirectly via yawPitchFromOrientation() without ensuring the cached angles are
synced, so they can return stale values; before computing yaw/pitch call
ensureAnglesSynced() (or use getOrientation() which already calls
ensureAnglesSynced()) so the orientation/angles are up-to-date. Update
getYawDegrees(), getPitchDegrees() to resync (call ensureAnglesSynced() or
getOrientation()) then call yawPitchFromOrientation(y,p) and return the correct
component.

In `@src/vertexnova/interaction/orbital_camera_manipulator.cpp`:
- Around line 298-332: viewDirectionYawPitch currently forces eTop/eBottom to
±kViewDirPitchLimitDeg (89°) which prevents exact pole-facing views; change it
to set pitch to exact ±90° for ViewDirection::eTop and ::eBottom (keep yaw
as-is) and remove the hardcoded 89° here. Then ensure the camera-basis
construction that consumes yaw/pitch (the code that converts yaw/pitch ->
orientation in the orbital camera logic) detects the pole case (pitch == ±90°)
and uses a stable orthonormal fallback basis (e.g., choose a deterministic
right/forward vector such as world X axis) instead of perturbing the requested
direction; reference viewDirectionYawPitch and kViewDirPitchLimitDeg to locate
the change and the downstream basis construction that must handle the pole
robustly.

In `@tests/inspect_3d_controller_test.cpp`:
- Around line 55-74: The test currently only verifies the manipulator state
after calling Inspect3DController::fitToAABB, but must also assert the attached
camera was snapped immediately when orbit animation is disabled; after
ctrl.fitToAABB(...), add assertions that the controller's attached camera (the
cam set via ctrl.setCamera(cam)) has its position and look-at/target updated to
the expected snap values (matching the computed center-of-interest/fit) rather
than waiting for onUpdate(), using the camera getter methods you have (e.g., the
cam instance's position and target accessors) and comparing them to the expected
values.

---

Outside diff comments:
In `@src/vertexnova/interaction/free_look_manipulator.cpp`:
- Around line 104-113: FreeLookManipulator::setCamera currently overwrites any
previously configured pending orientation when attaching a camera because it
always calls syncOrientationFromCamera() if camera_ is non-null; preserve a
pending orientation or reject the preconfigured case: modify setCamera (which
calls CameraManipulatorBase::setCamera) so that if a stored pending orientation
exists (e.g., set earlier while camera_ was null) you do not call
syncOrientationFromCamera() and you keep orientation_dirty_ as set by the prior
setter, otherwise (no pending orientation) continue to sync from camera_;
alternatively mirror setYawPitchDegrees() behavior by validating/rejecting the
no-camera case up-front. Ensure the logic references camera_,
orientation_dirty_, syncOrientationFromCamera(), and the pending orientation
storage used by the prior setters.

In `@src/vertexnova/interaction/orbital_camera_manipulator.cpp`:
- Around line 800-809: OrbitalCameraManipulator::setLandmark currently updates
coi_world_, orbit_distance_ and the camera target but leaves the cached
quaternion (orbital_rot_->orientation) stale; after anim_->stop(), after you
call camera_->setTarget(coi_world_) and camera_->updateMatrices(), resynchronize
the cached orientation by assigning orbital_rot_->orientation from the camera's
current orientation (e.g. from camera_->getOrientation() or by building the
quaternion from camera_->getViewBasis()/getOrientation() and normalize it) so
subsequent calls like getOrientation(), setOrbitDistance(), fitToAABB(), or any
view-direction animations start from the correct, up-to-date rotation state.
Ensure you update the same orbital_rot_ object used elsewhere (not a new local)
and preserve pivot_mode_/coi_world_/orbit_distance_ logic as-is.

---

Nitpick comments:
In `@include/vertexnova/interaction/orbital_camera_manipulator.h`:
- Around line 154-157: The setter setOrientation lacks a contract for non-unit
or zero-length quaternions; update its doc comment to state whether inputs are
normalized and how zero-length is handled. Specifically, document that
setOrientation(const vne::math::Quatf& rotation) noexcept will normalize
non-unit quaternions before applying them, treat a zero-length quaternion as the
identity rotation (or alternatively assert/throw if you prefer a stricter
policy—pick one consistent with project style), and clear rotation inertia and
update the camera accordingly; reference setOrientation and getOrientation in
the comment so callers know expected behavior and the resulting orientation
value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 82e76d50-602e-4681-ba6a-477eca7d0d90

📥 Commits

Reviewing files that changed from the base of the PR and between 67e8b35 and 50a9639.

📒 Files selected for processing (22)
  • docs/vertexnova/interaction/diagrams/component.drawio
  • docs/vertexnova/interaction/interaction.md
  • examples/02_medical_3d_inspect/02_example.cpp
  • examples/02_medical_3d_inspect/README.md
  • examples/06_custom_input_bindings/06_example.cpp
  • examples/07_camera_rig_composition/README.md
  • include/vertexnova/interaction/camera_manipulator_base.h
  • include/vertexnova/interaction/free_look_manipulator.h
  • include/vertexnova/interaction/input_mapper.h
  • include/vertexnova/interaction/inspect_3d_controller.h
  • include/vertexnova/interaction/interaction_types.h
  • include/vertexnova/interaction/orbital_camera_manipulator.h
  • src/vertexnova/interaction/free_look_manipulator.cpp
  • src/vertexnova/interaction/input_mapper.cpp
  • src/vertexnova/interaction/inspect_3d_controller.cpp
  • src/vertexnova/interaction/interaction_utils.cpp
  • src/vertexnova/interaction/interaction_utils.h
  • src/vertexnova/interaction/orbital_camera_manipulator.cpp
  • tests/free_look_manipulator_test.cpp
  • tests/inspect_3d_controller_test.cpp
  • tests/manipulator_regression_test.cpp
  • tests/orbital_camera_manipulator_test.cpp
✅ Files skipped from review due to trivial changes (3)
  • include/vertexnova/interaction/camera_manipulator_base.h
  • include/vertexnova/interaction/input_mapper.h
  • examples/02_medical_3d_inspect/README.md
🚧 Files skipped from review as they are similar to previous changes (7)
  • examples/06_custom_input_bindings/06_example.cpp
  • docs/vertexnova/interaction/diagrams/component.drawio
  • examples/07_camera_rig_composition/README.md
  • src/vertexnova/interaction/interaction_utils.cpp
  • examples/02_medical_3d_inspect/02_example.cpp
  • tests/orbital_camera_manipulator_test.cpp
  • include/vertexnova/interaction/inspect_3d_controller.h

Comment on lines +42 to +43
/** Clamp |forward·worldUp| below 1 for stable @c asin in @ref FreeLookManipulator::clampFpsPitch. */
constexpr float kPitchAsinSinAbsMax = 0.999f;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

clampFpsPitch() never reaches the 89° guard.

kPitchAsinSinAbsMax is 0.999f, but sin(89°) is about 0.99985. After the clamp on Line 243, asin(s) tops out around 87.4°, so the early return on Lines 246-247 is always taken and over-pitch rotations are never corrected in FPS mode.

🐛 Proposed fix
-/** Clamp |forward·worldUp| below 1 for stable `@c` asin in `@ref` FreeLookManipulator::clampFpsPitch. */
-constexpr float kPitchAsinSinAbsMax = 0.999f;
+/** Clamp into the valid `@c` asin domain while still allowing the 89° pitch guard to trigger. */
+constexpr float kPitchAsinSinAbsMax = 1.0f;
As per coding guidelines, "Check invariants, pre/post-conditions, edge cases, and undefined behavior in C++ code."

Also applies to: 237-257

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/vertexnova/interaction/free_look_manipulator.cpp` around lines 42 - 43,
The constant kPitchAsinSinAbsMax (used by clampFpsPitch) is too low (0.999f) so
asin clamps to ≈87.4° and never reaches the intended 89° guard; fix by raising
the threshold to match sin(89°) (e.g., 0.99985f or 0.9999f) or even better
compute it as the largest float < 1.0 via std::nextafter(1.0f, 0.0f) and use
that value in clampFpsPitch (and any other uses in the 237-257 range) so
over-pitch detection/correction behaves as intended.

Comment on lines +259 to +270
float FreeLookManipulator::getYawDegrees() const noexcept {
float y = 0.0f;
float p = 0.0f;
yawPitchFromOrientation(y, p);
return y;
}

float FreeLookManipulator::getPitchDegrees() const noexcept {
float y = 0.0f;
float p = 0.0f;
yawPitchFromOrientation(y, p);
return p;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Resync before returning yaw/pitch getters.

getOrientation() already calls ensureAnglesSynced(), but these accessors read orientation_ directly. That leaves them stale after paths that only mark the state dirty, such as the zoom flow in src/vertexnova/interaction/navigation_3d_controller.cpp.

💡 Proposed fix
 float FreeLookManipulator::getYawDegrees() const noexcept {
+    const_cast<FreeLookManipulator*>(this)->ensureAnglesSynced();
     float y = 0.0f;
     float p = 0.0f;
     yawPitchFromOrientation(y, p);
     return y;
 }
 
 float FreeLookManipulator::getPitchDegrees() const noexcept {
+    const_cast<FreeLookManipulator*>(this)->ensureAnglesSynced();
     float y = 0.0f;
     float p = 0.0f;
     yawPitchFromOrientation(y, p);
     return p;
 }
As per coding guidelines, "Check invariants, pre/post-conditions, edge cases, and undefined behavior in C++ code."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/vertexnova/interaction/free_look_manipulator.cpp` around lines 259 - 270,
getYawDegrees() and getPitchDegrees() read orientation_ indirectly via
yawPitchFromOrientation() without ensuring the cached angles are synced, so they
can return stale values; before computing yaw/pitch call ensureAnglesSynced()
(or use getOrientation() which already calls ensureAnglesSynced()) so the
orientation/angles are up-to-date. Update getYawDegrees(), getPitchDegrees() to
resync (call ensureAnglesSynced() or getOrientation()) then call
yawPitchFromOrientation(y,p) and return the correct component.

Comment on lines +298 to +332
/** Top/bottom view presets: magnitude below 90° to stay off the polar singularity with world up. */
constexpr float kViewDirPitchLimitDeg = 89.0f;

void viewDirectionYawPitch(ViewDirection dir, float& yaw, float& pitch) noexcept {
switch (dir) {
case ViewDirection::eFront:
yaw = 0.0f;
pitch = 0.0f;
break;
case ViewDirection::eBack:
yaw = kYawDegBack;
pitch = 0.0f;
break;
case ViewDirection::eLeft:
yaw = kYawDegLeft;
pitch = 0.0f;
break;
case ViewDirection::eRight:
yaw = kYawDegRight;
pitch = 0.0f;
break;
case ViewDirection::eTop:
yaw = 0.0f;
pitch = -kViewDirPitchLimitDeg;
break;
case ViewDirection::eBottom:
yaw = 0.0f;
pitch = kViewDirPitchLimitDeg;
break;
case ViewDirection::eIso:
yaw = kYawDegIso;
pitch = kPitchDegIso;
break;
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

eTop / eBottom no longer produce exact axis-aligned views.

viewDirectionYawPitch() hard-codes ±89°, so those presets always keep a 1° tilt instead of looking exactly along ±world-up. That contradicts the public ViewDirection contract and makes the preset views unreliable for exact CAD/inspection alignment. Please handle the pole case with a stable fallback basis instead of changing the requested direction.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/vertexnova/interaction/orbital_camera_manipulator.cpp` around lines 298 -
332, viewDirectionYawPitch currently forces eTop/eBottom to
±kViewDirPitchLimitDeg (89°) which prevents exact pole-facing views; change it
to set pitch to exact ±90° for ViewDirection::eTop and ::eBottom (keep yaw
as-is) and remove the hardcoded 89° here. Then ensure the camera-basis
construction that consumes yaw/pitch (the code that converts yaw/pitch ->
orientation in the orbital camera logic) detects the pole case (pitch == ±90°)
and uses a stable orthonormal fallback basis (e.g., choose a deterministic
right/forward vector such as world X axis) instead of perturbing the requested
direction; reference viewDirectionYawPitch and kViewDirPitchLimitDeg to locate
the change and the downstream basis construction that must handle the pole
robustly.

Comment on lines +55 to 74
TEST(Inspect3DController, SetOrbitAnimationEnabledFalseSnapFitWithoutSteppingUpdate) {
vne::interaction::Inspect3DController ctrl;
auto cam = makePerspCamera();
cam->setPosition(vne::math::Vec3f(0.0f, 0.0f, 5.0f));
cam->lookAt(vne::math::Vec3f(0.0f, 0.0f, 0.0f), vne::math::Vec3f(0.0f, 1.0f, 0.0f));
ctrl.setCamera(cam);
ctrl.onResize(1280.0f, 720.0f);

ctrl.orbitalCameraManipulator().setFitAnimationDuration(0.5f);
ctrl.setOrbitAnimationEnabled(false);
EXPECT_FALSE(ctrl.isOrbitAnimationEnabled());

ctrl.fitToAABB(vne::math::Vec3f(9.0f, -1.0f, -1.0f), vne::math::Vec3f(11.0f, 1.0f, 1.0f));
const auto coi = ctrl.orbitalCameraManipulator().getCenterOfInterestWorld();
EXPECT_NEAR(coi.x(), 10.0f, 0.02f);
EXPECT_FLOAT_EQ(ctrl.orbitalCameraManipulator().getFitAnimationDuration(), 0.5f);

ctrl.setOrbitAnimationEnabled(true);
EXPECT_TRUE(ctrl.isOrbitAnimationEnabled());
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Assert the camera snap, not just the manipulator state.

This test is meant to prove fitToAABB() applies immediately when orbit animation is disabled, but it only checks getCenterOfInterestWorld() and the stored duration. It would still pass if the controller deferred the actual camera pose update until onUpdate(). Please assert the attached camera’s target/position right after ctrl.fitToAABB(...).

Suggested assertion tightening
     ctrl.fitToAABB(vne::math::Vec3f(9.0f, -1.0f, -1.0f), vne::math::Vec3f(11.0f, 1.0f, 1.0f));
     const auto coi = ctrl.orbitalCameraManipulator().getCenterOfInterestWorld();
     EXPECT_NEAR(coi.x(), 10.0f, 0.02f);
+    EXPECT_NEAR(cam->getTarget().x(), 10.0f, 0.02f);
+    EXPECT_GT((cam->getPosition() - vne::math::Vec3f(0.0f, 0.0f, 5.0f)).length(), 1e-4f);
     EXPECT_FLOAT_EQ(ctrl.orbitalCameraManipulator().getFitAnimationDuration(), 0.5f);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/inspect_3d_controller_test.cpp` around lines 55 - 74, The test
currently only verifies the manipulator state after calling
Inspect3DController::fitToAABB, but must also assert the attached camera was
snapped immediately when orbit animation is disabled; after ctrl.fitToAABB(...),
add assertions that the controller's attached camera (the cam set via
ctrl.setCamera(cam)) has its position and look-at/target updated to the expected
snap values (matching the computed center-of-interest/fit) rather than waiting
for onUpdate(), using the camera getter methods you have (e.g., the cam
instance's position and target accessors) and comparing them to the expected
values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants