```{-# Language MultiParamTypeClasses #-}
{-# Language FlexibleInstances     #-}
--------------------------------------------------------------------------------
-- |
-- Module     : Geometry.SetOperations.CrossPoint
-- Copyright  : (C) 2017 Maksymilian Owsianny
-- Maintainer : Maksymilian.Owsianny@gmail.com
--
--------------------------------------------------------------------------------
module Geometry.SetOperations.CrossPoint
( Sign (..)
, toSign

, CrossPoint     (..)
, MakeCrossPoint (..)
, toCrossPoint

) where

import Protolude
import Linear
import Linear.Affine (Point)
import qualified Linear.Affine as Point

import Data.EqZero
import Geometry.Plane.General

--------------------------------------------------------------------------------

data Sign = M | Z | P deriving (Show, Eq)

toSign :: (EqZero n, Ord n, Num n) => n -> Sign
toSign x
| eqZero x  = Z
| x < 0     = M
| otherwise = P

data CrossPoint v n = CP
{ orientation :: Plane v n -> Sign
, getPoint    :: Point v n
}

-- | Convert a point to CrossPoint
toCrossPoint :: (EqZero n, Foldable v, Num n, Ord n)
=> Point v n -> CrossPoint v n
toCrossPoint pt = CP orient pt
where
orient p = toSign . ((planeLast p) +) . sum
\$ zipWith (*) (toList \$ planeVector p) (toList pt)

class MakeCrossPoint v n where
makeCrossPoint :: v (Plane v n) -> Maybe (CrossPoint v n)

instance (Fractional n, Ord n, EqZero n) => MakeCrossPoint V2 n where
makeCrossPoint planes
| eqZero d2 = Nothing
| otherwise = Just \$ CP orient solved
where
V2     (Plane (V2 a b) c)
(Plane (V2 d e) f) = planes
-- orient (Plane (V2 g h) i) = toSign \$ d2*(g*d0 - h*d1 + i*d2)
orient (Plane (V2 g h) i) = toSign \$ g*dd0 + h*dd1 + i

dd0 = d2*d0
dd1 = d2*d1

d0 = b*f - c*e
d1 = a*f - c*d
d2 = a*e - b*d

dd = 1/d2
solved = Point.P \$ V2 (dd*d0) (dd*d1)

instance (Fractional n, Ord n, EqZero n) => MakeCrossPoint V3 n where
makeCrossPoint planes
| eqZero d3 = Nothing
| otherwise = Just \$ CP orient solved
where
V3     (Plane (V3 a b c) d)
(Plane (V3 e f g) h)
(Plane (V3 i j k) l) = planes
orient (Plane (V3 m n o) p) = toSign \$ -d3*(m*d0 - n*d1 + o*d2 - p*d3)

d0 = k*m1 - j*m0 + l*m2
d1 = k*m3 - i*m0 + l*m4
d2 = j*m3 - i*m1 + l*m5
d3 = i*m2 - j*m4 + k*m5

m0 = c*h - d*g
m1 = b*h - d*f
m2 = c*f - b*g
m3 = a*h - d*e
m4 = c*e - a*g
m5 = b*e - a*f

dd = 1/d3
solved = Point.P \$ V3 (-dd*d0) (dd*d1) (-dd*d2)

```