{-# 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