diff --git a/Algebra/Polygon.hs b/Algebra/Polygon.hs index cd1d654..3b1d5e3 100644 --- a/Algebra/Polygon.hs +++ b/Algebra/Polygon.hs @@ -4,14 +4,15 @@ module Algebra.Polygon where import Algebra.Vector import Data.Maybe +import Diagrams.TwoD.Types import MyPrelude -- |Split a polygon by a given segment which must be vertices of the -- polygon (returns empty array otherwise). -splitPoly :: [PT] - -> Segment - -> [[PT]] +splitPoly :: [P2] + -> (P2, P2) + -> [[P2]] splitPoly pts (a, b) | elem a pts && elem b pts = [b : takeWhile (/= b) shiftedPoly, a : dropWhile (/= b) shiftedPoly] @@ -21,7 +22,7 @@ splitPoly pts (a, b) -- |Get all edges of a polygon. -polySegments :: [PT] -> [Segment] +polySegments :: [P2] -> [(P2, P2)] polySegments p@(x':_:_:_) = go p ++ [(last p, x')] where go (x:y:xs) = (x, y) : go (y:xs) @@ -32,7 +33,7 @@ polySegments _ = [] -- |Check whether the given segment is inside the polygon. -- This doesn't check for segments that are completely outside -- of the polygon yet. -isInsidePoly :: [PT] -> Segment -> Bool +isInsidePoly :: [P2] -> (P2, P2) -> Bool isInsidePoly pts seg = null . catMaybes @@ -41,21 +42,21 @@ isInsidePoly pts seg = -- |Check whether two points are adjacent vertices of a polygon. -adjacent :: PT -> PT -> [PT] -> Bool +adjacent :: P2 -> P2 -> [P2] -> Bool adjacent u v = any (\x -> x == (u, v) || x == (v, u)) . polySegments -- |Check whether the polygon is a triangle polygon. -isTrianglePoly :: [PT] -> Bool +isTrianglePoly :: [P2] -> Bool isTrianglePoly [_, _, _] = True isTrianglePoly _ = False -- |Get all triangle polygons. -triangleOnly :: [[PT]] -> [[PT]] +triangleOnly :: [[P2]] -> [[P2]] triangleOnly = filter isTrianglePoly -- |Get all non-triangle polygons. -nonTriangleOnly :: [[PT]] -> [[PT]] +nonTriangleOnly :: [[P2]] -> [[P2]] nonTriangleOnly = filter (not . isTrianglePoly) diff --git a/Algebra/Vector.hs b/Algebra/Vector.hs index 4bbbf44..12934dd 100644 --- a/Algebra/Vector.hs +++ b/Algebra/Vector.hs @@ -13,13 +13,6 @@ import GHC.Float import MyPrelude -type Vec = R2 -type PT = P2 -type Coord = (Double, Double) -type Segment = (PT, PT) -type Square = (Coord, Coord) - - data Alignment = CW | CCW | CL @@ -31,13 +24,13 @@ data Alignment = CW -- ((xmin, ymin), (xmax, ymax)) dimToSquare :: (Double, Double) -- ^ x dimension -> (Double, Double) -- ^ y dimension - -> Square -- ^ square describing those dimensions + -> ((Double, Double), (Double, Double)) -- ^ square describing those dimensions dimToSquare (x1, x2) (y1, y2) = ((x1, y1), (x2, y2)) -- |Checks whether the Point is in a given Square. -inRange :: Square -- ^ the square: ((xmin, ymin), (xmax, ymax)) - -> PT -- ^ Coordinate +inRange :: ((Double, Double), (Double, Double)) -- ^ the square: ((xmin, ymin), (xmax, ymax)) + -> P2 -- ^ Coordinate -> Bool -- ^ result inRange ((xmin, ymin), (xmax, ymax)) (coords -> x :& y) = x >= min xmin xmax @@ -47,7 +40,7 @@ inRange ((xmin, ymin), (xmax, ymax)) (coords -> x :& y) -- |Get the angle between two vectors. -getAngle :: Vec -> Vec -> Double +getAngle :: R2 -> R2 -> Double getAngle a b = acos . flip (/) (vecLength a * vecLength b) @@ -56,48 +49,50 @@ getAngle a b = -- |Get the length of a vector. -vecLength :: Vec -> Double +vecLength :: R2 -> Double vecLength v = sqrt (x^(2 :: Int) + y^(2 :: Int)) where (x, y) = unr2 v -- |Compute the scalar product of two vectors. -scalarProd :: Vec -> Vec -> Double +scalarProd :: R2 -> R2 -> Double scalarProd (R2 a1 a2) (R2 b1 b2) = a1 * b1 + a2 * b2 -- |Multiply a scalar with a vector. -scalarMul :: Double -> Vec -> Vec +scalarMul :: Double -> R2 -> R2 scalarMul d (R2 a b) = R2 (a * d) (b * d) -- |Construct a vector that points to a point from the origin. -pt2Vec :: PT -> Vec +pt2Vec :: P2 -> R2 pt2Vec = r2 . unp2 -- |Give the point which is at the coordinates the vector -- points to from the origin. -vec2Pt :: Vec -> PT +vec2Pt :: R2 -> P2 vec2Pt = p2 . unr2 -- |Construct a vector between two points. -vp2 :: PT -- ^ vector origin - -> PT -- ^ vector points here - -> Vec +vp2 :: P2 -- ^ vector origin + -> P2 -- ^ vector points here + -> R2 vp2 a b = pt2Vec b - pt2Vec a -- |Computes the determinant of 3 points. -det :: PT -> PT -> PT -> Double +det :: P2 -> P2 -> P2 -> Double det (coords -> ax :& ay) (coords -> bx :& by) (coords -> cx :& cy) = (bx - ax) * (cy - ay) - (by - ay) * (cx - ax) -- |Get the point where two lines intesect, if any. -intersectSeg' :: Segment -> Segment -> Maybe PT +intersectSeg' :: (P2, P2) -- ^ first segment + -> (P2, P2) -- ^ second segment + -> Maybe P2 intersectSeg' (a, b) (c, d) = glossToPt <$> intersectSegSeg (ptToGloss a) (ptToGloss b) @@ -110,7 +105,7 @@ intersectSeg' (a, b) (c, d) = -- |Get the point where two lines intesect, if any. Excludes the -- case of end-points intersecting. -intersectSeg'' :: Segment -> Segment -> Maybe PT +intersectSeg'' :: (P2, P2) -> (P2, P2) -> Maybe P2 intersectSeg'' (a, b) (c, d) = case intersectSeg' (a, b) (c, d) of Just x -> if x `notElem` [a,b,c,d] then Just a else Nothing Nothing -> Nothing @@ -120,7 +115,7 @@ intersectSeg'' (a, b) (c, d) = case intersectSeg' (a, b) (c, d) of -- * clock-wise -- * counter-clock-wise -- * collinear -getOrient :: PT -> PT -> PT -> Alignment +getOrient :: P2 -> P2 -> P2 -> Alignment getOrient a b c = case compare (det a b c) 0 of LT -> CW GT -> CCW @@ -130,7 +125,7 @@ getOrient a b c = case compare (det a b c) 0 of --- |Checks if 3 points a,b,c do not build a clockwise triangle by --- connecting a-b-c. This is done by computing the determinant and --- checking the algebraic sign. -notcw :: PT -> PT -> PT -> Bool +notcw :: P2 -> P2 -> P2 -> Bool notcw a b c = case getOrient a b c of CW -> False _ -> True @@ -139,22 +134,22 @@ notcw a b c = case getOrient a b c of --- |Checks if 3 points a,b,c do build a clockwise triangle by --- connecting a-b-c. This is done by computing the determinant and --- checking the algebraic sign. -cw :: PT -> PT -> PT -> Bool +cw :: P2 -> P2 -> P2 -> Bool cw a b c = not . notcw a b $ c -- |Sort X and Y coordinates lexicographically. -sortedXY :: [PT] -> [PT] +sortedXY :: [P2] -> [P2] sortedXY = fmap p2 . sortLex . fmap unp2 -- |Sort Y and X coordinates lexicographically. -sortedYX :: [PT] -> [PT] +sortedYX :: [P2] -> [P2] sortedYX = fmap p2 . sortLexSwapped . fmap unp2 -- |Sort all points according to their X-coordinates only. -sortedX :: [PT] -> [PT] +sortedX :: [P2] -> [P2] sortedX xs = fmap p2 . sortBy (\(a1, _) (a2, _) -> compare a1 a2) @@ -162,7 +157,7 @@ sortedX xs = -- |Sort all points according to their Y-coordinates only. -sortedY :: [PT] -> [PT] +sortedY :: [P2] -> [P2] sortedY xs = fmap p2 . sortBy (\(_, b1) (_, b2) -> compare b1 b2) @@ -170,25 +165,25 @@ sortedY xs = -- |Apply a function on the coordinates of a point. -onPT :: (Coord -> Coord) -> PT -> PT +onPT :: ((Double, Double) -> (Double, Double)) -> P2 -> P2 onPT f = p2 . f . unp2 -- |Compare the y-coordinate of two points. -ptCmpY :: PT -> PT -> Ordering +ptCmpY :: P2 -> P2 -> Ordering ptCmpY (coords -> _ :& y1) (coords -> _ :& y2) = compare y1 y2 -- |Compare the x-coordinate of two points. -ptCmpX :: PT -> PT -> Ordering +ptCmpX :: P2 -> P2 -> Ordering ptCmpX (coords -> x1 :& _) (coords -> x2 :& _) = compare x1 x2 -posInfPT :: PT +posInfPT :: P2 posInfPT = p2 (read "Infinity", read "Infinity") -negInfPT :: PT +negInfPT :: P2 negInfPT = p2 (negate . read $ "Infinity", negate . read $ "Infinity") diff --git a/Algorithms/GrahamScan.hs b/Algorithms/GrahamScan.hs index 1cd4ac9..d394fae 100644 --- a/Algorithms/GrahamScan.hs +++ b/Algorithms/GrahamScan.hs @@ -3,6 +3,7 @@ module Algorithms.GrahamScan where import Algebra.Vector +import Diagrams.TwoD.Types import MyPrelude @@ -74,18 +75,18 @@ ys = [] return [(100, 100), (400, 200)] ========================================================= --} -grahamCH :: [PT] -> [PT] +grahamCH :: [P2] -> [P2] grahamCH vs = grahamUCH vs ++ (tailInit . grahamLCH $ vs) -- |Get the lower part of the convex hull. -grahamLCH :: [PT] -> [PT] +grahamLCH :: [P2] -> [P2] grahamLCH vs = uncurry (\x y -> last . scanH x $ y) (first reverse . splitAt 3 . sortedXY $ vs) -- |Get the upper part of the convex hull. -grahamUCH :: [PT] -> [PT] +grahamUCH :: [P2] -> [P2] grahamUCH vs = uncurry (\x y -> last . scanH x $ y) (first reverse . splitAt 3 . reverse . sortedXY $ vs) @@ -95,9 +96,9 @@ grahamUCH vs = uncurry (\x y -> last . scanH x $ y) -- If it's the upper or lower half depends on the input. -- Also, the first list is expected to be reversed since we only care -- about the last 3 elements and want to stay efficient. -scanH :: [PT] -- ^ the first 3 starting points in reversed order - -> [PT] -- ^ the rest of the points - -> [[PT]] -- ^ all convex hull points iterations for the half +scanH :: [P2] -- ^ the first 3 starting points in reversed order + -> [P2] -- ^ the rest of the points + -> [[P2]] -- ^ all convex hull points iterations for the half scanH hs@(x:y:z:xs) (r':rs') | notcw z y x = hs : scanH (r':hs) rs' | otherwise = hs : scanH (x:z:xs) (r':rs') @@ -111,12 +112,12 @@ scanH hs _ = [hs] -- |Compute all steps of the graham scan algorithm to allow -- visualizing it. -- Whether the upper or lower hull is computed depends on the input. -grahamCHSteps :: Int -> [PT] -> [PT] -> [[PT]] +grahamCHSteps :: Int -> [P2] -> [P2] -> [[P2]] grahamCHSteps c xs' ys' = take c . scanH xs' $ ys' -- |Get all iterations of the upper hull of the graham scan algorithm. -grahamUHSteps :: [PT] -> [[PT]] +grahamUHSteps :: [P2] -> [[P2]] grahamUHSteps vs = (++) [getLastX 2 . sortedXY $ vs] . rmdups @@ -127,7 +128,7 @@ grahamUHSteps vs = -- |Get all iterations of the lower hull of the graham scan algorithm. -grahamLHSteps :: [PT] -> [[PT]] +grahamLHSteps :: [P2] -> [[P2]] grahamLHSteps vs = (++) [take 2 . sortedXY $ vs] . rmdups diff --git a/Algorithms/KDTree.hs b/Algorithms/KDTree.hs index 82ab39a..7d7b35f 100644 --- a/Algorithms/KDTree.hs +++ b/Algorithms/KDTree.hs @@ -42,9 +42,9 @@ instance Not Direction where -- |Construct a kd-tree from a list of points in O(n log n). -kdTree :: [PT] -- ^ list of points to construct the kd-tree from +kdTree :: [P2] -- ^ list of points to construct the kd-tree from -> Direction -- ^ initial direction of the root-node - -> KDTree PT -- ^ resulting kd-tree + -> KDTree P2 -- ^ resulting kd-tree kdTree xs' = go (sortedX xs') (sortedY xs') where go [] _ _ = KTNil @@ -67,10 +67,10 @@ kdTree xs' = go (sortedX xs') (sortedY xs') -- If you want to partition against the pivot of X, then you pass -- partition' (pivot xs) (ys, xs) -- and get ((y1, y2), (x1, x2)). -partition' :: PT -- ^ the pivot to partition against - -> (PT -> PT -> Ordering) -- ^ ptCmpY or ptCmpX - -> ([PT], [PT]) -- ^ both lists (X, Y) or (Y, X) - -> (([PT], [PT]), ([PT], [PT])) -- ^ ((x1, x2), (y1, y2)) or +partition' :: P2 -- ^ the pivot to partition against + -> (P2 -> P2 -> Ordering) -- ^ ptCmpY or ptCmpX + -> ([P2], [P2]) -- ^ both lists (X, Y) or (Y, X) + -> (([P2], [P2]), ([P2], [P2])) -- ^ ((x1, x2), (y1, y2)) or -- ((y1, y2), (x1, x2)) partition' piv cmp' (xs, ys) = ((x1, x2), (y1, y2)) where @@ -83,16 +83,16 @@ partition' piv cmp' (xs, ys) = ((x1, x2), (y1, y2)) -- |Partition two sorted lists of points X and Y against the pivot of -- Y. This function is unsafe as it does not check if there is a valid -- pivot. -partitionY :: ([PT], [PT]) -- ^ both lists (X, Y) - -> (([PT], [PT]), ([PT], [PT])) -- ^ ((x1, x2), (y1, y2)) +partitionY :: ([P2], [P2]) -- ^ both lists (X, Y) + -> (([P2], [P2]), ([P2], [P2])) -- ^ ((x1, x2), (y1, y2)) partitionY (xs, ys) = partition' (fromJust . pivot $ ys) ptCmpY (xs, ys) -- |Partition two sorted lists of points X and Y against the pivot of -- X. This function is unsafe as it does not check if there is a valid -- pivot. -partitionX :: ([PT], [PT]) -- ^ both lists (X, Y) - -> (([PT], [PT]), ([PT], [PT])) -- ^ ((x1, x2), (y1, y2)) +partitionX :: ([P2], [P2]) -- ^ both lists (X, Y) + -> (([P2], [P2]), ([P2], [P2])) -- ^ ((x1, x2), (y1, y2)) partitionX (xs, ys) = (\(x, y) -> (y, x)) . partition' (fromJust . pivot $ xs) ptCmpX $ (ys, xs) @@ -100,7 +100,9 @@ partitionX (xs, ys) = (\(x, y) -> (y, x)) -- |Execute a range search in O(log n). It returns a tuple -- of the points found in the range and also gives back a pretty -- rose tree suitable for printing. -rangeSearch :: KDTree PT -> Square -> ([PT], Tree String) +rangeSearch :: KDTree P2 -- ^ tree to search in + -> ((Double, Double), (Double, Double)) -- ^ square describing the range + -> ([P2], Tree String) rangeSearch kd' sq' = (goPt kd' sq', goTree kd' sq' True) where -- either y1 or x1 depending on the orientation @@ -110,7 +112,7 @@ rangeSearch kd' sq' = (goPt kd' sq', goTree kd' sq' True) -- either the second or first of the tuple, depending on the orientation cur' dir = if' (dir == Vertical) snd fst -- All points in the range. - goPt :: KDTree PT -> Square -> [PT] + goPt :: KDTree P2 -> ((Double, Double), (Double, Double)) -> [P2] goPt KTNil _ = [] goPt (KTNode ln pt dir rn) sq = [pt | inRange sq pt] @@ -122,7 +124,7 @@ rangeSearch kd' sq' = (goPt kd' sq', goTree kd' sq' True) (goPt rn sq) []) -- A pretty rose tree suitable for printing. - goTree :: KDTree PT -> Square -> Bool -> Tree String + goTree :: KDTree P2 -> ((Double, Double), (Double, Double)) -> Bool -> Tree String goTree KTNil _ _ = Node "nil" [] goTree (KTNode ln pt dir rn) sq vis | ln == KTNil && rn == KTNil = Node treeText [] @@ -179,7 +181,7 @@ getDirection _ = Nothing -- |Convert a kd-tree to a rose tree, for pretty printing. -kdTreeToRoseTree :: KDTree PT -> Tree String +kdTreeToRoseTree :: KDTree P2 -> Tree String kdTreeToRoseTree (KTNil) = Node "nil" [] kdTreeToRoseTree (KTNode ln val _ rn) = Node (show . unp2 $ val) [kdTreeToRoseTree ln, kdTreeToRoseTree rn] diff --git a/Algorithms/PolygonIntersection.hs b/Algorithms/PolygonIntersection.hs index 5969b43..85998d3 100644 --- a/Algorithms/PolygonIntersection.hs +++ b/Algorithms/PolygonIntersection.hs @@ -18,14 +18,14 @@ import QueueEx -- successor are saved for convenience. data PolyPT = PolyA { - id' :: PT - , pre :: PT - , suc :: PT + id' :: P2 + , pre :: P2 + , suc :: P2 } | PolyB { - id' :: PT - , pre :: PT - , suc :: PT + id' :: P2 + , pre :: P2 + , suc :: P2 } deriving (Show, Eq) @@ -42,7 +42,7 @@ isPolyB = not . isPolyA -- |Shift a list of sorted convex hull points of a polygon so that -- the first element in the list is the one with the highest y-coordinate. -- This is done in O(n). -sortLexPoly :: [PT] -> [PT] +sortLexPoly :: [P2] -> [P2] sortLexPoly ps = maybe [] (`shiftM` ps) (elemIndex (yMax ps) ps) where yMax = foldl1 (\x y -> if ptCmpY x y == GT then x else y) @@ -50,8 +50,8 @@ sortLexPoly ps = maybe [] (`shiftM` ps) (elemIndex (yMax ps) ps) -- |Make a PolyPT list out of a regular list of points, so -- the predecessor and successors are all saved. -mkPolyPTList :: (PT -> PT -> PT -> PolyPT) -- ^ PolyA or PolyB function - -> [PT] -- ^ polygon points +mkPolyPTList :: (P2 -> P2 -> P2 -> PolyPT) -- ^ PolyA or PolyB function + -> [P2] -- ^ polygon points -> [PolyPT] mkPolyPTList f' pts@(x':y':_:_) = f' x' (last pts) y' : go f' pts @@ -64,7 +64,7 @@ mkPolyPTList _ _ = [] -- |Sort the points of two polygons according to their y-coordinates, -- while saving the origin of that point. This is done in O(n). -sortLexPolys :: ([PT], [PT]) -> [PolyPT] +sortLexPolys :: ([P2], [P2]) -> [PolyPT] sortLexPolys (pA'@(_:_), pB'@(_:_)) = queueToList $ go (Q.fromList . mkPolyPTList PolyA . sortLexPoly $ pA') (Q.fromList . mkPolyPTList PolyB . sortLexPoly $ pB') @@ -104,7 +104,7 @@ sortLexPolys _ = [] -- |Get all points that intersect between both polygons. This is done -- in O(n). -intersectionPoints :: [PolyPT] -> [PT] +intersectionPoints :: [PolyPT] -> [P2] intersectionPoints xs' = rmdups . go $ xs' where go [] = [] @@ -113,7 +113,7 @@ intersectionPoints xs' = rmdups . go $ xs' -- Get the scan line or in other words the -- Segment pairs we are going to check for intersection. - scanLine :: [PolyPT] -> ([Segment], [Segment]) + scanLine :: [PolyPT] -> ([(P2, P2)], [(P2, P2)]) scanLine sp@(_:_) = (,) (getSegment isPolyA) (getSegment isPolyB) where getSegment f = fromMaybe [] @@ -124,7 +124,7 @@ intersectionPoints xs' = rmdups . go $ xs' -- Gets the actual intersections between the segments of -- both polygons we currently examine. This is done in O(1) -- since we have max 4 segments. - segIntersections :: ([Segment], [Segment]) -> [PT] + segIntersections :: ([(P2, P2)], [(P2, P2)]) -> [P2] segIntersections (a@(_:_), b@(_:_)) = catMaybes . fmap (\[x, y] -> intersectSeg' x y) diff --git a/Algorithms/PolygonTriangulation.hs b/Algorithms/PolygonTriangulation.hs index 7313a96..5c34295 100644 --- a/Algorithms/PolygonTriangulation.hs +++ b/Algorithms/PolygonTriangulation.hs @@ -6,6 +6,7 @@ import Algebra.Polygon import Algebra.Vector import qualified Control.Arrow as A import Data.Maybe +import Diagrams.TwoD.Types import Safe @@ -18,12 +19,12 @@ data VCategory = VStart -- |Classify all vertices on a polygon into five categories (see VCategory). -classifyList :: [PT] -> [(PT, VCategory)] +classifyList :: [P2] -> [(P2, VCategory)] classifyList p@(x:y:_:_) = -- need to handle the first and last element separately [classify (last p) x y] ++ go p ++ [classify (last . init $ p) (last p) x] where - go :: [PT] -> [(PT, VCategory)] + go :: [P2] -> [(P2, VCategory)] go (x':y':z':xs) = classify x' y' z' : go (y':z':xs) go _ = [] classifyList _ = [] @@ -31,10 +32,10 @@ classifyList _ = [] -- |Classify a vertex on a polygon given it's next and previous vertex -- into five categories (see VCategory). -classify :: PT -- ^ prev vertex - -> PT -- ^ classify this one - -> PT -- ^ next vertex - -> (PT, VCategory) +classify :: P2 -- ^ prev vertex + -> P2 -- ^ classify this one + -> P2 -- ^ next vertex + -> (P2, VCategory) classify prev v next | isVStart prev v next = (v, VStart) | isVSplit prev v next = (v, VSplit) @@ -45,9 +46,9 @@ classify prev v next -- |Whether the vertex, given it's next and previous vertex, -- is a start vertex. -isVStart :: PT -- ^ previous vertex - -> PT -- ^ vertice to check - -> PT -- ^ next vertex +isVStart :: P2 -- ^ previous vertex + -> P2 -- ^ vertice to check + -> P2 -- ^ next vertex -> Bool isVStart prev v next = ptCmpY next v == LT && ptCmpY prev v == LT && cw next v prev @@ -55,9 +56,9 @@ isVStart prev v next = -- |Whether the vertex, given it's next and previous vertex, -- is a split vertex. -isVSplit :: PT -- ^ previous vertex - -> PT -- ^ vertice to check - -> PT -- ^ next vertex +isVSplit :: P2 -- ^ previous vertex + -> P2 -- ^ vertice to check + -> P2 -- ^ next vertex -> Bool isVSplit prev v next = ptCmpY prev v == LT && ptCmpY next v == LT && cw prev v next @@ -65,9 +66,9 @@ isVSplit prev v next = -- |Whether the vertex, given it's next and previous vertex, -- is an end vertex. -isVEnd :: PT -- ^ previous vertex - -> PT -- ^ vertice to check - -> PT -- ^ next vertex +isVEnd :: P2 -- ^ previous vertex + -> P2 -- ^ vertice to check + -> P2 -- ^ next vertex -> Bool isVEnd prev v next = ptCmpY prev v == GT && ptCmpY next v == GT && cw next v prev @@ -75,9 +76,9 @@ isVEnd prev v next = -- |Whether the vertex, given it's next and previous vertex, -- is a merge vertex. -isVMerge :: PT -- ^ previous vertex - -> PT -- ^ vertice to check - -> PT -- ^ next vertex +isVMerge :: P2 -- ^ previous vertex + -> P2 -- ^ vertice to check + -> P2 -- ^ next vertex -> Bool isVMerge prev v next = ptCmpY next v == GT && ptCmpY prev v == GT && cw prev v next @@ -85,9 +86,9 @@ isVMerge prev v next = -- |Whether the vertex, given it's next and previous vertex, -- is a regular vertex. -isVRegular :: PT -- ^ previous vertex - -> PT -- ^ vertice to check - -> PT -- ^ next vertex +isVRegular :: P2 -- ^ previous vertex + -> P2 -- ^ vertice to check + -> P2 -- ^ next vertex -> Bool isVRegular prev v next = (not . isVStart prev v $ next) @@ -98,7 +99,7 @@ isVRegular prev v next = -- |A polygon P is y-monotone, if it has no split and merge vertices. -isYmonotone :: [PT] -> Bool +isYmonotone :: [P2] -> Bool isYmonotone poly = not . any (\x -> x == VSplit || x == VMerge) @@ -107,12 +108,12 @@ isYmonotone poly = -- |Partition P into y-monotone pieces. -monotonePartitioning :: [PT] -> [[PT]] +monotonePartitioning :: [P2] -> [[P2]] monotonePartitioning pts | isYmonotone pts = [pts] | otherwise = go (monotoneDiagonals pts) pts where - go :: [Segment] -> [PT] -> [[PT]] + go :: [(P2, P2)] -> [P2] -> [[P2]] go (x:xs) pts'@(_:_) | isYmonotone a && isYmonotone b = [a, b] | isYmonotone b = b : go xs a @@ -124,37 +125,37 @@ monotonePartitioning pts -- |Try to eliminate the merge and split vertices by computing the -- diagonals we have to use for splitting the polygon. -monotoneDiagonals :: [PT] -> [Segment] +monotoneDiagonals :: [P2] -> [(P2, P2)] monotoneDiagonals pts = catMaybes . go $ classifyList pts where - go :: [(PT, VCategory)] -> [Maybe Segment] + go :: [(P2, VCategory)] -> [Maybe (P2, P2)] go (x:xs) = case snd x of VMerge -> getSeg (belowS . fst $ x) (fst x) : go xs VSplit -> getSeg (aboveS . fst $ x) (fst x) : go xs _ -> [] ++ go xs go [] = [] - getSeg :: [PT] -- all points above/below the current point - -> PT -- current point - -> Maybe Segment + getSeg :: [P2] -- all points above/below the current point + -> P2 -- current point + -> Maybe (P2, P2) getSeg [] _ = Nothing getSeg (z:zs) pt | isInsidePoly pts (z, pt) = Just (z, pt) | otherwise = getSeg zs pt - aboveS :: PT -> [PT] + aboveS :: P2 -> [P2] aboveS pt = tail . dropWhile (/= pt) $ sortedYX pts - belowS :: PT -> [PT] + belowS :: P2 -> [P2] belowS pt = reverse . takeWhile (/= pt) $ sortedYX pts -- |Triangulate a y-monotone polygon. -triangulate :: [PT] -> [[PT]] +triangulate :: [P2] -> [[P2]] triangulate pts = go pts . A.first reverse . splitAt 3 . reverse . sortedYX $ pts where - go :: [PT] -- current polygon - -> ([PT], [PT]) -- (stack of visited vertices, rest) + go :: [P2] -- current polygon + -> ([P2], [P2]) -- (stack of visited vertices, rest) -- sorted by Y-coordinate - -> [[PT]] + -> [[P2]] go xs (p@[_, _], r:rs) = go xs (r:p, rs) go xs (p@(u:vi:vi1:ys), rs) -- case 1 and 3 diff --git a/Algorithms/QuadTree.hs b/Algorithms/QuadTree.hs index 7251e0b..19b5723 100644 --- a/Algorithms/QuadTree.hs +++ b/Algorithms/QuadTree.hs @@ -56,7 +56,8 @@ data Orient = North | South | East | West -- |Get a sub-square of the current square, e.g. nw, ne, sw or se. -nwSq, neSq, swSq, seSq :: Square -> Square +nwSq, neSq, swSq, seSq :: ((Double, Double), (Double, Double)) -- ^ current square + -> ((Double, Double), (Double, Double)) -- ^ sub-square nwSq ((xl, yl), (xu, yu)) = (,) (xl, (yl + yu) / 2) ((xl + xu) / 2, yu) neSq ((xl, yl), (xu, yu)) = (,) ((xl + xu) / 2, (yl + yu) / 2) (xu, yu) swSq ((xl, yl), (xu, yu)) = (,) (xl, yl) ((xl + xu) / 2, (yl + yu) / 2) @@ -79,9 +80,9 @@ isSEchild _ = False -- |Builds a quadtree of a list of points which recursively divides up 2D -- space into quadrants, so that every leaf-quadrant stores either zero or one -- point. -quadTree :: [PT] -- ^ the points to divide - -> Square -- ^ the initial square around the points - -> QuadTree PT -- ^ the quad tree +quadTree :: [P2] -- ^ the points to divide + -> ((Double, Double), (Double, Double)) -- ^ the initial square around the points + -> QuadTree P2 -- ^ the quad tree quadTree [] _ = TNil quadTree [pt] _ = TLeaf pt quadTree pts sq = TNode (quadTree nWPT . nwSq $ sq) (quadTree nEPT . neSq $ sq) @@ -95,9 +96,9 @@ quadTree pts sq = TNode (quadTree nWPT . nwSq $ sq) (quadTree nEPT . neSq $ sq) -- |Get all squares of a quad tree. -quadTreeSquares :: Square -- ^ the initial square around the points - -> QuadTree PT -- ^ the quad tree - -> [Square] -- ^ all squares of the quad tree +quadTreeSquares :: ((Double, Double), (Double, Double)) -- ^ the initial square around the points + -> QuadTree P2 -- ^ the quad tree + -> [((Double, Double), (Double, Double))] -- ^ all squares of the quad tree quadTreeSquares sq (TNil) = [sq] quadTreeSquares sq (TLeaf _) = [sq] quadTreeSquares sq (TNode nw ne sw se) = @@ -107,7 +108,9 @@ quadTreeSquares sq (TNode nw ne sw se) = -- |Get the current square of the zipper, relative to the given top -- square. -getSquareByZipper :: Square -> QTZipper a -> Square +getSquareByZipper :: ((Double, Double), (Double, Double)) -- ^ top square + -> QTZipper a + -> ((Double, Double), (Double, Double)) -- ^ current square getSquareByZipper sq z = go sq (reverse . snd $ z) where go sq' [] = sq' @@ -200,7 +203,7 @@ lookupByNeighbors :: [Orient] -> QTZipper a -> Maybe (QTZipper a) lookupByNeighbors = flip (foldlM (flip findNeighbor)) -quadTreeToRoseTree :: QTZipper PT -> Tree String +quadTreeToRoseTree :: QTZipper P2 -> Tree String quadTreeToRoseTree z' = go (rootNode z') where go z = case z of diff --git a/Graphics/Diagram/AlgoDiags.hs b/Graphics/Diagram/AlgoDiags.hs index 6e12c73..2c6fba2 100644 --- a/Graphics/Diagram/AlgoDiags.hs +++ b/Graphics/Diagram/AlgoDiags.hs @@ -2,7 +2,6 @@ module Graphics.Diagram.AlgoDiags where -import Algebra.Vector(PT,Square) import Algorithms.GrahamScan import Algorithms.QuadTree import Algorithms.KDTree @@ -124,7 +123,9 @@ kdSquares = Diag f where -- Gets all lines that make up the kdSquares. Every line is -- described by two points, start and end respectively. - kdLines :: KDTree PT -> Square -> [(PT, PT)] + kdLines :: KDTree P2 + -> ((Double, Double), (Double, Double)) -- ^ square + -> [(P2, P2)] kdLines (KTNode ln pt Horizontal rn) ((xmin, ymin), (xmax, ymax)) = (\(x, _) -> [(p2 (x, ymin), p2 (x, ymax))]) (unp2 pt) @@ -179,7 +180,7 @@ kdTreeDiag = Diag f -- |Get the quad tree corresponding to the given points and diagram properties. -qt :: [PT] -> DiagProp -> QuadTree PT +qt :: [P2] -> DiagProp -> QuadTree P2 qt vt p = quadTree vt (diagDimSquare p) @@ -192,7 +193,9 @@ quadPathSquare = Diag f (uncurry rectByDiagonal # lw thin # lc red) (getSquare (stringToQuads (quadPath p)) (qt (mconcat vts) p, [])) where - getSquare :: [Either Quad Orient] -> QTZipper PT -> Square + getSquare :: [Either Quad Orient] + -> QTZipper P2 + -> ((Double, Double), (Double, Double)) getSquare [] z = getSquareByZipper (diagDimSquare p) z getSquare (q:qs) z = case q of Right x -> getSquare qs (fromMaybe z (findNeighbor x z)) @@ -208,7 +211,9 @@ gifQuadPath = GifDiag f (uncurry rectByDiagonal # lw thick # lc col) <$> getSquares (stringToQuads (quadPath p)) (qt vt p, []) where - getSquares :: [Either Quad Orient] -> QTZipper PT -> [Square] + getSquares :: [Either Quad Orient] + -> QTZipper P2 + -> [((Double, Double), (Double, Double))] getSquares [] z = [getSquareByZipper (diagDimSquare p) z] getSquares (q:qs) z = case q of Right x -> getSquareByZipper (diagDimSquare p) z : @@ -228,7 +233,7 @@ treePretty = Diag f . quadPath $ p) where - getCurQT :: [Either Quad Orient] -> QTZipper PT -> QTZipper PT + getCurQT :: [Either Quad Orient] -> QTZipper P2 -> QTZipper P2 getCurQT [] z = z getCurQT (q:qs) z = case q of Right x -> getCurQT qs (fromMaybe z (findNeighbor x z)) diff --git a/Graphics/Diagram/Core.hs b/Graphics/Diagram/Core.hs index c31bfb4..f072639 100644 --- a/Graphics/Diagram/Core.hs +++ b/Graphics/Diagram/Core.hs @@ -15,15 +15,15 @@ data Diag = Diag { mkDiag :: DiagProp - -> [[PT]] + -> [[P2]] -> Diagram Cairo R2 } | GifDiag { mkGifDiag :: DiagProp -> Colour Double - -> ([PT] -> [[PT]]) - -> [PT] + -> ([P2] -> [[P2]]) + -> [P2] -> [Diagram Cairo R2] } | EmptyDiag (Diagram Cairo R2) @@ -49,7 +49,7 @@ data DiagProp = MkProp { -- |The path to a quad in the quad tree. quadPath :: String, -- |The square for the kd-tree range search. - rangeSquare :: Square + rangeSquare :: ((Double, Double), (Double, Double)) } @@ -134,19 +134,19 @@ maybeDiag b d | otherwise = mempty -filterValidPT :: DiagProp -> [PT] -> [PT] +filterValidPT :: DiagProp -> [P2] -> [P2] filterValidPT = filter . inRange . diagDimSquare -diagDimSquare :: DiagProp -> Square +diagDimSquare :: DiagProp -> ((Double, Double), (Double, Double)) diagDimSquare p = dimToSquare (xDimension p) $ yDimension p -- |Draw a list of points. -drawP :: [PT] -- ^ the points to draw +drawP :: [P2] -- ^ the points to draw -> Double -- ^ dot size -> Diagram Cairo R2 -- ^ the resulting diagram drawP [] _ = mempty @@ -172,7 +172,7 @@ rectByDiagonal (xmin, ymin) (xmax, ymax) = -- |Creates a Diagram from a point that shows the coordinates -- in text format, such as "(1.0, 2.0)". -pointToTextCoord :: PT -> Diagram Cairo R2 +pointToTextCoord :: P2 -> Diagram Cairo R2 pointToTextCoord pt = text ("(" ++ (show . trim') x ++ ", " ++ (show . trim') y ++ ")") # scale 10 where diff --git a/Graphics/Diagram/Gtk.hs b/Graphics/Diagram/Gtk.hs index bdb4c77..32e7f19 100644 --- a/Graphics/Diagram/Gtk.hs +++ b/Graphics/Diagram/Gtk.hs @@ -2,7 +2,6 @@ module Graphics.Diagram.Gtk where -import Algebra.Vector(PT) import qualified Data.ByteString.Char8 as B import Data.List(find) import Diagrams.Backend.Cairo @@ -46,7 +45,7 @@ diagTreAlgos = -- |Create the Diagram from the points. -diag :: DiagProp -> [DiagAlgo] -> [[PT]] -> Diagram Cairo R2 +diag :: DiagProp -> [DiagAlgo] -> [[P2]] -> Diagram Cairo R2 diag p das vts = maybe mempty (\x -> mkDiag x p vts) $ mconcat -- get the actual [Diag] array diff --git a/Parser/Meshparser.hs b/Parser/Meshparser.hs index 9b61f0b..ce3408f 100644 --- a/Parser/Meshparser.hs +++ b/Parser/Meshparser.hs @@ -2,7 +2,6 @@ module Parser.Meshparser (meshToArr, facesToArr) where -import Algebra.Vector(PT) import Control.Applicative import Data.Attoparsec.ByteString.Char8 import Data.Either @@ -12,7 +11,7 @@ import Diagrams.TwoD.Types -- |Convert a text String with multiple vertices and faces into -- a list of vertices, ordered by the faces specification. -facesToArr :: B.ByteString -> [[PT]] +facesToArr :: B.ByteString -> [[P2]] facesToArr str = fmap (fmap (\y -> meshToArr str !! (fromIntegral y - 1))) (faces str) where @@ -22,7 +21,7 @@ facesToArr str = fmap (fmap (\y -> meshToArr str !! (fromIntegral y - 1))) -- |Convert a text String with multiple vertices into -- an array of float tuples. meshToArr :: B.ByteString -- ^ the string to convert - -> [PT] -- ^ the resulting vertice table + -> [P2] -- ^ the resulting vertice table meshToArr = fmap p2 . rights diff --git a/Test/Vector.hs b/Test/Vector.hs index 52c1423..358d567 100644 --- a/Test/Vector.hs +++ b/Test/Vector.hs @@ -82,40 +82,40 @@ instance Arbitrary P2 where -- the point describing the lower left corner of the square -- must be part of the square -inRangeProp1 :: Square -> Bool +inRangeProp1 :: ((Double, Double), (Double, Double)) -> Bool inRangeProp1 sq@((x1, y1), _) = inRange sq (p2 (x1, y1)) -- the point describing the upper right corner of the square -- must be part of the square -inRangeProp2 :: Square -> Bool +inRangeProp2 :: ((Double, Double), (Double, Double)) -> Bool inRangeProp2 sq@(_, (x2, y2)) = inRange sq (p2 (x2, y2)) -- the point describing the upper left corner of the square -- must be part of the square -inRangeProp3 :: Square -> Bool +inRangeProp3 :: ((Double, Double), (Double, Double)) -> Bool inRangeProp3 sq@((x1, _), (_, y2)) = inRange sq (p2 (x1, y2)) -- the point describing the lower right corner of the square -- must be part of the square -inRangeProp4 :: Square -> Bool +inRangeProp4 :: ((Double, Double), (Double, Double)) -> Bool inRangeProp4 sq@((_, y1), (x2, _)) = inRange sq (p2 (x2, y1)) -- generating random points within the square -inRangeProp5 :: Square -> Positive Double -> Positive Double -> Bool +inRangeProp5 :: ((Double, Double), (Double, Double)) -> Positive Double -> Positive Double -> Bool inRangeProp5 sq@((x1, y1), (x2, y2)) (Positive a) (Positive b) = inRange sq (p2 (x1 + ((x2 - x1) / (a + 1)), y1 + ((y2 - y1) / (b + 1)))) -- generating random points outside of the square -inRangeProp6 :: Square -> Positive Double -> Positive Double -> Bool +inRangeProp6 :: ((Double, Double), (Double, Double)) -> Positive Double -> Positive Double -> Bool inRangeProp6 sq@((x1, y1), (x2, y2)) (Positive a) (Positive b) = (not . inRange sq $ p2 (max x1 x2 + (a + 1), max y1 y2 + (b + 1))) && (not . inRange sq $ p2 (max x1 x2 + (a + 1), max y1 y2 - (b + 1))) @@ -126,51 +126,51 @@ inRangeProp6 sq@((x1, y1), (x2, y2)) (Positive a) (Positive b) = -- apply id function on the point -onPTProp1 :: PT -> Bool +onPTProp1 :: P2 -> Bool onPTProp1 pt = onPT id pt == pt -- add a random value to the point coordinates -onPTProp2 :: PT -> Positive R2 -> Bool +onPTProp2 :: P2 -> Positive R2 -> Bool onPTProp2 pt (Positive (R2 rx ry)) = onPT (\(x, y) -> (x + rx, y + ry)) pt /= pt -- angle between two vectors both on the x-axis must be 0 -getAngleProp1 :: Positive Vec -> Positive Vec -> Bool +getAngleProp1 :: Positive R2 -> Positive R2 -> Bool getAngleProp1 (Positive (R2 x1 _)) (Positive (R2 x2 _)) = getAngle (R2 x1 0) (R2 x2 0) == 0 -- angle between two vectors both on the y-axis must be 0 -getAngleProp2 :: Positive Vec -> Positive Vec -> Bool +getAngleProp2 :: Positive R2 -> Positive R2 -> Bool getAngleProp2 (Positive (R2 _ y1)) (Positive (R2 _ y2)) = getAngle (R2 0 y1) (R2 0 y2) == 0 -- angle between two vectors both on the x-axis but with opposite direction -- must be pi -getAngleProp3 :: Positive Vec -> Positive Vec -> Bool +getAngleProp3 :: Positive R2 -> Positive R2 -> Bool getAngleProp3 (Positive (R2 x1 _)) (Positive (R2 x2 _)) = getAngle (R2 (negate x1) 0) (R2 x2 0) == pi -- angle between two vectors both on the y-axis but with opposite direction -- must be pi -getAngleProp4 :: Positive Vec -> Positive Vec -> Bool +getAngleProp4 :: Positive R2 -> Positive R2 -> Bool getAngleProp4 (Positive (R2 _ y1)) (Positive (R2 _ y2)) = getAngle (R2 0 (negate y1)) (R2 0 y2) == pi -- angle between vector in x-axis direction and y-axis direction must be -- p/2 -getAngleProp5 :: Positive Vec -> Positive Vec -> Bool +getAngleProp5 :: Positive R2 -> Positive R2 -> Bool getAngleProp5 (Positive (R2 x1 _)) (Positive (R2 _ y2)) = getAngle (R2 x1 0) (R2 0 y2) == pi / 2 -- commutative -getAngleProp6 :: Positive Vec -> Positive Vec -> Bool +getAngleProp6 :: Positive R2 -> Positive R2 -> Bool getAngleProp6 (Positive v1) (Positive v2) = getAngle v1 v2 == getAngle v2 v1 @@ -183,7 +183,7 @@ getAngleProp7 (PosRoundR2 v) -- commutative -scalarProdProp1 :: Vec -> Vec -> Bool +scalarProdProp1 :: R2 -> R2 -> Bool scalarProdProp1 v1 v2 = v1 `scalarProd` v2 == v2 `scalarProd` v1 @@ -212,7 +212,7 @@ scalarProdProp4 (RoundDouble s1) (RoundDouble s2) (RoundR2 v1) (RoundR2 v2) -- orthogonal -scalarProdProp5 :: Positive Vec -> Positive Vec -> Bool +scalarProdProp5 :: Positive R2 -> Positive R2 -> Bool scalarProdProp5 (Positive (R2 x1 _)) (Positive (R2 _ y2)) = scalarProd (R2 x1 0) (R2 0 y2) == 0 @@ -226,40 +226,40 @@ dimToSquareProp1 (x1, x2) (y1, y2) = -- multiply scalar with result of vecLength or with the vector itself... -- both results must be the same. We can't check against 0 -- because of sqrt in vecLength. -vecLengthProp1 :: PosRoundDouble -> Vec -> Bool +vecLengthProp1 :: PosRoundDouble -> R2 -> Bool vecLengthProp1 (PosRoundDouble r) v = abs (vecLength v * r - vecLength (scalarMul r v)) < 0.0001 -- convert to vector and back again -pt2VecProp1 :: PT -> Bool +pt2VecProp1 :: P2 -> Bool pt2VecProp1 pt = (vec2Pt . pt2Vec $ pt) == pt -- unbox coordinates and check if equal -pt2VecProp2 :: PT -> Bool +pt2VecProp2 :: P2 -> Bool pt2VecProp2 pt = (unr2 . pt2Vec $ pt) == unp2 pt -- convert to point and back again -vec2PtProp1 :: Vec -> Bool +vec2PtProp1 :: R2 -> Bool vec2PtProp1 v = (pt2Vec . vec2Pt $ v) == v -- unbox coordinates and check if equal -vec2PtProp2 :: Vec -> Bool +vec2PtProp2 :: R2 -> Bool vec2PtProp2 v = (unp2 . vec2Pt $ v) == unr2 v -- vector from a to b must not be the same as b to a -vp2Prop1 :: PT -> PT -> Bool +vp2Prop1 :: P2 -> P2 -> Bool vp2Prop1 p1' p2' | p1' == origin && p2' == origin = True | otherwise = vp2 p1' p2' /= vp2 p2' p1' -- negating vector from a to be must be the same as vector b to a -vp2Prop2 :: PT -> PT -> Bool +vp2Prop2 :: P2 -> P2 -> Bool vp2Prop2 p1' p2' | p1' == origin && p2' == origin = True | otherwise = vp2 p1' p2' == (\(R2 x y) -> negate x ^& negate y) @@ -270,5 +270,5 @@ vp2Prop2 p1' p2' -- determinant of the 3 same points is always 0 -detProp1 :: PT -> Bool +detProp1 :: P2 -> Bool detProp1 pt' = det pt' pt' pt' == 0