gbnet-hs-0.1.0.0: src/GBNet/Replication/Interest.hs
-- |
-- Module : GBNet.Replication.Interest
-- Description : Interest management for area-of-interest filtering
--
-- Determines which entities are relevant to a given observer, reducing
-- bandwidth by only replicating nearby or important entities.
--
-- Use 'RadiusInterest' for sphere-based filtering or 'GridInterest' for
-- cell-based spatial partitioning.
module GBNet.Replication.Interest
( -- * Position type
Position,
-- * Interest manager typeclass
InterestManager (..),
-- * Radius-based interest
RadiusInterest,
newRadiusInterest,
radiusInterestRadius,
-- * Grid-based interest
GridInterest,
newGridInterest,
gridInterestCellSize,
)
where
import Control.DeepSeq (NFData (..))
-- | 3D position as (x, y, z).
type Position = (Float, Float, Float)
-- | Typeclass for determining entity relevance to an observer.
--
-- Implement this for custom interest filtering strategies.
class InterestManager a where
-- | Returns 'True' if the entity at @entityPos@ is relevant to the
-- observer at @observerPos@.
relevant :: a -> Position -> Position -> Bool
-- | Returns a priority modifier for the entity relative to the observer.
-- Values > 1.0 increase priority, < 1.0 decrease it.
-- Default returns 1.0 (no modification).
priorityMod :: a -> Position -> Position -> Float
priorityMod _ _ _ = 1.0
-- | Radius-based interest: entities within @radius@ distance are relevant.
--
-- Also provides distance-based priority: closer entities get higher priority.
data RadiusInterest = RadiusInterest
{ riRadius :: !Float,
riRadiusSq :: !Float
}
deriving (Show)
instance NFData RadiusInterest where
rnf (RadiusInterest r rs) = rnf r `seq` rnf rs
-- | Create a radius-based interest manager.
newRadiusInterest :: Float -> RadiusInterest
newRadiusInterest radius =
RadiusInterest
{ riRadius = radius,
riRadiusSq = radius * radius
}
-- | Get the radius.
radiusInterestRadius :: RadiusInterest -> Float
radiusInterestRadius = riRadius
instance InterestManager RadiusInterest where
relevant ri (ex, ey, ez) (ox, oy, oz) =
let dx = ex - ox
dy = ey - oy
dz = ez - oz
distSq = dx * dx + dy * dy + dz * dz
in distSq <= riRadiusSq ri
priorityMod ri (ex, ey, ez) (ox, oy, oz) =
let dx = ex - ox
dy = ey - oy
dz = ez - oz
distSq = dx * dx + dy * dy + dz * dz
radiusSq = riRadiusSq ri
in if distSq >= radiusSq
then 0.0
else -- Linear falloff: closer entities get higher priority
1.0 - sqrt (distSq / radiusSq)
-- | Grid-based area-of-interest: entities in the same or neighboring cells
-- are considered relevant.
--
-- More efficient than radius checks for large numbers of entities when
-- combined with spatial hashing.
data GridInterest = GridInterest
{ giCellSize :: !Float,
giInvCellSize :: !Float
}
deriving (Show)
instance NFData GridInterest where
rnf (GridInterest c i) = rnf c `seq` rnf i
-- | Create a grid-based interest manager.
newGridInterest :: Float -> GridInterest
newGridInterest cellSize =
GridInterest
{ giCellSize = cellSize,
giInvCellSize = 1.0 / cellSize
}
-- | Get the cell size.
gridInterestCellSize :: GridInterest -> Float
gridInterestCellSize = giCellSize
-- | Grid cell coordinate (internal).
data GridCell = GridCell !Int !Int !Int
deriving (Eq, Show)
-- | Convert position to grid cell.
toCell :: GridInterest -> Position -> GridCell
toCell gi (x, y, z) =
let inv = giInvCellSize gi
in GridCell
(floor (x * inv))
(floor (y * inv))
(floor (z * inv))
instance InterestManager GridInterest where
relevant gi entityPos observerPos =
let GridCell ex ey ez = toCell gi entityPos
GridCell ox oy oz = toCell gi observerPos
in abs (ex - ox) <= 1 && abs (ey - oy) <= 1 && abs (ez - oz) <= 1