shapes-0.1.0.0: src/Physics/Contact/GJK.hs
{-# LANGUAGE MagicHash #-}
{- |
Gilbert-Johnson-Keerthi (GJK) for finding the closest part of a Minkowski space
to the origin.
Choose a simplex using opposite extents along some axis.
Extend the simplex in the direction of the origin.
If the simplex encloses the origin, stop.
based on slides/video by Casey Muratori:
* https://www.youtube.com/watch?v=Qupqu1xe7Io
The loop of GJK is:
- extend the simplex along the search direction
- shift to the closest component of the simplex
- do this until we can't extend the simplex any more
(search stopped short of the origin)
-}
module Physics.Contact.GJK where
import GHC.Prim
import GHC.Types (Double (D#), isTrue#)
import Physics.Contact.ConvexHull
import Physics.Linear
import Utils.Utils
-- | 2-simplex. The first element is the most recently added. (like the head of a list)
data Simplex3 =
Simplex3 !Neighborhood
!Neighborhood
!Neighborhood
deriving (Show)
data Simplex2 =
Simplex2 !Neighborhood
!Neighborhood
deriving (Show)
data Simplex1 =
Simplex1 !Neighborhood
deriving (Show)
type Simplex12 = Either Simplex1 Simplex2
type Simplex23 = Either Simplex2 Simplex3
data Simplex
= Simplex' Simplex12
| Simplex3' Simplex3
deriving (Show)
closestSimplex :: ConvexHull -> P2 -> Simplex
closestSimplex hull origin = loop (Left $ Simplex1 a) d
where
a = _hullNeighborhood 0 hull
d = diffP2 origin $ _neighborhoodCenter a
loop :: Simplex12 -> V2 -> Simplex
loop simplex d =
case extendSimplex simplex aa of
Nothing -> Simplex' simplex -- search failed
Just simplex ->
case shiftSimplex simplex origin of
Right simplex -> Simplex3' simplex -- enclosed the origin
Left (simplex, d) -> loop simplex d
where
aa = support hull d
a = _neighborhoodCenter aa
ao = diffP2 a origin
extendSimplex :: Simplex12 -> Neighborhood -> Maybe Simplex23
extendSimplex (Left simplex) aa =
Left <$> extendSimplex1 simplex aa
extendSimplex (Right simplex) aa = Right <$> extendSimplex2 simplex aa
extendSimplex1 :: Simplex1 -> Neighborhood -> Maybe Simplex2
extendSimplex1 simplex@(Simplex1 bb) aa
| _neighborhoodIndex bb == _neighborhoodIndex aa = Nothing -- it's a repeat
| otherwise = Just $ mkSimplex2 aa simplex
extendSimplex2 :: Simplex2 -> Neighborhood -> Maybe Simplex3
extendSimplex2 simplex@(Simplex2 bb cc) aa
| bi == ai || ci == ai = Nothing -- it's a repeat
| otherwise = Just $ mkSimplex3 aa simplex
where ai = _neighborhoodIndex aa
bi = _neighborhoodIndex bb
ci = _neighborhoodIndex cc
shiftSimplex :: Simplex23 -> P2 -> Either (Simplex12, V2) Simplex3
shiftSimplex (Left simplex) origin = Left $ shiftSimplex2 simplex origin
shiftSimplex (Right simplex) origin =
case shiftSimplex3 simplex origin of
Nothing -> Right simplex
Just result -> Left result
shiftSimplex2 :: Simplex2
-- ^ 1D simplex of 2 points
-> P2
-- ^ origin (the target point)
-> (Simplex12, V2)
-- ^ new simplex, new search direction
shiftSimplex2 aabb@(Simplex2 aa bb) origin
| sameDirection ab ao = (Right aabb, crossV2V2 ab ao ab)
-- search perpendicular to AB toward the origin.
| otherwise = (Left $ Simplex1 aa, ao) -- throw out B, search from A toward the origin.
where
a = _neighborhoodCenter aa
b = _neighborhoodCenter bb
ab = diffP2 b a
ao = diffP2 origin a
shiftSimplex3 :: Simplex3
-- ^ 2D simplex of 3 points
-> P2
-- ^ origin (the target point)
-> Maybe (Simplex12, V2)
-- ^ (new simplex, new search direction) OR simplex encloses the origin!
shiftSimplex3 (Simplex3 aa bb cc) origin
| sameDirection abcac ao =
if sameDirection ac ao
then Just (Right $ Simplex2 aa cc, crossV2V2 ac ao ac)
else Just star
| sameDirection ababc ao = Just star
| otherwise = Nothing -- simplex encloses the origin
where
a = _neighborhoodCenter aa
b = _neighborhoodCenter bb
c = _neighborhoodCenter cc
ab = diffP2 b a
ac = diffP2 c a
ao = diffP2 origin a
abc = ab `crossV2` ac
abcac = abc `zcrossV2` ac
ababc = ab `crosszV2` abc
star =
if sameDirection ab ao
then (Right $ Simplex2 aa bb, crossV2V2 ab ao ab)
else (Left $ Simplex1 aa, ao)
sameDirection :: V2 -> V2 -> Bool
sameDirection a b = isTrue# (a `dotV2` b >## 0.0##)
mkSimplex3 :: Neighborhood -> Simplex2 -> Simplex3
mkSimplex3 aa (Simplex2 bb cc) = Simplex3 aa bb cc
mkSimplex2 :: Neighborhood -> Simplex1 -> Simplex2
mkSimplex2 aa (Simplex1 bb) = Simplex2 aa bb