Program Listing for File pybind11ParamsCaster.h

Program Listing for File pybind11ParamsCaster.h#

Return to documentation for file (include/Karana/SOADyn/pybind11ParamsCaster.h)

/*
 * Copyright (c) 2024-2026 Karana Dynamics Pty Ltd. All rights reserved.
 *
 * NOTICE TO USER:
 *
 * This source code and/or documentation (the "Licensed Materials") is
 * the confidential and proprietary information of Karana Dynamics Inc.
 * Use of these Licensed Materials is governed by the terms and conditions
 * of a separate software license agreement between Karana Dynamics and the
 * Licensee ("License Agreement"). Unless expressly permitted under that
 * agreement, any reproduction, modification, distribution, or disclosure
 * of the Licensed Materials, in whole or in part, to any third party
 * without the prior written consent of Karana Dynamics is strictly prohibited.
 *
 * THE LICENSED MATERIALS ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
 * KARANA DYNAMICS DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
 * BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, AND
 * FITNESS FOR A PARTICULAR PURPOSE.
 *
 * IN NO EVENT SHALL KARANA DYNAMICS BE LIABLE FOR ANY DAMAGES WHATSOEVER,
 * INCLUDING BUT NOT LIMITED TO LOSS OF PROFITS, DATA, OR USE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, WHETHER IN CONTRACT, TORT,
 * OR OTHERWISE ARISING OUT OF OR IN CONNECTION WITH THE LICENSED MATERIALS.
 *
 * U.S. Government End Users: The Licensed Materials are a "commercial item"
 * as defined at 48 C.F.R. 2.101, and are provided to the U.S. Government
 * only as a commercial end item under the terms of this license.
 *
 * Any use of the Licensed Materials in individual or commercial software must
 * include, in the user documentation and internal source code comments,
 * this Notice, Disclaimer, and U.S. Government Use Provision.
 */

/**
 * @file
 * @brief Casters for *Params types.
 */

#pragma once

#include "Karana/SOADyn/PhysicalBody.h"
#include "Karana/SOADyn/PhysicalHinge.h"
#include "Karana/SOADyn/PhysicalSubhinge.h"
#include "Karana/SOADyn/PinSubhinge.h"
#include "Karana/Scene/SceneFileObjectSpec.h"
#include "Karana/Scene/ScenePartSpec.h"
#include <pybind11/eigen.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>

namespace py = pybind11;
namespace kd = Karana::Dynamics;
namespace kc = Karana::Core;
namespace km = Karana::Math;
namespace ks = Karana::Scene;
namespace Karana::Dynamics {

    /// The module to import Subhinge DataStructs from.
    constexpr std::string_view subhingeDSModule = "Karana.Dynamics._params";

    /**
     * @brief Get the Python subhinge DataStruct class associated with the given name.
     *
     * @param name The name of the DataStruct to get.
     * @return The Python class for the DataStruct.
     */
    inline py::object subhingeDSClass(const char *name) {
        return py::module_::import(subhingeDSModule.data()).attr(name);
    }

