Refactor, try to limit dependencies

This commit is contained in:
2022-07-28 00:59:56 +01:00
parent 8aa5c17ab9
commit 160560af5f
418 changed files with 15105 additions and 13343 deletions
+176 -132
View File
@@ -1,58 +1,58 @@
{-# LANGUAGE TupleSections #-}
{- | Basic collision detection for a moving point.
- Conventions: input is typically a stream of walls.
- Collide returns an endpoint and maybe a collided with
- object (wall).
- Reflect maybe gives a slightly pushed out point from a collision and a
- relection velocity.
- relection velocity.
- Overlap returns a stream of walls.
- A point will typically be moving.
- A ball refers to a moving circle, i.e. a point with a radius.
- -}
module Dodge.Base.Collide
( collidePoint
, collideSegCrs
, collidePointWallsFilterStream
, collidePointTestFilter
, overlapSegWalls
, overlapSegCrs
, overlap1SegCrs
, bounceBall
, bouncePoint
, sortStreamOn
, minStreamOn
, collideCircWallsStream
, circOnSomeWall
, circOnAnyCr
, overlapCircWalls
, overlapCircWallsClosest
, crsNearPoint
, allVisibleWalls
, hasLOS
, hasLOSIndirect
, hasButtonLOS
, canSee
, canSeeIndirect
, isWalkable
, anythingHitCirc
) where
import Dodge.Data
import Dodge.Zoning
import Dodge.Base.Wall
import Geometry
import FoldableHelp
-
-}
module Dodge.Base.Collide (
collidePoint,
collideSegCrs,
collidePointWallsFilterStream,
collidePointTestFilter,
overlapSegWalls,
overlapSegCrs,
overlap1SegCrs,
bounceBall,
bouncePoint,
sortStreamOn,
minStreamOn,
collideCircWallsStream,
circOnSomeWall,
circOnAnyCr,
overlapCircWalls,
overlapCircWallsClosest,
crsNearPoint,
allVisibleWalls,
hasLOS,
hasLOSIndirect,
hasButtonLOS,
canSee,
canSeeIndirect,
isWalkable,
anythingHitCirc,
) where
import Control.Lens
import qualified Data.IntSet as IS
import Data.List (sortOn)
import Data.Maybe
import Dodge.Base.Wall
import Dodge.Data.World
import Dodge.Zoning
import FoldableHelp
import Geometry
import qualified IntMapHelp as IM
import Control.Lens
import StreamingHelp
import qualified Streaming.Prelude as S
import qualified Data.IntSet as IS
import StreamingHelp
--collidePoint :: Point2 -> Point2
--collidePoint :: Point2 -> Point2
-- -> StreamOf Wall
-- -> (Point2, Maybe Wall)
--{-# INLINE collidePoint #-}
@@ -60,54 +60,69 @@ import qualified Data.IntSet as IS
-- where
-- findPoint (p,mwl) wl = maybe (p,mwl) (,Just wl) . uncurry (intersectSegSeg sp p) . _wlLine $ wl
collidePoint :: Point2 -> Point2
-> [Wall]
-> (Point2, Maybe Wall)
collidePoint ::
Point2 ->
Point2 ->
[Wall] ->
(Point2, Maybe Wall)
{-# INLINE collidePoint #-}
collidePoint sp ep = foldl' findPoint (ep, Nothing)
where
findPoint (p,mwl) wl = maybe (p,mwl) (,Just wl) . uncurry (intersectSegSeg sp p) . _wlLine $ wl
findPoint (p, mwl) wl = maybe (p, mwl) (,Just wl) . uncurry (intersectSegSeg sp p) . _wlLine $ wl
overlap1SegCrs :: Point2 -> Point2
-> [Creature]
-> [(Point2, Creature)]
overlap1SegCrs ::
Point2 ->
Point2 ->
[Creature] ->
[(Point2, Creature)]
{-# INLINE overlap1SegCrs #-}
overlap1SegCrs sp ep = mapMaybe
(\cr -> (,cr) <$> listToMaybe (intersectCircSeg (_crPos cr) (_crRad cr) sp ep ))
overlap1SegCrs sp ep =
mapMaybe
(\cr -> (,cr) <$> listToMaybe (intersectCircSeg (_crPos cr) (_crRad cr) sp ep))
overlapSegCrs :: Point2 -> Point2
-> StreamOf Creature
-> StreamOf ([Point2], Creature)
overlapSegCrs ::
Point2 ->
Point2 ->
StreamOf Creature ->
StreamOf ([Point2], Creature)
{-# INLINE overlapSegCrs #-}
overlapSegCrs sp ep = S.mapMaybe
(\cr -> f cr $ intersectCircSeg (_crPos cr) (_crRad cr) sp ep )
overlapSegCrs sp ep =
S.mapMaybe
(\cr -> f cr $ intersectCircSeg (_crPos cr) (_crRad cr) sp ep)
where
f _ [] = Nothing
f cr ps = Just (ps,cr)
f _ [] = Nothing
f cr ps = Just (ps, cr)
collideSegCrs :: Point2 -> Point2
-> StreamOf Creature
-> (Point2, Maybe Creature)
collideSegCrs ::
Point2 ->
Point2 ->
StreamOf Creature ->
(Point2, Maybe Creature)
{-# INLINE collideSegCrs #-}
collideSegCrs sp ep = runIdentity . S.fold_ findPoint (ep, Nothing) id
where
findPoint (p,mcr) cr
= maybe (p,mcr) (,Just cr) $ listToMaybe (intersectCircSeg (_crPos cr) (_crRad cr) sp p)
findPoint (p, mcr) cr =
maybe (p, mcr) (,Just cr) $ listToMaybe (intersectCircSeg (_crPos cr) (_crRad cr) sp p)
doBounce :: Float -> Point2 -> Point2 -> (Point2, Maybe Wall) -> Maybe (Point2, Point2)
{-# INLINE doBounce #-}
doBounce x sp ep (p, mwl) = mwl <&> \wl ->
( p +.+ normalizeV (vNormal (uncurry (-.-) (_wlLine wl)))
, reflVelWallDamp x wl (ep -.- sp)
)
doBounce x sp ep (p, mwl) =
mwl <&> \wl ->
( p +.+ normalizeV (vNormal (uncurry (-.-) (_wlLine wl)))
, reflVelWallDamp x wl (ep -.- sp)
)
bounceBall :: Float -> Point2 -> Point2 -> Float
-> [Wall]
-> Maybe (Point2,Point2)
bounceBall ::
Float ->
Point2 ->
Point2 ->
Float ->
[Wall] ->
Maybe (Point2, Point2)
{-# INLINE bounceBall #-}
bounceBall x sp ep r = doBounce x sp ep . collideCircWalls sp ep r
bouncePoint :: (Wall -> Bool) -> Float -> Point2 -> Point2 -> World -> Maybe (Point2,Point2)
bouncePoint :: (Wall -> Bool) -> Float -> Point2 -> Point2 -> World -> Maybe (Point2, Point2)
{-# INLINE bouncePoint #-}
bouncePoint t x sp ep = doBounce x sp ep . collidePointWallsFilterStream t sp ep
@@ -115,39 +130,41 @@ bouncePoint t x sp ep = doBounce x sp ep . collidePointWallsFilterStream t sp ep
-- whether this is actually faster
collidePointTestFilter :: (Wall -> Bool) -> Point2 -> Point2 -> [Wall] -> Bool
{-# INLINE collidePointTestFilter #-}
collidePointTestFilter t sp ep
= any (isJust . uncurry (intersectSegSeg sp ep) . _wlLine)
. filter t
collidePointTestFilter t sp ep =
any (isJust . uncurry (intersectSegSeg sp ep) . _wlLine)
. filter t
---- this COULD be written in terms of collidePointWallsFilterStream, TODO test
---- whether this is actually faster
--collidePointTestFilter :: (Wall -> Bool) -> Point2 -> Point2 -> StreamOf Wall -> Bool
--{-# INLINE collidePointTestFilter #-}
--collidePointTestFilter t sp ep = runIdentity
--collidePointTestFilter t sp ep = runIdentity
-- . S.any_ (isJust . uncurry (intersectSegSeg sp ep) . _wlLine)
-- . S.filter t
collidePointWallsFilterStream :: (Wall -> Bool) -> Point2 -> Point2 -> World -> (Point2, Maybe Wall)
{-# INLINE collidePointWallsFilterStream #-}
collidePointWallsFilterStream t sp ep = collidePoint sp ep
. filter t
. wlsNearSeg sp ep
collidePointWallsFilterStream t sp ep =
collidePoint sp ep
. filter t
. wlsNearSeg sp ep
--overlapSegWalls :: Point2 -> Point2 -> StreamOf Wall
-- -> StreamOf (Point2,Wall)
--{-# INLINE overlapSegWalls #-}
--overlapSegWalls sp ep = S.mapMaybe $ \wl -> uncurry (intersectSegSeg sp ep) (_wlLine wl) <&> (,wl)
overlapSegWalls :: Point2 -> Point2 -> [Wall] -> [(Point2,Wall)]
overlapSegWalls :: Point2 -> Point2 -> [Wall] -> [(Point2, Wall)]
{-# INLINE overlapSegWalls #-}
overlapSegWalls sp ep = mapMaybe $ \wl -> uncurry (intersectSegSeg sp ep) (_wlLine wl) <&> (,wl)
visibleWalls :: Point2 -> Point2 -> World -> [(Point2,Wall)]
visibleWalls :: Point2 -> Point2 -> World -> [(Point2, Wall)]
{-# INLINE visibleWalls #-}
visibleWalls sp ep = takeUntil (not . wlIsOpaque . snd)
. sortOn (dist sp . fst)
. overlapSegWalls sp ep
. wlsNearSeg sp ep
visibleWalls sp ep =
takeUntil (not . wlIsOpaque . snd)
. sortOn (dist sp . fst)
. overlapSegWalls sp ep
. wlsNearSeg sp ep
--visibleWalls :: Point2 -> Point2 -> World -> StreamOf (Point2,Wall)
--{-# INLINE visibleWalls #-}
@@ -157,7 +174,7 @@ visibleWalls sp ep = takeUntil (not . wlIsOpaque . snd)
-- . overlapSegWalls sp ep
-- . wlsNearSeg sp ep )
allVisibleWalls :: World -> [(Point2,Wall)]
allVisibleWalls :: World -> [(Point2, Wall)]
{-# INLINE allVisibleWalls #-}
allVisibleWalls w = concatMap (flip (visibleWalls vPos) w . (+.+ vPos)) $ nRays 20
where
@@ -168,7 +185,7 @@ allVisibleWalls w = concatMap (flip (visibleWalls vPos) w . (+.+ vPos)) $ nRays
--allVisibleWalls w = concats $ S.subst (flip (visibleWalls vPos) w . (+.+ vPos)) $ S.each (nRays 20)
-- where
-- vPos = _cameraViewFrom w
--overlapCircWalls :: Point2 -> Float -> StreamOf Wall
-- -> StreamOf (Point2,Wall)
--{-# INLINE overlapCircWalls #-}
@@ -177,61 +194,81 @@ allVisibleWalls w = concatMap (flip (visibleWalls vPos) w . (+.+ vPos)) $ nRays
-- dointersect wl = f (_wlLine wl) <&> (,wl)
-- f (a,b) = intersectSegSeg p (p +.+ r *.* normalizeV ((0.5 *.* (a +.+ b)) -.- p)) a b
overlapCircWalls :: Point2 -> Float -> [Wall]
-> [(Point2,Wall)]
overlapCircWalls ::
Point2 ->
Float ->
[Wall] ->
[(Point2, Wall)]
{-# INLINE overlapCircWalls #-}
overlapCircWalls p r = mapMaybe dointersect
where
dointersect wl = f (_wlLine wl) <&> (,wl)
f (a,b) = intersectSegSeg p (p +.+ r *.* normalizeV ((0.5 *.* (a +.+ b)) -.- p)) a b
f (a, b) = intersectSegSeg p (p +.+ r *.* normalizeV ((0.5 *.* (a +.+ b)) -.- p)) a b
-- | note that this does not push the circle away from the wall at all
collideCircWalls :: Point2 -> Point2 -> Float -> [Wall] -> (Point2, Maybe Wall)
{-# INLINE collideCircWalls #-}
collideCircWalls sp ep rad = foldl' findPoint (ep, Nothing)
where
findPoint (p,mwl) wl = maybe (p,mwl) (,Just wl) . uncurry (intersectSegSeg sp p)
. shiftbyrad
. _wlLine $ wl
shiftbyrad (a,b) = bimap f f
(a +.+ rad *.* normalizeV (a -.-b)
,b +.+ rad *.* normalizeV (b -.-a)
)
findPoint (p, mwl) wl =
maybe (p, mwl) (,Just wl) . uncurry (intersectSegSeg sp p)
. shiftbyrad
. _wlLine
$ wl
shiftbyrad (a, b) =
bimap
f
f
( a +.+ rad *.* normalizeV (a -.- b)
, b +.+ rad *.* normalizeV (b -.- a)
)
where
f = (+.+) (rad *.* normalizeV (vNormal $ a -.- b))
-- | note that this does not push the circle away from the wall at all
collideCircWallsStream :: Point2 -> Point2 -> Float -> StreamOf Wall
-> (Point2, Maybe Wall)
collideCircWallsStream ::
Point2 ->
Point2 ->
Float ->
StreamOf Wall ->
(Point2, Maybe Wall)
{-# INLINE collideCircWallsStream #-}
collideCircWallsStream sp ep rad = runIdentity . S.fold_ findPoint (ep, Nothing) id
where
findPoint (p,mwl) wl = maybe (p,mwl) (,Just wl) . uncurry (intersectSegSeg sp p)
. shiftbyrad
. _wlLine $ wl
shiftbyrad (a,b) = bimap f f
(a +.+ rad *.* normalizeV (a -.-b)
,b +.+ rad *.* normalizeV (b -.-a)
)
findPoint (p, mwl) wl =
maybe (p, mwl) (,Just wl) . uncurry (intersectSegSeg sp p)
. shiftbyrad
. _wlLine
$ wl
shiftbyrad (a, b) =
bimap
f
f
( a +.+ rad *.* normalizeV (a -.- b)
, b +.+ rad *.* normalizeV (b -.- a)
)
where
f = (+.+) (rad *.* normalizeV (vNormal $ a -.- b))
overlapCircWallsClosest :: Point2 -> Float -> [Wall] -> Maybe (Point2,Wall)
overlapCircWallsClosest :: Point2 -> Float -> [Wall] -> Maybe (Point2, Wall)
{-# INLINE overlapCircWallsClosest #-}
overlapCircWallsClosest p r = safeMinimumOn (dist p . fst)
. overlapCircWalls p r
overlapCircWallsClosest p r =
safeMinimumOn (dist p . fst)
. overlapCircWalls p r
--overlapCircWallsClosest :: Point2 -> Float -> StreamOf Wall -> Maybe (Point2,Wall)
--{-# INLINE overlapCircWallsClosest #-}
--overlapCircWallsClosest p r = minStreamOn (dist p . fst)
-- . overlapCircWalls p r
{- | Test if a circle collides with any wall.
- Note no check on whether the wall is walkable. -}
{- | Test if a circle collides with any wall.
- Note no check on whether the wall is walkable.
-}
circOnSomeWall :: Point2 -> Float -> World -> Bool
{-# INLINE circOnSomeWall #-}
circOnSomeWall p rad = any (uncurry (circOnSeg p rad) . _wlLine)
. wlsNearPoint p
circOnSomeWall p rad =
any (uncurry (circOnSeg p rad) . _wlLine)
. wlsNearPoint p
--circOnSomeWall :: Point2 -> Float -> World -> Bool
--{-# INLINE circOnSomeWall #-}
@@ -244,53 +281,60 @@ circOnAnyCr p r w = IS.foldr f False $ crIXsNearPoint p w
where
f cid bl = maybe False (\cr -> dist p (_crPos cr) < r + _crRad cr) (w ^? cWorld . creatures . ix cid) || bl
{- | More general collision tests follow -}
-- | More general collision tests follow
hasLOS :: Point2 -> Point2 -> World -> Bool
{-# INLINE hasLOS #-}
hasLOS p1 p2 = not
. collidePointTestFilter (const True) p1 p2
. wlsNearSeg p1 p2
hasLOS p1 p2 =
not
. collidePointTestFilter (const True) p1 p2
. wlsNearSeg p1 p2
hasButtonLOS :: Point2 -> Point2 -> World -> Bool
{-# INLINE hasButtonLOS #-}
hasButtonLOS p1 p2 = not
. collidePointTestFilter (not . _wlTouchThrough) p1 p2
. wlsNearSeg p1 p2
hasButtonLOS p1 p2 =
not
. collidePointTestFilter (not . _wlTouchThrough) p1 p2
. wlsNearSeg p1 p2
hasLOSIndirect :: Point2 -> Point2 -> World -> Bool
{-# INLINE hasLOSIndirect #-}
hasLOSIndirect p1 p2 = not
. collidePointTestFilter wlIsOpaque p1 p2
. wlsNearSeg p1 p2
hasLOSIndirect p1 p2 =
not
. collidePointTestFilter wlIsOpaque p1 p2
. wlsNearSeg p1 p2
isWalkable :: Point2 -> Point2 -> World -> Bool
{-# INLINE isWalkable #-}
isWalkable p1 p2 = not
. collidePointTestFilter (not . (^?! wlPathable)) p1 p2
. wlsNearSeg p1 p2
isWalkable p1 p2 =
not
. collidePointTestFilter (not . (^?! wlPathable)) p1 p2
. wlsNearSeg p1 p2
canSee :: Int -> Int -> World -> Bool
{-# INLINE canSee #-}
canSee i j w = hasLOS p1 p2 w
where
where
p1 = _crPos (_creatures (_cWorld w) IM.! i)
p2 = _crPos (_creatures (_cWorld w) IM.! j) -- unsafe
p2 = _crPos (_creatures (_cWorld w) IM.! j) -- unsafe
canSeeIndirect :: Int -> Int -> World -> Bool
{-# INLINE canSeeIndirect #-}
canSeeIndirect i j w = hasLOSIndirect ipos jpos w
where
where
ipos = _crPos (_creatures (_cWorld w) IM.! i)
jpos = _crPos (_creatures (_cWorld w) IM.! j)
anythingHitCirc :: Float -> Point2 -> Point2 -> World -> Bool
anythingHitCirc rad sp ep w = hitCr || isJust (sequence hitWl)
where
hitCr = IS.foldr f False $ crsNearSeg sp ep w
f cid bl = maybe False (\cr -> null $ intersectCircSeg (_crPos cr) (rad + _crRad cr) sp ep)
hitCr = IS.foldr f False $ crsNearSeg sp ep w
f cid bl =
maybe
False
(\cr -> null $ intersectCircSeg (_crPos cr) (rad + _crRad cr) sp ep)
(w ^? cWorld . creatures . ix cid)
|| bl
|| bl
hitWl = collideCircWalls sp ep rad $ wlsNearPoint ep w
-- this should probably be wallsOnLine or something
hitWl = collideCircWalls sp ep rad $ wlsNearPoint ep w
-- this should probably be wallsOnLine or something