@@ -329,28 +329,246 @@ are otherwise visibly incomplete after the 2.x → 3.0 porting effort.
329329
330330### 13.4 `mrpt_poses` — Unimplemented PDF operations
331331
332- Some probability distribution classes still have methods that throw at run-time.
332+ All previously-stubbed methods have been implemented:
333+
334+ - **`CPosePDFGrid`**: `copyFrom()` (same-type copy or sample-based approximation),
335+ `changeCoordinatesReference()` (cell remapping with bounds check),
336+ `bayesianFusion()` (element-wise product for same-type grids, or Parzen KDE
337+ for particle inputs), `inverse()` (cell remapping via SE(2) inverse).
338+ - **`CPose3DPDFGrid`**: `copyFrom()`, `saveToTextFile()` (sparse text dump with
339+ header), `changeCoordinatesReference()`, `bayesianFusion()` (same-size grids),
340+ `inverse()`.
341+ - **`CPose3DPDFParticles`**: `bayesianFusion()` — KDE-based fusion when p2 is
342+ particles, Mahalanobis-based when p2 is Gaussian.
343+ - **`CPosePDFParticles`**: `bayesianFusion()` — Parzen window evaluation via the
344+ existing `evaluatePDF_parzen()` helper; Mahalanobis fallback for other types.
345+ - **`CPointPDFParticles`**: `copyFrom()` (same-type deep copy, or sampling from
346+ Gaussian), `bayesianFusion()` — KDE-based particle fusion.
333347
334- **Still unimplemented** (throwing at run-time):
348+ ### 13.5 `mrpt_slam` — Incomplete algorithms
335349
336- - **`CPosePDFGrid`**: `copyFrom()`, `changeCoordinatesReference()`,
337- `bayesianFusion()`, `inverse()`.
338- - **`CPose3DPDFGrid`**: `saveToTextFile()`, `changeCoordinatesReference()`,
339- `bayesianFusion()`, `inverse()`.
340- - **`CPose3DPDFParticles`**: `bayesianFusion()`.
341- - **`CPosePDFParticles`**: `bayesianFusion()`, `evaluateNormalizedPDF()`.
342- - **`CPointPDFParticles`**: `bayesianFusion()`.
350+ - **`CICP` (3-D mode)**: Only `icpClassic` is implemented for 3-D point-cloud
351+ alignment. The `CICP` class Doxygen header now documents this limitation and
352+ directs users to the **mp2p_icp** library
353+ (https://github.com/MOLAorg/mp2p_icp) for production-quality 3-D scan
354+ matching with richer metrics and robust kernels.
343355
344- ### 13.5 `mrpt_slam` — Incomplete algorithms
356+ ### 13.6 `mrpt_nav` — Comprehensive modernisation plan
357+
358+ The `mrpt_nav` module is ~20 years old and the largest candidate for
359+ refactoring in MRPT 3.0. The plan below is grouped into phases of
360+ increasing risk/effort. Each item is prefixed with a priority tag:
361+ **[P0]** = do first (low risk, high value),
362+ **[P1]** = next (medium risk),
363+ **[P2]** = later (significant API change).
364+
365+ ---
366+
367+ #### 13.6.1 Enum modernisation
368+
369+ **[P0]** Convert all plain `enum` to `enum class`:
370+
371+ | Current | Proposed |
372+ |---------|----------|
373+ | `CAbstractNavigator::TState` (`IDLE`, `NAVIGATING`, …) | `enum class TState` |
374+ | `CAbstractNavigator::TErrorCode` (`ERR_NONE`, …) | `enum class TErrorCode` |
375+ | `CHolonomicND::TSituations` (`SITUATION_TARGET_DIRECTLY`, …) | `enum class TSituations` |
376+ | `PTG_collision_behavior_t` (`COLL_BEH_BACK_AWAY`, …) | `enum class PTGCollisionBehavior` (rename too) |
377+
378+ ---
379+
380+ #### 13.6.2 `[[nodiscard]]` annotations
381+
382+ **[P0]** Add `[[nodiscard]]` to every query / getter that returns a
383+ value callers should not silently discard:
384+
385+ - `CAbstractNavigator`: `getCurrentState()`, `getErrorReason()`
386+ - `CWaypointsNavigator`: `getWaypointNavStatus()`
387+ - `CAbstractPTGBasedReactive`: `getPTG_count()`, `getPTG()`,
388+ `getLastLogRecord()`, `getTargetApproachSlowDownDistance()`
389+ - `CParameterizedTrajectoryGenerator`: `getRefDistance()`,
390+ `getAlphaValuesCount()`, `getPathStepCount()`, `getPathPose()`,
391+ `getPathDist()`, `index2alpha()`, `alpha2index()`, `isInitialized()`
392+ - All holonomic `TOptions` getters.
393+
394+ ---
395+
396+ #### 13.6.3 Naming consistency
397+
398+ **[P0]** Standardise naming across the module:
399+
400+ - **Config structs**: Rename all `TOptions` to `TParams` (or vice-versa)
401+ so that every configuration struct in the module uses the same suffix.
402+ Holonomic classes use `TOptions`; navigator classes use `TParams`;
403+ `CMultiObjectiveMotionOptimizerBase` uses `TParamsBase`. Pick one.
404+ - **STEP-numbered methods**: `STEP1_InitPTGs()`, `STEP2_SenseObstacles()`,
405+ `STEP3_WSpaceToTPSpace()`, `STEP8_GenerateLogRecord()` should be renamed
406+ to descriptive names (e.g. `initPTGs()`, `senseObstacles()`,
407+ `workspaceToTPSpace()`, `generateLogRecord()`).
408+ - **`impl_` prefix**: Some virtual hooks use `impl_` (e.g.
409+ `impl_waypoint_is_reachable`), others do not. Standardise on a single
410+ convention (recommend dropping the prefix and relying on `protected` access).
411+ - **Underscore style in identifiers**: `TP_Obstacles` vs `TPObstacles`,
412+ `WS_Obstacles` vs `PWS_Obstacles`. Pick one convention.
413+ - **`getDescription()` in PTGs**: The `os::sprintf` calls in
414+ `CPTG_DiffDrive_CC`, `CPTG_DiffDrive_CCS`, `CPTG_DiffDrive_CS`,
415+ `CPTG_DiffDrive_alpha` should use `mrpt::format()` and their C-style
416+ casts `(int)K` should become `static_cast<int>(K)`.
417+
418+ ---
419+
420+ #### 13.6.4 Thread-safety fixes
421+
422+ **[P1]** Eliminate bare `lock()`/`unlock()` calls and manual locking APIs:
423+
424+ - `CAbstractPTGBasedReactive::preDestructor()` (line 75–76) does
425+ `m_nav_cs.lock(); m_nav_cs.unlock();` outside any RAII guard — an
426+ exception between these calls would deadlock. Replace with
427+ `std::scoped_lock lk(m_nav_cs);`.
428+ - `CWaypointsNavigator::beginWaypointsAccess()` /
429+ `endWaypointsAccess()` expose raw `lock()`/`unlock()` to users.
430+ Replace with a RAII accessor that returns a guard object holding a
431+ reference to the waypoint data:
432+ ```cpp
433+ [[nodiscard]] auto waypointsAccess() {
434+ return mrpt::lockHelper(m_nav_waypoints_cs, m_waypoints);
435+ }
436+ ```
437+ - ** Global mutable state** : ` OUTPUT_DEBUG_PATH_PREFIX ` and
438+ ` COLLISION_BEHAVIOR ` in ` CParameterizedTrajectoryGenerator.cpp ` are
439+ ` static ` globals with no synchronisation. Guard with
440+ ` std::mutex ` or make them ` thread_local ` , or move them into a
441+ per-instance configuration field.
442+
443+ ---
444+
445+ #### 13.6.5 Return-value modernisation (output-reference cleanup)
446+
447+ ** [ P1] ** Replace output-reference parameters with return values or
448+ structured returns. Key candidates:
449+
450+ | Method | Current | Proposed |
451+ | --------| ---------| ----------|
452+ | ` CRobot2NavInterface::getCurrentPoseAndSpeeds() ` | 4 output ` & ` params | Return ` struct PoseAndSpeeds { TPose2D pose; TTwist2D vel; … }; ` |
453+ | ` CAbstractHolonomicReactiveMethod::navigate() ` | ` NavOutput& ` | Return ` NavOutput ` by value |
454+ | ` CAbstractPTGBasedReactive::getLastLogRecord() ` | ` CLogFileRecord& ` output | Return ` std::optional<CLogFileRecord> ` or ` CLogFileRecord ` |
455+ | ` CMultiObjectiveMotionOptimizerBase::decide() ` | ` int& ` best index | Return ` std::optional<size_t> ` (nullopt = no good motion) |
456+ | ` CParameterizedTrajectoryGenerator::inverseMap_WS2TP() ` | ` int& k, double& d ` output | Return ` std::optional<std::pair<uint16_t,double>> ` |
457+
458+ ---
459+
460+ #### 13.6.6 Raw-pointer cleanup
461+
462+ ** [ P1] ** Remove remaining raw-pointer ownership/borrowing:
463+
464+ - ` CAbstractHolonomicReactiveMethod::m_associatedPTG ` — raw `const
465+ CParameterizedTrajectoryGenerator* `. Replace with
466+ ` std::weak_ptr<const CParameterizedTrajectoryGenerator> ` or a
467+ non-owning ` std::reference_wrapper ` .
468+ - ` TCandidateMovementPTG::PTG ` — raw pointer, nullable. Replace with
469+ ` const CParameterizedTrajectoryGenerator* ` documented as non-owning,
470+ or better, an index into the PTG vector.
471+ - ` CAbstractPTGBasedReactive::getHoloMethod() ` returns a raw pointer;
472+ return a reference or ` std::optional<std::reference_wrapper<>> ` .
473+
474+ ---
475+
476+ #### 13.6.7 Decompose ` CAbstractPTGBasedReactive `
477+
478+ ** [ P2] ** This 467-line header (11 virtual methods, 6 nested structs,
479+ private members spanning PTG management, holonomic method management,
480+ logging, obstacle processing, velocity generation, and profiling) is the
481+ single largest maintenance burden. Proposed decomposition:
482+
483+ 1 . ** Extract ` PTGSetManager ` ** — owns the ` vector<CParameterizedTrajectoryGenerator::Ptr> ` ,
484+ handles ` STEP1_InitPTGs() ` , ` getPTG() ` , ` getPTG_count() ` ,
485+ collision-grid builds.
486+ 2 . ** Extract ` NavigationLogger ` ** — owns the ` CLogFileRecord ` ,
487+ ` m_critZoneLastLog ` , log-file path management, ` STEP8_GenerateLogRecord() ` .
488+ 3 . ** Extract ` VelocityFilter ` ** — owns ` TSentVelCmd ` , the speed-filter
489+ tau, and ` filterVelocityCommand() ` .
490+ 4 . Keep the main class as a thin orchestrator that wires the above
491+ together via dependency injection (constructor parameters or setters).
492+
493+ ---
494+
495+ #### 13.6.8 Refactor long functions
496+
497+ ** [ P2] ** Several functions exceed 200 lines and mix multiple levels of
498+ abstraction. Each should be broken into named helpers:
499+
500+ | Function | Lines | Suggested split |
501+ | ----------| -------| -----------------|
502+ | ` PlannerSimple2D::computePath() ` | ~ 465 | Extract ` wavePropagation() ` , ` backtrackPath() ` |
503+ | ` PlannerRRT_SE2_TPS::solve() ` | ~ 380 | Extract ` sampleFreeSpace() ` , ` extendTree() ` , ` optimizePath() ` |
504+ | ` CHolonomicFullEval::evalSingleTarget() ` | ~ 290 | Extract ` scoreCandidateDirection() ` , ` applyPenalties() ` |
505+ | ` CHolonomicFullEval::navigate() ` | ~ 170 | Extract ` findGaps() ` , ` selectBestDirection() ` |
506+ | ` CHolonomicND::gapsEstimator() ` | ~ 150 | Extract gap-merging and gap-filtering helpers |
507+ | ` CLogFileRecord::serializeFrom() ` | ~ 450 | Version-switch per struct field group |
508+
509+ ---
510+
511+ #### 13.6.9 Magic numbers → named constants
512+
513+ ** [ P1] ** Replace hard-coded thresholds with named constants or
514+ configuration-file parameters:
515+
516+ - ` CHolonomicFullEval.cpp ` : 0.01, 0.95, 1.02, 1.05, ` round(nDirs * 0.1) `
517+ - ` CHolonomicND.cpp ` : 0.01, 0.05, 0.02, 0.1, 0.5 (ratio thresholds)
518+ - ` CHolonomicVFF.cpp ` : ` std::min(1e6, …) ` sentinel
519+ - ` PlannerSimple2D.cpp ` : ` CELL_EMPTY = 0x8000000 ` etc. — already named,
520+ but could use documentation comments explaining why those values.
521+
522+ ---
523+
524+ #### 13.6.10 Test coverage
525+
526+ ** [ P1] ** Current coverage is approximately 25 %. The following classes
527+ have ** zero dedicated unit tests** and should be prioritised:
528+
529+ - ** Holonomic methods in isolation** : ` CHolonomicVFF ` , ` CHolonomicND ` ,
530+ ` CHolonomicFullEval ` — currently only tested indirectly inside
531+ integration tests.
532+ - ** Waypoint navigation** : ` CWaypointsNavigator ` , ` TWaypoint ` ,
533+ ` TWaypointSequence ` — substantial state machine, no unit tests.
534+ - ** Manual-sequence navigator** : ` CNavigatorManualSequence ` .
535+ - ** RRT planners** : ` PlannerRRT_SE2_TPS ` , ` PlannerRRT_common ` .
536+ - ** Multi-objective optimiser** : ` CMultiObjectiveMotionOptimizerBase ` ,
537+ ` CMultiObjMotionOpt_Scalarization ` .
538+ - ** Clearance diagram** : ` ClearanceDiagram ` — data structure used
539+ everywhere, zero tests.
540+
541+ ---
542+
543+ #### 13.6.11 Documentation
544+
545+ ** [ P1] ** ` lib_mrpt_nav.md ` is a 32-line stub. Expand with:
546+
547+ - A module-architecture diagram (subsystems: holonomic, reactive, planners,
548+ tpspace) and their relationships.
549+ - A "getting started" section explaining which class to instantiate for
550+ the most common use-cases (2-D reactive, waypoint following, path
551+ planning).
552+ - Configuration-file schema documentation (` .ini ` key names per class).
553+ - Algorithm summaries for VFF, ND, FullEval, and the PTG families.
554+ - Cross-references to the ReactiveNavigationDemo example.
555+
556+ ---
345557
346- - **`CICP` (3-D mode)**: Only `icpClassic` is implemented for 3-D ICP; Action: mention in docs
347- that users should use the independent project mp2p_icp instead.
558+ #### 13.6.12 Remaining code-quality items
348559
349- ### 13.6 `mrpt_nav` — Planner optimisation
560+ ** [ P0 ] ** Quick wins:
350561
351- - Re-read the entire library, look for potential conceptual / implementation subtle bugs, and improve in general all classes documentation.
352- - **`CParameterizedTrajectoryGenerator`**: One obstacle post-processing enum
353- value is not handled and throws.
562+ - ` MRPT_TODO("Optimize getNearestNode() with KD-tree!") ` in
563+ ` PlannerRRT_SE2_TPS.cpp:27 ` — either implement or convert to a tracked
564+ issue.
565+ - ` #if 0 ` dead code in ` CHolonomicND.cpp:437–447 ` — remove.
566+ - ` static size_t SAVE_LOG_SOLVE_COUNT ` in ` PlannerRRT_SE2_TPS.cpp:72 ` —
567+ global mutable counter used only for debugging; remove or guard.
568+ - ` CLogFileRecord::serializeFrom() ` line 349 uses raw ` new ` for
569+ deserialization — replace with ` std::make_shared ` .
570+ - C-style casts ` (int)K ` in ` CPTG_DiffDrive_CC.cpp:71 ` and
571+ ` CPTG_DiffDrive_CCS.cpp:71 ` — replace with ` static_cast<int>(K) ` .
354572
355573### 13.7 ` mrpt_maps ` — Miscellaneous
356574
0 commit comments