Karana.Collision
================

.. py:module:: Karana.Collision

.. autoapi-nested-parse::

   Models and classes for collision detection and contact force calculation.



Submodules
----------

.. toctree::
   :maxdepth: 1

   /generated/python_api/Karana/Collision/Collision_types/index


Classes
-------

.. autoapisummary::

   Karana.Collision.ContactForceBase
   Karana.Collision.ContactForceManager
   Karana.Collision.DampedContactForce
   Karana.Collision.DampedContactForceParams
   Karana.Collision.DampedContactForceScratch
   Karana.Collision.FrameCollider
   Karana.Collision.FrameContact
   Karana.Collision.HuntCrossleyContactForce
   Karana.Collision.HuntCrossleyContactForceParams
   Karana.Collision.HuntCrossleyContactForceScratch
   Karana.Collision.NonlinearContactForce
   Karana.Collision.NonlinearContactForceParams
   Karana.Collision.NonlinearContactForceScratch


Package Contents
----------------

.. py:class:: ContactForceBase

   Bases: :py:obj:`Karana.Core.Base`


   Base class for all contact force models.

   See :ref:`collision_dynamics_sec` for more discussion on contact and
   collision dynamics.


   .. py:method:: applyForce(contact: FrameContact, st: Karana.Dynamics.SubTree) -> tuple[Karana.Dynamics.Node, Karana.Dynamics.Node]

      Apply the contact force for the associated contact.

      This first calls computeForce to get the contact force. Then, it
      applies it to frames if those frames belong to a body.

      :param contact: The contact to apply the force for.
      :param st: The SubTree we are computing forces for.

      :returns: A pair of contact force nodes at which we applied the force.



   .. py:method:: toDS() -> Karana.Collision.Collision_types.ContactForceBaseDS

      Create a ContactForceBaseDS from this ContactForceBase model.

      :returns: * *ContactForceBaseDS*
                * *A ContactForceBaseDS instance with values set to match this model.*



.. py:class:: ContactForceManager(name: str)

   Bases: :py:obj:`ContactForceBase`


   A ContactForceBase that delegates based on the Frame pair

   See :ref:`collision_dynamics_sec` for more discussion on contact and
   collision dynamics.


   .. py:method:: create(name: str) -> ContactForceManager
      :staticmethod:


      Create an instance of ContactForceManager.

      :param name: The name for the ContactForceManager instance.

      :returns: A ks_ptr to the newly created ContactForceManager.



   .. py:method:: clearDelegates() -> None

      Clear out all registered ContactForceBase instances



   .. py:method:: getDelegate(contact: FrameContact) -> ContactForceBase
                  getDelegate() -> ContactForceBase
                  getDelegate(frame1: Karana.Frame.Frame, frame2: Karana.Frame.Frame, strict: bool = False) -> ContactForceBase

      Get the most specific matching ContactForceBase

      :param contact: The contact to get the delegate for.

      :returns: The ContactForceBase (may be nullptr)



   .. py:method:: setDelegate(force: ContactForceBase = None) -> None
                  setDelegate(frame1: Karana.Frame.Frame, frame2: Karana.Frame.Frame, force: ContactForceBase = None) -> None

      Set the ContactForceBase used by default

      :param force: The ContactForceBase (may be nullptr)



.. py:class:: DampedContactForce(name: str)

   Bases: :py:obj:`ContactForceBase`


   ContactForceBase implementation using a Hunt-Crossley model

   Based on https://chatgpt.com/s/t_6955d6cac734819194154f53251a0038

   See :ref:`collision_dynamics_sec` for more discussion on contact and
   collision dynamics.


   .. py:method:: create(name: str) -> DampedContactForce
      :staticmethod:


      Create an instance of the DampedContactForce contact force model.

      :param name: The name for the DampedContactForce instance.

      :returns: A ks_ptr to the newly created DampedContactForce contact force
                model.



   .. py:property:: params
      :type: DampedContactForceParams


      Parameters used for the contact model.


   .. py:property:: scratch
      :type: DampedContactForceScratch


      Scratch values


.. py:class:: DampedContactForceParams

   Structure that holds the DampedContactForce contact force parameters.


   .. py:property:: dmax
      :type: float


      DMAx - max penetration value where damping stops increasing


   .. py:property:: e
      :type: float


      Damping exponent (default cubic dependency)


   .. py:property:: kc
      :type: float


      Normal penetration restitution/damping coefficient


   .. py:property:: kp
      :type: float


      Normal penetration stiffness coefficient


   .. py:property:: linear_region_tol
      :type: float


      Tolerance at which friction is linearly interpolated between the real
      value and 0.

      To avoid jittering for cases like rolling without friction, near zero,
      the friction force uses a linear interpolation between the full
      friction force and zero. This avoids rapid, large changes in the
      friction force as the velocity changes sign, and instead, smoothly,
      linearly interpolates between these large forces over a region of
      linear_region_tol * 2.


   .. py:property:: mu
      :type: float


      Friction coefficient


   .. py:property:: n
      :type: float


      Exponent


