shapes-0.1.0.0: src/Physics/Constraints/Contact.hs
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{- |
Generate and solve all contact constraints for pairs of colliding objects.
-}
module Physics.Constraints.Contact where
import GHC.Generics (Generic)
import Control.DeepSeq
import Control.Lens
import qualified Data.Vector.Unboxed as V
import Data.Vector.Unboxed.Deriving
import Physics.Constraint
import qualified Physics.Constraints.Contact.Friction as F
import qualified Physics.Constraints.Contact.NonPenetration as NP
import Physics.Constraints.Types
import Physics.Contact
import Physics.Contact.Types
import Utils.Descending
import Utils.Utils
{- |
Indicates a specific pair of "features" on a specific pair of objects that are touching.
This is how we check if we can reuse a cached solution from the previous frame.
(We can reuse the cached solution if it has the same 'ObjectFeatureKey')
-}
data ObjectFeatureKey k = ObjectFeatureKey
{ _ofkObjKeys :: (k, k) -- ^ (first shape's key, second shape's key)
, _ofkFeatKeys :: (Int, Int) -- ^ (first shape's feature's key, second shape's feature's key)
} deriving (Generic, Show, NFData, Eq, Ord)
makeLenses ''ObjectFeatureKey
derivingUnbox
"ObjectFeatureKey"
[t|forall k. (V.Unbox k) =>
ObjectFeatureKey k -> ((k, k), (Int, Int))|]
[|\ObjectFeatureKey {..} -> (_ofkObjKeys, _ofkFeatKeys)|]
[|uncurry ObjectFeatureKey|]
-- | Calculate all contacts between a pair of shapes.
keyedContacts ::
(k, k)
-> (Shape, Shape)
-> Descending (ObjectFeatureKey k, Flipping Contact')
keyedContacts ij ab = fmap f contacts
where contacts = generateContacts ab
f (featKeys, contact) = (ObjectFeatureKey ij featKeys, contact)
{-# INLINE f #-}
{-# INLINE keyedContacts #-}
-- | Build a constraint from a pair of shapes and a contact between them.
constraintGen ::
ContactBehavior
-> Double
-> Flipping Contact'
-> (PhysicalObj, PhysicalObj)
-> ContactResult Constraint
constraintGen beh dt fContact ab =
ContactResult { _crNonPen = NP.constraintGen beh dt fContact ab
, _crFriction = F.constraintGen fContact ab }
{-# INLINE constraintGen #-}
{- |
Given an already-applied Lagrangian and the newly-calculated Lagrangian,
figure out what portion of the newly-calculated Lagrangian should actually be applied.
-}
solutionProcessor ::
(Double, Double) -- ^ coefficients of friction for a pair of shapes (a, b)
-> ContactResult Lagrangian -- ^ cached solution
-> ContactResult Lagrangian -- ^ new incremental solution
-> Processed (ContactResult Lagrangian) -- ^ 1. incremental solution to actually apply, 2. new cached solution
solutionProcessor mu_ab (ContactResult npCached fCached) (ContactResult npNew fNew) =
ContactResult <$> npProcessed <*> fProcessed
where npProcessed = NP.solutionProcessor npCached npNew
fProcessed = F.solutionProcessor mu_ab (_processedToCache npProcessed) fCached fNew
{-# INLINE solutionProcessor #-}