feat(solver): define IKCSolver C++ API types and interface (#292)
Add the pluggable solver API as header-only files under src/Mod/Assembly/Solver/. This is Phase 1a of the pluggable solver system (INTER_SOLVER.md). New files: - Types.h: BaseJointKind enum (24 decomposed constraint types), Transform, Part, Constraint, SolveContext, SolveResult, and supporting types. Uses standalone types (no FreeCAD dependency) for future server worker compatibility. - IKCSolver.h: Abstract solver interface with solve(), drag protocol (pre_drag/drag_step/post_drag), kinematic simulation (run_kinematic/num_frames/update_for_frame), and diagnostics. Only solve(), name(), and supported_joints() are pure virtual; all other methods have default implementations. - SolverRegistry.h: Thread-safe singleton registry for solver backends with factory-based registration and default solver selection. - CMakeLists.txt: INTERFACE library target (header-only for now). Modified: - Assembly/CMakeLists.txt: add_subdirectory(Solver) - Assembly/App/CMakeLists.txt: link KCSolve INTERFACE target
This commit is contained in:
181
src/Mod/Assembly/Solver/IKCSolver.h
Normal file
181
src/Mod/Assembly/Solver/IKCSolver.h
Normal file
@@ -0,0 +1,181 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
/****************************************************************************
|
||||
* *
|
||||
* Copyright (c) 2025 Kindred Systems <development@kindred-systems.com> *
|
||||
* *
|
||||
* This file is part of FreeCAD. *
|
||||
* *
|
||||
* FreeCAD is free software: you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU Lesser General Public License as *
|
||||
* published by the Free Software Foundation, either version 2.1 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* FreeCAD is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with FreeCAD. If not, see *
|
||||
* <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef KCSOLVE_IKCSOLVER_H
|
||||
#define KCSOLVE_IKCSOLVER_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Types.h"
|
||||
|
||||
namespace KCSolve
|
||||
{
|
||||
|
||||
/// Abstract interface for a pluggable assembly constraint solver.
|
||||
///
|
||||
/// Solver backends implement this interface. The Assembly module calls
|
||||
/// through it via the SolverRegistry. A minimal solver only needs to
|
||||
/// implement solve(), name(), and supported_joints() — all other methods
|
||||
/// have default implementations that either delegate to solve() or
|
||||
/// return sensible defaults.
|
||||
///
|
||||
/// Method mapping to current AssemblyObject operations:
|
||||
///
|
||||
/// solve() <-> AssemblyObject::solve()
|
||||
/// pre_drag() <-> AssemblyObject::preDrag()
|
||||
/// drag_step() <-> AssemblyObject::doDragStep()
|
||||
/// post_drag() <-> AssemblyObject::postDrag()
|
||||
/// run_kinematic() <-> AssemblyObject::generateSimulation()
|
||||
/// num_frames() <-> AssemblyObject::numberOfFrames()
|
||||
/// update_for_frame() <-> AssemblyObject::updateForFrame()
|
||||
/// diagnose() <-> AssemblyObject::updateSolveStatus()
|
||||
|
||||
class IKCSolver
|
||||
{
|
||||
public:
|
||||
virtual ~IKCSolver() = default;
|
||||
|
||||
/// Human-readable solver name (e.g. "OndselSolver (Lagrangian)").
|
||||
virtual std::string name() const = 0;
|
||||
|
||||
/// Return the set of BaseJointKind values this solver supports.
|
||||
/// The registry uses this for capability-based solver selection.
|
||||
virtual std::vector<BaseJointKind> supported_joints() const = 0;
|
||||
|
||||
// ── Static solve ───────────────────────────────────────────────
|
||||
|
||||
/// Solve the assembly for static equilibrium.
|
||||
/// @param ctx Complete description of parts, constraints, and options.
|
||||
/// @return Result with updated placements and diagnostics.
|
||||
virtual SolveResult solve(const SolveContext& ctx) = 0;
|
||||
|
||||
/// Incrementally update an already-solved assembly after parameter
|
||||
/// changes (e.g. joint angle/distance changed during joint creation).
|
||||
/// Default: delegates to solve().
|
||||
virtual SolveResult update(const SolveContext& ctx)
|
||||
{
|
||||
return solve(ctx);
|
||||
}
|
||||
|
||||
// ── Interactive drag ───────────────────────────────────────────
|
||||
//
|
||||
// Three-phase protocol for interactive part dragging:
|
||||
// 1. pre_drag() — solve initial state, prepare for dragging
|
||||
// 2. drag_step() — called on each mouse move with updated positions
|
||||
// 3. post_drag() — finalize and release internal solver state
|
||||
//
|
||||
// Solvers can maintain internal state across the drag session for
|
||||
// better interactive performance. This addresses a known weakness
|
||||
// in the current direct-OndselSolver integration.
|
||||
|
||||
/// Prepare for an interactive drag session.
|
||||
/// @param ctx Assembly state before dragging begins.
|
||||
/// @param drag_parts IDs of parts being dragged.
|
||||
/// @return Initial solve result.
|
||||
virtual SolveResult pre_drag(const SolveContext& ctx,
|
||||
const std::vector<std::string>& /*drag_parts*/)
|
||||
{
|
||||
return solve(ctx);
|
||||
}
|
||||
|
||||
/// Perform one incremental drag step.
|
||||
/// @param drag_placements Current placements of the dragged parts
|
||||
/// (part ID + new transform).
|
||||
/// @return Updated placements for all affected parts.
|
||||
virtual SolveResult drag_step(
|
||||
const std::vector<SolveResult::PartResult>& /*drag_placements*/)
|
||||
{
|
||||
return SolveResult {SolveStatus::Success};
|
||||
}
|
||||
|
||||
/// End an interactive drag session and finalize state.
|
||||
virtual void post_drag()
|
||||
{
|
||||
}
|
||||
|
||||
// ── Kinematic simulation ───────────────────────────────────────
|
||||
|
||||
/// Run a kinematic simulation over the time range in ctx.simulation.
|
||||
/// After this call, num_frames() returns the frame count and
|
||||
/// update_for_frame(i) retrieves individual frame placements.
|
||||
/// Default: delegates to solve() (ignoring simulation params).
|
||||
virtual SolveResult run_kinematic(const SolveContext& /*ctx*/)
|
||||
{
|
||||
return SolveResult {SolveStatus::Failed};
|
||||
}
|
||||
|
||||
/// Number of simulation frames available after run_kinematic().
|
||||
virtual std::size_t num_frames() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Retrieve part placements for simulation frame at index.
|
||||
/// @pre index < num_frames()
|
||||
virtual SolveResult update_for_frame(std::size_t /*index*/)
|
||||
{
|
||||
return SolveResult {SolveStatus::Failed};
|
||||
}
|
||||
|
||||
// ── Diagnostics ────────────────────────────────────────────────
|
||||
|
||||
/// Analyze the assembly for redundant, conflicting, or malformed
|
||||
/// constraints. May require a prior solve() call for some solvers.
|
||||
virtual std::vector<ConstraintDiagnostic> diagnose(const SolveContext& /*ctx*/)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// ── Capability queries ─────────────────────────────────────────
|
||||
|
||||
/// Whether this solver produces deterministic results given
|
||||
/// identical input.
|
||||
virtual bool is_deterministic() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Whether this solver handles fixed-joint part bundling internally.
|
||||
/// When false, the caller bundles parts connected by Fixed joints
|
||||
/// before building the SolveContext. When true, the solver receives
|
||||
/// unbundled parts and optimizes internally.
|
||||
virtual bool supports_bundle_fixed() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
IKCSolver() = default;
|
||||
|
||||
// Non-copyable, non-movable (polymorphic base class)
|
||||
IKCSolver(const IKCSolver&) = delete;
|
||||
IKCSolver& operator=(const IKCSolver&) = delete;
|
||||
IKCSolver(IKCSolver&&) = delete;
|
||||
IKCSolver& operator=(IKCSolver&&) = delete;
|
||||
};
|
||||
|
||||
} // namespace KCSolve
|
||||
|
||||
#endif // KCSOLVE_IKCSOLVER_H
|
||||
Reference in New Issue
Block a user