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:
@@ -6,6 +6,7 @@ set(Assembly_LIBS
|
||||
Spreadsheet
|
||||
FreeCADApp
|
||||
OndselSolver
|
||||
KCSolve
|
||||
)
|
||||
|
||||
generate_from_py(AssemblyObject)
|
||||
|
||||
@@ -11,6 +11,7 @@ else ()
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
add_subdirectory(Solver)
|
||||
add_subdirectory(App)
|
||||
|
||||
if(BUILD_GUI)
|
||||
|
||||
12
src/Mod/Assembly/Solver/CMakeLists.txt
Normal file
12
src/Mod/Assembly/Solver/CMakeLists.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
# Phase 1a: header-only INTERFACE library.
|
||||
# Phase 1b will convert to SHARED when .cpp files are added.
|
||||
|
||||
add_library(KCSolve INTERFACE)
|
||||
|
||||
target_include_directories(KCSolve
|
||||
INTERFACE
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_BINARY_DIR}/src
|
||||
)
|
||||
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
|
||||
164
src/Mod/Assembly/Solver/SolverRegistry.h
Normal file
164
src/Mod/Assembly/Solver/SolverRegistry.h
Normal file
@@ -0,0 +1,164 @@
|
||||
// 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_SOLVERREGISTRY_H
|
||||
#define KCSOLVE_SOLVERREGISTRY_H
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "IKCSolver.h"
|
||||
|
||||
namespace KCSolve
|
||||
{
|
||||
|
||||
/// Factory function that creates a solver instance.
|
||||
using CreateSolverFn = std::function<std::unique_ptr<IKCSolver>()>;
|
||||
|
||||
/// Singleton registry for pluggable solver backends.
|
||||
///
|
||||
/// Solver plugins register themselves at module load time via
|
||||
/// register_solver(). The Assembly module retrieves solvers via get().
|
||||
///
|
||||
/// Thread safety: all public methods are internally synchronized.
|
||||
///
|
||||
/// Usage:
|
||||
/// // Registration (at module init):
|
||||
/// KCSolve::SolverRegistry::instance().register_solver(
|
||||
/// "ondsel", []() { return std::make_unique<OndselAdapter>(); });
|
||||
///
|
||||
/// // Retrieval:
|
||||
/// auto solver = KCSolve::SolverRegistry::instance().get(); // default
|
||||
/// auto solver = KCSolve::SolverRegistry::instance().get("ondsel");
|
||||
|
||||
class SolverRegistry
|
||||
{
|
||||
public:
|
||||
/// Access the singleton instance.
|
||||
static SolverRegistry& instance()
|
||||
{
|
||||
static SolverRegistry reg;
|
||||
return reg;
|
||||
}
|
||||
|
||||
/// Register a solver backend.
|
||||
/// @param name Unique solver name (e.g. "ondsel").
|
||||
/// @param factory Factory function that creates solver instances.
|
||||
/// @return true if registration succeeded, false if name taken.
|
||||
bool register_solver(const std::string& name, CreateSolverFn factory)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
auto [it, inserted] = factories_.emplace(name, std::move(factory));
|
||||
if (inserted && default_name_.empty()) {
|
||||
default_name_ = name; // first registered becomes default
|
||||
}
|
||||
return inserted;
|
||||
}
|
||||
|
||||
/// Create an instance of the named solver.
|
||||
/// @param name Solver name. If empty, uses the default solver.
|
||||
/// @return Solver instance, or nullptr if not found.
|
||||
std::unique_ptr<IKCSolver> get(const std::string& name = {}) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
const std::string& key = name.empty() ? default_name_ : name;
|
||||
if (key.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto it = factories_.find(key);
|
||||
if (it == factories_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return it->second();
|
||||
}
|
||||
|
||||
/// Return the names of all registered solvers.
|
||||
std::vector<std::string> available() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
std::vector<std::string> names;
|
||||
names.reserve(factories_.size());
|
||||
for (const auto& [name, _] : factories_) {
|
||||
names.push_back(name);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
/// Query which BaseJointKind values a named solver supports.
|
||||
/// Creates a temporary instance to call supported_joints().
|
||||
std::vector<BaseJointKind> joints_for(const std::string& name) const
|
||||
{
|
||||
auto solver = get(name);
|
||||
if (!solver) {
|
||||
return {};
|
||||
}
|
||||
return solver->supported_joints();
|
||||
}
|
||||
|
||||
/// Set the default solver name.
|
||||
/// @return true if the name is registered, false otherwise.
|
||||
bool set_default(const std::string& name)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (factories_.find(name) == factories_.end()) {
|
||||
return false;
|
||||
}
|
||||
default_name_ = name;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Get the default solver name.
|
||||
std::string get_default() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return default_name_;
|
||||
}
|
||||
|
||||
/// Scan a directory for solver plugins (Phase 1b).
|
||||
/// Currently a no-op placeholder. Will dlopen/LoadLibrary shared
|
||||
/// objects that export kcsolve_create() / kcsolve_api_version().
|
||||
void scan(const std::string& /*directory*/)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
SolverRegistry() = default;
|
||||
~SolverRegistry() = default;
|
||||
|
||||
SolverRegistry(const SolverRegistry&) = delete;
|
||||
SolverRegistry& operator=(const SolverRegistry&) = delete;
|
||||
SolverRegistry(SolverRegistry&&) = delete;
|
||||
SolverRegistry& operator=(SolverRegistry&&) = delete;
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
std::unordered_map<std::string, CreateSolverFn> factories_;
|
||||
std::string default_name_;
|
||||
};
|
||||
|
||||
} // namespace KCSolve
|
||||
|
||||
#endif // KCSOLVE_SOLVERREGISTRY_H
|
||||
286
src/Mod/Assembly/Solver/Types.h
Normal file
286
src/Mod/Assembly/Solver/Types.h
Normal file
@@ -0,0 +1,286 @@
|
||||
// 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_TYPES_H
|
||||
#define KCSOLVE_TYPES_H
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace KCSolve
|
||||
{
|
||||
|
||||
// ── Transform ──────────────────────────────────────────────────────
|
||||
//
|
||||
// Rigid-body transform: position (x, y, z) + unit quaternion (w, x, y, z).
|
||||
// Semantically equivalent to Base::Placement but free of FreeCAD dependencies
|
||||
// so that KCSolve headers remain standalone (for future server worker use).
|
||||
//
|
||||
// Quaternion convention: (w, x, y, z) — mathematical standard.
|
||||
// Note: Base::Rotation(q0,q1,q2,q3) uses (x, y, z, w) ordering.
|
||||
// The adapter layer handles this swap.
|
||||
|
||||
struct Transform
|
||||
{
|
||||
std::array<double, 3> position {0.0, 0.0, 0.0};
|
||||
std::array<double, 4> quaternion {1.0, 0.0, 0.0, 0.0}; // w, x, y, z
|
||||
|
||||
static Transform identity()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
// ── BaseJointKind ──────────────────────────────────────────────────
|
||||
//
|
||||
// Decomposed primitive constraint types. Uses SOLIDWORKS-inspired vocabulary
|
||||
// from the INTER_SOLVER.md spec rather than OndselSolver internal names.
|
||||
//
|
||||
// The existing Assembly::JointType (13 values) and Assembly::DistanceType
|
||||
// (35+ values) map to these via the adapter layer. In particular, the
|
||||
// "Distance" JointType is decomposed based on geometry classification
|
||||
// (see makeMbdJointDistance in AssemblyObject.cpp).
|
||||
|
||||
enum class BaseJointKind : std::uint8_t
|
||||
{
|
||||
// Point constraints (decomposed from JointType::Distance)
|
||||
Coincident, // PointOnPoint, d=0 — 3 DOF removed
|
||||
PointOnLine, // Point constrained to a line — 2 DOF removed
|
||||
PointInPlane, // Point constrained to a plane — 1 DOF removed
|
||||
|
||||
// Axis/surface constraints (decomposed from JointType::Distance)
|
||||
Concentric, // Coaxial (line-line, circle-circle, cyl-cyl) — 4 DOF removed
|
||||
Tangent, // Face-on-face tangency — 1 DOF removed
|
||||
Planar, // Coplanar faces — 3 DOF removed
|
||||
LineInPlane, // Line constrained to a plane — 2 DOF removed
|
||||
|
||||
// Axis orientation constraints (direct from JointType)
|
||||
Parallel, // Parallel axes — 2 DOF removed
|
||||
Perpendicular, // 90-degree axes — 1 DOF removed
|
||||
Angle, // Arbitrary axis angle — 1 DOF removed
|
||||
|
||||
// Standard kinematic joints (direct 1:1 from JointType)
|
||||
Fixed, // Rigid weld — 6 DOF removed
|
||||
Revolute, // Hinge — 5 DOF removed
|
||||
Cylindrical, // Rotation + sliding on axis — 4 DOF removed
|
||||
Slider, // Linear translation — 5 DOF removed
|
||||
Ball, // Spherical — 3 DOF removed
|
||||
Screw, // Helical (rotation + coupled translation) — 5 DOF removed
|
||||
Universal, // U-joint / Cardan — 4 DOF removed (future)
|
||||
|
||||
// Mechanical element constraints
|
||||
Gear, // Gear pair or belt (sign determines direction)
|
||||
RackPinion, // Rack-and-pinion
|
||||
Cam, // Cam-follower (future)
|
||||
Slot, // Slot constraint (future)
|
||||
|
||||
// Distance variants with non-zero offset
|
||||
DistancePointPoint, // Point-to-point with offset — 2 DOF removed
|
||||
DistanceCylSph, // Cylinder-sphere distance — varies
|
||||
|
||||
Custom, // Solver-specific extension point
|
||||
};
|
||||
|
||||
// ── Part ───────────────────────────────────────────────────────────
|
||||
|
||||
struct Part
|
||||
{
|
||||
std::string id;
|
||||
Transform placement;
|
||||
double mass {1.0};
|
||||
bool grounded {false};
|
||||
};
|
||||
|
||||
// ── Constraint ─────────────────────────────────────────────────────
|
||||
//
|
||||
// A constraint between two parts. Built from a FreeCAD JointObject by
|
||||
// the adapter layer (classifying geometry into the specific BaseJointKind).
|
||||
|
||||
struct Constraint
|
||||
{
|
||||
std::string id; // FreeCAD document object name (e.g. "Joint001")
|
||||
|
||||
std::string part_i; // solver-side part ID for first reference
|
||||
Transform marker_i; // coordinate system on part_i
|
||||
|
||||
std::string part_j; // solver-side part ID for second reference
|
||||
Transform marker_j; // coordinate system on part_j
|
||||
|
||||
BaseJointKind type {};
|
||||
|
||||
// Scalar parameters (interpretation depends on type):
|
||||
// Angle: params[0] = angle in radians
|
||||
// RackPinion: params[0] = pitch radius
|
||||
// Screw: params[0] = pitch
|
||||
// Gear: params[0] = radiusI, params[1] = radiusJ (negative for belt)
|
||||
// DistancePointPoint: params[0] = distance
|
||||
// DistanceCylSph: params[0] = distance
|
||||
// Planar: params[0] = offset
|
||||
// Concentric: params[0] = distance
|
||||
// PointInPlane: params[0] = offset
|
||||
// LineInPlane: params[0] = offset
|
||||
std::vector<double> params;
|
||||
|
||||
// Joint limits (length or angle bounds)
|
||||
struct Limit
|
||||
{
|
||||
enum class Kind : std::uint8_t
|
||||
{
|
||||
TranslationMin,
|
||||
TranslationMax,
|
||||
RotationMin,
|
||||
RotationMax,
|
||||
};
|
||||
|
||||
Kind kind {};
|
||||
double value {0.0};
|
||||
double tolerance {1.0e-9};
|
||||
};
|
||||
std::vector<Limit> limits;
|
||||
|
||||
bool activated {true};
|
||||
};
|
||||
|
||||
// ── MotionDef ──────────────────────────────────────────────────────
|
||||
//
|
||||
// A motion driver for kinematic simulation.
|
||||
|
||||
struct MotionDef
|
||||
{
|
||||
enum class Kind : std::uint8_t
|
||||
{
|
||||
Rotational,
|
||||
Translational,
|
||||
General,
|
||||
};
|
||||
|
||||
Kind kind {};
|
||||
std::string joint_id; // which constraint this drives
|
||||
std::string marker_i;
|
||||
std::string marker_j;
|
||||
|
||||
// Motion law expressions (function of time 't').
|
||||
// For General: both are set. Otherwise only the relevant one.
|
||||
std::string rotation_expr;
|
||||
std::string translation_expr;
|
||||
};
|
||||
|
||||
// ── SimulationParams ───────────────────────────────────────────────
|
||||
//
|
||||
// Parameters for kinematic simulation (run_kinematic).
|
||||
// Maps to create_mbdSimulationParameters() in AssemblyObject.cpp.
|
||||
|
||||
struct SimulationParams
|
||||
{
|
||||
double t_start {0.0};
|
||||
double t_end {1.0};
|
||||
double h_out {0.01}; // output time step
|
||||
double h_min {1.0e-9};
|
||||
double h_max {1.0};
|
||||
double error_tol {1.0e-6};
|
||||
};
|
||||
|
||||
// ── SolveContext ───────────────────────────────────────────────────
|
||||
//
|
||||
// Complete input to a solve operation. Built by the adapter layer
|
||||
// from FreeCAD document objects.
|
||||
|
||||
struct SolveContext
|
||||
{
|
||||
std::vector<Part> parts;
|
||||
std::vector<Constraint> constraints;
|
||||
std::vector<MotionDef> motions;
|
||||
|
||||
// Present when running kinematic simulation via run_kinematic().
|
||||
std::optional<SimulationParams> simulation;
|
||||
|
||||
// Hint: bundle parts connected by Fixed joints into single rigid bodies.
|
||||
// When true and the solver does not support_bundle_fixed(), the adapter
|
||||
// layer pre-bundles before passing to the solver.
|
||||
bool bundle_fixed {false};
|
||||
};
|
||||
|
||||
// ── SolveStatus ────────────────────────────────────────────────────
|
||||
//
|
||||
// Matches the return codes from AssemblyObject::solve().
|
||||
|
||||
enum class SolveStatus : std::int8_t
|
||||
{
|
||||
Success = 0,
|
||||
Failed = -1,
|
||||
InvalidFlip = -2, // orientation flipped past threshold
|
||||
NoGroundedParts = -6, // no grounded parts in assembly
|
||||
};
|
||||
|
||||
// ── ConstraintDiagnostic ───────────────────────────────────────────
|
||||
//
|
||||
// Per-constraint diagnostic information from updateSolveStatus().
|
||||
|
||||
struct ConstraintDiagnostic
|
||||
{
|
||||
enum class Kind : std::uint8_t
|
||||
{
|
||||
Redundant,
|
||||
Conflicting,
|
||||
PartiallyRedundant,
|
||||
Malformed,
|
||||
};
|
||||
|
||||
std::string constraint_id; // FreeCAD object name
|
||||
Kind kind {};
|
||||
std::string detail; // human-readable description
|
||||
};
|
||||
|
||||
// ── SolveResult ────────────────────────────────────────────────────
|
||||
//
|
||||
// Output of a solve operation.
|
||||
|
||||
struct SolveResult
|
||||
{
|
||||
SolveStatus status {SolveStatus::Success};
|
||||
|
||||
// Updated placements for each part (only parts that moved).
|
||||
struct PartResult
|
||||
{
|
||||
std::string id;
|
||||
Transform placement;
|
||||
};
|
||||
std::vector<PartResult> placements;
|
||||
|
||||
// Degrees of freedom remaining (-1 = unknown).
|
||||
int dof {-1};
|
||||
|
||||
// Constraint diagnostics (redundant, conflicting, etc.).
|
||||
std::vector<ConstraintDiagnostic> diagnostics;
|
||||
|
||||
// For kinematic simulation: number of computed frames.
|
||||
std::size_t num_frames {0};
|
||||
};
|
||||
|
||||
} // namespace KCSolve
|
||||
|
||||
#endif // KCSOLVE_TYPES_H
|
||||
Reference in New Issue
Block a user