module Geometry.Plane.General
( Plane (..)
, Plane2, Plane3
, Plane2D, Plane3D
, MakePlane (..)
, unsafeMakePlane
, flipPlane
, collinear
, PlanesRelation (..), Incidence (..), Orientation (..)
, planesRelation
, isParallel
) where
import Protolude hiding (zipWith, zero)
import Data.Maybe (fromJust)
import qualified Data.List as List
import Linear
import Linear.Affine (Point, (.-.))
import qualified Linear.Affine as Point
import Data.EqZero
data Plane v n = Plane
{ planeVector :: !(v n)
, planeLast :: !n
} deriving (Eq, Ord, Show)
type Plane2 = Plane V2
type Plane3 = Plane V3
type Plane2D = Plane V2 Double
type Plane3D = Plane V3 Double
instance (NFData (v n), NFData n) => NFData (Plane v n) where
rnf (Plane vs l) = rnf vs `seq` rnf l
flipPlane :: (Functor v, Num n) => Plane v n -> Plane v n
flipPlane (Plane v n) = Plane (fmap negate v) (negate n)
class MakePlane v n where
makePlane :: v (Point v n) -> Maybe (Plane v n)
instance (Num n, Eq n) => MakePlane V3 n where
makePlane (V3 p1 p2 p3)
| n == zero = Nothing
| otherwise = Just $ Plane n d
where
n = cross (p2 .-. p1) (p3 .-. p1)
d = negate $ dot n $ unPoint p1
unsafeMakePlane :: MakePlane v n => v (Point v n) -> Plane v n
unsafeMakePlane = fromJust . makePlane
unPoint :: Point v n -> v n
unPoint (Point.P x) = x
collinear :: (Foldable v, Num n, EqZero n) => v n -> v n -> Bool
collinear v w = all f $ combinations 2 $ zipWith (,) v w
where
f [(a, b), (c, d)] = eqZero $ a*d b*c
f _ = False
combinations :: Int -> [a] -> [[a]]
combinations k is
| k <= 0 = [ [] ]
| otherwise = [ x:r | x:xs <- tails is, r <- combinations (k1) xs ]
zipWith :: Foldable f => (a -> b -> c) -> f a -> f b -> [c]
zipWith f a b = List.zipWith f (toList a) (toList b)
coincidence :: (Foldable v, Num n, EqZero n) => Plane v n -> Plane v n -> Bool
coincidence (Plane v1 d1) (Plane v2 d2) = all f $ zipWith (,) v1 v2
where
f (x1, x2) = eqZero $ x1*d2 x2*d1
coorientation :: (Foldable v, Num n, Ord n, EqZero n)
=> Plane v n -> Plane v n -> Bool
coorientation (Plane v1 d1) (Plane v2 d2)
= all geqZero $ d1*d2 : zipWith (*) v1 v2
data PlanesRelation = Parallel Incidence Orientation | Crossing deriving Show
data Incidence = CoIncident | NonIncident deriving Show
data Orientation = CoOriented | AntiOriented deriving Show
planesRelation :: (Foldable v, Num n, Ord n, EqZero n)
=> Plane v n -> Plane v n -> PlanesRelation
planesRelation p1@(Plane v1 _) p2@(Plane v2 _)
| collinear v1 v2 = Parallel incidence orientation
| otherwise = Crossing
where
incidence = bool NonIncident CoIncident $ coincidence p1 p2
orientation = bool AntiOriented CoOriented $ coorientation p1 p2
isParallel :: (Foldable v, Num n, Ord n, EqZero n)
=> Plane v n -> Plane v n -> Bool
isParallel a b = case planesRelation a b of
Parallel _ _ -> True
Crossing -> False