{-# LANGUAGE ScopedTypeVariables  #-}
{-# LANGUAGE UndecidableInstances #-}
--------------------------------------------------------------------------------
-- |
-- Module      :  HGeometry.Point
-- Copyright   :  (C) Frank Staals
-- License     :  see the LICENSE file
-- Maintainer  :  Frank Staals
--
-- \(d\)-dimensional points.
--
--------------------------------------------------------------------------------
module HGeometry.Point
  ( Point
  , PointF(Point, toVec, Point1, Point2, Point3, Point4)
  -- , {- | construct a 1-dimensional point -} pattern Point1
  -- , {- | construct a 2-dimensional point -} pattern Point2
  -- , {- | construct a 3-dimensional point -} pattern Point3
  -- , {- | construct a 4-dimensional point -} pattern Point4
  , Point_(..), pattern Point1_, pattern Point2_, pattern Point3_, pattern Point4_
  , ConstructablePoint_(..)
  , HasVector(..)
  , HasCoordinates(..)
  , asPoint
  , origin
  -- , pointFromPoint
  , pointFromList

  , coord
  , xCoord, yCoord, zCoord, wCoord
  , dCoord

  , projectPoint

  , Affine_(..)
  , CCW(CCW, CW, CoLinear), ccw, isCoLinear

  , ccwCmpAround
  , cwCmpAround
  , ccwCmpAroundWith
  , cwCmpAroundWith
  , sortAround
  , insertIntoCyclicOrder
--
--  , StrictCCW(SCCW, SCW)
--  , strictCcw
--
--
 , Quadrant(..), quadrantWith, quadrant, partitionIntoQuadrants

  , cmpByDistanceTo
  , cmpInDirection2

  , squaredEuclideanDist, euclideanDist
  , HasSquaredEuclideanDistance(..)

  , HasPoints(..), HasPoints'
  , CanonicalPoint
  ) where

import Control.Lens (Lens', Iso', coerced, (^.))
import Data.Type.Ord
import HGeometry.Point.Class
import HGeometry.Point.EuclideanDistance
import HGeometry.Point.Orientation
import HGeometry.Point.PointF
import HGeometry.Point.Quadrants
import HGeometry.Properties
import HGeometry.Vector
-- import Data.Coerce

--------------------------------------------------------------------------------
-- $setup
-- >>> import HGeometry.Vector
-- >>> import Control.Lens(Lens', Iso', (^.), coerced)

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

-- | d-dimensional points
type Point d r = PointF (Vector d r)

-- | Convert a generic point into a Point d r, dropping any additional
-- information we may now about it.
asPoint :: forall point d r. Point_ point d r => Lens' point (Point d r)
asPoint :: forall point (d :: Nat) r.
Point_ point d r =>
Lens' point (Point d r)
asPoint = (Vector d r -> f (Vector d r)) -> point -> f point
forall (d :: Nat) r s.
(Dimension point ~ d, NumType point ~ r, Dimension point ~ d,
 NumType point ~ s) =>
Lens point point (Vector d r) (Vector d s)
forall point point' (d :: Nat) r s.
(HasVector point point', Dimension point ~ d, NumType point ~ r,
 Dimension point' ~ d, NumType point' ~ s) =>
Lens point point' (Vector d r) (Vector d s)
Lens point point (Vector d r) (Vector d r)
vector((Vector d r -> f (Vector d r)) -> point -> f point)
-> ((Point d r -> f (Point d r)) -> Vector d r -> f (Vector d r))
-> (Point d r -> f (Point d r))
-> point
-> f point
forall b c a. (b -> c) -> (a -> b) -> a -> c
.(p (Point d r) (f (Point d r)) -> p (Vector d r) (f (Vector d r))
forall s t a b. (Coercible s a, Coercible t b) => Iso s t a b
forall {p :: * -> * -> *} {f :: * -> *}.
(Profunctor p, Functor f) =>
p (Point d r) (f (Point d r)) -> p (Vector d r) (f (Vector d r))
coerced :: Iso' (Vector d r) (Point d r))
{-# INLINE asPoint #-}

--------------------------------------------------------------------------------
-- * Convenience functions to construct 1, 2 and 3 dimensional points

-- | A bidirectional pattern synonym for 1 dimensional points.
pattern Point1   :: r -> Point 1 r
pattern $bPoint1 :: forall r. r -> Point 1 r
$mPoint1 :: forall {r} {r}. Point 1 r -> (r -> r) -> ((# #) -> r) -> r
Point1 x = Point (Vector1 x)
{-# COMPLETE Point1 #-}

-- | A bidirectional pattern synonym for 2 dimensional points.
pattern Point2       :: r -> r -> Point 2 r
pattern $bPoint2 :: forall r. r -> r -> Point 2 r
$mPoint2 :: forall {r} {r}. Point 2 r -> (r -> r -> r) -> ((# #) -> r) -> r
Point2 x y = Point (Vector2 x y)
{-# COMPLETE Point2 #-}

-- | A bidirectional pattern synonym for 3 dimensional points.
pattern Point3       :: r -> r -> r -> Point 3 r
pattern $bPoint3 :: forall r. r -> r -> r -> Point 3 r
$mPoint3 :: forall {r} {r}.
Point 3 r -> (r -> r -> r -> r) -> ((# #) -> r) -> r
Point3 x y z = (Point (Vector3 x y z))
{-# COMPLETE Point3 #-}

-- | A bidirectional pattern synonym for 4 dimensional points.
pattern Point4         :: r -> r -> r -> r -> Point 4 r
pattern $bPoint4 :: forall r. r -> r -> r -> r -> Point 4 r
$mPoint4 :: forall {r} {r}.
Point 4 r -> (r -> r -> r -> r -> r) -> ((# #) -> r) -> r
Point4 x y z w = (Point (Vector4 x y z w))
{-# COMPLETE Point4 #-}

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

-- | Project a point into a lower dimension.
projectPoint   :: forall i point d r.
                  (Point_ point d r, i <= d, Has_ Vector_ i r) => point -> Point i r
projectPoint :: forall (i :: Nat) point (d :: Nat) r.
(Point_ point d r, i <= d, Has_ Vector_ i r) =>
point -> Point i r
projectPoint point
p = Vector i r -> PointF (Vector i r)
forall v. v -> PointF v
Point (Vector i r -> PointF (Vector i r))
-> (Vector d r -> Vector i r) -> Vector d r -> PointF (Vector i r)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Vector d r -> Vector i r
forall (i :: Nat) (d :: Nat) vector vector' r.
(i <= d, Vector_ vector d r, Vector_ vector' i r) =>
vector -> vector'
prefix (Vector d r -> PointF (Vector i r))
-> Vector d r -> PointF (Vector i r)
forall a b. (a -> b) -> a -> b
$ point
ppoint -> Getting (Vector d r) point (Vector d r) -> Vector d r
forall s a. s -> Getting a s a -> a
^.(Point d r -> Const (Vector d r) (Point d r))
-> point -> Const (Vector d r) point
forall point (d :: Nat) r.
Point_ point d r =>
Lens' point (Point d r)
Lens' point (Point d r)
asPoint((Point d r -> Const (Vector d r) (Point d r))
 -> point -> Const (Vector d r) point)
-> ((Vector d r -> Const (Vector d r) (Vector d r))
    -> Point d r -> Const (Vector d r) (Point d r))
-> Getting (Vector d r) point (Vector d r)
forall b c a. (b -> c) -> (a -> b) -> a -> c
.(Vector d r -> Const (Vector d r) (Vector d r))
-> Point d r -> Const (Vector d r) (Point d r)
forall (d :: Nat) r s.
(Dimension (Point d r) ~ d, NumType (Point d r) ~ r,
 Dimension (Point d r) ~ d, NumType (Point d r) ~ s) =>
Lens (Point d r) (Point d r) (Vector d r) (Vector d s)
forall point point' (d :: Nat) r s.
(HasVector point point', Dimension point ~ d, NumType point ~ r,
 Dimension point' ~ d, NumType point' ~ s) =>
Lens point point' (Vector d r) (Vector d s)
Lens (Point d r) (Point d r) (Vector d r) (Vector d r)
vector



-- | Canonical point in the dimension and numtype of the geometry
type CanonicalPoint geom = Point (Dimension geom) (NumType geom)