    /**
     * @brief Convert a Python subhinge DataStruct to its associated C++ parameter object.
     *
     * @param src The Python subhinge DataStruct
     * @return The associated C++ parameter object.
     */
    inline kc::ks_ptr<kd::PhysicalSubhingeParams> subhingeParamsFromDS(const py::handle &src) {
        if (py::isinstance(src, subhingeDSClass("PinSubhingeParams"))) {
            return kd::PinSubhingeParams::create(py::cast<km::Vec3>(src.attr("unit_axis")),
                                                 py::cast<bool>(src.attr("prescribed")),
                                                 py::cast<km::Vec>(src.attr("joint_limits")));
        }
        if (py::isinstance(src, subhingeDSClass("LinearSubhingeParams"))) {
            return kd::LinearSubhingeParams::create(py::cast<km::Vec3>(src.attr("unit_axis")),
                                                    py::cast<bool>(src.attr("prescribed")),
                                                    py::cast<km::Vec>(src.attr("joint_limits")));
        }
        if (py::isinstance(src, subhingeDSClass("ScrewSubhingeParams"))) {
            return kd::ScrewSubhingeParams::create(py::cast<km::Vec3>(src.attr("unit_axis")),
                                                   py::cast<double>(src.attr("pitch")),
                                                   py::cast<bool>(src.attr("prescribed")));
        }
        if (py::isinstance(src, subhingeDSClass("Linear3SubhingeParams"))) {
            return kd::Linear3SubhingeParams::create(py::cast<bool>(src.attr("prescribed")));
        }
        if (py::isinstance(src, subhingeDSClass("SphericalSubhingeParams"))) {
            return kd::SphericalSubhingeParams::create(py::cast<bool>(src.attr("prescribed")));
        }
        if (py::isinstance(src, subhingeDSClass("SphericalQuatSubhingeParams"))) {
            return kd::SphericalQuatSubhingeParams::create(py::cast<bool>(src.attr("prescribed")));
        }
        if (py::isinstance(src, subhingeDSClass("LockedPhysicalSubhingeParams"))) {
            return kd::PhysicalSubhingeParams::create();
        }

        return {};
    }

    /**
     * @brief Convert a C++ subhinge parameter to its associated Python DataStruct.
     *
     * @param src The C++ subhinge parameter
     * @return The associated Python DataStruct
     */
    inline py::object subhingeParamsToDS(const kc::ks_ptr<kd::PhysicalSubhingeParams> &src) {
        if (auto params = kc::dynamic_pointer_cast<kd::PinSubhingeParams>(src)) {
            return subhingeDSClass("PinSubhingeParams")(py::arg("unit_axis") = params->unit_axis,
                                                        py::arg("joint_limits") =
                                                            params->joint_limits,
                                                        py::arg("prescribed") = params->prescribed);
        }
        if (auto params = kc::dynamic_pointer_cast<kd::LinearSubhingeParams>(src)) {
            return subhingeDSClass("LinearSubhingeParams")(
                py::arg("unit_axis") = params->unit_axis,
                py::arg("joint_limits") = params->joint_limits,
                py::arg("prescribed") = params->prescribed);
        }
        if (auto params = kc::dynamic_pointer_cast<kd::ScrewSubhingeParams>(src)) {
            return subhingeDSClass("ScrewSubhingeParams")(py::arg("unit_axis") = params->unit_axis,
                                                          py::arg("pitch") = params->pitch,
                                                          py::arg("prescribed") =
                                                              params->prescribed);
        }
        if (auto params = kc::dynamic_pointer_cast<kd::Linear3SubhingeParams>(src)) {
            return subhingeDSClass("Linear3SubhingeParams")(py::arg("prescribed") =
                                                                params->prescribed);
        }
        if (auto params = kc::dynamic_pointer_cast<kd::SphericalSubhingeParams>(src)) {
            return subhingeDSClass("SphericalSubhingeParams")(py::arg("prescribed") =
                                                                  params->prescribed);
        }
        if (auto params = kc::dynamic_pointer_cast<kd::SphericalQuatSubhingeParams>(src)) {
            return subhingeDSClass("SphericalQuatSubhingeParams")(py::arg("prescribed") =
                                                                      params->prescribed);
        }
        return subhingeDSClass("LockedPhysicalSubhingeParams")();
    }

    /**
     * @brief Get the PhysicalBodyParams DataStruct class.
     *
     * @return The PhysicalBodyParams DataStruct class.
     */
    inline py::object physicalBodyParamsDSClass() {
        return py::module_::import("Karana.Dynamics._params").attr("PhysicalBodyParams");
    }

} // namespace Karana::Dynamics

namespace pybind11::detail {

    /** Python DataStruct name associated with a concrete subhinge parameter type. */
    template <typename Params> struct SubhingeParamsDSName;

