module GeomAlg.Polygon (
Polygon (..), Polygon2, Polygon3, mapPolygon,
vertices, edges, angles,
area, contains, isConvex,
deleteCollinear,
extreme, extremeArr,
naiveInConvex, inConvexInteriour, inConvex
)
where
import GeomAlg.Point ( Point ((<==>), (<.>), (<*>)))
import GeomAlg.Point2 ( Point2 (..), P2, Point (origin), isOn, isRightTurnOrOn,
isCollinear, isLeftTurnOrOn, xcoord, ycoord, isOn )
import GeomAlg.Point3 ( Point3 )
import GeomAlg.Line ( Line (..), fromPDL, distanceFromLine, intersect, doIntersect )
import GeomAlg.External.Utilities ( rotateL, rotateR, rotateTo, snd3, uncurry3, rotateToBy )
import qualified GeomAlg.Triangle as Triangle
import Array ( Array, listArray, (!) )
data (Point p, Num a) => Polygon p a
= PolygonCW [p a]
| PolygonCCW [p a]
type Polygon2 a = Polygon Point2 a
type Polygon3 a = Polygon Point3 a
mapPolygon :: (Point p, Num a, Num b) => (p a -> p b)
-> Polygon p a -> Polygon p b
mapPolygon f (PolygonCW xs) = PolygonCW (map f xs)
mapPolygon f (PolygonCCW xs) = PolygonCCW (map f xs)
vertices :: (Point p, Num a) => Polygon p a -> [p a]
vertices (PolygonCW ps) = reverse ps
vertices (PolygonCCW ps) = ps
edges :: [a] -> [(a,a)]
edges xs = zip xs (rotateL xs)
angles :: [a] -> [(a,a,a)]
angles xs = zip3 (rotateR xs) xs (rotateL xs)
instance (Eq a, Num a, Point p) => Eq (Polygon p a) where
x == y = and (zipWith (<==>) ys (rotateToBy (<==>) (head ys) xs))
where xs = vertices x
ys = vertices y
area :: Fractional a => Polygon2 a -> a
area poly = sumTri ps
where sumTri (q:r:rest) = Triangle.area (Triangle.Triangle (p,q,r)) + sumTri (r:rest)
sumTri _ = 0
(p:ps) = vertices poly
contains :: (Num a, Ord a) => Polygon2 a -> P2 a -> Bool
contains poly p = eq>0 || odd pos || odd neg
where (pos, eq, neg) = countCrossings (edges qs) 0 0 0
qs = map (()p) (last ps : init ps)
ps = vertices poly
countCrossings :: (Ord a, Num a) => [(P2 a, P2 a)]
-> Int -> Int -> Int -> (Int, Int, Int)
countCrossings [] cp ce cn = (cp, ce, cn)
countCrossings ((p@(Point2 (x',y')), q@(Point2 (x,y))):ps) cp ce cn
| straddlesXaxis = incr
| straddlesFromBelow || p==origin || q==origin
= countCrossings ps cp (ce+1) cn
| otherwise = countCrossings ps cp ce cn
where straddlesXaxis = (y>0 && y'<=0) || (y'>0 && y<=0)
straddlesFromBelow = (y<=0 && y'==0) && (y==0 && y'<=0)
&& ((x<=0 && x'>=0) || (x>=0 && x'<=0))
incr | sgn>0 = countCrossings ps (cp+1) ce cn
| sgn==0 = countCrossings ps cp (ce+1) cn
| sgn<0 = countCrossings ps cp ce (cn+1)
where sgn = signum (x*y' x'*y) * signum (y' y)
isConvex :: (Ord a, Num a) => Polygon2 a -> Bool
isConvex = all (uncurry3 isLeftTurnOrOn) . angles . vertices
deleteCollinear :: (Ord a, Num a) => [P2 a] -> [P2 a]
deleteCollinear xs@(_:_:_:_) = [ i | (m,i,p)<-angles xs, not (isCollinear m i p)]
deleteCollinear xs = xs
extreme :: (Num a, Ord a) => P2 a -> Polygon2 a -> P2 a
extreme u p = arr ! extremeArr arr u n
where ps = vertices p
n = length ps
arr = listArray (0,n1) ps
extremeArr :: (Num a, Ord a) => Array Int (P2 a) -> P2 a -> Int -> Int
extremeArr p u n = search 0 0
where
ur = Point2 (ycoord u, xcoord u)
search a b
| adot==0 && ur<.>vecA<0 = a
| cdot==0 && ur<.>vecC<0 = c
| cdot<0 && bdot>0 = c
| a==c = if adot>0 then b else a
| otherwise = goOn
where
goOn
| adot>=0 && cdot<=0 = search a c
| adot<=0 && cdot>=0 = search c b
| adot>0 && cdot>0 = if y>0 then search a c else search c b
| adot<0 && cdot<0 = if y<0 then search a c else search c b
c | a < b = (a+b) `div` 2
| otherwise = (a+b+n) `div` 2 `mod` n
vecA = p!a1 p!a where a1 = (a+1) `mod` n
vecC = p!c1 p!c where c1 = (c+1) `mod` n
vecB = p!c p!c2 where c2 = (c+(n1)) `mod` n
adot = u <.> vecA
cdot = u <.> vecC
bdot = u <.> vecB
y = (u <.> p!a) (u <.> p!c)
naiveInConvex :: (Num a, Ord a) => P2 a -> Polygon2 a -> Bool
q `naiveInConvex` p = case vs of
[] -> False
[s] -> q==s
[s,t] -> isOn s t q
ps -> all ok (edges vs)
where vs = vertices p
ok (s,t) = isLeftTurnOrOn s t q
inConvexInteriour :: (Fractional a, Ord a) => P2 a -> Polygon2 a -> Bool
p `inConvexInteriour` poly = not (doIntersect (Segment qm q) (Segment c p)
|| doIntersect (Segment q qp) (Segment c p))
where ps@(a:_:b:_) = vertices poly
n = length ps
arr = listArray (0,n1) ps
c = 0.5 <*> (a+b)
iq = extremeArr arr (pc) n
qm = arr ! ((iq1) `mod` n)
q = arr ! iq
qp = arr ! ((iq+1) `mod` n)
inConvex :: (Fractional a, Ord a) => P2 a -> Polygon2 a -> Bool
p `inConvex` poly = not (doIntersect (Segment qm q) (Segment c p)
|| doIntersect (Segment q qp) (Segment c p))
|| (isLeftTurnOrOn qm q p && isLeftTurnOrOn q qp p)
where ps@(a:_:b:_) = vertices poly
n = length ps
arr = listArray (0,n1) ps
c = 0.5 <*> (a+b)
iq = extremeArr arr (pc) n
qm = arr ! ((iq1) `mod` n)
q = arr ! iq
qp = arr ! ((iq+1) `mod` n)