.. py:class:: DampedContactForceScratch

   Structure that holds the DampedContactForce scratch data


   .. py:property:: C
      :type: float


      effecting damping coefficient, C = kd * (pen/dmax)^e (pen < dmax)


   .. py:property:: f
      :type: Annotated[numpy.typing.NDArray[numpy.float64], [3, 1]]


      contact force


   .. py:property:: linvel
      :type: Annotated[numpy.typing.NDArray[numpy.float64], [3, 1]]


      contact linear velocity


   .. py:property:: penetration
      :type: float


      Normal penetration


.. py:class:: FrameCollider(proxy_scene: Karana.Scene.ProxyScene, collision_scene: Karana.Scene.CollisionScene)

   Helper to bridge CollisionScene collisions to Frames

   See :ref:`collision_dynamics_sec` for more discussion on contact and
   collision dynamics.


   .. py:method:: clearIgnoredFramePairs() -> None

      Stop ignoring any frame-pair collisions



   .. py:method:: collide(col_func: collections.abc.Callable[[FrameContact], None], filter_func: collections.abc.Callable[[Karana.Scene.CollisionScenePart, Karana.Scene.CollisionScenePart], bool] = None) -> None

      Process all collisions

      Sweeps through all contacts between geometries attached to Frames,
      calling the collision handler for each unfiltered contact.

      :param collision_handler:
                                - Callback to handle a contact
      :param filter:
                     - Callback to skip potential collisions in the broadphase.



   .. py:method:: dump(prefix: str = '') -> None

      Print dumpString on std::cout.

      :param prefix: A string to use as prefix for each output line



   .. py:method:: dumpString(prefix: str = '') -> str

      Return information about the object.

      :param prefix: A string to use as prefix for each output line

      :returns: String with the information about the object.



   .. py:method:: getCollisionScene() -> Karana.Scene.CollisionScene

      Return the CollisionScene used by this FrameCollider.

      :returns: The CollisionScene used by this FrameCollider.



   .. py:method:: getProxyScene() -> Karana.Scene.ProxyScene

      Return the ProxyScene used by this FrameCollider.

      :returns: The ProxyScene used by this FrameCollider.



   .. py:method:: ignoreAllCurrentlyTouchingPairs() -> None

      Call ignoreFramePair for each pair with a collision



   .. py:method:: ignoreFramePair(frame1: Karana.Frame.Frame, frame2: Karana.Frame.Frame) -> None

      Ignore collisions between a pair of frames

      :param frame1: The first frame in the pair
      :param frame2: The second frame in the pair



   .. py:method:: lookupPartFrame(part: Karana.Scene.CollisionScenePart) -> Karana.Frame.Frame
                  lookupPartFrame(id: SupportsInt | SupportsIndex) -> Karana.Frame.Frame

      Get the Frame that a CollisionScenePart is attached to

      :param part:
                   - A CollisionScenePart managed by ProxyScene

      :returns: The ancestor Frame for the part.



   .. py:method:: toDS() -> Karana.Collision.Collision_types.FrameColliderDS

      Create a FrameColliderDS from this FrameCollider model.

      :returns: * *FrameColliderDS*
                * *A FrameColliderDS instance with values set to match this model.*



   .. py:method:: unignoreFramePair(frame1: Karana.Frame.Frame, frame2: Karana.Frame.Frame) -> None

      Stop ignoring collisions between a pair of frames

      :param frame1: The first frame in the pair
      :param frame2: The second frame in the pair



.. py:class:: FrameContact

   Data for a contact between a pair of Frames


   .. py:property:: frame_1
      :type: Karana.Frame.Frame


      The first Frame involved in the contact


   .. py:property:: frame_2
      :type: Karana.Frame.Frame


      The second Frame involved in the contact


   .. py:property:: location_1
      :type: Karana.Math.Ktyping.Length[Karana.Math.Ktyping.Vec3]


      Contact location on the first Frame relative to the first Frame and
      expressed in the first frame


   .. py:property:: location_2
      :type: Karana.Math.Ktyping.Length[Karana.Math.Ktyping.Vec3]


      Contact location on the second Frame relative to the second Frame and
      expressed in the second frame


   .. py:property:: normal_1
      :type: Annotated[numpy.typing.NDArray[numpy.float64], [3, 1]]


      Contact normal expressed first Frame


   .. py:property:: normal_2
      :type: Annotated[numpy.typing.NDArray[numpy.float64], [3, 1]]


      Contact normal expressed second Frame


   .. py:property:: penetration
      :type: Karana.Math.Ktyping.Length[float]


      Maximum penetration distance