    /** DataStruct associated with PinSubhingeParams. */
    template <> struct SubhingeParamsDSName<kd::PinSubhingeParams> {
        /**
         * @brief Python class for PinSubhingeParams
         */
        static constexpr auto value = const_name("Karana.Dynamics.SOADyn_types.PinSubhingeParams");
    };

    /** DataStruct associated with LinearSubhingeParams. */
    template <> struct SubhingeParamsDSName<kd::LinearSubhingeParams> {
        /**
         * @brief Python class for LinearSubhingeParams
         */
        static constexpr auto value =
            const_name("Karana.Dynamics.SOADyn_types.LinearSubhingeParams");
    };

    /** DataStruct associated with ScrewSubhingeParams. */
    template <> struct SubhingeParamsDSName<kd::ScrewSubhingeParams> {
        /**
         * @brief Python class for ScrewSubhingeParams
         */
        static constexpr auto value =
            const_name("Karana.Dynamics.SOADyn_types.ScrewSubhingeParams");
    };

    /** DataStruct associated with Linear3SubhingeParams. */
    template <> struct SubhingeParamsDSName<kd::Linear3SubhingeParams> {
        /**
         * @brief Python class for Linear3SubhingeParams
         */
        static constexpr auto value =
            const_name("Karana.Dynamics.SOADyn_types.Linear3SubhingeParams");
    };

    /** DataStruct associated with SphericalSubhingeParams. */
    template <> struct SubhingeParamsDSName<kd::SphericalSubhingeParams> {
        /**
         * @brief Python class for SphericalSubhingeParams
         */
        static constexpr auto value =
            const_name("Karana.Dynamics.SOADyn_types.SphericalSubhingeParams");
    };

    /** DataStruct associated with SphericalQuatSubhingeParams. */
    template <> struct SubhingeParamsDSName<kd::SphericalQuatSubhingeParams> {
        /**
         * @brief Python class for SphericalQuatSubhingeParams
         */
        static constexpr auto value =
            const_name("Karana.Dynamics.SOADyn_types.SphericalQuatSubhingeParams");
    };

    /**
     * @brief General type caster that can be used on all concerte PhysicalSubhingeParams subtypes.
     */
    template <typename Params> struct ConcreteSubhingeParamsCaster {
        /// The type this pybind11 caster uses
        using T = kc::ks_ptr<Params>;

        /// A pybind11 caster for PhysicalSubhingeParams subtypes
        PYBIND11_TYPE_CASTER(T, SubhingeParamsDSName<Params>::value);

        /**
         * @brief Load a C++ PhysicalSubhingeParams subtype from its Python DataStruct
         *
         * @param src The associated Python DataStruct
         * @return The C++ PhysicalSubhingeParams subtype associated with the DataStruct
         */
        bool load(handle src, bool) {
            value = kc::dynamic_pointer_cast<Params>(Karana::Dynamics::subhingeParamsFromDS(src));
            return static_cast<bool>(value);
        }

        /**
         * @brief Convert a C++ PhysicalSubhingeParams subtype to its Python DataStruct
         *
         * @param src A C++ PhysicalSubhingeParams subtype
         * @return The associated Python DataStruct
         */
        static handle cast(const T &src, return_value_policy, handle) {
            if (!src) {
                return py::none().release();
            }
            return Karana::Dynamics::subhingeParamsToDS(src).release();
        }
    };

