{-# LANGUAGE OverloadedStrings #-} -- | This module defines the syntax of a Ti/k/Z script. -- -- To generate a Ti/k/Z script, first create a 'TPath' using -- data constructors, or alternatively, use a 'PathBuilder' -- from the "Text.LaTeX.Packages.TikZ.PathBuilder" module. -- -- Once a 'TPath' is created, use 'path' to render a picture -- from it. Use 'scope' to apply some parameters to your picture, -- such line width or color. module Text.LaTeX.Packages.TikZ.Syntax ( -- * Points TPoint , pointAt , pointAtXY , pointAtXYZ , relPoint , relPoint_ -- * Paths -- ** Types , TPath (..) , GridOption (..) , Step (..) -- ** Critical points , startingPoint , lastPoint -- ** Functions , (->-) -- * Parameters , Parameter (..) , TikZColor (..) , Color (..) , Word8 -- * TikZ , TikZ , emptytikz , path , scope , ActionType (..) , (->>) -- * Sugar , draw , fill , clip , shade , filldraw , shadedraw ) where import Text.LaTeX.Base.Types import Text.LaTeX.Base.Render import Text.LaTeX.Base.Syntax import Text.LaTeX.Packages.Color import Data.Monoid import Data.Foldable (foldMap) import qualified Data.Sequence as S -- POINTS -- | A point in Ti/k/Z. data TPoint = DimPoint Measure Measure | XYPoint Double Double | XYZPoint Double Double Double | RelPoint TPoint | RelPoint_ TPoint deriving Show instance Render TPoint where render (DimPoint x y) = "(" <> renderCommas [x,y] <> ")" render (XYPoint x y) = "(" <> renderCommas [x,y] <> ")" render (XYZPoint x y z) = "(" <> renderCommas [x,y,z] <> ")" render (RelPoint p) = "++" <> render p render (RelPoint_ p) = "+" <> render p -- | Point using 'Measure's for coordinantes. pointAt :: Measure -> Measure -> TPoint pointAt = DimPoint -- | Point using numbers as coordinates. pointAtXY :: Double -> Double -> TPoint pointAtXY = XYPoint -- | Three-dimensional point. pointAtXYZ :: Double -> Double -> Double -> TPoint pointAtXYZ = XYZPoint -- | Makes a point relative to the previous. relPoint :: TPoint -> TPoint relPoint (RelPoint x) = RelPoint x relPoint (RelPoint_ x) = RelPoint x relPoint p = RelPoint p relPoint_ :: TPoint -> TPoint relPoint_ (RelPoint x) = RelPoint_ x relPoint_ (RelPoint_ x) = RelPoint_ x relPoint_ p = RelPoint_ p -- PATHS -- | Type for TikZ paths. Every 'TPath' has two fundamental points: the /starting point/ -- and the /last point/. -- The starting point is set using the 'Start' constructor. -- The last point then is modified by the other constructors. -- Below a explanation of each one of them. -- Note that both starting point and last point may coincide. -- You can use the functions 'startingPoint' and 'lastPoint' to calculate them. -- After creating a 'TPath', use 'path' to do something useful with it. data TPath = Start TPoint -- ^ Let @y = Start p@. -- -- /Operation:/ Set the starting point of a path. -- -- /Last point:/ The last point of @y@ is @p@. | Cycle TPath -- ^ Let @y = Cycle x@. -- -- /Operation:/ Close a path with a line from the last point of @x@ to -- the starting point of @x@. -- -- /Last point:/ The last point of @y@ is the starting point of @x@. | Line TPath TPoint -- ^ Let @y = Line x p@. -- -- /Operation:/ Extend the current path from the last point of @x@ -- in a straight line to @p@. -- -- /Last point:/ The last point of @y@ is @p@. | Rectangle TPath TPoint -- ^ Let @y = Rectangle x p@. -- -- /Operation:/ Define a rectangle using the last point of -- @x@ as one corner and @p@ as the another corner. -- -- /Last point:/ The last point of @y@ is @p@. | Circle TPath Double -- ^ Let @y = Circle x r@. -- -- /Operation:/ Define a circle with center at the last point -- of x and radius @r@. -- -- /Last point:/ The last point of @y@ is the same as the last -- point of @x@. | Ellipse TPath Double Double -- ^ Let @y = Ellipse x r1 r2@. -- -- /Operation:/ Define a ellipse with center at the last -- point of @x@, width the double of @r1@ and height -- the double of @r2@. -- -- /Last point:/ The last point of @y@ is the same as the -- last point of @x@. | Grid TPath [GridOption] TPoint | Node TPath LaTeX -- ^ Let @y = Node x l@. -- -- /Operation:/ Set a text centered at the last point of @x@. -- -- /Last point:/ The last point of @y@ is the same as the last -- point of @x@. deriving Show data GridOption = GridStep Step deriving Show data Step = DimStep Measure | XYStep Double | PointStep TPoint deriving Show instance Render TPath where render (Start p) = render p render (Cycle p) = render p <> " -- cycle" render (Line p1 p2) = render p1 <> " -- " <> render p2 render (Rectangle p1 p2) = render p1 <> " rectangle " <> render p2 render (Circle p r) = render p <> " circle (" <> render r <> ")" render (Ellipse p r1 r2) = render p <> " ellipse (" <> render r1 <> " and " <> render r2 <> ")" render (Grid p1 [] p2) = render p1 <> " grid " <> render p2 render (Grid p1 xs p2) = render p1 <> " grid " <> render xs <> " " <> render p2 render (Node p l) = render p <> " node[transform shape] " <> render (TeXBraces l) instance Render GridOption where render (GridStep s) = "step=" <> render s instance Render Step where render (DimStep m) = render m render (XYStep q) = render q render (PointStep p) = render p -- Starting and Last points -- | Calculate the starting point of a 'TPath'. startingPoint :: TPath -> TPoint startingPoint (Start p) = p startingPoint (Cycle x) = startingPoint x startingPoint (Line x _) = startingPoint x startingPoint (Rectangle x _) = startingPoint x startingPoint (Circle x _) = startingPoint x startingPoint (Ellipse x _ _) = startingPoint x startingPoint (Grid x _ _) = startingPoint x startingPoint (Node x _) = startingPoint x -- | Calculate the last point of a 'TPath'. lastPoint :: TPath -> TPoint lastPoint (Start p) = p lastPoint (Cycle x) = startingPoint x lastPoint (Line _ p) = p lastPoint (Rectangle _ p) = p lastPoint (Circle x _) = lastPoint x lastPoint (Ellipse x _ _) = lastPoint x lastPoint (Grid _ _ p) = p lastPoint (Node x _) = lastPoint x -- Path builders -- | Alias of 'Line'. (->-) :: TPath -> TPoint -> TPath (->-) = Line -- Parameters -- | Color models accepted by Ti/k/Z. data TikZColor = BasicColor Color | RGBColor Word8 Word8 Word8 deriving Show instance Render TikZColor where render (BasicColor c) = render c render (RGBColor r g b) = "{rgb,255:red," <> render r <> ";green," <> render g <> ";blue," <> render b <> "}" -- | Parameters to use in a 'scope' to change how things -- are rendered within that scope. data Parameter = TWidth Measure | TColor TikZColor | TScale Double | TRotate Double -- ^ Angle is in degrees. deriving Show renderPair :: Render a => Text -> a -> Text renderPair x y = x <> "=" <> render y instance Render Parameter where render (TWidth m) = renderPair "line width" m render (TColor c) = renderPair "color" c render (TScale q) = renderPair "scale" q render (TRotate a) = renderPair "rotate" a -- TikZ -- | A Ti/k/Z script. data TikZ = PathAction [ActionType] TPath | Scope [Parameter] TikZ | TikZSeq (S.Seq TikZ) deriving Show -- | Different types of actions that can be performed -- with a 'TPath'. See 'path' for more information. data ActionType = Draw | Fill | Clip | Shade deriving Show -- | Just an empty script. emptytikz :: TikZ emptytikz = TikZSeq mempty instance Render TikZ where render (PathAction ts p) = "\\path" <> render ts <> " " <> render p <> " ; " render (Scope ps t) = "\\begin{scope}" <> render ps <> render t <> "\\end{scope}" render (TikZSeq ts) = foldMap render ts instance Render ActionType where render Draw = "draw" render Fill = "fill" render Clip = "clip" render Shade = "shade" -- | A path can be used in different ways. -- -- * 'Draw': Just draw the path. -- -- * 'Fill': Fill the area inside the path. -- -- * 'Clip': Clean everything outside the path. -- -- * 'Shade': Shade the area inside the path. -- -- It is possible to stack different effects in the list. -- -- Example of usage: -- -- > path [Draw] $ Start (pointAtXY 0 0) ->- pointAtXY 1 1 -- -- Most common usages are exported as functions. See -- 'draw', 'fill', 'clip', 'shade', 'filldraw' and -- 'shadedraw'. path :: [ActionType] -> TPath -> TikZ path = PathAction -- | Applies a scope to a Ti/k/Z script. scope :: [Parameter] -> TikZ -> TikZ scope = Scope -- | Sequence two Ti/k/Z scripts. (->>) :: TikZ -> TikZ -> TikZ (TikZSeq s1) ->> (TikZSeq s2) = TikZSeq (s1 <> s2) (TikZSeq s) ->> a = TikZSeq $ s S.|> a a ->> (TikZSeq s) = TikZSeq $ a S.<| s a ->> b = TikZSeq $ a S.<| S.singleton b -- SUGAR -- | Equivalent to @path [Draw]@. draw :: TPath -> TikZ draw = path [Draw] -- | Equivalent to @path [Fill]@. fill :: TPath -> TikZ fill = path [Fill] -- | Equivalent to @path [Clip]@. clip :: TPath -> TikZ clip = path [Clip] -- | Equivalent to @path [Shade]@. shade :: TPath -> TikZ shade = path [Shade] -- | Equivalent to @path [Fill,Draw]@. filldraw :: TPath -> TikZ filldraw = path [Fill,Draw] -- | Equivalent to @path [Shade,Draw]@. shadedraw :: TPath -> TikZ shadedraw = path [Shade,Draw]