never executed always true always false
1 module Reanimate.Math.EarClip
2 ( earCut
3 , earCut'
4 , earClip
5 , earClip'
6 , isEarCorner
7 ) where
8
9 import Data.List
10 import qualified Data.Set as Set
11 import Data.Tuple
12
13 import Reanimate.Math.Common
14 import Reanimate.Math.Triangulate
15
16 import qualified Data.Vector as V
17 import qualified Geometry.Earcut as C
18 import Linear.V2
19
20 -- import Debug.Trace
21
22 earCut :: (Real a) => Ring a -> Triangulation
23 earCut = last . earCut'
24
25 earCut' :: (Real a) => Ring a -> [Triangulation]
26 earCut' p =
27 map (edgesToTriangulation (ringSize p)) $ inits $ nub
28 [ if fst pair < snd pair then pair else swap pair
29 | (a,b,c) <- V.toList (C.earcut lst)
30 , pair <- [(a,b), (a,c), (b,c)]
31 , fst pair /= (snd pair+1) `mod` ringSize p
32 , fst pair /= (snd pair-1) `mod` ringSize p
33 ]
34 where
35 lst = [ (x,y) | V2 x y <- V.toList $ V.map (fmap realToFrac) $ ringUnpack p ]
36
37 -- Triangulation by ear clipping. O(n^2)
38 earClip :: (Fractional a, Ord a, Epsilon a) => Ring a -> Triangulation
39 earClip = last . earClip'
40
41 earClip' :: (Fractional a, Ord a, Epsilon a) => Ring a -> [Triangulation]
42 earClip' p = map (edgesToTriangulation $ ringSize p) $ inits $
43 let ears = Set.fromList [ i
44 | i <- elts
45 , isEarCorner p elts (mod (i-1) n) i (mod (i+1) n) ]
46 in worker ears (mkQueue elts)
47 where
48 n = ringSize p
49 elts = [0 .. n-1]
50 -- worker :: Set.Set Int -> PolyQueue Int -> [(P,P)]
51 worker _ears queue | isSimpleQ queue = []
52 worker ears queue
53 | x `Set.member` ears =
54 let dq = dropQ queue
55 v0 = prevQ 1 queue
56 v1 = prevQ 0 queue
57 v3 = peekQ dq
58 v4 = peekQ (nextQ dq)
59 e1 = if isEarCorner p (toList dq) v0 v1 v3
60 then Set.insert v1 ears
61 else Set.delete v1 ears
62 e2 = if isEarCorner p (toList dq) v1 v3 v4
63 then Set.insert v3 e1
64 else Set.delete v3 e1
65 in (v1,v3) : worker e2 dq
66 | otherwise = worker ears (nextQ queue)
67 where
68 x = peekQ queue
69
70 data PolyQueue a = PolyQueue a [a] [a] [a]
71
72 -- sizeQ :: PolyQueue a -> Int
73 -- sizeQ (PolyQueue _ a b _) = 1 + length a + length b
74
75 mkQueue :: [a] -> PolyQueue a
76 mkQueue (x:xs) = PolyQueue x xs [] (reverse (x:xs))
77 mkQueue [] = error "mkQueue: empty"
78
79 toList :: PolyQueue a -> [a]
80 toList (PolyQueue e a b _) = e : a ++ b
81
82 isSimpleQ :: PolyQueue a -> Bool
83 isSimpleQ (PolyQueue _ xs ys _) =
84 case xs ++ ys of
85 [_,_] -> True
86 _ -> False
87
88 peekQ :: PolyQueue a -> a
89 peekQ (PolyQueue e _ _ _) = e
90
91 nextQ :: PolyQueue a -> PolyQueue a
92 nextQ (PolyQueue x [] ys p) =
93 let (y:xs) = reverse (x:ys)
94 in PolyQueue y xs [] (x:p)
95 nextQ (PolyQueue x (y:xs) ys p) = PolyQueue y xs (x:ys) (x:p)
96
97 dropQ :: PolyQueue a -> PolyQueue a
98 dropQ (PolyQueue _ [] ys p) =
99 let (x:xs) = reverse ys
100 in PolyQueue x xs [] p
101 dropQ (PolyQueue _ (x:xs) ys p) = PolyQueue x xs ys p
102
103 prevQ :: Int -> PolyQueue a -> a
104 prevQ nth (PolyQueue _ _ _ p) = p!!nth
105
106 -- O(n)
107 -- Returns true if ac can be cut from polygon. That is, true if 'b' is an ear.
108 -- isEarCorner polygon a b c = True iff ac can be cut
109 isEarCorner :: (Fractional a, Ord a, Epsilon a) => Ring a -> [Int] -> Int -> Int -> Int -> Bool
110 isEarCorner p polygon a b c =
111 isLeftTurn aP bP cP &&
112 -- If it is a right turn then the line ac will be outside the polygon
113 and [ not (isInside aP bP cP (ringAccess p k))
114 | k <- polygon, k /= a && k /= b && k /= c
115 ]
116 where
117 aP = ringAccess p a
118 bP = ringAccess p b
119 cP = ringAccess p c