    /**
     * @brief Convert PhysicalSubhingeParams C++ objects to/from Python PhysicalSubhingeParams
     * DataStruct objects.
     */
    template <> struct type_caster<kc::ks_ptr<kd::PhysicalSubhingeParams>> {
        /// Typedef for the type used by this type caster
        using T = kc::ks_ptr<kd::PhysicalSubhingeParams>;

        /// pybind11 type caster for PhysicalSubhingeParams
        PYBIND11_TYPE_CASTER(T, const_name("Karana.Dynamics.SOADyn_types.PhysicalSubhingeParams"));

        /** Load a C++ parameter pointer from a Python PhysicalSubhingeParams. */
        /**
         * @brief Load a C++ PhysicalSubhingeParams from a Python PhysicalSubhingeParams DataStruct.
         *
         * @param src The Python PhysicalSubhingeParams DataStruct
         * @return A C++ PhysicalSubhingeParams
         */
        bool load(handle src, bool) {
            value = Karana::Dynamics::subhingeParamsFromDS(src);
            return static_cast<bool>(value);
        }

        /** Cast a C++ parameter pointer to its concrete Python PhysicalSubhingeParams. */
        /**
         * @brief Cast a C++ PhysicalSubhingeParams to the Python PhysicalSubhingeParams DataStruct.
         *
         * @param src The C++ PhysicalSubhingeParams
         * @return The Python PhysicalSubhingeParams DataStruct
         */
        static handle cast(const T &src, return_value_policy, handle) {
            if (!src) {
                return py::none().release();
            }
            return subhingeParamsToDS(src).release();
        }
    };

    /** Convert PinSubhingeParams shared pointers to and from PinSubhingeParams objects. */
    template <>
    struct type_caster<kc::ks_ptr<kd::PinSubhingeParams>>
        : ConcreteSubhingeParamsCaster<kd::PinSubhingeParams> {};

    /** Convert LinearSubhingeParams shared pointers to and from LinearSubhingeParams objects. */
    template <>
    struct type_caster<kc::ks_ptr<kd::LinearSubhingeParams>>
        : ConcreteSubhingeParamsCaster<kd::LinearSubhingeParams> {};

    /** Convert ScrewSubhingeParams shared pointers to and from ScrewSubhingeParams objects. */
    template <>
    struct type_caster<kc::ks_ptr<kd::ScrewSubhingeParams>>
        : ConcreteSubhingeParamsCaster<kd::ScrewSubhingeParams> {};

    /** Convert Linear3SubhingeParams shared pointers to and from Linear3SubhingeParams objects. */
    template <>
    struct type_caster<kc::ks_ptr<kd::Linear3SubhingeParams>>
        : ConcreteSubhingeParamsCaster<kd::Linear3SubhingeParams> {};

    /** Convert SphericalSubhingeParams pointers to and from SphericalSubhingeParams objects. */
    template <>
    struct type_caster<kc::ks_ptr<kd::SphericalSubhingeParams>>
        : ConcreteSubhingeParamsCaster<kd::SphericalSubhingeParams> {};

    /** Convert quaternion spherical parameter pointers to and from their DataStructs. */
    template <>
    struct type_caster<kc::ks_ptr<kd::SphericalQuatSubhingeParams>>
        : ConcreteSubhingeParamsCaster<kd::SphericalQuatSubhingeParams> {};

