marxup-3.0.0: MarXup/Diagram/Tikz.hs
{-# LANGUAGE TypeSynonymInstances, FlexibleContexts, FlexibleInstances, GeneralizedNewtypeDeriving, MultiParamTypeClasses, RecursiveDo, TypeFamilies, OverloadedStrings, RecordWildCards,UndecidableInstances, PackageImports, TemplateHaskell #-}
module MarXup.Diagram.Tikz where
import MarXup.Diagram.Layout
import MarXup.Diagram.Point
import MarXup.Diagram.Path
import Control.Lens hiding (element)
import Prelude hiding (sum,mapM_,mapM,concatMap)
import Control.Applicative
import Data.List (intercalate)
import Data.String
import MarXup
import MarXup.MultiRef
import MarXup.Tex
import Numeric (showFFloat)
import Data.Traversable
import Data.Foldable
import Data.Monoid
import Control.Monad.Reader
instance Element Expr where
type Target Expr = Dia
element x = do
v <- valueOf x
diaRaw $ showDistance v
instance Element (Diagram ()) where
type Target (Diagram ()) = TeX
element d = do
texLn "" -- otherwise beamer does not understand where a tikzpicture ends (?!!)
braces $ do
cmd0 "normalsize"
-- otherwise the boxes use "normalsize", while tikz inherits
-- the smaller or bigger size from the current scope. Actually,
-- every text styling should be reset, but I don't know how to
-- do that.
env "tikzpicture" $
Tex $ runDiagram d
--------------------
-- Point rendering
instance Element Point where
type Target Point = Diagram ()
element (Point x y) = "(" <> element x <> "," <> element y <> ")"
diaDebug msg = diaRaw $ "\n%DBG:" ++ msg ++ "\n"
instance (Element (Point' v),Monoid (Target (Point' v)), IsString (Target (Point' v))) => Element (Segment v) where
type Target (Segment v) = Target (Point' v)
element (StraightTo p) = "--" <> element p
element (CurveTo c d p) = "..controls" <> element c <> "and" <> element d <> ".." <> element p
element Cycle = "--cycle"
-- element (VH p) = "|-" <> element p
-- element (HV p) = "-|" <> element p
-- element (Rounded Nothing) = "[sharp corners]"
-- element (Rounded (Just r)) = "[" <> element (constant r) <> "]"
instance Element Path where
type Target Path = Diagram ()
element = path
path :: Path -> Dia
path = frozenPath <=< freeze
frozenPath :: FrozenPath -> Dia
frozenPath p = do
options <- view diaPathOptions
diaRaw $ "\\path"
<> element options
<> case p of
EmptyPath -> ""
(Path start segs) -> element start ++ concatMap element segs
diaRaw ";\n"
showDistance :: Constant -> String
showDistance x = showFFloat (Just 4) x tikzUnit
where tikzUnit = "pt"
instance Element FrozenPoint where
type Target FrozenPoint = String
element pt = frozenPointElim pt $ \x y -> "(" <> showDistance x <> "," <> showDistance y <> ")"
-----------------
-- Path Options
localPathOptions :: (PathOptions -> PathOptions) -> Diagram a -> Diagram a
localPathOptions f = local (over diaPathOptions f)
instance Show LineTip where
show t = case t of
ToTip -> "to"
StealthTip -> "stealth"
CircleTip -> "o"
NoTip -> ""
LatexTip -> "latex"
ReversedTip x -> show x ++ " reversed"
BracketTip -> "["
ParensTip -> "("
ultraThin, veryThin, thin, semiThick, thick, veryThick, ultraThick :: Constant
ultraThin = 0.1
veryThin = 0.2
thin = 0.4
semiThick = 0.6
thick = 0.8
veryThick = 1.2
ultraThick = 1.6
showDashPat :: DashPattern -> String
showDashPat xs = intercalate " " ["on " <> showDistance on <>
" off " <> showDistance off | (on,off) <- xs]
solid o@PathOptions{..} = o { _dashPattern = [] }
dotted o@PathOptions{..} = o { _dashPattern = [(_lineWidth,2)] }
denselyDotted o@PathOptions{..} = o { _dashPattern = [(_lineWidth, 1)] }
looselyDotted o@PathOptions{..} = o { _dashPattern = [(_lineWidth, 4)] }
dashed o@PathOptions{..} = o { _dashPattern = [(3, 3)] }
denselyDashed o@PathOptions{..} = o { _dashPattern = [(3, 2)] }
looselyDashed o@PathOptions{..} = o { _dashPattern = [(3, 6)] }
dashdotted o@PathOptions{..} = o { _dashPattern = [(3, 2), (_lineWidth, 2)] }
denselyDashdotted o@PathOptions{..} = o { _dashPattern = [(3, 1), (_lineWidth, 1)] }
looselyDashdotted o@PathOptions{..} = o { _dashPattern = [(3, 4), (_lineWidth, 4)] }
using = localPathOptions
stroke color = using (outline color)
draw = stroke "black"
noOutline = set drawColor Nothing
outline color = set drawColor (Just color)
fill color = set fillColor (Just color)
zigzagDecoration = set decoration (Decoration "zigzag")
instance Element PathOptions where
type Target PathOptions = String
element PathOptions{..} = "["
<> show _startTip <> "-" <> show _endTip <> ","
<> col "draw" _drawColor
<> col "fill" _fillColor
<> "line width=" <> showDistance _lineWidth <> ","
<> "line cap=" <> (case _lineCap of
RoundCap -> "round"
RectCap -> "rect"
ButtCap -> "butt") <> ","
<> "line join=" <> (case _lineJoin of
RoundJoin -> "round"
BevelJoin -> "bevel"
MiterJoin -> "miter") <> ","
<> "dash pattern=" <> showDashPat _dashPattern
<> (case _decoration of
Decoration [] -> ""
Decoration d -> ",decorate,decoration=" ++ d)
<> "]"
where col attr = maybe "" (\c -> attr <> "=" <> c <> ",")
----------
-- Text
drawText :: Point -> TeX -> Diagram BoxSpec
drawText point t = do
diaRawTex $ tex $ "\\node[anchor=north west,inner sep=0] at "
element point
(_,box) <- diaRawTex $ inBox $ braces $ t
diaRawTex $ tex ";\n"
return box