2014-10-10 15:40:08 +00:00
|
|
|
{-# OPTIONS_HADDOCK ignore-exports #-}
|
|
|
|
|
2014-12-03 21:02:42 +00:00
|
|
|
module Graphics.Diagram.Core where
|
2014-10-10 15:40:08 +00:00
|
|
|
|
2014-10-25 21:54:07 +00:00
|
|
|
import Algebra.Vector
|
2014-10-10 15:40:08 +00:00
|
|
|
import Diagrams.Backend.Cairo
|
|
|
|
import Diagrams.Prelude
|
|
|
|
import MyPrelude
|
|
|
|
|
|
|
|
|
|
|
|
-- |Represents a Cairo Diagram. This allows us to create multiple
|
|
|
|
-- diagrams with different algorithms but based on the same
|
|
|
|
-- coordinates and common properties.
|
2014-10-25 21:54:07 +00:00
|
|
|
data Diag =
|
|
|
|
Diag
|
|
|
|
{
|
|
|
|
mkDiag :: DiagProp
|
2015-05-21 00:14:15 +00:00
|
|
|
-> [[P2 Double]]
|
|
|
|
-> Diagram Cairo
|
2014-10-25 21:54:07 +00:00
|
|
|
}
|
|
|
|
| GifDiag
|
|
|
|
{
|
|
|
|
mkGifDiag :: DiagProp
|
|
|
|
-> Colour Double
|
2015-05-21 00:14:15 +00:00
|
|
|
-> ([P2 Double] -> [[P2 Double]])
|
|
|
|
-> [P2 Double]
|
|
|
|
-> [Diagram Cairo]
|
2014-10-25 21:54:07 +00:00
|
|
|
}
|
2015-05-21 00:14:15 +00:00
|
|
|
| EmptyDiag (Diagram Cairo)
|
2014-10-10 15:40:08 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |Holds the properties for a Diagram, like thickness of 2d points etc.
|
|
|
|
-- This can also be seen as a context when merging multiple diagrams.
|
|
|
|
data DiagProp = MkProp {
|
|
|
|
-- |The thickness of the dots.
|
2014-11-15 23:10:57 +00:00
|
|
|
dotSize :: Double,
|
2014-10-10 15:40:08 +00:00
|
|
|
-- |The dimensions of the x-axis.
|
2014-12-17 02:35:33 +00:00
|
|
|
xDimension :: (Double, Double),
|
2014-10-10 15:40:08 +00:00
|
|
|
-- |The dimensions of the y-axis.
|
2014-12-17 02:35:33 +00:00
|
|
|
yDimension :: (Double, Double),
|
2014-10-10 15:40:08 +00:00
|
|
|
-- |Algorithm to use.
|
2014-11-15 23:10:57 +00:00
|
|
|
algo :: Int,
|
2014-10-10 15:40:08 +00:00
|
|
|
-- |If we want to show the grid.
|
2014-11-15 23:10:57 +00:00
|
|
|
haveGrid :: Bool,
|
2014-10-10 15:40:08 +00:00
|
|
|
-- |If we want to show the coordinates as text.
|
2014-11-15 23:10:57 +00:00
|
|
|
showCoordText :: Bool,
|
2014-10-10 15:40:08 +00:00
|
|
|
-- |Square size used to show the grid and x/y-axis.
|
2014-11-15 23:10:57 +00:00
|
|
|
squareSize :: Double,
|
2014-11-14 20:28:56 +00:00
|
|
|
-- |The path to a quad in the quad tree.
|
2014-11-29 22:45:53 +00:00
|
|
|
quadPath :: String,
|
|
|
|
-- |The square for the kd-tree range search.
|
2015-01-14 17:17:35 +00:00
|
|
|
rangeSquare :: ((Double, Double), (Double, Double))
|
2014-10-10 15:40:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
instance Def DiagProp where
|
2014-11-15 23:10:57 +00:00
|
|
|
def = diagDefaultProp
|
2014-10-10 15:40:08 +00:00
|
|
|
|
|
|
|
|
|
|
|
instance Monoid Diag where
|
2014-10-25 21:54:07 +00:00
|
|
|
mempty = EmptyDiag mempty
|
|
|
|
mappend d1@(Diag {}) d2@(Diag {}) = Diag g
|
|
|
|
where
|
|
|
|
g p obj = mkDiag d1 p obj <> mkDiag d2 p obj
|
|
|
|
mappend d1@(GifDiag {}) d2@(Diag {}) = GifDiag g
|
|
|
|
where
|
2014-12-07 03:33:45 +00:00
|
|
|
g p col f vt = mkGifDiag d1 p col f vt ++ [mkDiag d2 p [vt]]
|
2014-10-25 21:54:07 +00:00
|
|
|
mappend d1@(Diag {}) d2@(GifDiag {}) = GifDiag g
|
|
|
|
where
|
2014-12-07 03:33:45 +00:00
|
|
|
g p col f vt = mkDiag d2 p [vt] : mkGifDiag d1 p col f vt
|
2014-10-25 21:54:07 +00:00
|
|
|
mappend d1@(GifDiag {}) d2@(GifDiag {}) = GifDiag g
|
2014-10-10 15:40:08 +00:00
|
|
|
where
|
2014-10-25 21:54:07 +00:00
|
|
|
g p col f vt = mkGifDiag d1 p col f vt ++ mkGifDiag d2 p col f vt
|
|
|
|
mappend (EmptyDiag _) g = g
|
|
|
|
mappend g (EmptyDiag _) = g
|
|
|
|
|
2014-10-10 15:40:08 +00:00
|
|
|
mconcat = foldr mappend mempty
|
|
|
|
|
|
|
|
|
|
|
|
-- |The default properties of the Diagram.
|
2014-11-15 23:10:57 +00:00
|
|
|
diagDefaultProp :: DiagProp
|
2014-11-29 22:45:53 +00:00
|
|
|
diagDefaultProp = MkProp 2 (0,500) (0,500)
|
|
|
|
0 False False 50 "" ((0,500),(0,500))
|
2014-10-10 15:40:08 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |Extract the lower bound of the x-axis dimension.
|
2014-11-15 23:10:57 +00:00
|
|
|
diagXmin :: DiagProp -> Double
|
|
|
|
diagXmin = fst . xDimension
|
2014-10-10 15:40:08 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |Extract the upper bound of the x-axis dimension.
|
2014-11-15 23:10:57 +00:00
|
|
|
diagXmax :: DiagProp -> Double
|
|
|
|
diagXmax = snd . xDimension
|
2014-10-10 15:40:08 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |Extract the lower bound of the y-axis dimension.
|
2014-11-15 23:10:57 +00:00
|
|
|
diagYmin :: DiagProp -> Double
|
|
|
|
diagYmin = fst . yDimension
|
2014-10-10 15:40:08 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |Extract the upper bound of the y-axis dimension.
|
2014-11-15 23:10:57 +00:00
|
|
|
diagYmax :: DiagProp -> Double
|
|
|
|
diagYmax = snd . yDimension
|
2014-10-11 01:59:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |The full width of the x dimension.
|
2014-11-15 23:10:57 +00:00
|
|
|
diagWidth :: DiagProp -> Double
|
|
|
|
diagWidth p = diagXmax p - diagXmin p
|
2014-10-11 01:59:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |The full height of the y dimension.
|
2014-11-15 23:10:57 +00:00
|
|
|
diagHeight :: DiagProp -> Double
|
|
|
|
diagHeight p = diagYmax p - diagYmin p
|
2014-10-11 01:59:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |The offset on the x-axis to move the grid and the white rectangle
|
|
|
|
-- to the right place.
|
2014-11-15 23:10:57 +00:00
|
|
|
diagWidthOffset :: DiagProp -> Double
|
|
|
|
diagWidthOffset p = diagXmin p + (diagWidth p / 2)
|
2014-10-11 01:59:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |The offset on the y-axis to move the grid and the white rectangle
|
|
|
|
-- to the right place.
|
2014-11-15 23:10:57 +00:00
|
|
|
diagHeightOffset :: DiagProp -> Double
|
2014-12-07 19:00:54 +00:00
|
|
|
diagHeightOffset p = diagYmin p + (diagHeight p / 2)
|
2014-10-10 15:40:08 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |Returns the specified diagram if True is passed,
|
|
|
|
-- otherwise returns the empty diagram. This is just for convenience
|
|
|
|
-- to avoid if else constructs.
|
|
|
|
maybeDiag :: Bool -> Diag -> Diag
|
|
|
|
maybeDiag b d
|
|
|
|
| b = d
|
|
|
|
| otherwise = mempty
|
2014-10-25 21:54:07 +00:00
|
|
|
|
|
|
|
|
2015-05-21 00:14:15 +00:00
|
|
|
filterValidPT :: DiagProp -> [P2 Double] -> [P2 Double]
|
2014-12-17 02:35:33 +00:00
|
|
|
filterValidPT =
|
|
|
|
filter
|
|
|
|
. inRange
|
|
|
|
. diagDimSquare
|
|
|
|
|
|
|
|
|
2015-01-14 17:17:35 +00:00
|
|
|
diagDimSquare :: DiagProp -> ((Double, Double), (Double, Double))
|
2014-12-17 02:35:33 +00:00
|
|
|
diagDimSquare p = dimToSquare (xDimension p) $ yDimension p
|
2014-12-03 21:02:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |Draw a list of points.
|
2015-05-21 00:14:15 +00:00
|
|
|
drawP :: [P2 Double] -- ^ the points to draw
|
2014-12-03 21:02:42 +00:00
|
|
|
-> Double -- ^ dot size
|
2015-05-21 00:14:15 +00:00
|
|
|
-> Diagram Cairo -- ^ the resulting diagram
|
2014-12-03 21:02:42 +00:00
|
|
|
drawP [] _ = mempty
|
|
|
|
drawP vt ds =
|
|
|
|
position (zip vt (repeat dot))
|
|
|
|
where
|
2015-05-21 00:14:15 +00:00
|
|
|
dot = circle ds :: Diagram Cairo
|
2014-12-03 21:02:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |Create a rectangle around a diagonal line, which has sw
|
|
|
|
-- as startpoint and nw as endpoint.
|
|
|
|
rectByDiagonal :: (Double, Double) -- ^ sw point
|
|
|
|
-> (Double, Double) -- ^ nw point
|
2015-05-21 00:14:15 +00:00
|
|
|
-> Diagram Cairo
|
2014-12-17 02:35:33 +00:00
|
|
|
rectByDiagonal (xmin, ymin) (xmax, ymax) =
|
|
|
|
fromVertices [p2 (xmin, ymin)
|
|
|
|
, p2 (xmax, ymin)
|
|
|
|
, p2 (xmax, ymax)
|
|
|
|
, p2 (xmin, ymax)
|
|
|
|
, p2 (xmin, ymin)
|
|
|
|
]
|
2014-12-03 21:02:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- |Creates a Diagram from a point that shows the coordinates
|
|
|
|
-- in text format, such as "(1.0, 2.0)".
|
2015-05-21 00:14:15 +00:00
|
|
|
pointToTextCoord :: P2 Double -> Diagram Cairo
|
2014-12-03 21:02:42 +00:00
|
|
|
pointToTextCoord pt =
|
|
|
|
text ("(" ++ (show . trim') x ++ ", " ++ (show . trim') y ++ ")") # scale 10
|
|
|
|
where
|
|
|
|
trim' :: Double -> Double
|
|
|
|
trim' x' = fromInteger . round $ x' * (10^(2 :: Int)) /
|
|
|
|
(10.0^^(2 :: Int))
|
|
|
|
(x, y) = unp2 pt
|