.. py:class:: HuntCrossleyContactForce(name: str)

   Bases: :py:obj:`ContactForceBase`


   ContactForceBase implementation using a Hunt-Crossley model

   See :ref:`collision_dynamics_sec` for more discussion on contact and
   collision dynamics.


   .. py:method:: create(name: str) -> HuntCrossleyContactForce
      :staticmethod:


      Create an instance of the HuntCrossleyContactForce contact force
      model.

      :param name: The name for the HuntCrossleyContactForce instance.

      :returns: A ks_ptr to the newly created HuntCrossleyContactForce contact
                force model.



   .. py:method:: toDS() -> Karana.Collision.Collision_types.HuntCrossleyContactForceDS

      Create a HuntCrossleyContactForceDS from this HuntCrossleyContactForce model.

      :returns: * *HuntCrossleyContactForceDS*
                * *A HuntCrossleyContactForceDS instance with values set to match this model.*



   .. py:property:: params
      :type: HuntCrossleyContactForceParams


      Parameters used for the contact model.


   .. py:property:: scratch
      :type: HuntCrossleyContactForceScratch


      Scratch values


.. py:class:: HuntCrossleyContactForceParams

   Structure that holds the HuntCrossleyContactForce contact force
   parameters.


   .. py:property:: dissipation
      :type: float


      Hunt-Crossley dissipation ratio


   .. py:property:: kp
      :type: float


      Normal penetration stiffness coefficient


   .. py:property:: linear_region_tol
      :type: float


      Tolerance at which friction is linearly interpolated between the real
      value and 0.

      To avoid jittering for cases like rolling without friction, near zero,
      the friction force uses a linear interpolation between the full
      friction force and zero. This avoids rapid, large changes in the
      friction force as the velocity changes sign, and instead, smoothly,
      linearly interpolates between these large forces over a region of
      linear_region_tol * 2.


   .. py:property:: mu
      :type: float


      Friction coefficient


   .. py:property:: n
      :type: float


      Exponent


.. py:class:: HuntCrossleyContactForceScratch

   Structure that holds the HuntCrossleyContactForce scratch data


   .. py:property:: f
      :type: Annotated[numpy.typing.NDArray[numpy.float64], [3, 1]]


      contact force


   .. py:property:: linvel
      :type: Annotated[numpy.typing.NDArray[numpy.float64], [3, 1]]


      contact linear velocity


   .. py:property:: penetration
      :type: float


      Normal penetration


.. py:class:: NonlinearContactForce(name: str)

   Bases: :py:obj:`ContactForceBase`


   Nonlinear penalty contact force with smoothly regularized damping and
   friction.

   With penetration :math:`\delta`, signed closing speed :math:`v_n`,
   stiffness :math:`k_p`, exponent :math:`n`, damping :math:`k_c`, and
   full-damping depth :math:`d_{\max}`, the normal force magnitude is \f[
   F_n=\max\left(0,k_p\delta^n+k_c\,S(\delta/d_{\max})v_n\right). \f] By
   default, :math:`S(a)=a^2(3-2a)` with :math:`a` clipped to
   :math:`[0,1]`. Tangential friction follows a cubic, velocity-
   regularized Coulomb curve. Its magnitude rises smoothly from zero to
   :math:`\mu_s F_n` at the stiction-transition speed :math:`v_s`, falls
   smoothly to :math:`\mu_d F_n` at the friction-transition speed
   :math:`v_d`, and remains on that dynamic plateau thereafter.

   The damping and friction transitions use a cubic polynomial
   approximation to a step function.


   .. py:method:: create(name: str) -> NonlinearContactForce
      :staticmethod:


      Create a nonlinear contact-force model.

      :param name: Object name.

      :returns: Newly allocated model.



   .. py:method:: toDS() -> Karana.Collision.Collision_types.NonlinearContactForceDS

      Create a NonlinearContactForceDS from this NonlinearContactForce model.

      :returns: * *NonlinearContactForceDS*
                * *A NonlinearContactForceDS instance with values set to match this model.*



   .. py:property:: params
      :type: NonlinearContactForceParams


      Contact-law parameters.


   .. py:property:: scratch
      :type: NonlinearContactForceScratch


      Latest computed contact values.


.. py:class:: NonlinearContactForceParams

   Parameters used by the nonlinear contact-force model.


   .. py:property:: damping_ramp_depth
      :type: float


      Penetration at full damping in meters.


   .. py:property:: friction_transition_speed
      :type: float


      Slip speed at the end of the friction transition in m/s.


   .. py:property:: kc
      :type: float


      Maximum normal damping coefficient in N s/m.


   .. py:property:: kp
      :type: float


      Penalty stiffness in N/m^n.


   .. py:property:: mu_dynamic
      :type: float


      Dynamic friction coefficient.


   .. py:property:: mu_static
      :type: float | None


      Static coefficient, or nullopt to use mu_dynamic.


   .. py:property:: n
      :type: float


      Positive penetration exponent.


   .. py:property:: stiction_transition_speed
      :type: float


      Slip speed of the static-friction peak in m/s.


.. py:class:: NonlinearContactForceScratch

   Scratch data populated by the latest force computation.


   .. py:property:: f
      :type: Annotated[numpy.typing.NDArray[numpy.float64], [3, 1]]


      Total force on object 1 expressed in frame 1.


   .. py:property:: linvel
      :type: Annotated[numpy.typing.NDArray[numpy.float64], [3, 1]]


      Relative contact-point velocity expressed in frame 1.


   .. py:property:: penetration
      :type: float


      Positive penetration depth in meters.


