packages feed

hgeometry-0.14: src/Data/Geometry/Polygon.hs

{-# OPTIONS_GHC -fno-warn-orphans #-}
--------------------------------------------------------------------------------
-- |
-- Module      :  Data.Geometry.Polygon
-- Copyright   :  (C) Frank Staals
-- License     :  see the LICENSE file
-- Maintainer  :  Frank Staals
--
-- A Polygon data type and some basic functions to interact with them.
--
--------------------------------------------------------------------------------
module Data.Geometry.Polygon
  ( -- * Types
    PolygonType(..)
  , Polygon(..)
  , _SimplePolygon, _MultiPolygon
  , SimplePolygon, MultiPolygon, SomePolygon

    -- * Conversion
  , fromPoints
  , fromCircularVector

  , simpleFromPoints
  , simpleFromCircularVector

  , unsafeFromPoints
  , unsafeFromCircularVector
  , unsafeFromVector
  , toVector
  , toPoints

  , isSimple

    -- * Accessors

  , size
  , polygonVertices, listEdges

  , outerBoundary, outerBoundaryVector
  , unsafeOuterBoundaryVector
  , outerBoundaryEdges
  , outerVertex, outerBoundaryEdge

  , polygonHoles, polygonHoles'
  , holeList

    -- * Properties

  , area, signedArea
  , centroid

    -- * Queries
  , inPolygon, insidePolygon, onBoundary


  , isTriangle, isStarShaped

  , isCounterClockwise
  , toCounterClockWiseOrder, toCounterClockWiseOrder'
  , toClockwiseOrder, toClockwiseOrder'
  , reverseOuterBoundary

  , rotateLeft
  , rotateRight
  , maximumVertexBy
  , minimumVertexBy


   -- * Misc
  , pickPoint
  , findDiagonal

  , withIncidentEdges, numberVertices

  , extremesLinear, cmpExtreme

  , findRotateTo

  ) where

import           Algorithms.Geometry.InPolygon
import           Algorithms.Geometry.LinearProgramming.LP2DRIC
import           Algorithms.Geometry.LinearProgramming.Types
import           Control.Lens hiding (Simple)
import           Control.Monad.Random.Class
import           Data.Ext
import qualified Data.Foldable as F
import           Data.Geometry.Boundary
import           Data.Geometry.HalfSpace (rightOf)
import           Data.Geometry.Line
import           Data.Geometry.LineSegment
import           Data.Geometry.Point
import           Data.Geometry.Polygon.Core
import           Data.Geometry.Polygon.Extremes
import           Data.Geometry.Properties
import           Data.Ord (comparing)
import qualified Data.Sequence as Seq

--------------------------------------------------------------------------------
-- * Polygons

-- | Test if a Simple polygon is star-shaped. Returns a point in the kernel
-- (i.e. from which the entire polygon is visible), if it exists.
--
--
-- \(O(n)\) expected time
isStarShaped    :: (MonadRandom m, Ord r, Fractional r)
                => SimplePolygon p r -> m (Maybe (Point 2 r))
isStarShaped (toClockwiseOrder -> pg) =
    solveBoundedLinearProgram $ LinearProgram c (F.toList hs)
  where
    c  = pg^.outerVertex 1.core.vector
    -- the first vertex is the intersection point of the two supporting lines
    -- bounding it, so the first two edges bound the shape in this sirection
    hs = fmap (rightOf . supportingLine) . outerBoundaryEdges $ pg


--------------------------------------------------------------------------------
-- * Instances

type instance IntersectionOf (Line 2 r) (Boundary (Polygon t p r)) =
  '[Seq.Seq (Either (Point 2 r) (LineSegment 2 () r))]

type instance IntersectionOf (Point 2 r) (Polygon t p r) = [NoIntersection, Point 2 r]

instance (Fractional r, Ord r) => Point 2 r `HasIntersectionWith` Polygon t p r where
  q `intersects` pg = q `inPolygon` pg /= Outside

instance (Fractional r, Ord r) => Point 2 r `IsIntersectableWith` Polygon t p r where
  nonEmptyIntersection = defaultNonEmptyIntersection
  q `intersect` pg | q `intersects` pg = coRec q
                   | otherwise         = coRec NoIntersection

-- instance IsIntersectableWith (Line 2 r) (Boundary (Polygon t p r)) where
--   nonEmptyIntersection _ _ (CoRec xs) = null xs
--   l `intersect` (Boundary (SimplePolygon vs)) =
--     undefined
  -- l `intersect` (Boundary (MultiPolygon vs hs)) = coRec .
  --    Seq.sortBy f . Seq.fromList
  --     . concatMap (unpack . (l `intersect`) . Boundary)
  --     $ SimplePolygon vs : hs
  --   where
  --     unpack (CoRec x) = x
  --     f = undefined

instance (Fractional r, Ord r) => HasSquaredEuclideanDistance (Boundary (Polygon t p r)) where
  pointClosestToWithDistance q = F.minimumBy (comparing snd)
                               . fmap (pointClosestToWithDistance q)
                               . listEdges . review _Boundary

instance (Fractional r, Ord r) => HasSquaredEuclideanDistance (Polygon t p r) where
  pointClosestToWithDistance q pg
    | q `intersects` pg = (q, 0)
    | otherwise         = pointClosestToWithDistance q (Boundary pg)