shapes-0.1.0.0: src/Physics/Contact/CircleVsHull.hs
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TupleSections #-}
module Physics.Contact.CircleVsHull where
import GHC.Types (Double (..))
import Data.Either
import Physics.Contact.Circle
import Physics.Contact.ConvexHull
import Physics.Contact.GJK
import Physics.Contact.Types
import Physics.Linear
import Utils.Utils
-- | There's only one contact point between a circle and a convex hull.
generateContacts :: Circle -> ConvexHull -> Maybe (Int, Contact')
-- ^ (hull feature index, contact manifold) -- circle is always the penetrator
generateContacts circle@Circle {..} hull = convertSimplex circle simplex
where
simplex = closestSimplex hull _circleCenter
-- TODO: handle the "deep overlap" case (3-point simplex)
convertSimplex :: Circle -> Simplex -> Maybe (Int, Contact')
convertSimplex circle (Simplex' simplex) = convertSimplex12 circle simplex
convertSimplex _ (Simplex3' _) = Nothing
convertSimplex12 :: Circle -> Simplex12 -> Maybe (Int, Contact')
convertSimplex12 circle = either (processSimplex1 circle) (processSimplex2 circle)
processSimplex1 :: Circle -> Simplex1 -> Maybe (Int, Contact')
processSimplex1 circle (Simplex1 aa) =
(_neighborhoodIndex aa, ) <$> processSimplex_ circle (_neighborhoodCenter aa)
processSimplex2 :: Circle -> Simplex2 -> Maybe (Int, Contact')
processSimplex2 circle@Circle {..} simplex@(Simplex2 feature _) =
(_neighborhoodIndex feature, ) <$> processSimplex_ circle a
where
a = _circleCenter `closestAlong` simplex
processSimplex_ :: Circle -> P2 -> Maybe Contact'
processSimplex_ Circle {..} a
| sqRadius < abSq = Nothing -- distance greater than circle radius
| otherwise =
Just $
Contact'
{ _contactEdgeNormal' = negateV2 normal
, _contactPenetrator' = a
, _contactDepth' = _circleRadius - abLength
}
where
b = _circleCenter
sqRadius = _circleRadius * _circleRadius
ab = diffP2 b a
abSq = sqLengthV2 ab
abLength = sqrt abSq
normal = sdivV2 abLength ab
closestAlong :: P2 -- ^ target point
-> Simplex2 -- ^ line segment
-> P2
o `closestAlong` (Simplex2 aa bb) = aoAlong `vplusP2` a
where a = _neighborhoodCenter aa
b = _neighborhoodCenter bb
ao = diffP2 o a
ab = diffP2 b a
abNorm = normalizeV2 ab
aoAlong = D# (ao `dotV2` abNorm) `smulV2` abNorm