    /**
     * @brief Caster to convert C++ PhysicalHingeParams to and from Python PhysicalHingeParams
     * DataStruct objects.
     */
    template <> struct type_caster<kd::PhysicalHingeParams> {
        /// Pybind11 caster for PhysicalHingeParams
        PYBIND11_TYPE_CASTER(kd::PhysicalHingeParams,
                             const_name("Karana.Dynamics.SOADyn_types.PhysicalHingeParams"));

        /**
         * @brief Load a C++ PhysicalHingeParams from a Python PhysicalHingeParams DataStruct.
         *
         * @param src The Python PhysicalHingeParams DataStruct
         * @return The C++ PhysicalHingeParams
         */
        bool load(handle src, bool) {
            if (src.is_none()) {
                value = {};
                return true;
            }

            py::object hinge_ds =
                py::module_::import("Karana.Dynamics.SOADyn_types").attr("PhysicalHingeParams");
            if (!py::isinstance(src, hinge_ds)) {
                return false;
            }

            value = kd::PhysicalHingeParams{
                py::cast<kd::HingeType>(src.attr("hinge_type")),
                py::cast<std::vector<kc::ks_ptr<kd::PhysicalSubhingeParams>>>(
                    src.attr("subhinge_params"))};
            return true;
        }

        /** Cast PhysicalHingeParams to a Python PhysicalHingeParams. */
        /**
         * @brief Cast PhysicalHingeParams to a Python PhysicalHingeParams DataStruct.
         *
         * @param src The C++ PhysicalHingeParams
         * @return The Python PhysicalHingeParams DataStruct
         */
        static handle cast(const kd::PhysicalHingeParams &src, return_value_policy, handle) {
            py::object hinge_ds =
                py::module_::import("Karana.Dynamics.SOADyn_types").attr("PhysicalHingeParams");
            py::object result = hinge_ds(py::arg("hinge_type") = src.hinge_type,
                                         py::arg("subhinge_params") = src.subhinge_params);
            return result.release();
        }
    };

    /** Convert PhysicalBodyParams values to and from Python PhysicalBodyParams DataStructs. */
    template <> struct type_caster<kd::PhysicalBodyParams> {
        // We use an std::optional in this type caster because PhysicalBodyParms is not default
        // constructable
        /// Type caster to use to convert PhysicalBodyParams
        PYBIND11_TYPE_CASTER(std::optional<kd::PhysicalBodyParams>,
                             const_name("Karana.Dynamics._params.PhysicalBodyParams"));

        // Add conversions to PhysicalBodyParams types from value
        /**
         * @brief Get a PhysicalBodyParams reference from this caster.
         *
         * @return A PhysicalBodyParams reference.
         */
        operator const kd::PhysicalBodyParams &() const { return *value; }

        /**
         * @brief Get a PhysicalBodyParams reference from this caster.
         *
         * @return A PhysicalBodyParams reference.
         */
        operator kd::PhysicalBodyParams &() { return *value; }

        /**
         * @brief Load PhysicalBodyParams from its Python DataStruct.
         *
         * @param src The Python DataStruct to turn into PhysicalBodyParams.
         * @return The PhysicalBodyParams created from the DataStruct.
         */
        bool load(handle src, bool) {
            py::object cls = kd::physicalBodyParamsDSClass();
            if (!py::isinstance(src, cls)) {
                return false;
            }

            value.emplace(py::cast<km::SpatialInertia>(src.attr("spatial_inertia")),
                          py::cast<km::HomTran>(src.attr("body_to_joint_transform")),
                          py::cast<kd::PhysicalHingeParams>(src.attr("hinge_params")),
                          py::cast<kc::ks_ptr<kd::ParentBodyAttachmentParams>>(
                              src.attr("parent_body_attachment_params")),
                          py::cast<std::vector<ks::ScenePartSpec>>(src.attr("scene_part_specs")),
                          py::cast<std::vector<ks::SceneFileObjectSpec>>(
                              src.attr("scene_file_object_specs")));
            return true;
        }

        /**
         * @brief Cast PhysicalBodyParams to its Python DataStruct.
         *
         * @param src The C++ PhysicalBodyParams to turn into the Python DataStruct.
         * @return The Python DataStruct associated with the C++ object.
         */
        static handle cast(const kd::PhysicalBodyParams &src, return_value_policy, handle) {
            py::object result = kd::physicalBodyParamsDSClass()(
                py::arg("spatial_inertia") = src.spatial_inertia,
                py::arg("body_to_joint_transform") = src.body_to_joint_transform,
                py::arg("hinge_params") = src.hinge_params,
                py::arg("parent_body_attachment_params") = src.parent_body_attachment_params,
                py::arg("scene_part_specs") = src.scene_part_specs,
                py::arg("scene_file_object_specs") = src.scene_file_object_specs);
            return result.release();
        }
    };

} // namespace pybind11::detail