wumpus-drawing 0.1.0 → 0.9.0
raw patch · 80 files changed
Files
- CHANGES +58/−1
- demo/ArrowCircuit.hs +83/−64
- demo/Arrowheads.hs +82/−58
- demo/Automata.hs +121/−0
- demo/ClipPic.hs +96/−57
- demo/ColourCharts.hs +17/−15
- demo/Connectors.hs +84/−45
- demo/DotPic.hs +69/−88
- demo/FeatureModel.hs +38/−53
- demo/FontLoaderUtils.hs +0/−95
- demo/FontPic.hs +33/−22
- demo/LeftRightText.hs +79/−133
- demo/PetriNet.hs +124/−111
- demo/PictureCompo.hs +0/−151
- demo/SampleShapes.hs +189/−0
- demo/SingleChar.hs +69/−0
- demo/SingleLine.hs +61/−0
- demo/Symbols.hs +67/−198
- demo/TableChains.hs +0/−44
- src/Wumpus/Drawing/Arrows.hs +0/−28
- src/Wumpus/Drawing/Arrows/Connectors.hs +0/−104
- src/Wumpus/Drawing/Arrows/Tips.hs +0/−515
- src/Wumpus/Drawing/Basis/BezierCurve.hs +137/−0
- src/Wumpus/Drawing/Basis/DrawingPrimitives.hs +132/−0
- src/Wumpus/Drawing/Basis/Geometry.hs +51/−0
- src/Wumpus/Drawing/Basis/InclineTrails.hs +499/−0
- src/Wumpus/Drawing/Basis/ShapeTrails.hs +213/−0
- src/Wumpus/Drawing/Basis/Symbols.hs +369/−0
- src/Wumpus/Drawing/Chains.hs +0/−29
- src/Wumpus/Drawing/Chains/Base.hs +0/−117
- src/Wumpus/Drawing/Chains/Derived.hs +0/−126
- src/Wumpus/Drawing/Connectors.hs +29/−0
- src/Wumpus/Drawing/Connectors/Arrowheads.hs +453/−0
- src/Wumpus/Drawing/Connectors/Base.hs +168/−0
- src/Wumpus/Drawing/Connectors/BoxConnectors.hs +116/−0
- src/Wumpus/Drawing/Connectors/ConnectorPaths.hs +563/−0
- src/Wumpus/Drawing/Connectors/ConnectorProps.hs +158/−0
- src/Wumpus/Drawing/Dots/AnchorDots.hs +181/−125
- src/Wumpus/Drawing/Dots/Marks.hs +0/−243
- src/Wumpus/Drawing/Dots/SimpleDots.hs +281/−0
- src/Wumpus/Drawing/Extras/Axes.hs +75/−0
- src/Wumpus/Drawing/Extras/Clip.hs +46/−0
- src/Wumpus/Drawing/Extras/Grids.hs +210/−0
- src/Wumpus/Drawing/Extras/Loop.hs +93/−0
- src/Wumpus/Drawing/Geometry/Intersection.hs +0/−173
- src/Wumpus/Drawing/Geometry/Paths.hs +0/−138
- src/Wumpus/Drawing/Paths.hs +8/−9
- src/Wumpus/Drawing/Paths/Base.hs +1096/−643
- src/Wumpus/Drawing/Paths/Connectors.hs +0/−247
- src/Wumpus/Drawing/Paths/ControlPoints.hs +0/−189
- src/Wumpus/Drawing/Paths/Illustrate.hs +61/−0
- src/Wumpus/Drawing/Paths/Intersection.hs +318/−0
- src/Wumpus/Drawing/Paths/MonadicConstruction.hs +0/−160
- src/Wumpus/Drawing/Paths/PathBuilder.hs +470/−0
- src/Wumpus/Drawing/Paths/Vamps.hs +53/−0
- src/Wumpus/Drawing/Shapes.hs +18/−1
- src/Wumpus/Drawing/Shapes/Base.hs +201/−62
- src/Wumpus/Drawing/Shapes/Circle.hs +46/−35
- src/Wumpus/Drawing/Shapes/Diamond.hs +75/−60
- src/Wumpus/Drawing/Shapes/Ellipse.hs +57/−36
- src/Wumpus/Drawing/Shapes/InvSemicircle.hs +126/−0
- src/Wumpus/Drawing/Shapes/InvSemiellipse.hs +127/−0
- src/Wumpus/Drawing/Shapes/InvTriangle.hs +151/−0
- src/Wumpus/Drawing/Shapes/Parallelogram.hs +240/−0
- src/Wumpus/Drawing/Shapes/Rectangle.hs +88/−55
- src/Wumpus/Drawing/Shapes/Semicircle.hs +193/−0
- src/Wumpus/Drawing/Shapes/Semiellipse.hs +176/−0
- src/Wumpus/Drawing/Shapes/Trapezium.hs +220/−0
- src/Wumpus/Drawing/Shapes/Triangle.hs +203/−0
- src/Wumpus/Drawing/Text/Base/DocTextZero.hs +314/−0
- src/Wumpus/Drawing/Text/Base/Label.hs +217/−0
- src/Wumpus/Drawing/Text/DirectionZero.hs +53/−0
- src/Wumpus/Drawing/Text/DocSymbols.hs +78/−0
- src/Wumpus/Drawing/Text/LRText.hs +0/−409
- src/Wumpus/Drawing/Text/SafeFonts.hs +0/−167
- src/Wumpus/Drawing/Text/StandardFontDefs.hs +323/−0
- src/Wumpus/Drawing/Turtle/TurtleClass.hs +0/−91
- src/Wumpus/Drawing/Turtle/TurtleMonad.hs +0/−139
- src/Wumpus/Drawing/VersionNumber.hs +3/−3
- wumpus-drawing.cabal +76/−37
CHANGES view
@@ -1,4 +1,61 @@ +0.4.0 to 0.5.0+ + * Re-implemented Text drawing. Some functionality moved to + Wumpus-Basic.+ + * Re-implemented monadic Path Builder.+ +0.3.0 to 0.4.0:+ + * Simplified Trapezium shape so it only produces isosceles+ Trapeziums.+ + * Added Basis modules. These are \"mid-level\" modules similar + to ones in @Wumpus.Basic.Kernel.Objects@, however they are + considered less general so there are put here where they can + be imported individually (and so not pollute the namespace).+ + * Removed the Turtle modules. The new @LocTrace@ and @RefTrace@ + modules supercede the Turtle modules.+ +0.2.0 to 0.3.0:++ * Moved Turtle and Grids into the @Wumpus.Drawing.Extras@+ name-space. Modules here are considered sketches.++ * Re-implemented arrowheads and connectors.++ * Re-implemented monadic path construction. This is now + essentially \"turtle drawing\" with a path trace.++ * Removed chains - a simplified implementation is now + provided by Wumpus-Basic.++ * Re-implemented and expanded Text. ++0.1.0 to 0.2.0:++ * Added new Shapes.++ * Move Geometry modules to Wumpus-Basic.++ * Re-implemented Chains.++ * Re-implemented LR-Text. Added CatText.+ 0.1.0: - * Initial split from Wumpus-Basic.+ * Initial release - this is a split from @Wumpus-Basic@ making + the (very prototypical - read sub-alpha, unstable...) modules + in the @Drawing@ hierarchy a separate package.++ * Simplified Chains - chains are now regular lists (though often+ infinite). Drawings are made with chains using new zip-like+ functions.++ * Re-worked Shapes.++ * Re-worked Arrow and Arrow Tip types.++ * Re-worked ConnectorPaths.
demo/ArrowCircuit.hs view
@@ -5,59 +5,43 @@ -- Acknowledgment - the Arrow diagram is taken from Ross -- Paterson\'s slides /Arrows and Computation/. +-- NOTE - this example now rather out-of-date. Wumpus-Drawing has +-- some new features to make node/connector drawing a bit easier. module ArrowCircuit where -import Wumpus.Basic.Kernel-import Wumpus.Basic.System.FontLoader.Afm-import Wumpus.Basic.System.FontLoader.GhostScript-import Wumpus.Drawing.Arrows-import Wumpus.Drawing.Paths +import Wumpus.Drawing.Connectors+import qualified Wumpus.Drawing.Connectors.ConnectorPaths as C import Wumpus.Drawing.Shapes-import Wumpus.Drawing.Text.LRText-import Wumpus.Drawing.Text.SafeFonts+import Wumpus.Drawing.Text.DirectionZero+import Wumpus.Drawing.Text.StandardFontDefs -import Wumpus.Core -- package: wumpus-core+import Wumpus.Basic.Kernel -- package: wumpus-basic+import Wumpus.Basic.System.FontLoader -import FontLoaderUtils+import Wumpus.Core -- package: wumpus-core -import Data.AffineSpace+import Data.AffineSpace -- package: vector-space import System.Directory main :: IO ()-main = do - (mb_gs, mb_afm) <- processCmdLine default_font_loader_help- createDirectoryIfMissing True "./out/"- maybe gs_failk makeGSPicture $ mb_gs- maybe afm_failk makeAfmPicture $ mb_afm- where- gs_failk = putStrLn "No GhostScript font path supplied..."- afm_failk = putStrLn "No AFM v4.1 font path supplied..."--makeGSPicture :: FilePath -> IO ()-makeGSPicture font_dir = do - putStrLn "Using GhostScript metrics..."- (base_metrics, msgs) <- loadGSMetrics font_dir ["Times-Roman", "Times-Italic"]- mapM_ putStrLn msgs- let pic1 = runCtxPictureU (makeCtx base_metrics) circuit_pic- writeEPS "./out/arrow_circuit01.eps" pic1- writeSVG "./out/arrow_circuit01.svg" pic1 +main = simpleFontLoader main1 >> return () -makeAfmPicture :: FilePath -> IO ()-makeAfmPicture font_dir = do - putStrLn "Using AFM 4.1 metrics..."- (base_metrics, msgs) <- loadAfmMetrics font_dir ["Times-Roman", "Times-Italic"]- mapM_ putStrLn msgs+main1 :: FontLoader -> IO ()+main1 loader = do+ createDirectoryIfMissing True "./out/" + base_metrics <- loader [ Right times_roman_family ]+ printLoadErrors base_metrics let pic1 = runCtxPictureU (makeCtx base_metrics) circuit_pic- writeEPS "./out/arrow_circuit02.eps" pic1- writeSVG "./out/arrow_circuit02.svg" pic1 + writeEPS "./out/arrow_circuit.eps" pic1+ writeSVG "./out/arrow_circuit.svg" pic1 -makeCtx :: GlyphMetrics -> DrawingContext-makeCtx = fontFace times_roman . metricsContext 11+makeCtx :: FontLoadResult -> DrawingContext+makeCtx = set_font times_roman . metricsContext 11 @@ -68,9 +52,12 @@ -- Note `at` currently does not work for Shapes. -circuit_pic :: CtxPicture Double -circuit_pic = drawTracing $ do- a1 <- drawi $ (strokedShape $ rrectangle 12 66 30) `at` P2 0 72+circuit_pic :: CtxPicture+circuit_pic = drawTracing body ++body :: TraceDrawing Double ()+body = do+ a1 <- drawi $ rrectangle 12 66 30 `at` P2 0 72 atext a1 "CONST 0" a2 <- drawi $ (strokedShape $ circle 16) `at` P2 120 60 atext a2 "IF"@@ -78,41 +65,73 @@ atext a3 "+1" a4 <- drawi $ (strokedShape $ rectangle 66 30) `at` P2 120 0 atext a4 "DELAY 0"- connWith connLine (east a1) (east a1 .+^ hvec 76)- connWith connLine (east a2) (east a2 .+^ hvec 180)- connWith connLine (north a2 .+^ vvec 40) (north a2)- connWith connLine (north a3 .+^ vvec 16) (north a3) - connWith connRightVH (south a3) (east a4)- connWith (connRightHVH (-30)) (west a4) (southwest a2)+ connWith conn_line (east a1) ((.+^ hvec 76) $ east a1)+ connWith conn_line (east a2) ((.+^ hvec 180) $ east a2)+ connWith conn_line ((.+^ vvec 40) $ north a2) (north a2)+ connWith conn_line ((.+^ vvec 16) $ north a3) (north a3) + connWith conna_right (south a3) (east a4)+ connWith conna_orthovbar (west a4) (southwest a2) ptext (P2 40 10) "next" ptext (P2 152 100) "reset" ptext (P2 252 72) "output" return () + -- Note - need a variant of /bar/ that draws UDLR only. +connWith :: ( Real u, Floating u, InterpretUnit u ) + => ArrowConnector u -> Anchor u -> Anchor u -> TraceDrawing u ()+connWith con a0 a1 = localize double_point_size $ + drawc a0 a1 (ignoreAns con) -connWith :: ( TraceM m, DrawingCtxM m, u ~ MonUnit m- , Real u, Floating u, FromPtSize u ) - => ConnectorPath u -> Point2 u -> Point2 u -> m ()-connWith con p0 p1 = localize doublesize $ - drawi_ $ apply2R2 (rightArrow tri45 con) p0 p1 +atext :: ( CenterAnchor (t u), u ~ DUnit (t u)+ , Real u, Floating u, InterpretUnit u)+ => t u -> String -> TraceDrawing u ()+atext ancr ss = + draw $ textline CENTER ss `at` (center ancr) -atext :: ( CenterAnchor t, DUnit t ~ u- , Real u, Floating u, FromPtSize u- , TraceM m, DrawingCtxM m, u ~ MonUnit m )- => t -> String -> m ()-atext ancr ss = let pt = center ancr in- drawi_ $ ctrCenterLine ss `at` pt +ptext :: (Floating u, InterpretUnit u) + => Point2 u -> String -> TraceDrawing u ()+ptext pt ss = localize (font_attr times_italic 14) $ + draw $ textline CENTER ss `at` pt -ptext :: ( Real u, Floating u, FromPtSize u- , TraceM m, DrawingCtxM m, u ~ MonUnit m )- => Point2 u -> String -> m ()-ptext pt ss = localize (fontAttr times_italic 14) $ - drawi_ $ ctrCenterLine ss `at` pt +-- Note - return type is a LocImage not a shape...+--+rrectangle :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => Double -> u -> u -> LocImage u (Rectangle u)+rrectangle _r w h = strokedShape (rectangle w h)+ -- This should have round corners but they are currently+ -- disabled pending a re-think. + {- localize (round_corner_factor r) $ -} -rrectangle :: (Real u, Floating u, FromPtSize u) - => u -> u -> u -> LocShape u (Rectangle u)-rrectangle r w h = localize (roundCornerFactor $ realToFrac r) (rectangle w h)+++-- Cf. Parsec\'s Token module...++conn_line :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => ArrowConnector u+conn_line =+ renderConnectorConfig default_connector_props $ makeSglArrConn C.conn_line++conna_orthovbar :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => ArrowConnector u+conna_orthovbar = + renderConnectorConfig default_connector_props $ + makeSglArrConn C.conna_orthovbar+++conna_right :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => ArrowConnector u+conna_right = + renderConnectorConfig default_connector_props $ makeSglArrConn C.conna_right+++makeSglArrConn :: ConnectorPathSpec u -> ConnectorConfig u+makeSglArrConn cspec = + ConnectorConfig+ { conn_arrowl = Nothing+ , conn_arrowr = Just tri45+ , conn_path_spec = cspec+ }
demo/Arrowheads.hs view
@@ -2,82 +2,106 @@ module Arrowheads where --import Wumpus.Basic.Kernel-import Wumpus.Drawing.Arrows-import Wumpus.Drawing.Chains import Wumpus.Drawing.Colour.SVGColours-import Wumpus.Drawing.Paths hiding ( length )+import Wumpus.Drawing.Connectors+import qualified Wumpus.Drawing.Connectors.ConnectorPaths as C+import Wumpus.Drawing.Text.DirectionZero+import Wumpus.Drawing.Text.StandardFontDefs -import Wumpus.Core -- package: wumpus-core -import Data.AffineSpace -- package: vector-space+import Wumpus.Basic.Kernel -- package: wumpus-basic+import Wumpus.Basic.System.FontLoader +import Wumpus.Core -- package: wumpus-core++import Data.Monoid import System.Directory main :: IO ()-main = do - createDirectoryIfMissing True "./out/"- let pic1 = runCtxPictureU std_ctx arrow_drawing- writeEPS "./out/arrowheads01.eps" pic1- writeSVG "./out/arrowheads01.svg" pic1+main = simpleFontLoader main1 >> return () -arrow_drawing :: CtxPicture Double-arrow_drawing = drawTracing $ localize (dashPattern unit_dash_pattern) - $ tableGraphic arrtable+main1 :: FontLoader -> IO ()+main1 loader = do+ createDirectoryIfMissing True "./out/" + base_metrics <- loader [ Left helvetica ]+ printLoadErrors base_metrics+ let pic1 = runCtxPictureU (makeCtx base_metrics) arrow_drawing+ writeEPS "./out/arrowheads.eps" pic1+ writeSVG "./out/arrowheads.svg" pic1 -arrtable :: [(Arrowhead Double, Arrowhead Double)]++makeCtx :: FontLoadResult -> DrawingContext+makeCtx = set_font helvetica . metricsContext 14++++arrow_drawing :: CtxPicture+arrow_drawing = + drawTracing $ localize line_thick $ tableGraphic arrtable++arrtable :: [(String, ArrowTip)] arrtable = - [ (tri90, tri90)- , (tri60, tri60)- , (tri45, tri45)- , (otri90, otri90)- , (otri60, otri60)- , (otri45, otri45)- , (revtri90, revtri90)- , (revtri60, revtri60)- , (revtri45, revtri45)- , (orevtri90, orevtri90)- , (orevtri60, orevtri60)- , (orevtri45, orevtri45)- , (barb90, barb90)- , (barb60, barb60)- , (barb45, barb45)- , (revbarb90, revbarb90)- , (revbarb60, revbarb60)- , (revbarb45, revbarb45)- , (perp, perp)- , (bracket, bracket)- , (diskTip, diskTip)- , (odiskTip, odiskTip)- , (squareTip, squareTip)- , (osquareTip, osquareTip)- , (diamondTip, diamondTip)- , (odiamondTip, odiamondTip)- , (curveTip, curveTip)- , (revcurveTip, revcurveTip)+ [ ("tri90", tri90)+ , ("tri60", tri60)+ , ("tri45", tri45) + , ("otri90", otri90)+ , ("otri60", otri60)+ , ("otri45", otri45)+ , ("revtri90", revtri90)+ , ("revtri60", revtri60)+ , ("revtri45", revtri45)+ , ("orevtri90", orevtri90)+ , ("orevtri60", orevtri60)+ , ("orevtri45", orevtri45)+ , ("barb90", barb90)+ , ("barb60", barb60)+ , ("barb45", barb45)+ , ("revbarb90", revbarb90)+ , ("revbarb60", revbarb60)+ , ("revbarb45", revbarb45)+ , ("perp", perp)+ , ("bracket", bracket)+ , ("diskTip", diskTip)+ , ("odiskTip", odiskTip)+ , ("squareTip", squareTip)+ , ("osquareTip", osquareTip)+ , ("diamondTip", diamondTip)+ , ("odiamondTip", odiamondTip)+ , ("diamondWideTip", diamondWideTip)+ , ("odiamondWideTip", odiamondWideTip)+ , ("curveTip", curveTip)+ , ("revcurveTip", revcurveTip) ] -tableGraphic :: (Real u, Floating u, FromPtSize u) - => [(Arrowhead u, Arrowhead u)] -> TraceDrawing u ()-tableGraphic tips = zipchainWith makeArrowDrawing tips ps- where- ps = tableDown 20 (120,24) (P2 0 480) +tableGraphic :: [(String, ArrowTip)] -> TraceDrawing Double ()+tableGraphic tips = + drawl start $ distribColumnwiseTable 18 (180,24) $ map arrowGraphic tips+ where+ start = P2 0 480+ std_ctx :: DrawingContext-std_ctx = fillColour peru $ standardContext 18+std_ctx = fill_colour peru $ standardContext 18 -makeArrowDrawing :: (Real u, Floating u, FromPtSize u) - => (Arrowhead u, Arrowhead u) -> LocGraphic u-makeArrowDrawing (arrl,arrr) = - promoteR1 $ \p0 -> forget $- connect (leftRightArrow arrl arrr connLine) p0 (mkP1 p0)+-- Note - /null/ chain action needs a better type synonym name.+--+arrowGraphic :: (String, ArrowTip) -> LocGraphic Double+arrowGraphic (name, utip) = aconn `mappend` lbl where- mkP1 = (.+^ hvec 100)- -- forget needs a better name, then adding to Wumpus-Basic.- forget = fmap (replaceL uNil) + aconn = ignoreAns $ promoteLoc $ \pt ->+ connect (mkConn_line utip) pt (displace (hvec 60) pt)++ lbl = ignoreAns $ promoteLoc $ \pt -> + textline WW name `at` (displace (hvec 66) pt)++++mkConn_line :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => ArrowTip -> ArrowConnector u+mkConn_line = rightArrowConnector default_connector_props C.conn_line+
+ demo/Automata.hs view
@@ -0,0 +1,121 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++module Automata where++import Wumpus.Drawing.Connectors+import qualified Wumpus.Drawing.Connectors.ConnectorPaths as C+import Wumpus.Drawing.Extras.Loop+import Wumpus.Drawing.Paths+import Wumpus.Drawing.Shapes+import Wumpus.Drawing.Text.DirectionZero+import Wumpus.Drawing.Text.StandardFontDefs+import Wumpus.Drawing.Text.Base.Label+++import Wumpus.Basic.Kernel -- package: wumpus-basic+import Wumpus.Basic.System.FontLoader++import Wumpus.Core -- package: wumpus-core++import System.Directory++++main :: IO ()+main = simpleFontLoader main1 >> return ()++main1 :: FontLoader -> IO ()+main1 loader = do+ createDirectoryIfMissing True "./out/" + base_metrics <- loader [ Right times_roman_family ]+ printLoadErrors base_metrics+ let pic1 = runCtxPictureU (makeCtx base_metrics) automata+ writeEPS "./out/automata.eps" pic1+ writeSVG "./out/automata.svg" pic1 +++makeCtx :: FontLoadResult -> DrawingContext+makeCtx = + snap_grid_factors 60.0 60.0 . set_font times_roman . metricsContext 14+++automata :: CtxPicture+automata = udrawTracing (0::Double) $ do+ q0 <- nodei (8,0) $ state "q0"+ q1 <- drawi $ state "q1" `mat` above_right_of q0+ q2 <- drawi $ state "q2" `mat` below_right_of q0+ q3 <- drawi $ stopstate "q3" `mat` below_right_of q1++ s0 <- evalQuery $ left_of q0++ draw $ label_midway_of SE (textline `flip` "0") $ straightconn q0 q1+ draw $ label_midway_of SS (textline `flip` "0") $ arrloop (center q1) (north q1) + draw $ label_midway_of SW (textline `flip` "1") $ straightconn q1 q3+ draw $ label_midway_of NE (textline `flip` "1") $ straightconn q0 q2+ draw $ label_midway_of NW (textline `flip` "0") $ straightconn q2 q3+ draw $ label_midway_of NN (textline `flip` "1") $ arrloop (center q2) (south q2) + draw $ label_atstart_of EE (textline `flip` "start") $ astraightconn s0 (west q0) ++ return ()++-- Monadic at - this is a hack that needs a rethink...+-- +infixr 1 `mat`++mat :: InterpretUnit u => LocImage u a -> Query u (Point2 u) -> Image u a+mat img mq = liftQuery mq >>= \pt -> img `at` pt++state :: String -> DLocImage DCircle+state ss = + localize (set_font times_italic) $ + label_center_of (textline `flip` ss) $ strokedShape $ circle 20+++stopstate :: String -> DLocImage DCircle +stopstate ss = + localize (set_font times_italic) $ + label_center_of (textline `flip` ss) $ dblStrokedShape $ circle 20++++straightconn :: ( Real u, Floating u, InterpretUnit u, Tolerance u+ , u ~ DUnit a, u ~ DUnit b+ , CenterAnchor a, RadialAnchor a+ , CenterAnchor b, RadialAnchor b+ )+ => a -> b -> Image u (AbsPath u)+straightconn a b =+ let (p0,p1) = radialConnectorPoints a b in connect conn_line p0 p1+++astraightconn :: ( Real u, Floating u, InterpretUnit u, Tolerance u)+ => Anchor u -> Anchor u -> Image u (AbsPath u)+astraightconn p0 p1 = connect conn_line p0 p1+++-- Note - there is a problem with @rightArrow@ as @loop@+-- manufactures the start and end points...+--+arrloop :: ( Real u, Floating u, InterpretUnit u, Tolerance u)+ => Anchor u -> Anchor u -> Image u (AbsPath u)+arrloop ctr p1 = + rightArrowPath $ loopPath zradius ctr zincl+ where+ v1 = pvec ctr p1+ zradius = vlength v1+ zincl = vdirection v1+++-- Cf. Parsec\'s Token module...++conn_line :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => ArrowConnector u+conn_line = rightArrowConnector default_connector_props C.conn_line tri45+++++rightArrowPath :: (Real u, Floating u, InterpretUnit u)+ => AbsPath u -> Image u (AbsPath u)+rightArrowPath = arrowDecoratePath Nothing (Just tri45)
demo/ClipPic.hs view
@@ -12,93 +12,132 @@ module ClipPic where -import Wumpus.Basic.Kernel-import Wumpus.Drawing.Chains import Wumpus.Drawing.Colour.SVGColours+import Wumpus.Drawing.Extras.Clip import Wumpus.Drawing.Paths-import Wumpus.Drawing.Text.SafeFonts+import Wumpus.Drawing.Text.StandardFontDefs +import Wumpus.Basic.Kernel -- package: wumpus-basic+ import Wumpus.Core -- package: wumpus-core import Data.AffineSpace -- package: vector-space +import Data.Monoid import System.Directory main :: IO () main = do createDirectoryIfMissing True "./out/"- let pic = runCtxPictureU pic_drawing_ctx big_pic- writeEPS "./out/clip_pic.eps" pic- writeSVG "./out/clip_pic.svg" pic+ let pic1 = runCtxPictureU std_ctx clip_pic+ writeEPS "./out/clip_pic.eps" pic1+ writeSVG "./out/clip_pic.svg" pic1 -pic_drawing_ctx :: DrawingContext-pic_drawing_ctx = standardContext 14+std_ctx :: DrawingContext+std_ctx = standardContext 14 -big_pic :: DCtxPicture-big_pic = pic1 `nextToV` zconcat [cpic1, cpic2, cpic3, cpic4]+-- Note - currently the code is quite messy, path drawing needs +-- some convenience combinators. -fillPath :: Num u => Path u -> Graphic u-fillPath = filledPath . toPrimPath+clip_pic :: CtxPicture+clip_pic = drawTracing $ localize (fill_colour medium_slate_blue) $ do+ drawl (P2 0 320) $ runPathSpec_ CFILL path01+ drawl (P2 112 320) $ localize (fill_colour powder_blue) $ + runPathSpec_ CFILL path02+ drawl (P2 384 416) $ runPathSpec_ CFILL path03+ drawl (P2 328 512) $ runPathSpec_ CFILL path04+ drawl (P2 0 0) $ clip1+ drawl (P2 112 0) $ clip2+ drawl (P2 384 96) $ clip3+ drawl (P2 328 192) $ clip4 -pic1 :: DCtxPicture-pic1 = drawTracing $- localize (fillColour medium_slate_blue) $ do- draw $ fillPath path01- localize (fillColour powder_blue) $ - draw $ fillPath path02- draw $ fillPath path03- draw $ fillPath path04 +-- PathSpec is overkill here...+--+extrPathSpec :: InterpretUnit u + => PathSpec u a -> LocQuery u (AbsPath u)+extrPathSpec spec = qpromoteLoc $ \pt ->+ fmap post $ qapplyLoc (stripGenPathSpec () CSTROKE spec) pt+ where+ post (_,_,c) = c -background :: RGBi -> DCtxPicture-background rgb = drawTracing $ - localize (strokeColour rgb) $ - unchain 112 iheartHaskell $ tableDown 18 (86,16) (P2 0 288)+background :: RGBi -> LocGraphic Double+background rgb = promoteLoc $ \_ -> + ignoreAns $ localize (text_colour rgb) $ ihh `at` P2 0 288+ where+ ihh = distribColumnwiseTable 18 (86,16) $ replicate 112 iheartHaskell+ -cpic1 :: DCtxPicture -cpic1 = clipCtxPicture (toPrimPath path01) (background black)+-- | This is one for Wumpus-Basic - the set of combinators to +-- shift between Images and Queries needs sorting out+--++zapLocQ :: InterpretUnit u => LocQuery u a-> LocImage u a+zapLocQ ma = promoteLoc $ \pt -> + askDC >>= \ctx ->+ let a = runLocQuery ctx pt ma in return a++clip1 :: LocGraphic Double+clip1 = zapLocQ (extrPathSpec path01) >>= \a -> + locClip a (background black) -cpic2 :: DCtxPicture-cpic2 = clipCtxPicture (toPrimPath path02) (background medium_violet_red)+clip2 :: LocGraphic Double+clip2 = zapLocQ (extrPathSpec path02) >>= \a -> + locClip a (background medium_violet_red) -cpic3 :: DCtxPicture -cpic3 = clipCtxPicture (toPrimPath path03) (background black)+clip3 :: LocGraphic Double+clip3 = zapLocQ (extrPathSpec path03) >>= \a -> locClip a (background black) -cpic4 :: DCtxPicture -cpic4 = clipCtxPicture (toPrimPath path04) (background black)+clip4 :: LocGraphic Double+clip4 = zapLocQ (extrPathSpec path04) >>= \a -> locClip a (background black) -iheartHaskell :: Num u => FromPtSize u => LocGraphic u-iheartHaskell = promoteR1 $ \pt -> - let body = textline "I Haskell" `at` pt- heart = localize (fontFace symbol) $ - textline "&heart;" `at` (pt .+^ hvec 7)- in body `oplus` heart+iheartHaskell :: LocGraphic Double+iheartHaskell = promoteLoc $ \pt -> + let body = dcTextlabel "I Haskell" `at` pt+ heart = localize (set_font symbol) $ + dcTextlabel "&heart;" `at` (pt .+^ hvec 7)+ in body `mappend` heart +-- Note - this needs a more uniform name. In Wumpus an object +-- that produces a @UNil@ is called a __Graphic but that +-- convention seems misleading here.+--+type UPathSpec u = PathSpec u (UNil u) -path01 :: Floating u => Path u-path01 = execPath zeroPt $ hline 80 >> rlineto (vec 112 160) - >> rlineto (vec (-112) 160)- >> hline (-80)- >> rlineto (vec 112 (-160))- >> rlineto (vec (-112) (-160))- +-- zeroPt+path01 :: UPathSpec Double+path01 = do + hpenline 80 + penline (vec 112 160) + penline (vec (-112) 160)+ hpenline (-80)+ penline (vec 112 (-160))+ penline (vec (-112) (-160))+ ureturn -path02 :: Floating u => Path u-path02 = execPath (P2 112 0) $ hline 80 >> rlineto (vec 72 112)- >> rlineto (vec 72 (-112))- >> hline 80- >> rlineto (vec (-224) 320)- >> hline (-80)- >> rlineto (vec 112 (-160))- >> rlineto (vec (-112) (-160)) -path03 :: Floating u => Path u-path03 = execPath (P2 384 96) $ hline 96 >> vline 56 >> hline (-136) +-- (P2 112 0)+path02 :: UPathSpec Double+path02 = do + hpenline 80 + penline (vec 72 112)+ penline (vec 72 (-112))+ hpenline 80+ penline (vec (-224) 320)+ hpenline (-80)+ penline (vec 112 (-160))+ penline (vec (-112) (-160))+ ureturn -path04 :: Floating u => Path u-path04 = execPath (P2 328 192) $ hline 152 >> vline 56 >> hline (-192) +-- (P2 384 96) +path03 :: UPathSpec Double+path03 = hpenline 96 >> vpenline 56 >> hpenline (-136) >> ureturn++-- (P2 328 192)+path04 :: UPathSpec Double+path04 = hpenline 152 >> vpenline 56 >> hpenline (-192) >> ureturn
demo/ColourCharts.hs view
@@ -4,11 +4,13 @@ import ColourChartUtils -import Wumpus.Basic.Kernel-import Wumpus.Drawing.Chains+import Wumpus.Drawing.Basis.DrawingPrimitives +import Wumpus.Basic.Kernel -- package: wumpus-basic+ import Wumpus.Core -- package: wumpus-core +import Data.Monoid import System.Directory @@ -21,37 +23,37 @@ writeSVG "./out/SVGcolours.svg" svg_pic -- let x11_p = runCtxPictureU draw_ctx x11_portrait- writeEPS "./out/X11colours.eps" $ uniformScale 0.75 x11_p+ writeEPS "./out/X11colours.eps" $ scale 0.75 0.75 x11_p let x11_l = runCtxPictureU draw_ctx x11_landscape writeSVG "./out/X11colours.svg" x11_l draw_ctx :: DrawingContext draw_ctx = (standardContext 9) -svg :: CtxPicture Double+svg :: CtxPicture svg = makeDrawing 52 all_svg_colours -x11_landscape :: CtxPicture Double+x11_landscape :: CtxPicture x11_landscape = makeDrawing 52 all_x11_colours -x11_portrait :: CtxPicture Double+x11_portrait :: CtxPicture x11_portrait = makeDrawing 72 all_x11_colours -makeDrawing :: Int -> [(String,RGBi)] -> DCtxPicture+makeDrawing :: Int -> [(String,RGBi)] -> CtxPicture makeDrawing row_count xs = drawTracing $ tableGraphic row_count xs tableGraphic :: Int -> [(String,RGBi)] -> TraceDrawing Double () tableGraphic row_count xs = - zipchainWith (\(name,rgb) -> colourSample name rgb) xs ps+ drawl pt $ distribColumnwiseTable row_count (152,11) gs where- ps = tableDown row_count (152,11) pt- pt = displaceV (fromIntegral $ 11 * row_count) zeroPt -+ pt = displace (vvec $ fromIntegral $ 11 * row_count) zeroPt + gs = map (uncurry colourSample) xs+ colourSample :: String -> RGBi -> LocGraphic Double-colourSample name rgb = localize (fillColour rgb) $ - promoteR1 $ \pt -> - oplus (borderedRectangle 15 10 `at` pt)- (textline name `at` displace 20 2 pt)+colourSample name rgb = localize (fill_colour rgb) $ + promoteLoc $ \pt -> + mappend (blRectangle DRAW_FILL_STROKE 15 10 `at` pt)+ (dcTextlabel name `at` displace (vec 20 2) pt)
demo/Connectors.hs view
@@ -2,71 +2,110 @@ module Connectors where --import Wumpus.Basic.Kernel-import Wumpus.Drawing.Arrows-import Wumpus.Drawing.Chains import Wumpus.Drawing.Colour.SVGColours-import Wumpus.Drawing.Paths hiding ( length )+import Wumpus.Drawing.Connectors+import qualified Wumpus.Drawing.Connectors.ConnectorPaths as C+import Wumpus.Drawing.Text.DirectionZero+import Wumpus.Drawing.Text.StandardFontDefs +import Wumpus.Basic.Kernel -- package: wumpus-basic+import Wumpus.Basic.System.FontLoader+ import Wumpus.Core -- package: wumpus-core ++import Data.Monoid import System.Directory + main :: IO ()-main = do - createDirectoryIfMissing True "./out/"- let pic1 = runCtxPictureU std_ctx conn_pic- writeEPS "./out/connectors01.eps" pic1- writeSVG "./out/connectors01.svg" pic1+main = simpleFontLoader main1 >> return () +main1 :: FontLoader -> IO ()+main1 loader = do+ createDirectoryIfMissing True "./out/" + base_metrics <- loader [ Left helvetica ]+ printLoadErrors base_metrics+ let pic1 = runCtxPictureU (makeCtx base_metrics) conn_pic+ writeEPS "./out/connectors.eps" pic1+ writeSVG "./out/connectors.svg" pic1 + +makeCtx :: FontLoadResult -> DrawingContext+makeCtx = set_font helvetica . metricsContext 11 -conn_pic :: CtxPicture Double-conn_pic = drawTracing $ tableGraphic $ conntable+conn_pic :: CtxPicture +conn_pic = drawTracing $ tableGraphic conntable -conntable :: [ConnectorPath Double]++conntable :: [(String, ConnectorPathSpec Double)] conntable = - [ connLine- , connRightVH- , connRightHV- , connRightVHV 15- , connRightHVH 15- , connIsosceles 25- , connIsosceles (-25)- , connIsosceles2 15- , connIsosceles2 (-15)- , connLightningBolt 15- , connLightningBolt (-15)- , connIsoscelesCurve 25- , connIsoscelesCurve (-25)- , connSquareCurve- , connUSquareCurve- , connTrapezoidCurve 40 0.5- , connTrapezoidCurve (-40) 0.5- , connZSquareCurve - , connUZSquareCurve + [ ("conn_line", C.conn_line)+ , ("conna_arc", C.conna_arc)+ , ("connb_arc", C.connb_arc)+ , ("conn_hdiagh", C.conn_hdiagh)+ , ("conn_vdiagv", C.conn_vdiagv)+ , ("conn_diagh", C.conn_diagh)+ , ("conn_diagv", C.conn_diagv)+ , ("conn_hdiag", C.conn_hdiag)+ , ("conn_vdiag", C.conn_vdiag)+ , ("conna_bar", C.conna_bar)+ , ("connb_bar", C.connb_bar)+ , ("conna_flam", C.conna_flam)+ , ("connb_flam", C.connb_flam)+ , ("conna_orthohbar", C.conna_orthohbar)+ , ("connb_orthohbar", C.connb_orthohbar)+ , ("conna_orthovbar", C.conna_orthovbar)+ , ("connb_orthovbar", C.connb_orthovbar)+ , ("conna_right", C.conna_right)+ , ("connb_right", C.connb_right)+ , ("conn_hrr", C.conn_hrr )+ , ("conn_rrh", C.conn_rrh)+ , ("conn_vrr", C.conn_vrr)+ , ("conn_rrv", C.conn_rrv)+ , ("conna_loop", C.conna_loop)+ , ("connb_loop", C.connb_loop)+ , ("conn_hbezier", C.conn_hbezier)+ , ("conn_vbezier", C.conn_vbezier) ] -tableGraphic :: (Real u, Floating u, FromPtSize u) - => [ConnectorPath u] -> TraceDrawing u ()-tableGraphic conns = zipchainWith makeConnDrawing conns ps- where- ps = tableDown 10 (120,52) (displaceV 520 zeroPt)+props :: ConnectorProps+props = default_connector_props { conn_src_arm = 1+ , conn_dst_arm = 1.5+ , conn_src_space = 0.5+ , conn_dst_space = 0.5 } +tableGraphic :: [(String, ConnectorPathSpec Double)] -> TraceDrawing Double ()+tableGraphic conns = + drawl start $ distribColumnwiseTable 6 (200,80) $ map makeConnDrawing conns+ where+ start = P2 0 520 + std_ctx :: DrawingContext-std_ctx = fillColour peru $ standardContext 18+std_ctx = fill_colour peru $ standardContext 18 -makeConnDrawing :: (Real u, Floating u, FromPtSize u) - => ConnectorPath u -> LocGraphic u-makeConnDrawing conn = - promoteR1 $ \p0 -> fmap (replaceL uNil) $ - connect (uniformArrow curveTip conn) p0 (mkP1 p0)+makeConnDrawing :: (String, ConnectorPathSpec Double) -> DLocGraphic +makeConnDrawing (ss,conn) = + promoteLoc $ \p0 -> fn p0 (displace (vec 72 42) p0) where- mkP1 = displace 100 40- + fn p0 p1 = mconcat [disk p0, disk p1, dcon p0 p1, lbl p1]++ disk pt = localize (fill_colour red) $ dcDisk DRAW_FILL 2 `at` pt+ dcon p0 p1 = ignoreAns $ connect biarrow p0 p1++ lbl pt = ignoreAns $ textline WW ss `at` (displace (V2 10 (-10)) pt)++ biarrow = renderConnectorConfig props conf+ ++ conf = ConnectorConfig { conn_arrowl = Just curveTip+ , conn_arrowr = Just curveTip+ , conn_path_spec = conn+ }++
demo/DotPic.hs view
@@ -2,122 +2,103 @@ module DotPic where --import Wumpus.Basic.Kernel-import Wumpus.Basic.System.FontLoader.Afm-import Wumpus.Basic.System.FontLoader.GhostScript-import Wumpus.Drawing.Chains import Wumpus.Drawing.Colour.SVGColours import Wumpus.Drawing.Dots.AnchorDots-import Wumpus.Drawing.Text.SafeFonts+import Wumpus.Drawing.Paths+import Wumpus.Drawing.Text.DirectionZero+import Wumpus.Drawing.Text.StandardFontDefs -import FontLoaderUtils+import Wumpus.Basic.Kernel -- package: wumpus-basic+import Wumpus.Basic.System.FontLoader import Wumpus.Core -- package: wumpus-core -import Data.AffineSpace -- package: vector-space-+import Data.Monoid import System.Directory -- main :: IO ()-main = do - (mb_gs, mb_afm) <- processCmdLine default_font_loader_help- createDirectoryIfMissing True "./out/"- maybe gs_failk makeGSPicture $ mb_gs- maybe afm_failk makeAfmPicture $ mb_afm- where- gs_failk = putStrLn "No GhostScript font path supplied..."- afm_failk = putStrLn "No AFM v4.1 font path supplied..."-+main = simpleFontLoader main1 >> return () -makeGSPicture :: FilePath -> IO ()-makeGSPicture font_dir = do - putStrLn "Using GhostScript metrics..."- (base_metrics, msgs) <- loadGSMetrics font_dir ["Helvetica"]- mapM_ putStrLn msgs+main1 :: FontLoader -> IO ()+main1 loader = do+ createDirectoryIfMissing True "./out/" + base_metrics <- loader [ Left helvetica ]+ printLoadErrors base_metrics let pic1 = runCtxPictureU (makeCtx base_metrics) dot_pic- writeEPS "./out/dot_pic01_gs.eps" pic1- writeSVG "./out/dot_pic01_gs.svg" pic1+ writeEPS "./out/dot_pic.eps" pic1+ writeSVG "./out/dot_pic.svg" pic1 --makeAfmPicture :: FilePath -> IO ()-makeAfmPicture font_dir = do - putStrLn "Using AFM 4.1 metrics..."- (base_metrics, msgs) <- loadAfmMetrics font_dir ["Helvetica"]- mapM_ putStrLn msgs- let pic1 = runCtxPictureU (makeCtx base_metrics) dot_pic- writeEPS "./out/dot_pic01_afm.eps" pic1- writeSVG "./out/dot_pic01_afm.svg" pic1- -makeCtx :: GlyphMetrics -> DrawingContext-makeCtx = fillColour peru . fontFace helvetica . metricsContext 24+makeCtx :: FontLoadResult -> DrawingContext+makeCtx = fill_colour peru . set_font helvetica . metricsContext 14 -dot_pic :: CtxPicture Double-dot_pic = drawTracing $ tableGraphic $ - [ dotHLine- , dotVLine- , dotX- , dotPlus- , dotCross- , dotDiamond- , dotDisk- , dotSquare- , dotCircle- , dotPentagon- , dotStar- , dotAsterisk- , dotOPlus- , dotOCross- , dotFOCross- , dotFDiamond- , dotText "%" - , dotTriangle- ]+dot_pic :: CtxPicture+dot_pic = drawTracing $ tableGraphic dottable -tableGraphic :: (Real u, Floating u, FromPtSize u) - => [DotLocImage u] -> TraceDrawing u ()-tableGraphic imgs = zipchainWith makeDotDrawing imgs ps- where- row_count = length imgs- ps = tableDown row_count (1,36) pt- pt = displaceV (fromIntegral $ 36 * row_count) zeroPt +-- Note - dots should probably have lower_case_with_underscore +-- names. +dottable :: [(String, DotLocImage Double)]+dottable = + [ ("smallDisk", smallDisk)+ , ("largeDisk", largeDisk)+ , ("smallCirc", smallCirc)+ , ("largeCirc", largeCirc)+ , ("dotNone", dotNone)+ , ("dotHBar", dotHBar)+ , ("dotVBar", dotVBar)+ , ("dotX", dotX)+ , ("dotPlus", dotPlus)+ , ("dotCross", dotCross)+ , ("dotDiamond", dotDiamond)+ , ("dotDisk", dotDisk)+ , ("dotSquare", dotSquare)+ , ("dotCircle", dotCircle)+ , ("dotPentagon", dotPentagon)+ , ("dotStar", dotStar)+ , ("dotAsterisk", dotAsterisk)+ , ("dotOPlus", dotOPlus)+ , ("dotOCross", dotOCross)+ , ("dotFOCross", dotFOCross)+ , ("dotFDiamond", dotFDiamond)+ , ("dotText" , dotText "%")+ , ("dotTriangle", dotTriangle) + ] --- This is a bit convoluted - maybe there should be chain-run --- functions for TraceDrawings as well as LocGraphics? -makeDotDrawing :: (Real u, Floating u, FromPtSize u) - => DotLocImage u -> LocGraphic u-makeDotDrawing dotF = - promoteR1 $ \pt -> - let all_points = map (pt .+^) displacements- in oconcat (dashline all_points)- (map (\p1 -> ignoreL $ dotF `at` p1) all_points)+tableGraphic :: [(String, DotLocImage Double)] -> TraceDrawing Double ()+tableGraphic imgs = + drawl pt $ distribColumnwiseTable row_count (180,36) + $ map makeDotDrawing imgs where- dashline = \ps -> localize attrUpd (openStroke $ vertexPath ps)+ row_count = 18+ pt = displace (vvec $ fromIntegral $ 36 * row_count) zeroPt - attrUpd :: DrawingContext -> DrawingContext- attrUpd = dashPattern (evenDashes 1) . strokeColour cadet_blue - ignoreL = fmap (replaceL uNil) -displacements :: Num u => [Vec2 u]-displacements = [V2 0 0, V2 64 20, V2 128 0, V2 192 20] +makeDotDrawing :: (String, DotLocImage Double) -> DLocGraphic +makeDotDrawing (name,df) = + drawing `mappend` moveStart (vec 86 14) lbl+ where+ drawing = runPathSpec_ OSTROKE path_spec --- Should these produce a DashPattern or a StrokeAttr?+ path_spec = updatePen path_style >>+ insertl dot >> + mapM (\v -> penline v >> insertl dot) steps >>+ ureturn+ + -evenDashes :: Int -> DashPattern -evenDashes n = Dash 0 [(n,n)]+ lbl = ignoreAns $ promoteLoc $ \pt -> + textline WW name `at` pt -dashOffset :: Int -> DashPattern -> DashPattern-dashOffset _ Solid = Solid-dashOffset n (Dash _ xs) = Dash n xs+ steps = [V2 25 15, V2 25 (-15), V2 25 15]+ dot = ignoreAns df+ path_style = packed_dotted . stroke_colour cadet_blue+
demo/FeatureModel.hs view
@@ -1,66 +1,46 @@-{-# LANGUAGE TypeFamilies #-} {-# OPTIONS -Wall #-} module FeatureModel where -import Wumpus.Basic.Kernel-import Wumpus.Basic.System.FontLoader.Afm-import Wumpus.Basic.System.FontLoader.GhostScript-import Wumpus.Drawing.Arrows-import Wumpus.Drawing.Paths +import Wumpus.Drawing.Connectors+import qualified Wumpus.Drawing.Connectors.ConnectorPaths as C+import Wumpus.Drawing.Paths import Wumpus.Drawing.Shapes-import Wumpus.Drawing.Text.LRText-import Wumpus.Drawing.Text.SafeFonts--import Wumpus.Core -- package: wumpus-core+import Wumpus.Drawing.Text.DirectionZero+import Wumpus.Drawing.Text.StandardFontDefs -import FontLoaderUtils+import Wumpus.Basic.Kernel -- package: wumpus-basic+import Wumpus.Basic.System.FontLoader +import Wumpus.Core -- package: wumpus-core import System.Directory main :: IO ()-main = do - (mb_gs, mb_afm) <- processCmdLine default_font_loader_help- createDirectoryIfMissing True "./out/"- putStrLn "Note - text centering does not seem to be working well at present..."- maybe gs_failk makeGSPicture $ mb_gs- maybe afm_failk makeAfmPicture $ mb_afm- where- gs_failk = putStrLn "No GhostScript font path supplied..."- afm_failk = putStrLn "No AFM v4.1 font path supplied..."+main = simpleFontLoader main1 >> return () -makeGSPicture :: FilePath -> IO ()-makeGSPicture font_dir = do - putStrLn "Using GhostScript metrics..."- (base_metrics, msgs) <- loadGSMetrics font_dir ["Courier-Bold"]- mapM_ putStrLn msgs+main1 :: FontLoader -> IO ()+main1 loader = do+ createDirectoryIfMissing True "./out/" + base_metrics <- loader [ Left courier_bold ]+ printLoadErrors base_metrics let pic1 = runCtxPictureU (makeCtx base_metrics) feature_model writeEPS "./out/feature_model01.eps" pic1 writeSVG "./out/feature_model01.svg" pic1 -makeAfmPicture :: FilePath -> IO ()-makeAfmPicture font_dir = do - putStrLn "Using AFM 4.1 metrics..."- (base_metrics, msgs) <- loadAfmMetrics font_dir ["Courier-Bold"]- mapM_ putStrLn msgs- let pic1 = runCtxPictureU (makeCtx base_metrics) feature_model - writeEPS "./out/feature_model02.eps" pic1- writeSVG "./out/feature_model02.svg" pic1 --makeCtx :: GlyphMetrics -> DrawingContext-makeCtx = fontFace courier_bold . metricsContext 18+makeCtx :: FontLoadResult -> DrawingContext+makeCtx = set_font courier_bold . metricsContext 18 -- Note - I haven't worked out how to do @alternative@, @or@ and -- @repetitions@ yet. -- -feature_model :: CtxPicture Double -feature_model = drawTracing $ do+feature_model :: CtxPicture+feature_model = udrawTracing (0::Double) $ do lea <- widebox "e" $ P2 150 160 lra <- widebox "r" $ P2 60 80 lsa <- widebox "s" $ P2 240 80@@ -91,46 +71,51 @@ -- Note - ctrCenterLine does not seem to be working well... -makeBox :: (Real u, Floating u, FromPtSize u) +makeBox :: (Real u, Floating u, InterpretUnit u, Tolerance u) => u -> String -> Point2 u -> TraceDrawing u (Box u) makeBox w ss pt = do a <- drawi $ (strokedShape $ rectangle w 20) `at` pt- drawi_ $ ctrCenterLine ss `at` center a- -- draw $ filledDisk 2 `at` center a+ drawl (center a) $ textline CENTER ss return a -box :: (Real u, Floating u, FromPtSize u) +box :: (Real u, Floating u, InterpretUnit u, Tolerance u) => String -> Point2 u -> TraceDrawing u (Box u) box = makeBox 40 -widebox :: (Real u, Floating u, FromPtSize u) +widebox :: (Real u, Floating u, InterpretUnit u, Tolerance u) => String -> Point2 u -> TraceDrawing u (Box u) widebox = makeBox 60 -connWith :: ( Real u, Floating u, FromPtSize u ) - => Arrowhead u -> Box u -> Box u -> TraceDrawing u (Path u)+connWith :: ( Real u, Floating u, InterpretUnit u, Tolerance u) + => ArrowTip -> Box u -> Box u -> TraceDrawing u (AbsPath u) connWith arrh b0 b1 = do lw <- getLineWidth let p0 = south b0- let p1 = northwards (realToFrac lw) b1- drawi $ apply2R2 (rightArrow arrh connLine) p0 p1+ let p1 = projectAnchor north (realToFrac lw) b1+ drawi $ connect (mkConn_line arrh) p0 p1 infixr 4 `cmandatory`, `coptional`, `cmandatory_`, `coptional_` -cmandatory :: ( Real u, Floating u, FromPtSize u ) - => Box u -> Box u -> TraceDrawing u (Path u)+cmandatory :: ( Real u, Floating u, InterpretUnit u, Tolerance u) + => Box u -> Box u -> TraceDrawing u (AbsPath u) cmandatory = connWith diskTip -coptional :: ( Real u, Floating u, FromPtSize u ) - => Box u -> Box u -> TraceDrawing u (Path u)+coptional :: ( Real u, Floating u, InterpretUnit u, Tolerance u) + => Box u -> Box u -> TraceDrawing u (AbsPath u) coptional = connWith odiskTip -cmandatory_ :: ( Real u, Floating u, FromPtSize u ) +cmandatory_ :: ( Real u, Floating u, InterpretUnit u, Tolerance u) => Box u -> Box u -> TraceDrawing u () cmandatory_ p0 p1 = connWith diskTip p0 p1 >> return () -coptional_ :: ( Real u, Floating u, FromPtSize u ) +coptional_ :: ( Real u, Floating u, InterpretUnit u, Tolerance u) => Box u -> Box u -> TraceDrawing u () coptional_ p0 p1 = connWith odiskTip p0 p1 >> return ()++++mkConn_line :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => ArrowTip -> ArrowConnector u+mkConn_line = rightArrowConnector default_connector_props C.conn_line
− demo/FontLoaderUtils.hs
@@ -1,95 +0,0 @@-{-# OPTIONS -Wall #-}---module FontLoaderUtils- (- processCmdLine- , default_font_loader_help- ) where---import Control.Applicative-import Control.Monad-import System.Directory-import System.Console.GetOpt-import System.Environment-import System.IO.Error---wumpus_gs_font_dir :: String-wumpus_gs_font_dir = "WUMPUS_GS_FONT_DIR"--wumpus_afm_font_dir :: String-wumpus_afm_font_dir = "WUMPUS_AFM_FONT_DIR"---default_font_loader_help :: String-default_font_loader_help = unlines $ - [ "This example uses glyph metrics loaded at runtime."- , "It can use either the metrics files supplied with GhostScript,"- , "or the AFM v4.1 metrics for the Core 14 fonts available from"- , "Adobe's website."- , "" - , "To use GhostScripts font metrics set the environemt variable"- , wumpus_gs_font_dir ++ " to point to the GhostScript fonts"- , "directory (e.g. /usr/share/ghostscript/fonts) or use the command"- , "line flag --gs=PATH_TO_GHOSTSCRIPT_FONTS"- , ""- , "To use the Adode Core 14 font metrics download the archive from"- , "the Adobe website and set the environment variable "- , wumpus_afm_font_dir ++ " to point to it, or use the command line"- , "flag -- afm=PATH_TO_AFM_CORE14_FONTS"- ]---data CmdLineFlag = Help- | GS_FontDir String- | AFM_FontDir String- deriving (Eq,Ord,Show)--processCmdLine :: String -> IO (Maybe FilePath, Maybe FilePath)-processCmdLine help_message = - let options = makeCmdLineOptions help_message in do- args <- getArgs- let (opts, _, _) = getOpt Permute options args- if Help `elem` opts then failk help_message- else succk opts- where- failk msg = putStr msg >> return (Nothing,Nothing) - succk flags = (,) <$> gsFontDirectory flags <*> afmFontDirectory flags - --makeCmdLineOptions :: String -> [OptDescr CmdLineFlag]-makeCmdLineOptions help_message =- [ Option ['h'] ["help"] (NoArg Help) help_message- , Option [] ["afm"] (ReqArg AFM_FontDir "DIR") "AFM v4.1 metrics dir"- , Option [] ["gs"] (ReqArg GS_FontDir "DIR") "GhoshScript font dir"- ]---gsFontDirectory :: [CmdLineFlag] -> IO (Maybe FilePath)-gsFontDirectory = step - where- step (GS_FontDir p:xs) = doesDirectoryExist p >>= \check -> - if check then return (Just p) else step xs-- step (_:xs) = step xs- step [] = envLookup wumpus_gs_font_dir- --afmFontDirectory :: [CmdLineFlag] -> IO (Maybe FilePath)-afmFontDirectory = step - where- step (AFM_FontDir p:xs) = doesDirectoryExist p >>= \check -> - if check then return (Just p) else step xs-- step (_:xs) = step xs- step [] = envLookup wumpus_afm_font_dir---envLookup :: String -> IO (Maybe String)-envLookup name = liftM fn $ try $ getEnv name- where- fn (Left _) = Nothing- fn (Right a) = Just a-
demo/FontPic.hs view
@@ -2,12 +2,12 @@ module FontPic where -import Wumpus.Basic.Kernel-import Wumpus.Drawing.Chains import Wumpus.Drawing.Colour.SVGColours ( steel_blue ) import Wumpus.Drawing.Colour.X11Colours ( indian_red1 )-import Wumpus.Drawing.Text.SafeFonts+import Wumpus.Drawing.Text.StandardFontDefs +import Wumpus.Basic.Kernel -- package: wumpus-basic+ import Wumpus.Core -- package: wumpus-core @@ -34,16 +34,17 @@ writeSVG "./out/font_symbol.svg" symbol_pic -fontMsg :: FontFace -> Int -> String-fontMsg ff sz = msgF []+fontMsg :: FontDef -> Int -> String+fontMsg ft sz = msgF [] where- msgF = showString (ps_font_name ff) . showChar ' ' . shows sz . showString "pt"+ msgF = showString name . showChar ' ' . shows sz . showString "pt"+ name = ps_font_name $ font_def_face ft -makeLabel :: RGBi -> FontFace -> Int -> DLocGraphic-makeLabel rgb ff sz = localize upd (textline $ fontMsg ff sz)+makeLabel :: RGBi -> FontDef -> Int -> DLocGraphic+makeLabel rgb ft sz = localize upd (dcTextlabel $ fontMsg ft sz) where- upd = fillColour rgb . fontAttr ff sz + upd = text_colour rgb . font_attr ft sz -- indian_red1 -- steel_blue@@ -54,32 +55,42 @@ positions :: [Int] positions = [0, 12, 27, 49, 78, 122] +-- Note - this chain has grown a bit crufty as the implementation +-- of chains has changed...+--+pointChain :: (Int -> DLocGraphic) -> DLocGraphic+pointChain fn = runChain_ chn_alg $ mapM (chain1 . fn) point_sizes+ where+ chn_alg = ChainScheme start step+ start = \pt -> (pt,point_sizes) -pointChain :: LocChain Double-pointChain = verticals $ map (fromIntegral . (+2)) point_sizes+ step _ (pt,[]) = let pnext = displace (vvec 50) pt in (pnext, (pnext, []))+ step _ (pt,(y:ys)) = let pnext = displace (vvec $ fromIntegral $ 2 + y) pt+ in (pnext, (pnext, ys)) -fontGraphic :: RGBi -> FontFace -> DPoint2 -> TraceDrawing Double ()-fontGraphic rgb ff pt = - let ps = pointChain pt in - zipchainWith (\sz -> makeLabel rgb ff sz) point_sizes ps+fontGraphic :: RGBi -> FontDef -> DLocGraphic +fontGraphic rgb ft = ignoreAns $ pointChain mkGF+ where+ mkGF sz = makeLabel rgb ft sz std_ctx :: DrawingContext std_ctx = standardContext 10 -fontDrawing :: [(RGBi,FontFace)] -> DCtxPicture+fontDrawing :: [(RGBi,FontDef)] -> CtxPicture fontDrawing xs = drawTracing $ - zipchainWithTD (\(rgb,ff) -> fontGraphic rgb ff) xs ps+ drawl start $ runChain_ chn_alg $ mapM (chain1 . uncurry fontGraphic) xs where- ps = tableDown 4 (1,180) (P2 0 (4*180))+ chn_alg = columnwiseTableScheme 4 (2,180)+ start = P2 0 (4*180) -------------------------------------------------------------------------------- -- Times -times_cxpic :: CtxPicture Double+times_cxpic :: CtxPicture times_cxpic = fontDrawing [ (steel_blue, times_roman) , (indian_red1, times_italic)@@ -87,7 +98,7 @@ , (indian_red1, times_bold_italic) ] -helvetica_cxpic :: CtxPicture Double+helvetica_cxpic :: CtxPicture helvetica_cxpic = fontDrawing [ (steel_blue, helvetica) , (indian_red1, helvetica_oblique)@@ -99,7 +110,7 @@ -------------------------------------------------------------------------------- -courier_cxpic :: CtxPicture Double+courier_cxpic :: CtxPicture courier_cxpic = fontDrawing [ (steel_blue, courier) , (indian_red1, courier_oblique)@@ -111,6 +122,6 @@ -------------------------------------------------------------------------------- -symbol_cxpic :: CtxPicture Double+symbol_cxpic :: CtxPicture symbol_cxpic = fontDrawing [ (steel_blue, symbol) ]
demo/LeftRightText.hs view
@@ -1,192 +1,138 @@ {-# OPTIONS -Wall #-} --- Note - @main@ is more convoluted than would normally be --- expected as it supports both sources of glyph metrics - the --- GhostScript distribution or the Core 14 metrics from Adobe.--- --- \"Real\" applications would be expected to choose one source. ------ I-am-not-a-lawyer, but it does look as though the Adobe font--- metrics are redistributable, the GhostScript metrics are --- seemingly redistributable under the same terms as the larger--- GhostScript distribution.--- module LeftRightText where -import Wumpus.Basic.Kernel-import Wumpus.Basic.System.FontLoader.Afm-import Wumpus.Basic.System.FontLoader.GhostScript import Wumpus.Drawing.Colour.SVGColours-import Wumpus.Drawing.Dots.Marks-import Wumpus.Drawing.Text.LRText-import Wumpus.Drawing.Text.SafeFonts--import FontLoaderUtils+import Wumpus.Drawing.Dots.SimpleDots+import Wumpus.Drawing.Text.DirectionZero+import Wumpus.Drawing.Text.StandardFontDefs +import Wumpus.Basic.Kernel -- package: wumpus-basic+import Wumpus.Basic.System.FontLoader -import Wumpus.Core -- package: wumpus-core+import Wumpus.Core -- package: wumpus-core import System.Directory - main :: IO ()-main = do - (mb_gs, mb_afm) <- processCmdLine default_font_loader_help- createDirectoryIfMissing True "./out/"- maybe gs_failk makeGSPicture $ mb_gs- maybe afm_failk makeAfmPicture $ mb_afm- where- gs_failk = putStrLn "No GhostScript font path supplied..."- afm_failk = putStrLn "No AFM v4.1 font path supplied..."+main = simpleFontLoader main1 >> return () +main1 :: FontLoader -> IO ()+main1 loader = do+ createDirectoryIfMissing True "./out/" + base_metrics <- loader [ Left helvetica ]+ printLoadErrors base_metrics+ let pic1 = runCtxPictureU (makeCtx base_metrics) text_pic+ writeEPS "./out/left_right_text.eps" pic1+ writeSVG "./out/left_right_text.svg" pic1 -makeGSPicture :: FilePath -> IO ()-makeGSPicture font_dir = do- putStrLn "Using GhostScript metrics..."- (gs_metrics, msgs) <- loadGSMetrics font_dir ["Helvetica"]- mapM_ putStrLn msgs- let pic1 = runCtxPictureU (makeCtx gs_metrics) text_pic- writeEPS "./out/lr_text01.eps" pic1- writeSVG "./out/lr_text01.svg" pic1 -makeAfmPicture :: FilePath -> IO ()-makeAfmPicture font_dir = do- putStrLn "Using AFM 4.1 metrics..."- (afm_metrics, msgs) <- loadAfmMetrics font_dir ["Helvetica"]- mapM_ putStrLn msgs- let pic2 = runCtxPictureU (makeCtx afm_metrics) text_pic- writeEPS "./out/lr_text02.eps" pic2- writeSVG "./out/lr_text02.svg" pic2 +makeCtx :: FontLoadResult -> DrawingContext+makeCtx = set_font helvetica . metricsContext 15 +text_pic :: CtxPicture+text_pic = udrawTracing (0::Double) $ localize text_margin_loose $ do + draw $ (fn left_text) `at` P2 0 400+ draw $ (fn center_text) `at` P2 150 400+ draw $ (fn right_text) `at` P2 300 400+ draw $ (fn blank_text) `at` P2 0 300+ draw $ (fn ne_oneline) `at` P2 150 300 + draw $ (fn cc_oneline) `at` P2 300 300 -makeCtx :: GlyphMetrics -> DrawingContext-makeCtx = fontFace helvetica . metricsContext 18 + draw $ (fn sw_oneline) `at` P2 0 200+ draw $ (fn ss_oneline) `at` P2 150 200+ draw $ (fn se_oneline) `at` P2 300 200+ draw $ (fn swr_single) `at` P2 0 100+ draw $ (fn ssr_single) `at` P2 150 100+ draw $ (fn ner_single) `at` P2 300 100 -text_pic :: CtxPicture Double-text_pic = drawTracing $ do - drawi_ $ (fn left_text) `at` P2 0 400- drawi_ $ (fn center_text) `at` P2 200 400- drawi_ $ (fn right_text) `at` P2 400 400- drawi_ $ (fn blank_text) `at` P2 0 300- drawi_ $ (fn bl_oneline) `at` P2 200 300- drawi_ $ (fn cc_oneline) `at` P2 400 300- drawi_ $ (fn newblr) `at` P2 0 200- drawi_ $ (fn newblc) `at` P2 200 200- drawi_ $ (fn newbll) `at` P2 400 200- drawi_ $ (fn rnewblr) `at` P2 0 100- drawi_ $ (fn rnewblc) `at` P2 200 100- drawi_ $ (fn rnewbll) `at` P2 400 100- drawi_ $ (fn rleft_text) `at` P2 0 (-75)- drawi_ $ (fn rcenter_text) `at` P2 200 (-75)- drawi_ $ (fn rright_text) `at` P2 400 (-75)- - draw $ redPlus `at` P2 0 400- draw $ redPlus `at` P2 200 400- draw $ redPlus `at` P2 400 400+ draw $ redPlus `at` P2 150 400+ draw $ redPlus `at` P2 300 400 draw $ redPlus `at` P2 0 300 - draw $ redPlus `at` P2 200 300 - draw $ redPlus `at` P2 400 300 + draw $ redPlus `at` P2 150 300 + draw $ redPlus `at` P2 300 300 draw $ redPlus `at` P2 0 200 - draw $ redPlus `at` P2 200 200 - draw $ redPlus `at` P2 400 200 + draw $ redPlus `at` P2 150 200 + draw $ redPlus `at` P2 300 200 draw $ redPlus `at` P2 0 100 - draw $ redPlus `at` P2 200 100 - draw $ redPlus `at` P2 400 100 + draw $ redPlus `at` P2 150 100 + draw $ redPlus `at` P2 300 100 draw $ redPlus `at` P2 0 (-75)- draw $ redPlus `at` P2 200 (-75)- draw $ redPlus `at` P2 400 (-75)+ draw $ redPlus `at` P2 150 (-75)+ draw $ redPlus `at` P2 300 (-75) where fn = illustrateBoundedLocGraphic -redPlus :: (Fractional u, FromPtSize u) => LocGraphic u-redPlus = localize (strokeColour red) markPlus+redPlus :: (Fractional u, InterpretUnit u) => LocGraphic u+redPlus = localize (stroke_colour red) dotPlus -newblc :: BoundedLocGraphic Double-newblc = - localize (strokeColour dark_slate_gray) $ - baseCenterLine "new baseline center" -newbll :: BoundedLocGraphic Double-newbll = - localize (strokeColour dark_slate_gray) $ - baseLeftLine "new baseline left"+-- single line+--+ne_oneline :: BoundedLocGraphic Double+ne_oneline = textline NE "north east" -newblr :: BoundedLocGraphic Double-newblr = - localize (strokeColour dark_slate_gray) $ - baseRightLine "new baseline right" +-- single line+--+se_oneline :: BoundedLocGraphic Double+se_oneline = textline SE "south east" -rnewblc :: BoundedLocGraphic Double-rnewblc = - localize (strokeColour dark_slate_gray) $ - rbaseCenterLine "baseline center" `rot` (0.25*pi)+-- single line+--+ss_oneline :: BoundedLocGraphic Double+ss_oneline = textline SS "south" -rnewbll :: BoundedLocGraphic Double-rnewbll = - localize (strokeColour dark_slate_gray) $ - rbaseLeftLine "baseline left" `rot` (0.25*pi)+-- single line+--+sw_oneline :: BoundedLocGraphic Double+sw_oneline = textline SW "south west" -rnewblr :: BoundedLocGraphic Double-rnewblr = - localize (strokeColour dark_slate_gray) $ - rbaseRightLine "baseline right" `rot` (0.25 * pi) -bl_oneline :: BoundedLocGraphic Double-bl_oneline = - localize (strokeColour dark_slate_gray) $ baseLeftLine "Baseline-left..."+-- single line rot+--+ssr_single :: BoundedLocGraphic Double+ssr_single = rtextline (0.25*pi) SS "south rot45" +-- single line rot+--+swr_single :: BoundedLocGraphic Double+swr_single = rtextline (0.25*pi) SW "south west rot45" +-- single line rot+--+ner_single :: BoundedLocGraphic Double+ner_single = rtextline (0.25*pi) NE "north east rot45"++ cc_oneline :: BoundedLocGraphic Double-cc_oneline = - localize (strokeColour dark_slate_gray) $ ctrCenterLine "Center-center..."+cc_oneline = textline CENTER "Center-center..." + blank_text :: BoundedLocGraphic Double-blank_text = - localize (strokeColour dark_slate_gray) $ multiAlignCenter ""+blank_text = textline BLC "" left_text :: BoundedLocGraphic Double-left_text = - localize (strokeColour dark_slate_gray) $ multiAlignLeft dummy_text+left_text = multilineText VALIGN_LEFT CENTER dummy_text right_text :: BoundedLocGraphic Double-right_text = - localize (strokeColour dark_slate_gray) $ multiAlignRight dummy_text+right_text = multilineText VALIGN_RIGHT CENTER dummy_text center_text :: BoundedLocGraphic Double-center_text = - localize (strokeColour dark_slate_gray) $ multiAlignCenter dummy_text---rleft_text :: BoundedLocGraphic Double-rleft_text = - localize (strokeColour dark_slate_gray) $ - rmultiAlignLeft dummy_text `rot` (0.25*pi)---rright_text :: BoundedLocGraphic Double-rright_text = - localize (strokeColour dark_slate_gray) $ - rmultiAlignRight dummy_text `rot` (0.25*pi)--rcenter_text :: BoundedLocGraphic Double-rcenter_text = - localize (strokeColour dark_slate_gray) $ - rmultiAlignCenter dummy_text `rot` (0.25*pi)-+center_text = multilineText VALIGN_CENTER CENTER dummy_text dummy_text :: String dummy_text = unlines $ [ "The quick brown"
demo/PetriNet.hs view
@@ -7,17 +7,15 @@ module PetriNet where -import Wumpus.Basic.Kernel-import Wumpus.Basic.System.FontLoader.Afm-import Wumpus.Basic.System.FontLoader.GhostScript-import Wumpus.Drawing.Arrows import Wumpus.Drawing.Colour.SVGColours-import Wumpus.Drawing.Paths+import Wumpus.Drawing.Connectors+import qualified Wumpus.Drawing.Connectors.ConnectorPaths as C import Wumpus.Drawing.Shapes-import Wumpus.Drawing.Text.SafeFonts-import Wumpus.Drawing.Text.LRText+import Wumpus.Drawing.Text.DirectionZero+import Wumpus.Drawing.Text.StandardFontDefs -import FontLoaderUtils+import Wumpus.Basic.Kernel -- package: wumpus-basic+import Wumpus.Basic.System.FontLoader import Wumpus.Core -- package: wumpus-core @@ -25,134 +23,149 @@ import System.Directory -- main :: IO ()-main = do - (mb_gs, mb_afm) <- processCmdLine default_font_loader_help- createDirectoryIfMissing True "./out/"- maybe gs_failk makeGSPicture $ mb_gs- maybe afm_failk makeAfmPicture $ mb_afm- where- gs_failk = putStrLn "No GhostScript font path supplied..."- afm_failk = putStrLn "No AFM v4.1 font path supplied..."+main = simpleFontLoader main1 >> return () -makeGSPicture :: FilePath -> IO ()-makeGSPicture font_dir = do - putStrLn "Using GhostScript metrics..."- (base_metrics, msgs) <- loadGSMetrics font_dir ["Helvetica", "Helvetica-Bold"]- mapM_ putStrLn msgs+main1 :: FontLoader -> IO ()+main1 loader = do+ createDirectoryIfMissing True "./out/" + base_metrics <- loader [ Right helvetica_family ]+ printLoadErrors base_metrics let pic1 = runCtxPictureU (makeCtx base_metrics) petri_net- writeEPS "./out/petri_net01.eps" pic1- writeSVG "./out/petri_net01.svg" pic1 + writeEPS "./out/petri_net.eps" pic1+ writeSVG "./out/petri_net.svg" pic1 -makeAfmPicture :: FilePath -> IO ()-makeAfmPicture font_dir = do - putStrLn "Using AFM 4.1 metrics..."- (base_metrics, msgs) <- loadAfmMetrics font_dir ["Helvetica", "Helvetica-Bold"]- mapM_ putStrLn msgs- let pic1 = runCtxPictureU (makeCtx base_metrics) petri_net- writeEPS "./out/petri_net02.eps" pic1- writeSVG "./out/petri_net02.svg" pic1 +makeCtx :: FontLoadResult -> DrawingContext+makeCtx = set_font helvetica . metricsContext 14 -makeCtx :: GlyphMetrics -> DrawingContext-makeCtx = fontFace helvetica . metricsContext 14-+petri_net :: CtxPicture+petri_net = udrawTracing (0::Double) $ do+ pw <- drawi $ place `at` (P2 0 140)+ tu1 <- drawi $ transition `at` (P2 70 140) + rtw <- drawi $ place `at` (P2 140 140)+ tu2 <- drawi $ transition `at` (P2 210 140)+ w <- drawi $ place `at` (P2 280 140)+ tu3 <- drawi $ transition `at` (P2 350 140)+ res <- drawi $ place `at` (P2 280 70)+ pr <- drawi $ place `at` (P2 0 0)+ tl1 <- drawi $ transition `at` (P2 70 0)+ rtr <- drawi $ place `at` (P2 140 0)+ tl2 <- drawi $ transition `at` (P2 210 0)+ r <- drawi $ place `at` (P2 280 0)+ tl3 <- drawi $ transition `at` (P2 350 0) -petri_net :: DCtxPicture-petri_net = drawTracing $ do- pw <- place 0 140- tu1 <- transition 70 140- rtw <- place 140 140- tu2 <- transition 210 140- w <- place 280 140- tu3 <- transition 350 140- res <- place 280 70- pr <- place 0 0- tl1 <- transition 70 0- rtr <- place 140 0- tl2 <- transition 210 0- r <- place 280 0- tl3 <- transition 350 0- connector' (east pw) (west tu1) - connector' (east tu1) (west rtw)- connector' (east rtw) (west tu2)- connector' (east tu2) (west w)- connector' (east w) (west tu3)- connectorC 32 (north tu3) (north pw)- connector' (east pr) (west tl1) - connector' (east tl1) (west rtr)- connector' (east rtr) (west tl2)- connector' (east tl2) (west r)- connector' (east r) (west tl3)- connectorC (-32) (south tl3) (south pr)- connector' (southwest res) (northeast tl2)- connector' (northwest tl3) (southeast res)- connectorD 6 (southwest tu3) (northeast res)- connectorD (-6) (southwest tu3) (northeast res) - connectorD 6 (northwest res) (southeast tu2)- connectorD (-6) (northwest res) (southeast tu2) + drawc (east pw) (west tu1) $ straightconn+ drawc (east tu1) (west rtw) $ straightconn+ drawc (east rtw) (west tu2) $ straightconn+ drawc (east tu2) (west w) $ straightconn+ drawc (east w) (west tu3) $ straightconn+ drawc (north tu3) (north pw) $ connectorC + drawc (east pr) (west tl1) $ straightconn+ drawc (east tl1) (west rtr) $ straightconn+ drawc (east rtr) (west tl2) $ straightconn+ drawc (east tl2) (west r) $ straightconn+ drawc (east r) (west tl3) $ straightconn+ drawc (south tl3) (south pr) $ connectorC'+ drawc (southwest res) (northeast tl2) $ straightconn+ drawc (northwest tl3) (southeast res) $ straightconn+ drawc (southwest tu3) (northeast res) $ connectorD+ drawc (southwest tu3) (northeast res) $ connectorD'+ drawc (northwest res) (southeast tu2) $ connectorD+ drawc (northwest res) (southeast tu2) $ connectorD' draw $ lblParensParens `at` (P2 (-36) 150) draw $ lblParensParens `at` (P2 300 60) draw $ lblParensParensParens `at` (P2 (-52) (-14))- draw $ lblBold "processing_w" `at` (southwards 12 pw)- draw $ lblBold "ready_to_write" `at` (southwards 12 rtw)- draw $ lblBold "writing" `at` (southwards 12 w)- draw $ lblBold' "resource" `at` (P2 300 72)- draw $ lblBold "processing_r" `at` (northwards 12 pr)- draw $ lblBold "ready_to_read" `at` (northwards 12 rtr)- draw $ lblBold "reading" `at` (northwards 12 r)+ drawl (projectAnchor south 12 pw) $ lblBold "processing_w"+ drawl (projectAnchor south 12 rtw) $ lblBold "ready_to_write"+ drawl (projectAnchor south 12 w) $ lblBold "writing"+ draw $ lblBold "resource" `at` (P2 300 72)+ drawl (projectAnchor north 12 pr) $ lblBold "processing_r"+ drawl (projectAnchor north 12 rtr) $ lblBold "ready_to_read"+ drawl (projectAnchor north 12 r) $ lblBold "reading"+ return () -greenFill :: DrawingCtxM m => m a -> m a-greenFill = localize (fillColour lime_green)+greenFill :: LocImage u a -> LocImage u a+greenFill = localize (fill_colour lime_green) -place :: (Real u, Floating u, DrawingCtxM m, TraceM m, u ~ MonUnit m) - => u -> u -> m (Circle u)-place x y = greenFill $ drawi $ (borderedShape $ circle 14) `at` P2 x y -transition :: ( Real u, Floating u, FromPtSize u- , DrawingCtxM m, TraceM m, u ~ MonUnit m ) - => u -> u -> m (Rectangle u)-transition x y = - greenFill $ drawi $ (borderedShape $ rectangle 32 22) `at` P2 x y+place :: DLocImage (Circle Double)+place = greenFill $ borderedShape $ circle 14 +transition :: DLocImage (Rectangle Double)+transition = greenFill $ borderedShape $ rectangle 32 22 -connector' :: ( TraceM m, DrawingCtxM m, u ~ MonUnit m- , Real u, Floating u, FromPtSize u ) - => Point2 u -> Point2 u -> m ()-connector' p0 p1 = - drawi_ $ apply2R2 (rightArrow tri45 connLine) p0 p1 +makeSglArrConn :: ConnectorPathSpec u -> ConnectorConfig u+makeSglArrConn cspec = + ConnectorConfig+ { conn_arrowl = Nothing+ , conn_arrowr = Just tri45+ , conn_path_spec = cspec+ } -connectorC :: ( Real u, Floating u, FromPtSize u- , DrawingCtxM m, TraceM m, u ~ MonUnit m )- => u -> Point2 u -> Point2 u -> m ()-connectorC v p0 p1 = - drawi_ $ apply2R2 (rightArrow tri45 (connRightVHV v)) p0 p1+straightconn :: ConnectorGraphic Double+straightconn = ignoreAns conn_line -connectorD :: ( Real u, Floating u, FromPtSize u- , DrawingCtxM m, TraceM m, u ~ MonUnit m )- => u -> Point2 u -> Point2 u -> m ()-connectorD u p0 p1 = - drawi_ $ apply2R2 (rightArrow tri45 (connIsosceles u)) p0 p1 +connectorC :: ConnectorGraphic Double+connectorC = ignoreAns conna_bar -lblParensParens :: Num u => LocGraphic u-lblParensParens = localize (fontFace helvetica) $ textline "(),()"+connectorC' :: ConnectorGraphic Double+connectorC' = ignoreAns connb_bar -lblParensParensParens :: Num u => LocGraphic u-lblParensParensParens = localize (fontFace helvetica) $ textline "(),(),()"+connectorD :: ConnectorGraphic Double+connectorD = ignoreAns conna_arc +connectorD' :: ConnectorGraphic Double+connectorD' = ignoreAns connb_arc -lblBold' :: Num u => String -> LocGraphic u-lblBold' ss = localize (fontFace helvetica_bold) $ textline ss +lblParensParens :: DLocGraphic+lblParensParens = + ignoreAns $ localize (set_font helvetica) $ textline CENTER "(),()" -lblBold :: (Real u, Floating u, FromPtSize u) => String -> LocGraphic u-lblBold ss = localize (fontFace helvetica_bold) $ post $ ctrCenterLine ss- where- post = fmap (replaceL uNil)+lblParensParensParens :: DLocGraphic+lblParensParensParens = + ignoreAns $ localize (set_font helvetica) $ textline CENTER "(),(),()"++++lblBold :: String -> DLocGraphic+lblBold ss = + ignoreAns $ localize (set_font helvetica_bold) $ textline CENTER ss++++-- Cf. Parsec\'s Token module...++conn_props :: ConnectorProps+conn_props = (default_connector_props { conn_src_arm = 2+ , conn_dst_arm = 2 + , conn_arc_ang = negate $ pi / 12 })++conn_line :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => ArrowConnector u+conn_line = renderConnectorConfig conn_props $ makeSglArrConn C.conn_line+++conna_bar :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => ArrowConnector u+conna_bar = renderConnectorConfig conn_props $ makeSglArrConn C.conna_bar+++connb_bar :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => ArrowConnector u+connb_bar = renderConnectorConfig conn_props $ makeSglArrConn C.connb_bar+++conna_arc :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => ArrowConnector u+conna_arc = renderConnectorConfig conn_props $ makeSglArrConn C.conna_arc++connb_arc :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => ArrowConnector u+connb_arc = renderConnectorConfig conn_props $ makeSglArrConn C.connb_arc
− demo/PictureCompo.hs
@@ -1,151 +0,0 @@-{-# OPTIONS -Wall #-}--module PictureCompo where--import Wumpus.Basic.Kernel-import Wumpus.Drawing.Colour.SVGColours--import Wumpus.Core -- package: wumpus-core--import System.Directory---main :: IO ()-main = do - createDirectoryIfMissing True "./out/"- let out1 = runCtxPictureU pic_drawing_ctx pictures- writeEPS "./out/picture_composition.eps" out1- writeSVG "./out/picture_composition.svg" out1---pic_drawing_ctx :: DrawingContext-pic_drawing_ctx = standardContext 14---pictures :: DCtxPicture-pictures = vsep 12 [ pic1, pic2, pic3, pic4- , pic5, pic6, pic7, pic8- , pic9, pic10, pic11, pic12 ]---drawBlueBounds :: (Real u, Floating u, FromPtSize u) - => CtxPicture u -> CtxPicture u-drawBlueBounds = mapCtxPicture (illustrateBounds blue)--pic1 :: DCtxPicture-pic1 = picAnno pic "red `over` green `over` blue"- where- pic :: DCtxPicture- pic = drawBlueBounds $ rect_red `over` rect_green `over` rect_blue--pic2 :: DCtxPicture-pic2 = picAnno pic "red `under` green `under` blue"- where- pic :: DCtxPicture- pic = drawBlueBounds $ rect_red `under` rect_green `under` rect_blue---pic3 :: DCtxPicture -pic3 = picAnno pic "red `centric` green `centric` blue"- where- pic :: DCtxPicture- pic = drawBlueBounds $ - rect_red `centric` rect_green `centric` rect_blue---- Note - nextToH only moves pictures in the horizontal.----pic4 :: DCtxPicture -pic4 = picAnno pic "red `nextToH` green `nextToH` blue"- where- pic :: DCtxPicture- pic = drawBlueBounds $ - rect_red `nextToH` rect_green `nextToH` rect_blue---- Note - nextToV only moves pictures in the vertical.----pic5 :: DCtxPicture -pic5 = picAnno pic "red `nextToV` green `nextToV` blue"- where- pic :: DCtxPicture- pic = drawBlueBounds $ - rect_red `nextToV` rect_green `nextToV` rect_blue---pic6 :: DCtxPicture-pic6 = picAnno pic "zconcat [red, green, blue]"- where- pic :: DCtxPicture- pic = drawBlueBounds $ - zconcat [rect_red, rect_green, rect_blue]---pic7 :: DCtxPicture-pic7 = picAnno pic "hcat [red, green, blue]"- where- pic :: DCtxPicture- pic = drawBlueBounds $ - hcat [rect_red, rect_green, rect_blue]--pic8 :: DCtxPicture-pic8 = picAnno pic "vcat [red, green, blue]"- where- pic :: DCtxPicture- pic = drawBlueBounds $ - vcat [rect_red, rect_green, rect_blue]--pic9 :: DCtxPicture-pic9 = picAnno pic "hsep 20 [red, green, blue]"- where- pic :: DCtxPicture- pic = drawBlueBounds $ - hsep 20 [rect_red, rect_green, rect_blue]--pic10 :: DCtxPicture-pic10 = picAnno pic "vsep 20 [red, green, blue]"- where- pic :: DCtxPicture- pic = drawBlueBounds $ - vsep 20 [rect_red, rect_green, rect_blue]---pic11 :: DCtxPicture-pic11 = picAnno pic "hcatA HTop [red, green, blue]"- where- pic :: DCtxPicture- pic = drawBlueBounds $ - hcatA HTop [rect_red, rect_green, rect_blue]---pic12 :: DCtxPicture-pic12 = picAnno pic "vcatA VCenter [red, green, blue]"- where- pic :: DCtxPicture- pic = drawBlueBounds $ - vcatA VCenter [rect_red, rect_green, rect_blue]--------------------------------------------------------------------------------------picAnno :: DCtxPicture -> String -> DCtxPicture-picAnno pic msg = alignHSep HCenter 30 pic lbl- where- lbl = drawTracing $ draw $ textline msg `at` zeroPt---rect_red :: DCtxPicture-rect_red = drawTracing $ - localize (fillColour indian_red)- (draw $ borderedRectangle 30 10 `at` (P2 0 10))- -rect_green :: DCtxPicture-rect_green = drawTracing $ - localize (fillColour olive_drab)- (draw $ borderedRectangle 15 15 `at` (P2 10 10))---rect_blue :: DCtxPicture-rect_blue = drawTracing $ - localize (fillColour powder_blue)- (draw $ borderedRectangle 20 30 `at` (P2 10 0))-
+ demo/SampleShapes.hs view
@@ -0,0 +1,189 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE FlexibleContexts #-}+{-# OPTIONS -Wall #-}+++module SampleShapes where+++import Wumpus.Drawing.Colour.SVGColours+import Wumpus.Drawing.Dots.SimpleDots+import Wumpus.Drawing.Shapes+import Wumpus.Drawing.Text.DirectionZero+import Wumpus.Drawing.Text.StandardFontDefs++import Wumpus.Basic.Kernel -- package: wumpus-basic+import Wumpus.Basic.System.FontLoader++import Wumpus.Core -- package: wumpus-core++import Control.Monad+import Data.Monoid+import System.Directory++main :: IO ()+main = simpleFontLoader main1 >> return ()++main1 :: FontLoader -> IO ()+main1 loader = do+ createDirectoryIfMissing True "./out/shapes/" + base_metrics <- loader [ Left courier ]+ printLoadErrors base_metrics+ let ctx = makeCtx base_metrics+ mapM_ (out1 ctx) shape_list+ where+ out1 ctx (name, shape_pic) = do + let pic1 = runCtxPictureU ctx $ shape_pic name+ writeEPS ("./out/shapes/" ++ name ++ "01.eps") pic1+ writeSVG ("./out/shapes/" ++ name ++ "01.svg") pic1+++type ShapeList = [(String, (String -> CtxPicture))]+++shape_list :: ShapeList+shape_list = + [ ( "circle"+ , shapePic voidExtra $ circle 150)+ , ( "diamond"+ , shapePic (apexAnchor >=> midPoints 4) $ diamond 150 100)+ , ( "ellipse"+ , shapePic voidExtra $ ellipse 150 100)+ , ( "invsemicircle"+ , shapePic (apexAnchor >=> topCorners) $ invsemicircle 150)+ , ( "invsemiellipse"+ , shapePic (apexAnchor >=> topCorners) $ invsemiellipse 100 150)+ , ( "invtriangle"+ , shapePic (apexAnchor >=> topCorners >=> midPoints 3) $ + invtriangle 300 150)+ , ( "parallelogram"+ , shapePic (topCorners >=> bottomCorners >=> midPoints 4) $ + zparallelogram 250 200)+ , ( "rectangle"+ , shapePic (topCorners >=> bottomCorners >=> midPoints 4) $ + rectangle 300 175)+ , ( "semicircle"+ , shapePic (apexAnchor >=> bottomCorners) $ semicircle 150) + , ( "semiellipse"+ , shapePic (apexAnchor >=> bottomCorners) $ semiellipse 100 150) + , ( "trapezium"+ , shapePic (bottomCorners >=> topCorners >=> midPoints 4) $ + trapezium 300 180 ang60)+ , ( "triangle"+ , shapePic (apexAnchor >=> bottomCorners >=> midPoints 3) $ + triangle 300 150 )+ ]++makeCtx :: FontLoadResult -> DrawingContext+makeCtx = set_font courier . metricsContext 16++rotate05 :: Rotate a => Image u a -> Image u a+rotate05 = rotate (d2r (5::Double))++-- Extra elaboration...++voidExtra :: a -> TraceDrawing u ()+voidExtra _ = return ()++++apexAnchor :: ( Real u, Floating u, InterpretUnit u+ , ApexAnchor a, u ~ DUnit a)+ => a -> TraceDrawing u a+apexAnchor a = do+ draw $ label EAST "(apex)" `at` apex a+ return a++bottomCorners :: ( Real u, Floating u, InterpretUnit u+ , BottomCornerAnchor a, u ~ DUnit a+ )+ => a -> TraceDrawing u a+bottomCorners a = do+ draw $ label SOUTH_WEST "(bottom left)" `at` bottomLeftCorner a+ draw $ label SOUTH_EAST "(bottom right)" `at` bottomRightCorner a+ return a++topCorners :: ( Real u, Floating u, InterpretUnit u+ , TopCornerAnchor a, u ~ DUnit a+ )+ => a -> TraceDrawing u a+topCorners a = do+ draw $ label NORTH_WEST "(top left)" `at` topLeftCorner a+ draw $ label NORTH_EAST "(top right)" `at` topRightCorner a+ return a+++midPoints :: ( Real u, Floating u, InterpretUnit u+ , SideMidpointAnchor a, u ~ DUnit a+ )+ => Int -> a -> TraceDrawing u a+midPoints n a = mapM_ mf [1..n] >> return a+ where+ mf i = let msg = "(side midpt " ++ show i ++ ")"+ in draw $ label EAST msg `at` sideMidpoint i a+++++shapePic :: ( Functor t+ , CenterAnchor (t Double)+ , CardinalAnchor (t Double)+ , CardinalAnchor2 (t Double)+ , RadialAnchor (t Double)+ , Scale (t Double)+ , Rotate (t Double)+ , Double ~ DUnit (t Double)+ ) + => (t Double -> DTraceDrawing a) -> DShape t -> String -> CtxPicture+shapePic mf sh name = udrawTracing (0::Double) $ do+ a1 <- localize shapeSty $ drawi $ + uniformScale 2 $ {- rotate05 $ -} shape `at` (P2 100 0)+ draw $ label NORTH "(center)" `at` center a1+ draw $ label NORTH "(north)" `at` north a1+ draw $ label SOUTH "(south)" `at` south a1+ draw $ label EAST "(east)" `at` east a1+ draw $ label WEST "(west)" `at` west a1+ draw $ label NORTH_EAST "(northeast)" `at` northeast a1+ draw $ label NORTH_WEST "(northwest)" `at` northwest a1+ draw $ label SOUTH_EAST "(southeast)" `at` southeast a1+ draw $ label SOUTH_WEST "(southwest)" `at` southwest a1+ draw $ label EAST "(10 deg)" `at` radialAnchor (d2r 10) a1+ draw $ label NORTH_WEST "(110 deg)" `at` radialAnchor (d2r 110) a1+ draw $ label WEST "(190 deg)" `at` radialAnchor (d2r 190) a1+ draw $ label NORTH "(250 deg)" `at` radialAnchor (d2r 250) a1+ draw $ label WEST "(200 deg)" `at` radialAnchor (d2r 200) a1++-- draw $ label WEST "(0 deg)" `at` radialAnchor (d2r 0) a1+ draw $ label NORTH "(224.5 deg)" `at` radialAnchor (d2r 225.5) a1+ _ <- mf a1+ return () + where+ shape = strokedShape $ setDecoration textF sh+ textF = promoteLocTheta $ \pt _ -> + ignoreAns (multilineText VALIGN_CENTER CENTER name) `at` pt++++++shapeSty :: DrawingContextF+shapeSty = stroke_colour light_steel_blue . line_ultra_thick++label :: (Real u, Floating u, InterpretUnit u) + => Cardinal -> String -> LocGraphic u+label cpos ss = dotX `mappend` msg+ where+ (rpos,fn) = go cpos+ msg = ignoreAns $ moveStart (fn 10) $ + multilineText VALIGN_CENTER rpos ss ++ go NORTH = (SS, go_north)+ go NORTH_EAST = (SW, go_north_east)+ go EAST = (WW, go_east) + go SOUTH_EAST = (NW, go_south_east)+ go SOUTH = (NN, go_south)+ go SOUTH_WEST = (NE, go_south_west)+ go WEST = (EE, go_west)+ go NORTH_WEST = (SE, go_north_west)+ +
+ demo/SingleChar.hs view
@@ -0,0 +1,69 @@+{-# OPTIONS -Wall #-}+++module SingleChar where+++import Wumpus.Drawing.Colour.SVGColours+import Wumpus.Drawing.Dots.SimpleDots+import Wumpus.Drawing.Text.StandardFontDefs++import Wumpus.Basic.Kernel -- package: wumpus-basic+import Wumpus.Basic.System.FontLoader++import Wumpus.Core -- package: wumpus-core++import System.Directory+++main :: IO ()+main = simpleFontLoader main1 >> return ()++main1 :: FontLoader -> IO ()+main1 loader = do+ createDirectoryIfMissing True "./out/" + base_metrics <- loader [ Left helvetica ]+ printLoadErrors base_metrics+ let pic1 = runCtxPictureU (makeCtx base_metrics) drawing01+ writeEPS "./out/single_char.eps" pic1+ writeSVG "./out/single_char.svg" pic1+++makeCtx :: FontLoadResult -> DrawingContext+makeCtx = set_font helvetica . metricsContext 18++++drawing01 :: CtxPicture+drawing01 = drawTracing $ localize (fill_colour red) $ mf +++-- Note - Baseline positions not meaningful for multiline text++mf :: TraceDrawing Double ()+mf = localize (text_margin 6.0 6.0) $ do+ draw $ (fn SS $ posChar 'S') `at` zeroPt+ draw $ redPlus `at` zeroPt++ draw $ (fn NN $ posChar 'N') `at` P2 40 0+ draw $ redPlus `at` P2 40 0++ draw $ (fn EE $ posChar 'E') `at` P2 80 0+ draw $ redPlus `at` P2 80 0++ draw $ (fn WW $ posChar 'W') `at` P2 120 0+ draw $ redPlus `at` P2 120 0++ draw $ (fn CENTER $ posChar 'C') `at` P2 160 0+ draw $ redPlus `at` P2 160 0++ draw $ (fn NE $ posChar 'X') `at` P2 200 0+ draw $ redPlus `at` P2 200 0++ where+ fn addr obj = illustrateBoundedLocGraphic $ runPosObjectBBox addr obj+++redPlus :: (Fractional u, InterpretUnit u) => LocGraphic u+redPlus = localize (stroke_colour red) dotPlus+
+ demo/SingleLine.hs view
@@ -0,0 +1,61 @@+{-# OPTIONS -Wall #-}+++module SingleLine where++import Wumpus.Drawing.Colour.SVGColours+import Wumpus.Drawing.Text.DirectionZero+import Wumpus.Drawing.Text.StandardFontDefs++import Wumpus.Basic.Kernel -- package: wumpus-basic+import Wumpus.Basic.System.FontLoader++import Wumpus.Core -- package: wumpus-core++import Data.Monoid+import System.Directory+++main :: IO ()+main = simpleFontLoader main1 >> return ()++main1 :: FontLoader -> IO ()+main1 loader = do+ createDirectoryIfMissing True "./out/" + base_metrics <- loader [ Left helvetica ]+ printLoadErrors base_metrics+ let pic1 = runCtxPictureU (makeCtx base_metrics) drawing01+ writeEPS "./out/single_line.eps" pic1+ writeSVG "./out/single_line.svg" pic1++++makeCtx :: FontLoadResult -> DrawingContext+makeCtx = set_font helvetica . metricsContext 12++++drawing01 :: CtxPicture+drawing01 = drawTracing $ localize (fill_colour red) $ mf +++mf :: TraceDrawing Double ()+mf = do+ draw $ testDraw NN `at` (P2 0 200)+ draw $ testDraw SS `at` (P2 75 200)+ draw $ testDraw EE `at` (P2 150 200)+ draw $ testDraw WW `at` (P2 225 200)+ draw $ testDraw NE `at` (P2 0 100)+ draw $ testDraw SE `at` (P2 75 100)+ draw $ testDraw SW `at` (P2 150 100)+ draw $ testDraw NW `at` (P2 225 100)+ draw $ testDraw CENTER `at` (P2 0 0)+ ++testDraw :: RectAddress -> LocGraphic Double+testDraw rpos = dcDisk DRAW_FILL 2 `mappend` (ignoreAns ans)+ where+ ans = textline rpos "Qwerty"+++
demo/Symbols.hs view
@@ -2,220 +2,89 @@ module Symbols where -import Wumpus.Basic.Kernel-import Wumpus.Drawing.Chains-import Wumpus.Drawing.Text.SafeFonts+import Wumpus.Drawing.Basis.Symbols+import Wumpus.Drawing.Colour.SVGColours+import Wumpus.Drawing.Text.DirectionZero+import Wumpus.Drawing.Text.StandardFontDefs +import Wumpus.Basic.Kernel -- package: wumpus-basic+import Wumpus.Basic.System.FontLoader import Wumpus.Core -- package: wumpus-core -import Prelude hiding ( pi, product )-+import Data.Monoid import System.Directory + main :: IO ()-main = do - createDirectoryIfMissing True "./out/"- let pic1 = runCtxPictureU std_ctx symbols+main = simpleFontLoader main1 >> return ()++main1 :: FontLoader -> IO ()+main1 loader = do+ createDirectoryIfMissing True "./out/" + base_metrics <- loader [ Left times_roman ]+ printLoadErrors base_metrics+ let pic1 = runCtxPictureU (makeCtx base_metrics) symb_pic writeEPS "./out/symbols.eps" pic1- writeSVG "./out/symbols.svg" pic1+ writeSVG "./out/symbols.svg" pic1 + +makeCtx :: FontLoadResult -> DrawingContext+makeCtx = fill_colour khaki . set_font times_roman . metricsContext 14 -std_ctx :: DrawingContext-std_ctx = fontFace times_roman $ standardContext 12+symb_pic :: CtxPicture +symb_pic = drawTracing $ tableGraphic symbtable --- Because the font changes, we draw the all the symbols in one--- run and all the labels in a second run. This helps Wumpus-Core --- generate better PostScript as there are less changes to the --- /graphics state/.----symbols :: DCtxPicture-symbols = drawTracing $ do- localize (fontFace symbol) $ zipchainWith sdraw all_letters ps- zipchainWith ldraw all_letters ps- where- ps = tableDown 30 (100,20) (P2 0 (30*20))- sdraw (s,_) = textline s- ldraw (_,name) = moveStartPoint (displaceH 16) (textline name)+symbtable :: [(String, LocGraphic Double)]+symbtable = + [ ("scircle", scircle 8) + , ("fcircle", fcircle 8) + , ("fscircle", fscircle 8) + , ("ssquare", ssquare 8) + , ("fsquare", fsquare 8) + , ("fssquare", fssquare 8) + , ("sleft_slice", sleft_slice 14)+ , ("fleft_slice", fleft_slice 14)+ , ("fsleft_slice", fsleft_slice 14)+ , ("sright_slice", sright_slice 14)+ , ("fright_slice", fright_slice 14)+ , ("fsright_slice", fsright_slice 14)+ , ("sleft_triangle", sleft_triangle 14)+ , ("fleft_triangle", fleft_triangle 14)+ , ("fsleft_triangle", fsleft_triangle 14)+ , ("sright_triangle", sright_triangle 14)+ , ("fright_triangle", fright_triangle 14)+ , ("fsright_triangle", fsright_triangle 14)+ , ("ochar", ochar $ CharLiteral 'a')+ , ("ochar - bad", ochar $ CharLiteral 'g')+ , ("ocharDescender", ocharDescender $ CharLiteral 'g')+ , ("ocharUpright", ocharUpright $ CharLiteral '8')+ , ("ocharUpright - bad", ocharUpright $ CharLiteral 'a')+ , ("ocurrency", ocurrency 8)+ , ("hbar", hbar 14)+ , ("vbar", vbar 14)+ , ("dbl_hbar", dbl_hbar 14)+ , ("dbl_vbar", dbl_vbar 14)+ ] -all_letters :: [(String, String)]-all_letters = - [ ("Α", "Alpha") - , ("Β", "Beta")- , ("Χ", "Chi")- , ("Δ", "Delta")- , ("Ε", "Epsilon")- , ("Η", "Eta")- , ("&Euro;", "Euro")- , ("Γ", "Gamma")- , ("&Ifraktur;", "Ifraktur")- , ("Ι", "Iota")- , ("Κ", "Kappa")- , ("Λ", "Lambda")- , ("Μ", "Mu")- , ("Ν", "Nu")- , ("Ω", "Omega")- , ("Ο", "Omicron")- , ("Φ", "Phi")- , ("Π", "Pi")- , ("Ψ", "Psi")- , ("&Rfraktur;", "Rfraktur")- , ("Ρ", "Rho")- , ("Σ", "Sigma")- , ("Τ", "Tau")- , ("Θ", "Theta")- , ("Υ", "Upsilon")- , ("&Upsilon1;", "Upsilon1")- , ("Ξ", "Xi")- , ("Ζ", "Zeta")- , ("ℵ", "aleph")- , ("α", "alpha")- , ("&ersand;", "ampersand")- , ("∠", "angle")- , ("&angleleft;", "angleleft")- , ("&angleright;", "angleright")- , ("&approxequal;", "approxequal")+tableGraphic :: [(String, LocGraphic Double)] -> TraceDrawing Double ()+tableGraphic symbs = + drawl start $ ignoreAns $ distribColumnwiseTable 14 (180,24)+ $ map makeSymbDrawing symbs+ where+ start = P2 0 520 - -- - , ("&arrowboth;", "arrowboth") - , ("&arrowdblboth;", "arrowdblboth")- , ("&arrowdbldown;", "arrowdbldown")- , ("&arrowdblleft;", "arrowdblleft")- , ("&arrowdblright;", "arrowdblright")- , ("&arrowdblup;", "arrowdblup")- , ("&arrowdown;", "arrowdown")- , ("&arrowleft;", "arrowleft")- , ("&arrowright;", "arrowright")- , ("&arrowup;", "arrowup")- , ("&asteriskmath;", "asteriskmath")- , ("&bar;", "bar")- , ("β", "beta")- , ("&braceleft;", "braceleft")- , ("&braceright;", "braceright")- , ("&bracketleft;", "bracketleft")- , ("&bracketright;", "bracketright")- , ("•", "bullet")- , ("&carriagereturn;", "carriagereturn")- , ("χ", "chi")+ +std_ctx :: DrawingContext+std_ctx = fill_colour peru $ standardContext 18 - --- , ("&circlemultiply;", "circlemultiply") - , ("&circleplus;", "circleplus")- , ("&club;", "club")- , (":", "colon")- , (",", "comma")- , ("&congruent;", "congruent")- , ("©rightsans;", "copyrightsans")- , ("©rightserif;", "copyrightserif")- , ("°ree;", "degree")- , ("δ", "delta")- , ("⋄", "diamond")- , ("÷", "divide")- , ("&dotmath;", "dotmath")- , ("&eight;", "eight")- , ("&element;", "element")- , ("&ellipsis;", "ellipsis")- , ("∅", "emptyset")- , ("ε", "epsilon")- , ("&equal;", "equal")- , ("&equivalence;", "equivalence")- , ("η", "eta")- , ("&exclam;", "exclam")- , ("&existential;", "existential")- , ("&five;", "five")- , ("&florin;", "florin")- , ("&four;", "four")- , ("&fraction;", "fraction")- , ("γ", "gamma")- , ("&gradient;", "gradient")- , ("&greater;", "greater")- , ("&greaterequal;", "greaterequal")- , ("&heart;", "heart")- , ("&infinity;", "infinity")- , ("&integral;", "integral") - -- - , ("&intersection;", "intersection")- , ("ι", "iota")- , ("κ", "kappa")- , ("λ", "lambda")- , ("&less;", "less")- , ("&lessequal;", "lessequal")- , ("&logicaland;", "logicaland")- , ("&logicalnot;", "logicalnot")- , ("&logicalor;", "logicalor")- , ("◊", "lozenge")- , ("−", "minus")- , ("&minute;", "minute")- , ("μ", "mu")- , ("&multiply;", "multiply")- , ("&nine;", "nine")- , ("¬element;", "notelement")- , ("¬equal;", "notequal")- , ("¬subset;", "notsubset")- , ("ν", "nu")- , ("&numbersign;", "numbersign")- , ("ω", "omega")- , ("&omega1;", "omega1")- , ("ο", "omicron")- , ("&one;", "one")- , ("&parenleft;", "parenleft")- , ("&parenright;", "parenright") - --- , ("&partialdiff;", "partialdiff")- , ("&percent;", "percent")- , (".", "period")- , ("&perpendicular;", "perpendicular")- , ("φ", "phi")- , ("&phi1;", "phi1")- , ("π", "pi")- , ("+", "plus")- , ("&plusminus;", "plusminus")- , ("&product;", "product")- , ("&propersubset;", "propersubset")- , ("&propersuperset;", "propersuperset")- , ("&proportional;", "proportional")- , ("ψ", "psi")- , ("&question;", "question")- , ("&radical;", "radical")- , ("&radicalex;", "radicalex")- , ("&reflexsubset;", "reflexsubset")- , ("&reflexsuperset;", "reflexsuperset")- , ("®istersans;", "registersans")- , ("®isterserif;", "registerserif")- , ("ρ", "rho")- - -- - , ("&second;", "second")- , ("&semicolon;", "semicolon")- , ("&seven;", "seven")- , ("σ", "sigma")- , ("&sigma1;", "sigma1")- , ("&similar;", "similar")- , ("&six;", "six")- , ("&slash;", "slash")- , ("&space;", "space")- , ("&spade;", "spade")- , ("&suchthat;", "suchthat")- , ("&summation;", "summation")- , ("τ", "tau")- , ("∴", "therefore")- , ("θ", "theta")- , ("&theta1;", "theta1")- , ("&three;", "three")- , ("&trademarksans;", "trademarksans")- , ("&trademarkserif;", "trademarkserif")- , ("&two;", "two")- , ("&underscore;", "underscore")- , ("&union;", "union")- , ("&universal;", "universal")- , ("υ", "upsilon")- , ("&weierstrass;", "weierstrass")- , ("ξ", "xi")- , ("&zero;", "zero")- , ("ζ", "zeta")- ]+makeSymbDrawing :: (String, LocGraphic Double) -> DLocGraphic +makeSymbDrawing (ss,symb) = symb <> moveStart (hvec 20) lbl+ where+ lbl = ignoreAns $ textline WW ss+
− demo/TableChains.hs
@@ -1,44 +0,0 @@-{-# OPTIONS -Wall #-}--module TableChains where---import Wumpus.Basic.Kernel-import Wumpus.Drawing.Chains-import Wumpus.Drawing.Colour.SVGColours--import Wumpus.Core -- package: wumpus-core--import System.Directory---main :: IO ()-main = do - createDirectoryIfMissing True "./out/"- let pic1 = runCtxPictureU std_ctx table_drawing- writeEPS "./out/table_chains01.eps" pic1- writeSVG "./out/table_chains01.svg" pic1--table_drawing :: CtxPicture Double-table_drawing = drawTracing $ tableGraphic---tableGraphic :: (Real u, Floating u, FromPtSize u) - => TraceDrawing u ()-tableGraphic = do - draw $ filledDisk 3 `at` dstart- draw $ filledDisk 3 `at` rstart- zipchainWith (textline . show) [1..20::Int] downs- zipchainWith (textline . show) [1..20::Int] rights- where- downs = tableDown 4 (36,24) dstart- rights = tableRight 5 (36,24) rstart-- dstart = P2 0 480- rstart = P2 240 480-- -std_ctx :: DrawingContext-std_ctx = fillColour peru $ standardContext 18--
− src/Wumpus/Drawing/Arrows.hs
@@ -1,28 +0,0 @@-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Drawing.Arrows--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : Stephen Tetley <stephen.tetley@gmail.com>--- Stability : highly unstable--- Portability : GHC------ Shim module for arrow connectors and arrowheads.--- -----------------------------------------------------------------------------------module Wumpus.Drawing.Arrows- ( -- module Wumpus.Drawing.Arrows.Tips- , module Wumpus.Drawing.Arrows.Connectors-- ) where--import Wumpus.Drawing.Arrows.Tips-import Wumpus.Drawing.Arrows.Connectors--
− src/Wumpus/Drawing/Arrows/Connectors.hs
@@ -1,104 +0,0 @@-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Drawing.Arrows.Connectors--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : Stephen Tetley <stephen.tetley@gmail.com>--- Stability : highly unstable--- Portability : GHC------ Draw arrows.--------------------------------------------------------------------------------------module Wumpus.Drawing.Arrows.Connectors- ( -- ArrowConnector- , leftArrow- , rightArrow- , leftRightArrow- , uniformArrow-- ) where--import Wumpus.Basic.Kernel-import Wumpus.Drawing.Arrows.Tips-import Wumpus.Drawing.Paths------ An arrowhead always know how to draw itself (filled triangle, --- stroked barb, etc.)------ A Path is currently always drawn with openStroke,--- eventually there might be scope for drawing --- e.g. parallel lines ====-------- | A connector with arrow tips. The connector is an /Image/,--- drawing it returns the path - positions can be taken on the --- path (e.g. @midpoint@) for further decoration.----type ArrowConnector u = ConnectorImage u (Path u)----- | Connector with an arrow tip at the start point \/ left.----leftArrow :: (Real u, Floating u) - => Arrowhead u -> ConnectorPath u -> ArrowConnector u-leftArrow arrh conn = promoteR2 $ \p0 p1 -> - connect conn p0 p1 >>= \cpath -> - arrowhead_retract_dist arrh >>= \dl -> - let path1 = shortenL dl cpath- ang = directionL path1- g1 = openStroke $ toPrimPath path1- g2 = atRot (arrowhead_draw arrh) p0 ang- in fmap (replaceL cpath) $ g1 `oplus` g2 ---- Note - returns original path- ---- | Connector with an arrow tip at the end point \/ right.----rightArrow :: (Real u, Floating u) - => Arrowhead u -> ConnectorPath u -> ArrowConnector u-rightArrow arrh conn = promoteR2 $ \p0 p1 -> - connect conn p0 p1 >>= \cpath -> - arrowhead_retract_dist arrh >>= \dr -> - let path1 = shortenR dr cpath- ang = directionR path1- g1 = openStroke $ toPrimPath path1- g2 = atRot (arrowhead_draw arrh) p1 ang- in fmap (replaceL cpath) $ g1 `oplus` g2------ | Connector with two arrow tips, possibly different.----leftRightArrow :: (Real u, Floating u) - => Arrowhead u -> Arrowhead u -> ConnectorPath u - -> ArrowConnector u-leftRightArrow arrL arrR conn = promoteR2 $ \p0 p1 -> - connect conn p0 p1 >>= \cpath -> - arrowhead_retract_dist arrL >>= \dL -> - arrowhead_retract_dist arrR >>= \dR -> - let path1 = shortenPath dL dR cpath- angL = directionL path1- angR = directionR path1- g1 = openStroke $ toPrimPath path1- gL = atRot (arrowhead_draw arrL) p0 angL- gR = atRot (arrowhead_draw arrR) p1 angR- in fmap (replaceL cpath) $ g1 `oplus` gL `oplus` gR----- | Connector with the same arrow tip at both ends.----uniformArrow :: (Real u, Floating u) - => Arrowhead u -> ConnectorPath u -> ArrowConnector u-uniformArrow arrh cp = leftRightArrow arrh arrh cp-
− src/Wumpus/Drawing/Arrows/Tips.hs
@@ -1,515 +0,0 @@-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Drawing.Arrows.Tips--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : Stephen Tetley <stephen.tetley@gmail.com>--- Stability : highly unstable--- Portability : GHC------ Anchor points on shapes.------ \*\* WARNING \*\* this module is an experiment, and may --- change significantly in future revisions.--- -----------------------------------------------------------------------------------module Wumpus.Drawing.Arrows.Tips- ( --- Arrowhead(..)-- - , tri90- , tri60- , tri45- , otri90- , otri60- , otri45-- , revtri90- , revtri60- , revtri45- , orevtri90- , orevtri60- , orevtri45-- , barb90- , barb60- , barb45- , revbarb90- , revbarb60- , revbarb45-- , perp-- , bracket-- , diskTip- , odiskTip- , squareTip- , osquareTip- , diamondTip- , odiamondTip-- , curveTip- , revcurveTip-- ) where--import Wumpus.Basic.Kernel-import Wumpus.Drawing.Paths--import Wumpus.Core -- package: wumpus-core--import Data.AffineSpace -- package: vector-space--import Control.Applicative----- | Encode an arrowhead as a Graphic and a retract distance - --- lines should be shortened for certain drawings (e.g. open--- triangles).------ The retract distance is context sensitive - usually just on--- the markHeight (or halfMarkHeight) so it has to be calculated--- w.r.t. the DrawingCtx.----data Arrowhead u = Arrowhead - { arrowhead_retract_dist :: DrawingInfo u- , arrowhead_draw :: LocThetaGraphic u - }----- Design note - this used to be a newtype wrapper over a --- LocThetaImage that returned retract distance. But, considering --- the dataflow / evaluation, in some respects Arrowhead is not an --- ideal LocThetaImage as we want the retract distance to work out --- how to draw the image.------ Images do not inherently encode objects that should be drawn --- and evaluated at the same time (and lazy eval permits this) but --- it seems to helpful to think that Images should be evaluated as --- they are drawn.-------- | Tiplen is length of the tip \*along the line it follows\*. ------ > |\--- > | \--- > | /--- > |/--- > --- > | | -- tip len------- | Tip width is the distance between upper and lower --- arrow points.------ > __--- > |\ --- > | \ tip--- > | / width--- > |/ __--- > ------ | This one is for triangular tips defined by their tip angle--- e.g. 90deg, 60deg, 45deg.------ The tip width will be variable (tip length should be the --- markHeight).--- -triVecsByAngle :: Floating u => u -> Radian -> Radian -> (Vec2 u, Vec2 u)-triVecsByAngle tiplen halfang theta = (vec_to_upper, vec_to_lower)- where- hypo_len = tiplen / (fromRadian $ cos halfang)- rtheta = pi + theta -- theta in the opposite direction- vec_to_upper = avec (circularModulo $ rtheta - halfang) hypo_len- vec_to_lower = avec (circularModulo $ rtheta + halfang) hypo_len ------ | This one is for triangles when the tip height and tip width--- are known.----triVecsByDist :: (Real u, Floating u) - => u -> u -> Radian -> (Vec2 u, Vec2 u)-triVecsByDist tiplen half_tipwidth theta = (vec_to_upper, vec_to_lower)- where- hypo_len = sqrt $ (tiplen*tiplen) + (half_tipwidth*half_tipwidth)- halfang = toRadian $ atan (half_tipwidth / tiplen) - rtheta = pi + theta -- theta in the opposite direction- vec_to_upper = avec (circularModulo $ rtheta - halfang) hypo_len- vec_to_lower = avec (circularModulo $ rtheta + halfang) hypo_len -----{--markHeightPlusLineWidth :: (Fractional u, FromPtSize u) => DrawingR u-markHeightPlusLineWidth = - (\h lw -> h + realToFrac lw) <$> markHeight <*> lineWidth--}---markHeightLessLineWidth :: (Fractional u, FromPtSize u) => CF u-markHeightLessLineWidth = - (\h lw -> h - realToFrac lw) <$> markHeight <*> getLineWidth----- noRetract ignores both the angle and the point.------ Its common for the rectraction not to care about the angle or --- the point and only care about the DrawingCtx.----noRetract :: Num u => DrawingInfo u-noRetract = pure 0 ----- | Arrow tips are drawn with a sloid line even if the connector--- line is dashed (tips also override round corners)--solidArrTip :: DrawingCtxM m => m a -> m a-solidArrTip mf = localize (dashPattern Solid . roundCornerFactor 0) mf---solidOpenStroke :: Num u => PrimPath u -> Graphic u-solidOpenStroke = solidArrTip . openStroke--solidClosedStroke :: Num u => PrimPath u -> Graphic u-solidClosedStroke = solidArrTip . closedStroke--solidStrokedDisk :: Num u => u -> LocGraphic u-solidStrokedDisk = solidArrTip . strokedDisk------------------------------------------------------------------------------------tipBody :: FromPtSize u => (Point2 u -> Radian -> u -> CF a) -> LocThetaCF u a-tipBody mf = promoteR2 $ \pt theta -> markHeight >>= \h -> mf pt theta h ---- | Tripoints takes the \*tip length\* is the mark height.------ This means that the 90deg tip has a tip width greater-than the--- mark height (but that is okay - seemingly this is how TikZ --- does it).----tripointsByAngle :: (Floating u, FromPtSize u)- => Radian -> LocThetaCF u (Point2 u, Point2 u)-tripointsByAngle triang = - tipBody $ \pt theta h -> - let (vup,vlo) = triVecsByAngle h (0.5*triang) theta- in pure (pt .+^ vup, pt .+^ vlo)- --revtripointsByAngle :: (Floating u, FromPtSize u)- => Radian - -> LocThetaCF u (Point2 u, Point2 u, Point2 u)-revtripointsByAngle triang = - tipBody $ \pt theta h -> - let theta' = circularModulo $ pi+theta - (vup,vlo) = triVecsByAngle h (0.5*triang) theta'- back_tip = pt .-^ avec theta h - in pure (back_tip .+^ vup, back_tip, back_tip .+^ vlo)----tripointsByDist :: (Real u, Floating u, FromPtSize u)- => LocThetaCF u (Point2 u, Point2 u)-tripointsByDist = - tipBody $ \pt theta h -> - let (vup,vlo) = triVecsByDist h (0.5*h) theta- in pure (pt .+^ vup, pt .+^ vlo)- ---revtripointsByDist :: (Real u, Floating u, FromPtSize u)- => LocThetaCF u (Point2 u, Point2 u, Point2 u)-revtripointsByDist = - tipBody $ \pt theta h -> - let theta' = circularModulo $ pi+theta - (vup,vlo) = triVecsByDist h (0.5*h) theta'- back_tip = pt .-^ avec theta h - in pure (back_tip .+^ vup, back_tip, back_tip .+^ vlo)-------- width = xchar_height--- filled with stroke colour!--triTLG :: (Floating u, Real u, FromPtSize u)- => Radian -> (PrimPath u -> Graphic u) -> LocThetaGraphic u-triTLG triang drawF = - promoteR2 $ \pt theta ->- localize bothStrokeColour $ - apply2R2 (tripointsByAngle triang) pt theta >>= \(u,v) -> - drawF $ vertexPath [pt,u,v]----tri90 :: (Floating u, Real u, FromPtSize u) => Arrowhead u-tri90 = Arrowhead markHeight (triTLG (pi/2) filledPath)---tri60 :: (Floating u, Real u, FromPtSize u) => Arrowhead u-tri60 = Arrowhead markHeight (triTLG (pi/3) filledPath)---tri45 :: (Floating u, Real u, FromPtSize u) => Arrowhead u-tri45 = Arrowhead markHeight (triTLG (pi/4) filledPath)--otri90 :: (Floating u, Real u, FromPtSize u) => Arrowhead u-otri90 = Arrowhead markHeight (triTLG (pi/2) solidClosedStroke)--otri60 :: (Floating u, Real u, FromPtSize u) => Arrowhead u-otri60 = Arrowhead markHeight (triTLG (pi/3) solidClosedStroke)--otri45 :: (Floating u, Real u, FromPtSize u) => Arrowhead u-otri45 = Arrowhead markHeight (triTLG (pi/4) solidClosedStroke)----- width = xchar_height--- filled with stroke colour!--revtriTLG :: (Floating u, Real u, FromPtSize u)- => Radian -> (PrimPath u -> Graphic u) -> LocThetaGraphic u-revtriTLG triang drawF = - promoteR2 $ \pt theta -> - localize bothStrokeColour $ - apply2R2 (revtripointsByAngle triang) pt theta >>= \(u,pt',v) -> - drawF $ vertexPath [u,pt',v]----revtri90 :: (Floating u, Real u, FromPtSize u) => Arrowhead u-revtri90 = Arrowhead markHeightLessLineWidth- (revtriTLG (pi/2) filledPath)--revtri60 :: (Floating u, Real u, FromPtSize u) => Arrowhead u-revtri60 = Arrowhead markHeightLessLineWidth- (revtriTLG (pi/3) filledPath)--revtri45 :: (Floating u, Real u, FromPtSize u) => Arrowhead u-revtri45 = Arrowhead markHeightLessLineWidth- (revtriTLG (pi/4) filledPath)---orevtri90 :: (Floating u, Real u, FromPtSize u) => Arrowhead u-orevtri90 = Arrowhead markHeightLessLineWidth- (revtriTLG (pi/2) solidClosedStroke)--orevtri60 :: (Floating u, Real u, FromPtSize u) => Arrowhead u-orevtri60 = Arrowhead markHeightLessLineWidth- (revtriTLG (pi/3) solidClosedStroke)--orevtri45 :: (Floating u, Real u, FromPtSize u) => Arrowhead u-orevtri45 = Arrowhead markHeightLessLineWidth- (revtriTLG (pi/4) solidClosedStroke)----barbTLG :: (Floating u, Real u, FromPtSize u) => Radian -> LocThetaGraphic u-barbTLG ang = - promoteR2 $ \pt theta -> - apply2R2 (tripointsByAngle ang) pt theta >>= \(u,v) -> - solidOpenStroke $ vertexPath [u,pt,v]----barb90 :: (Floating u, Real u, FromPtSize u) => Arrowhead u-barb90 = Arrowhead noRetract (barbTLG (pi/2))--barb60 :: (Floating u, Real u, FromPtSize u) => Arrowhead u-barb60 = Arrowhead noRetract (barbTLG (pi/3))---barb45 :: (Floating u, Real u, FromPtSize u) => Arrowhead u-barb45 = Arrowhead noRetract (barbTLG (pi/4))----revbarbTLG :: (Floating u, Real u, FromPtSize u) => Radian -> LocThetaGraphic u-revbarbTLG ang = - promoteR2 $ \pt theta -> - apply2R2 (revtripointsByAngle ang) pt theta >>= \(u,pt',v) -> - solidOpenStroke $ vertexPath [u,pt',v]--revbarb90 :: (Floating u, Real u, FromPtSize u) => Arrowhead u-revbarb90 = Arrowhead markHeight (revbarbTLG (pi/2))---revbarb60 :: (Floating u, Real u, FromPtSize u) => Arrowhead u-revbarb60 = Arrowhead markHeight (revbarbTLG (pi/3))--revbarb45 :: (Floating u, Real u, FromPtSize u) => Arrowhead u-revbarb45 = Arrowhead markHeight (revbarbTLG (pi/4))---perpTLG :: (Floating u, FromPtSize u) => LocThetaGraphic u-perpTLG = - tipBody $ \pt theta h -> - let hh = 0.5*h in solidOpenStroke $ rperpPath hh pt theta---rperpPath :: Floating u => u -> Point2 u -> Radian -> PrimPath u-rperpPath hh ctr theta = primPath p0 [lineTo p1]- where- p0 = displacePerpendicular hh theta ctr- p1 = displacePerpendicular (-hh) theta ctr - ---perp :: (Floating u, FromPtSize u) => Arrowhead u-perp = Arrowhead noRetract perpTLG----bracketTLG :: (Floating u, FromPtSize u) => LocThetaGraphic u-bracketTLG = - tipBody $ \pt theta h -> - let hh = 0.5*h in solidOpenStroke $ rbracketPath hh pt theta---rbracketPath :: Floating u => u -> Point2 u -> Radian -> PrimPath u-rbracketPath hh pt theta = vertexPath [p0,p1,p2,p3]- where- p1 = displacePerpendicular hh theta pt- p0 = displaceParallel (-hh) theta p1- p2 = displacePerpendicular (-hh) theta pt- p3 = displaceParallel (-hh) theta p2- ----bracket :: (Floating u, FromPtSize u) => Arrowhead u-bracket = Arrowhead noRetract bracketTLG---diskTLG :: (Floating u, FromPtSize u) - => (u -> Point2 u -> Graphic u) -> LocThetaGraphic u-diskTLG drawF = - tipBody $ \pt theta h -> let hh = 0.5*h - ctr = pt .-^ avec theta hh - in drawF hh ctr---diskTip :: (Floating u, FromPtSize u) => Arrowhead u-diskTip = Arrowhead markHeight (diskTLG drawF)- where- drawF r pt = localize bothStrokeColour $ filledDisk r `at` pt---odiskTip :: (Floating u, FromPtSize u) => Arrowhead u-odiskTip = Arrowhead markHeight (diskTLG drawF)- where- drawF r pt = solidStrokedDisk r `at` pt---squareTLG :: (Floating u, FromPtSize u) - => (PrimPath u -> Graphic u) -> LocThetaGraphic u-squareTLG drawF = - tipBody $ \pt theta h -> drawF $ rsquarePath pt theta (0.5*h)---rsquarePath :: Floating u => Point2 u -> Radian -> u -> PrimPath u-rsquarePath pt theta hh = vertexPath [p0,p1,p2,p3]- where- p0 = displacePerpendicular hh theta pt- p3 = displacePerpendicular (-hh) theta pt- p1 = displaceParallel (-2*hh) theta p0- p2 = displaceParallel (-2*hh) theta p3- --squareTip :: (Floating u, FromPtSize u) => Arrowhead u-squareTip = Arrowhead markHeight (squareTLG drawF)- where- drawF = localize bothStrokeColour . filledPath---osquareTip :: (Floating u, FromPtSize u) => Arrowhead u-osquareTip = Arrowhead markHeight (squareTLG solidClosedStroke)---diamondTLG :: (Floating u, FromPtSize u) - => (PrimPath u -> Graphic u) -> LocThetaGraphic u-diamondTLG drawF = - tipBody $ \pt theta h -> drawF $ rdiamondPath pt theta (0.5*h)- --rdiamondPath :: Floating u => Point2 u -> Radian -> u -> PrimPath u-rdiamondPath pt theta hh = vertexPath [pt,p1,p2,p3]- where- ctr = displaceParallel (-2*hh) theta pt- p1 = displacePerpendicular hh theta ctr- p3 = displacePerpendicular (-hh) theta ctr- p2 = displaceParallel (-4*hh) theta pt- ---diamondTip :: (Floating u, FromPtSize u) => Arrowhead u-diamondTip = Arrowhead (fmap (2*) markHeightLessLineWidth) - (diamondTLG drawF)- where- drawF = localize bothStrokeColour . filledPath---odiamondTip :: (Floating u, FromPtSize u) => Arrowhead u-odiamondTip = Arrowhead (fmap (2*) markHeight) (diamondTLG solidClosedStroke)------- Note - points flipped to get the second trapezium to --- draw /underneath/.----curveTLG :: (Real u, Floating u, FromPtSize u) => LocThetaGraphic u-curveTLG = - tipBody $ \pt theta h -> - cxCurvePath pt theta (0.5*h) >>= \path ->- localize (joinRound . capRound) (solidOpenStroke path)---cxCurvePath :: (Real u, Floating u, FromPtSize u) - => Point2 u -> Radian -> u -> DrawingInfo (PrimPath u)-cxCurvePath pt theta hh =- apply2R2 tripointsByDist pt theta >>= \(tup,tlo) -> - let (u1,u2) = trapezoidFromBasePoints (0.25*hh) 0.5 pt tup- (l2,l1) = trapezoidFromBasePoints (0.25*hh) 0.5 tlo pt - in pure $ toPrimPath $ curve tup u2 u1 pt `append` curve pt l1 l2 tlo-----curveTip :: (Real u, Floating u, FromPtSize u) => Arrowhead u-curveTip = Arrowhead (fmap realToFrac getLineWidth) curveTLG----- Note - points flipped to get the second trapezium to --- draw /underneath/.----revcurveTLG :: (Real u, Floating u, FromPtSize u) => LocThetaGraphic u-revcurveTLG = - tipBody $ \pt theta h ->- cxRevcurvePath pt theta (0.5*h) >>= \path ->- localize (joinRound . capRound) (solidOpenStroke path)--cxRevcurvePath :: (Real u, Floating u, FromPtSize u) - => Point2 u -> Radian -> u -> DrawingInfo (PrimPath u)-cxRevcurvePath pt theta hh = - apply2R2 revtripointsByDist pt theta >>= \(tup,p1,tlo) -> - let (u1,u2) = trapezoidFromBasePoints (0.25*hh) 0.5 p1 tup- (l2,l1) = trapezoidFromBasePoints (0.25*hh) 0.5 tlo p1- in pure $ toPrimPath $ curve tup u2 u1 p1 `append` curve p1 l1 l2 tlo---revcurveTip :: (Real u, Floating u, FromPtSize u) => Arrowhead u-revcurveTip = Arrowhead markHeight revcurveTLG-
+ src/Wumpus/Drawing/Basis/BezierCurve.hs view
@@ -0,0 +1,137 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Basis.BezierCurve+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Datatype for cubic Bezier curve.+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Basis.BezierCurve+ ( ++ BezierCurve(..)+ , vbezierCurve+ , subdivide+ , subdividet++ , bezierLength++ ) where+++import Wumpus.Drawing.Basis.Geometry+++import Wumpus.Core -- package: wumpus-core++import Data.AffineSpace -- package: vector-space+++--------------------------------------------------------------------------------++++-- | A Strict cubic Bezier curve.+--+data BezierCurve u = BezierCurve !(Point2 u) !(Point2 u) !(Point2 u) !(Point2 u)+ deriving (Eq,Ord,Show)++type instance DUnit (BezierCurve u) = u++++vbezierCurve :: Num u + => Vec2 u -> Vec2 u -> Vec2 u -> Point2 u -> BezierCurve u+vbezierCurve v1 v2 v3 p0 = BezierCurve p0 p1 p2 p3+ where+ p1 = p0 .+^ v1+ p2 = p1 .+^ v2+ p3 = p2 .+^ v3++++-- | Curve subdivision via de Casteljau\'s algorithm.+--+subdivide :: Fractional u + => BezierCurve u -> (BezierCurve u, BezierCurve u)+subdivide (BezierCurve p0 p1 p2 p3) =+ (BezierCurve p0 p01 p012 p0123, BezierCurve p0123 p123 p23 p3)+ where+ p01 = midpoint p0 p1+ p12 = midpoint p1 p2+ p23 = midpoint p2 p3+ p012 = midpoint p01 p12+ p123 = midpoint p12 p23+ p0123 = midpoint p012 p123+++-- | subdivide with an affine weight along the line...+--+subdividet :: Real u+ => u -> BezierCurve u -> (BezierCurve u, BezierCurve u)+subdividet t (BezierCurve p0 p1 p2 p3) = + (BezierCurve p0 p01 p012 p0123, BezierCurve p0123 p123 p23 p3)+ where+ p01 = affineComb t p0 p1+ p12 = affineComb t p1 p2+ p23 = affineComb t p2 p3+ p012 = affineComb t p01 p12+ p123 = affineComb t p12 p23+ p0123 = affineComb t p012 p123+++--------------------------------------------------------------------------------++-- | 'bezierLength' : @ start_point * control_1 * control_2 * +-- end_point -> Length @ +--+-- Find the length of a Bezier curve. The result is an +-- approximation, with the /tolerance/ is 0.1 of a point. This+-- seems good enough for drawing (potentially the tolerance could +-- be larger still). +--+-- The result is found through repeated subdivision so the +-- calculation is potentially costly.+--+bezierLength :: (Floating u, Ord u, Tolerance u)+ => BezierCurve u -> u+bezierLength = gravesenLength length_tolerance ++++-- | Jens Gravesen\'s bezier arc-length approximation. +--+-- Note this implementation is parametrized on error tolerance.+--+gravesenLength :: (Floating u, Ord u) => u -> BezierCurve u -> u+gravesenLength err_tol crv = step crv + where+ step c = let l1 = ctrlPolyLength c+ l0 = cordLength c+ in if l1-l0 > err_tol+ then let (a,b) = subdivide c in step a + step b+ else 0.5*l0 + 0.5*l1++-- | Length of the tree lines spanning the control points.+--+ctrlPolyLength :: Floating u => BezierCurve u -> u+ctrlPolyLength (BezierCurve p0 p1 p2 p3) = len p0 p1 + len p1 p2 + len p2 p3+ where+ len pa pb = vlength $ pvec pa pb+++-- | Length of the cord - start point to end point.+--+cordLength :: Floating u => BezierCurve u -> u+cordLength (BezierCurve p0 _ _ p3) = vlength $ pvec p0 p3++
+ src/Wumpus/Drawing/Basis/DrawingPrimitives.hs view
@@ -0,0 +1,132 @@+{-# OPTIONS -Wall #-}+++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Basis.DrawingPrimitives+-- Copyright : (c) Stephen Tetley 2011-2012+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : highly unstable+-- Portability : GHC +--+-- Alternative to the @DrawingPrimitives@ module in Wumpus-Basic.+-- +-- The drawing primitives here are either slightly higher level or+-- less general (more quirky).+--+-- This module is expected to be imported qualified - other modules+-- (e.g. shapes and paths) are likely to export conflicting names.+-- +-- \*\* WARNING \*\* - much of this module is probably obsolete +-- (except wedge).+--+--------------------------------------------------------------------------------++module Wumpus.Drawing.Basis.DrawingPrimitives+ (+++ -- * Lines++ horizontalLine+ , verticalLine+ , pivotLine+++ -- * Rectangles+ , blRectangle+ , ctrRectangle+++ -- * Wedge+ , wedge++ )++ where++import Wumpus.Basic.Kernel -- package: wumpus-basic++import Wumpus.Core -- package: wumpus-core++import Data.AffineSpace -- package: vector-space+++import Data.Monoid+++--------------------------------------------------------------------------------+-- Lines+++-- | Draw a vertical line.+-- +verticalLine :: InterpretUnit u => u -> LocGraphic u +verticalLine len = locStraightLine $ vvec len++-- | Draw a horizontal line.+-- +horizontalLine :: InterpretUnit u => u -> LocGraphic u +horizontalLine len = locStraightLine $ hvec len++++-- | @pivotLine@ : @ left_length * right_length * incline -> LocGraphic @+--+-- Draw a /pivot/ line. The start point is a pivot along the line, +-- not the end. The left and right distances are the extension of+-- the line from the pivot. +--+pivotLine :: (Floating u, InterpretUnit u) => u -> u -> Radian -> LocGraphic u+pivotLine lu ru ang = promoteLoc $ \pt -> + straightLine (pt .+^ avec (ang+pi) lu) (pt .+^ avec ang ru)+++++--------------------------------------------------------------------------------+-- Rectangles++++-- | Draw a rectangle, start point is bottom left.+--+blRectangle :: InterpretUnit u => DrawMode -> u -> u -> LocGraphic u+blRectangle = dcRectangle+++-- | Draw a rectangle, start point is bottom left.+--+ctrRectangle :: (Fractional u, InterpretUnit u) + => DrawMode -> u -> u -> LocGraphic u+ctrRectangle mode w h = + moveStart (vec (-hw) (-hh)) $ dcRectangle mode w h+ where+ hw = 0.5 * w+ hh = 0.5 * h+++++--------------------------------------------------------------------------------+-- Wedge++++-- | wedge : mode * radius * apex_angle+-- +-- Wedge is drawn at the apex.+--+wedge :: (Real u, Floating u, InterpretUnit u) + => DrawMode -> u -> Radian -> LocThetaGraphic u+wedge mode radius ang = promoteLocTheta $ \pt inclin -> + let half_ang = 0.5 * ang + line_in = catline $ avec (inclin + half_ang) radius+ line_out = catline $ avec (inclin - half_ang) (-radius)+ w_arc = circularArc CW ang radius (inclin - half_pi)+ ct = line_in `mappend` w_arc `mappend` line_out+ in supplyLoc pt $ renderCatTrail (closedMode mode) ct+ +
+ src/Wumpus/Drawing/Basis/Geometry.hs view
@@ -0,0 +1,51 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Basis.Geometry+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Misc geometric operations.+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Basis.Geometry+ ( ++ midpoint+ , affineComb++ ) where+++import Wumpus.Core -- package: wumpus-core++import Data.AffineSpace -- package: vector-space+import Data.VectorSpace++--------------------------------------------------------------------------------++++--------------------------------------------------------------------------------+--+++-- | Affine combination...+--+affineComb :: Real u => u -> Point2 u -> Point2 u -> Point2 u+affineComb t p1 p2 = p1 .+^ t *^ (p2 .-. p1)+++-- | 'midpoint' : @ start_point * end_point -> Midpoint @+-- +-- Mid-point on the line formed between the two supplied points.+--+midpoint :: Fractional u => Point2 u -> Point2 u -> Point2 u+midpoint p0 p1 = p0 .+^ v1 ^/ 2 where v1 = p1 .-. p0
+ src/Wumpus/Drawing/Basis/InclineTrails.hs view
@@ -0,0 +1,499 @@+{-# OPTIONS -Wall #-}+++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Basis.InclineTrails+-- Copyright : (c) Stephen Tetley 2011-2012+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : highly unstable+-- Portability : GHC +--+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Basis.InclineTrails+ (++ incline_circle+ , incline_ellipse+ , incline_square+ , incline_rect+ , incline_diamond+ , incline_triangle+ , incline_barb+ , incline_tube+ , incline_chamf_rect++ , trail_diagh+ , trail_diagv+ , trail_hdiag+ , trail_vdiag++ , trail_hdiagh+ , trail_vdiagv++ , trail_perp_bar+ , trail_perp_bar2++ , trail_vflam+ , trail_ortho_hbar+ , trail_ortho_vbar++ , trail_hright+ , trail_vright++ , trail_hrr+ , trail_vrr+ , trail_rrh+ , trail_rrv++ , trail_rect_loop++ , vtriCurve+ , vrectCurve+ , vtrapCurve+ , vbowCurve+ , vwedgeCurve++ )+ where+++import Wumpus.Basic.Kernel++import Wumpus.Core -- package: wumpus-core++import Data.AffineSpace -- package: vector-space++import Data.Monoid+++-- Shapes...++incline_circle :: (Real u, Floating u) => Vec2 u -> AnaTrail u+incline_circle v1 = + anaCatTrail zeroVec (semicircleTrail CW v1 <> semicircleTrail CW rv1)+ where+ rv1 = vreverse v1++incline_ellipse :: (Real u, Floating u) => u -> Vec2 u -> AnaTrail u+incline_ellipse ry v1 = + anaCatTrail zeroVec (semiellipseTrail CW ry v1 <> semiellipseTrail CW ry rv1)+ where+ rv1 = vreverse v1++++incline_square :: (Real u, Floating u) => Vec2 u -> AnaTrail u+incline_square v1 = incline_rect (vlength v1) v1+++incline_rect :: (Real u, Floating u) => u -> Vec2 u -> AnaTrail u+incline_rect h v1 = + anaCatTrail (theta_down (0.5 * h) ang) catt+ where+ len = vlength v1+ ang = vdirection v1+ catt = mconcat [ trail_theta_right len ang+ , trail_theta_up h ang + , trail_theta_left len ang + , trail_theta_down h ang + ]+ +incline_diamond :: (Real u, Floating u) => u -> Vec2 u -> AnaTrail u+incline_diamond h v1 = anaCatTrail zeroVec catt+ where+ hw = 0.5 * vlength v1+ hh = 0.5 * h+ ang = vdirection v1+ catt = mconcat [ orthoCatTrail hw (-hh) ang+ , orthoCatTrail hw hh ang + , orthoCatTrail (-hw) hh ang + , orthoCatTrail (-hw) (-hh) ang + ]++-- | Note - vector represents midpoint of the baseline to the +-- tip. Angle is the ang of the tip.+--+-- This trail is primarily for drawing arrowheads.+-- +incline_triangle :: (Real u, Floating u) => Radian -> Vec2 u -> AnaTrail u+incline_triangle tip_ang v1 = + anaCatTrail (theta_up opposite theta) catt+ where+ half_ang = 0.5 * tip_ang+ theta = vdirection v1+ h = vlength v1+ opposite = h * (fromRadian $ tan half_ang)++ catt = mconcat [ trail_theta_adj_grazing h half_ang theta+ , trail_theta_bkwd_adj_grazing h half_ang theta+ , trail_theta_up (2 * opposite) theta+ ]+++-- | Note - vector represents midpoint of the baseline to the +-- tip. Angle is the ang of the tip.+--+-- This trail is primarily for drawing arrowheads. The resulting +-- path is /open/. +-- +incline_barb :: (Real u, Floating u) => Radian -> Vec2 u -> AnaTrail u+incline_barb tip_ang v1 = + anaCatTrail (theta_up opposite theta) catt+ where+ half_ang = 0.5 * tip_ang+ theta = vdirection v1+ h = vlength v1+ opposite = h * (fromRadian $ tan half_ang)++ catt = mconcat [ trail_theta_adj_grazing h half_ang theta+ , trail_theta_bkwd_adj_grazing h half_ang theta+ ]+++-- | @v1@ is the /interior/ vector.+--+incline_tube :: (Real u, Floating u) => u -> Vec2 u -> AnaTrail u+incline_tube h v1 = + anaCatTrail (theta_down_right hh ang) $ mconcat $+ [ trail_theta_right base_len ang+ , semicircleTrail CCW vup+ , trail_theta_left base_len ang+ , semicircleTrail CCW vdown+ ]+ where+ hh = 0.5 * h+ ang = vdirection v1 + base_len = vlength v1 - h+ vup = avec (ang + half_pi) h+ vdown = avec (ang - half_pi) h+++incline_chamf_rect :: (Real u, Floating u) => u -> Vec2 u -> AnaTrail u+incline_chamf_rect h v1 = + anaCatTrail zeroVec $ mconcat $+ [ trail_theta_down_right hh ang+ , trail_theta_right base_len ang+ , trail_theta_up_right hh ang+ , trail_theta_up_left hh ang+ , trail_theta_left base_len ang+ , trail_theta_down_left hh ang+ ]+ where+ hh = 0.5 * h+ ang = vdirection v1 + base_len = vlength v1 - h++-- | Diagonal-horizontal trail.+--+-- > --@+-- > /+-- > o+-- +trail_diagh :: (Real u, Floating u) => u -> Vec2 u -> CatTrail u+trail_diagh leg v1 = + let h = vector_y v1+ mid = (abs $ vector_x v1) - leg+ in case horizontalDirection $ vdirection v1 of+ RIGHTWARDS -> orthoCatTrail mid h 0 <> trail_right leg+ _ -> orthoCatTrail (-mid) h 0 <> trail_left leg++trail_diagv :: (Real u, Floating u) => u -> Vec2 u -> CatTrail u+trail_diagv leg v1 = + let w = vector_x v1+ mid = (abs $ vector_y v1) - leg+ in case verticalDirection $ vdirection v1 of+ UPWARDS -> orthoCatTrail w mid 0 <> trail_up leg+ _ -> orthoCatTrail w (-mid) 0 <> trail_down leg+++trail_hdiag :: (Real u, Floating u) => u -> Vec2 u -> CatTrail u+trail_hdiag leg v1 = + let h = vector_y v1+ mid = (abs $ vector_x v1) - leg+ in case horizontalDirection $ vdirection v1 of+ RIGHTWARDS -> trail_right leg <> orthoCatTrail mid h 0+ _ -> trail_left leg <> orthoCatTrail (-mid) h 0+++trail_vdiag :: (Real u, Floating u) => u -> Vec2 u -> CatTrail u+trail_vdiag leg v1 = + let w = vector_x v1+ mid = (abs $ vector_y v1) - leg+ in case verticalDirection $ vdirection v1 of+ UPWARDS -> trail_up leg <> orthoCatTrail w mid 0+ _ -> trail_down leg <> orthoCatTrail w (-mid) 0++++-- | Horizontal-diagonal-horizontal trail.+--+-- > --@+-- > /+-- > o--+-- +--+trail_hdiagh :: (Real u, Floating u) => u -> u -> Vec2 u -> CatTrail u+trail_hdiagh legl legr v1 = + let h = vector_y v1+ mid = (abs $ vector_x v1) - (legl + legr)+ in case horizontalDirection $ vdirection v1 of+ RIGHTWARDS -> mconcat [ trail_right legl+ , orthoCatTrail mid h 0+ , trail_right legr+ ] + _ -> mconcat [ trail_left legl+ , orthoCatTrail (-mid) h 0+ , trail_left legr+ ] ++++trail_vdiagv :: (Real u, Floating u) => u -> u -> Vec2 u -> CatTrail u+trail_vdiagv legl legr v1 = + let w = vector_x v1+ mid = (abs $ vector_y v1) - (legl + legr)+ in case verticalDirection $ vdirection v1 of+ UPWARDS -> mconcat [ trail_up legl+ , orthoCatTrail w mid 0+ , trail_up legr+ ] + _ -> mconcat [ trail_down legl+ , orthoCatTrail w (-mid) 0+ , trail_down legr+ ] ++-- | Uniform leg size.+--+trail_perp_bar :: (Real u, Floating u) + => ClockDirection -> u -> Vec2 u -> CatTrail u+trail_perp_bar CW h v1 = trail_perp_barCW h v1+trail_perp_bar _ h v1 = trail_perp_barCW (-h) v1+++trail_perp_barCW :: (Real u, Floating u) => u -> Vec2 u -> CatTrail u+trail_perp_barCW h v1 = + trail_theta_up h ang <> catline v1 <> trail_theta_down h ang+ where+ ang = vdirection v1+++-- | Bar connector - independent leg size, legs perpendicular.+--+-- +-- > o @ +-- > | |+-- > '----' +--+-- The bar is drawn /below/ the points.+--+trail_perp_bar2 :: (Real u, Floating u) + => ClockDirection -> u -> u -> Vec2 u -> CatTrail u+trail_perp_bar2 CW h1 h2 v1 = trail_perp_barCW2 h1 h2 v1+trail_perp_bar2 _ h1 h2 v1 = trail_perp_barCW2 (-h1) (-h2) v1+++trail_perp_barCW2 :: (Real u, Floating u) => u -> u -> Vec2 u -> CatTrail u+trail_perp_barCW2 h1 h2 v1 = + trail_theta_up h1 ang + <> orthoCatTrail (vlength v1) (negate $ h1 - h2) ang+ <> trail_theta_down h2 ang+ where+ ang = vdirection v1+++-- | Independent leg size.+--+trail_vflam :: (Real u, Floating u) + => ClockDirection -> u -> u -> Vec2 u -> CatTrail u+trail_vflam CW h1 h2 v1 = trail_vflamCW h1 h2 v1+trail_vflam _ h1 h2 v1 = trail_vflamCW (-h1) (-h2) v1+++trail_vflamCW :: (Real u, Floating u) => u -> u -> Vec2 u -> CatTrail u+trail_vflamCW h1 h2 v1 = + diffLines [ p0, p0 .+^ vvec h1, p1 .+^ vvec h2, p1 ]+ where+ p0 = zeroPt+ p1 = p0 .+^ v1+++-- | Height is minimum leg height. Ortho bar is horizontal.+--+trail_ortho_hbar :: (Real u, Floating u) + => ClockDirection -> u -> Vec2 u -> CatTrail u+trail_ortho_hbar CW h v1 = trail_ortho_hbarCW h v1+trail_ortho_hbar _ h v1 = trail_ortho_hbarCCW h v1+++trail_ortho_hbarCW :: (Real u, Floating u) + => u -> Vec2 u -> CatTrail u+trail_ortho_hbarCW ymin v1@(V2 x y) = case quadrant $ vdirection v1 of+ QUAD_NE -> trail_up ymaj <> trail_right x <> trail_down ymin+ QUAD_SE -> trail_up ymin <> trail_right x <> trail_down ymaj+ QUAD_NW -> trail_down ymin <> trail_left (abs x) <> trail_up ymaj+ QUAD_SW -> trail_down ymaj <> trail_left (abs x) <> trail_up ymin+ where+ ymaj = ymin + abs y+++++trail_ortho_hbarCCW :: (Real u, Floating u) + => u -> Vec2 u -> CatTrail u+trail_ortho_hbarCCW ymin v1@(V2 x y) = case quadrant $ vdirection v1 of+ QUAD_NE -> trail_down ymin <> trail_right x <> trail_up ymaj+ QUAD_SE -> trail_down ymaj <> trail_right x <> trail_up ymin+ QUAD_NW -> trail_up ymaj <> trail_left (abs x) <> trail_down ymin+ QUAD_SW -> trail_up ymin <> trail_left (abs x) <> trail_down ymaj+ where+ ymaj = ymin + abs y+++++-- | Width is minimum leg width. Ortho bar is vertical.+--+trail_ortho_vbar :: (Real u, Floating u) + => ClockDirection -> u -> Vec2 u -> CatTrail u+trail_ortho_vbar CW w v1 = trail_ortho_vbarCW w v1+trail_ortho_vbar _ w v1 = trail_ortho_vbarCCW w v1+++trail_ortho_vbarCW :: (Real u, Floating u) + => u -> Vec2 u -> CatTrail u+trail_ortho_vbarCW xmin v1@(V2 x y) = case quadrant $ vdirection v1 of+ QUAD_NE -> trail_left xmin <> trail_up y <> trail_right xmaj+ QUAD_NW -> trail_left xmaj <> trail_up y <> trail_right xmin+ QUAD_SE -> trail_right xmaj <> trail_down (abs y) <> trail_left xmin+ QUAD_SW -> trail_right xmin <> trail_down (abs y) <> trail_left xmaj+ where+ xmaj = xmin + abs x++++trail_ortho_vbarCCW :: (Real u, Floating u) + => u -> Vec2 u -> CatTrail u+trail_ortho_vbarCCW xmin v1@(V2 x y) = case quadrant $ vdirection v1 of+ QUAD_NE -> trail_right xmaj <> trail_up y <> trail_left xmin+ QUAD_NW -> trail_right xmin <> trail_up y <> trail_left xmaj+ QUAD_SE -> trail_left xmin <> trail_down (abs y) <> trail_right xmaj+ QUAD_SW -> trail_left xmaj <> trail_down (abs y) <> trail_right xmin+ where+ xmaj = xmin + abs x+++trail_hright :: Num u => Vec2 u -> CatTrail u+trail_hright (V2 x y) = trail_right x <> trail_up y+++trail_vright :: Num u => Vec2 u -> CatTrail u+trail_vright (V2 x y) = trail_up y <> trail_right x++trail_hrr :: (Floating u, Ord u) => u -> Vec2 u -> CatTrail u+trail_hrr x1 (V2 x y) = + trail_theta_right x1 ang + <> trail_theta_up y ang + <> trail_theta_right (abs x - x1) ang+ where+ ang = if x < 0 then pi else 0++trail_vrr :: (Floating u, Ord u) => u -> Vec2 u -> CatTrail u+trail_vrr y1 (V2 x y) = + trail_theta_up y1 ang + <> trail_theta_right x ang + <> trail_theta_up (abs y - y1) ang+ where+ ang = if y < 0 then pi else 0+++trail_rrh :: (Floating u, Ord u) => u -> Vec2 u -> CatTrail u+trail_rrh x1 (V2 x y) = + trail_theta_right (abs x - x1) ang + <> trail_theta_up y ang + <> trail_theta_right x1 ang+ where+ ang = if x < 0 then pi else 0++trail_rrv :: (Floating u, Ord u) => u -> Vec2 u -> CatTrail u+trail_rrv y1 (V2 x y) = + trail_theta_up (abs y - y1) ang + <> trail_theta_right x ang + <> trail_theta_up y1 ang+ where+ ang = if y < 0 then pi else 0+++trail_rect_loop :: (Real u, Floating u) + => ClockDirection -> u -> u -> u -> Vec2 u -> CatTrail u+trail_rect_loop CW = trail_rect_loopCW+trail_rect_loop _ = trail_rect_loopCCW++trail_rect_loopCW :: (Real u, Floating u) + => u -> u -> u -> Vec2 u -> CatTrail u+trail_rect_loopCW extl extr h v1 = + trail_theta_left extl ang+ <> trail_theta_up h ang+ <> trail_theta_right (len + extl + extr) ang+ <> trail_theta_down h ang+ <> trail_theta_left extr ang+ where+ ang = vdirection v1+ len = vlength v1++trail_rect_loopCCW :: (Real u, Floating u)+ => u -> u -> u -> Vec2 u -> CatTrail u+trail_rect_loopCCW extl extr h v1 = + trail_theta_left extl ang+ <> trail_theta_down h ang+ <> trail_theta_right (len + extl + extr) ang+ <> trail_theta_up h ang+ <> trail_theta_left extr ang+ where+ ang = vdirection v1+ len = vlength v1+++-- | 'triCurve' formulated with a /base vector/ rather than +-- base-width and angle of inclination.+--+vtriCurve :: (Real u, Floating u) + => ClockDirection -> u -> Vec2 u -> CatTrail u +vtriCurve clk h v1 = triCurve clk (vlength v1) h (vdirection v1)+ ++-- | 'rectCurve' formulated with a /base vector/ rather than +-- base-width and angle of inclination.+--+vrectCurve :: (Real u, Floating u) + => ClockDirection -> u -> Vec2 u -> CatTrail u+vrectCurve clk h v1 = rectCurve clk (vlength v1) h (vdirection v1)+++-- | 'trapCurve' formulated with a /base vector/ rather than +-- base-width and angle of inclination.+--+vtrapCurve :: (Real u, Floating u)+ => ClockDirection -> u -> Radian -> Vec2 u -> CatTrail u+vtrapCurve clk h interior_ang v1 = + trapCurve clk (vlength v1) h interior_ang (vdirection v1)+++-- | 'bowCurve' formulated with a /base vector/ rather than +-- base-width and angle of inclination.+--+vbowCurve :: (Real u, Floating u)+ => ClockDirection -> u -> Vec2 u -> CatTrail u+vbowCurve clk h v1 = bowCurve clk (vlength v1) h (vdirection v1) ++-- | 'wedgeCurve' formulated with a /base vector/ rather than +-- base-width and angle of inclination.+--+vwedgeCurve :: (Real u, Floating u)+ => ClockDirection -> u -> Vec2 u -> CatTrail u+vwedgeCurve clk h v1 = wedgeCurve clk (vlength v1) h (vdirection v1) +
+ src/Wumpus/Drawing/Basis/ShapeTrails.hs view
@@ -0,0 +1,213 @@+{-# OPTIONS -Wall #-}+++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Basis.ShapeTrails+-- Copyright : (c) Stephen Tetley 2011-2012+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : highly unstable+-- Portability : GHC +--+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Basis.ShapeTrails+ (++ circle_trail+ , rcircle_trail++ , ellipse_trail+ , rellipse_trail++ , rectangle_trail+ , rrectangle_trail++ , diamond_trail+ , rdiamond_trail++ , isosceles_triangle_trail+ , risosceles_triangle_trail++ , semicircle_trail+ , rsemicircle_trail++ , semiellipse_trail+ , rsemiellipse_trail++ , parallelogram_trail+ , rparallelogram_trail++ , trapezium_trail+ , rtrapezium_trail++ )++ where++import Wumpus.Drawing.Basis.InclineTrails++import Wumpus.Basic.Kernel++import Wumpus.Core -- package: wumpus-core++-- import Data.AffineSpace -- package: vector-space+import Data.VectorSpace++import Data.Monoid+++-- Shapes...++circle_trail :: (Real u, Floating u) => u -> AnaTrail u+circle_trail r = rcircle_trail r 0++rcircle_trail :: (Real u, Floating u) => u -> Radian -> AnaTrail u+rcircle_trail r ang = + modifyAna (\v -> v ^-^ avec ang r) $ incline_circle $ avec ang (2 * r)++++ellipse_trail :: (Real u, Floating u) => u -> u -> AnaTrail u+ellipse_trail rx ry = rellipse_trail rx ry 0++rellipse_trail :: (Real u, Floating u) => u -> u -> Radian -> AnaTrail u+rellipse_trail rx ry ang = + modifyAna (\v -> v ^-^ avec ang rx) $ incline_ellipse ry $ avec ang (2 * rx)+++rectangle_trail :: (Real u, Floating u) => u -> u -> AnaTrail u+rectangle_trail w h = rrectangle_trail w h 0+++rrectangle_trail :: (Real u, Floating u) => u -> u -> Radian -> AnaTrail u+rrectangle_trail w h ang = + anaCatTrail (orthoVec (negate $ 0.5 * w) (negate $ 0.5 * h) ang) catt+ where+ catt = mconcat [ trail_theta_right w ang+ , trail_theta_up h ang + , trail_theta_left w ang + , trail_theta_down h ang + ]+++diamond_trail :: (Real u, Floating u) => u -> u -> AnaTrail u+diamond_trail w h = rdiamond_trail w h 0 ++rdiamond_trail :: (Real u, Floating u) => u -> u -> Radian -> AnaTrail u+rdiamond_trail w h ang = + anaCatTrail (theta_right hw ang) catt+ where+ hw = 0.5 * w+ hh = 0.5 * h+ catt = mconcat [ orthoCatTrail (-hw) hh ang+ , orthoCatTrail (-hw) (-hh) ang + , orthoCatTrail hw (-hh) ang + , orthoCatTrail hw hh ang + ]+++isosceles_triangle_trail :: (Real u, Floating u) => u -> u -> AnaTrail u+isosceles_triangle_trail bw h = risosceles_triangle_trail bw h 0 ++-- | Drawn at the centroid (1/3 * h).+--+risosceles_triangle_trail :: (Real u, Floating u) + => u -> u -> Radian -> AnaTrail u+risosceles_triangle_trail bw h ang = + anaCatTrail (orthoVec (negate hbw) (negate $ h / 3) ang) catt+ where+ hbw = 0.5 * bw+ catt = mconcat [ trail_theta_right bw ang+ , orthoCatTrail (-hbw) h ang+ , orthoCatTrail (-hbw) (-h) ang+ ]+++semicircle_trail :: (Real u, Floating u) => u -> AnaTrail u+semicircle_trail r = rsemicircle_trail r 0++rsemicircle_trail :: (Real u, Floating u) => u -> Radian -> AnaTrail u+rsemicircle_trail r ang = + anaCatTrail (orthoVec (negate r) (negate hminor) ang) catt+ where+ -- hminor is the centroid formula for semicircle+ hminor = (4 * r) / (3 * pi)+ catt = trail_theta_right (2 * r) ang+ <> semicircleTrail CCW (avec ang (negate $ 2 * r) )++++semiellipse_trail :: (Real u, Floating u) => u -> u -> AnaTrail u+semiellipse_trail rx ry = rsemiellipse_trail rx ry 0++rsemiellipse_trail :: (Real u, Floating u) => u -> u -> Radian -> AnaTrail u+rsemiellipse_trail rx ry ang = + anaCatTrail (orthoVec (negate rx) (negate hminor) ang) catt+ where+ -- hminor is the centroid formula for semiellipse+ hminor = (4 * ry) / (3 * pi)+ catt = trail_theta_right (2 * rx) ang+ <> semiellipseTrail CCW ry (avec ang (negate $ 2 * rx) )++-- | Note - bottom left angle must be smaller than 180deg, +-- otherwise a runtime error is thrown.+--+parallelogram_trail :: Floating u => u -> u -> Radian -> AnaTrail u+parallelogram_trail w h bottom_left_ang = + rparallelogram_trail w h bottom_left_ang 0++-- | Note - bottom left angle must be smaller than 180deg, +-- otherwise a runtime error is thrown.+--+rparallelogram_trail :: Floating u => u -> u -> Radian -> Radian -> AnaTrail u+rparallelogram_trail w h bl_ang ang+ | bl_ang >= ang180 = + error "rparallelogram_trail - bottom left angle >= 180."+ | otherwise = anaCatTrail ctr_to_bl catt+ where+ -- Note - base_minor is negative for angles > 90+ base_minor = h / (fromRadian $ tan bl_ang)+ + vbase = theta_right w ang+ vrhs = orthoVec base_minor h ang+ vtop = vreverse vbase+ vlhs = vreverse vrhs+ ctr_to_bl = vreverse $ 0.5 *^ (vbase ^+^ vrhs)+ catt = mconcat $ map catline [ vbase, vrhs, vtop, vlhs ]+++-- | Note - bottom left angle must be smaller than 180deg, +-- otherwise a runtime error is thrown.+--+-- Also, no checking is perfomed on the relation between height+-- and bottom_left ang. Out of range values will draw \"twisted\"+-- trapezoids.+-- +trapezium_trail :: Floating u => u -> u -> Radian -> AnaTrail u+trapezium_trail w h bottom_left_ang = + rtrapezium_trail w h bottom_left_ang 0+++-- | Note - bottom left angle must be smaller than 180deg, +-- otherwise a runtime error is thrown.+--+rtrapezium_trail :: Floating u => u -> u -> Radian -> Radian -> AnaTrail u+rtrapezium_trail bw h bl_ang ang + | bl_ang >= ang180 = error "rtrapezium_trail - bottom left angle >= 180."+ | otherwise = anaCatTrail ctr_to_bl catt+ where+ -- Note - base_minor is negative for angles > 90+ base_minor = h / (fromRadian $ tan bl_ang)+ top_width = bw - (2 * base_minor)+ vbase = theta_right bw ang+ vrhs = orthoVec (-base_minor) h ang+ vtop = theta_left top_width ang+ vlhs = orthoVec (-base_minor) (-h) ang+ ctr_to_bl = orthoVec (negate $ 0.5 * bw) (negate $ 0.5 * h) ang+ catt = mconcat $ map catline [ vbase, vrhs, vtop, vlhs ]++
+ src/Wumpus/Drawing/Basis/Symbols.hs view
@@ -0,0 +1,369 @@+{-# OPTIONS -Wall #-}+++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Basis.Symbols+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : highly unstable+-- Portability : GHC +--+-- Symbols - many symbols expected to be re-defined as Dots or+-- character size PosObjects for DocText.+--+-- \*\* WARNING \*\* - naming conventention is to be determined...+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Basis.Symbols+ (++ scircle+ , fcircle+ , fscircle+ + , ssquare+ , fsquare+ , fssquare++ , sleft_slice+ , fleft_slice+ , fsleft_slice++ , sright_slice+ , fright_slice+ , fsright_slice++ , sleft_triangle+ , fleft_triangle+ , fsleft_triangle++ , sright_triangle+ , fright_triangle+ , fsright_triangle++ , ochar+ , ocharUpright+ , ocharDescender+ , ocurrency++ , empty_box++ , hbar+ , vbar+ , dbl_hbar+ , dbl_vbar++ )+ where++import Wumpus.Drawing.Basis.DrawingPrimitives++import Wumpus.Basic.Kernel++import Wumpus.Core -- package: wumpus-core++-- import Data.AffineSpace -- package: vector-space++import Data.Monoid++--+-- DESIGN NOTE +--+-- Should names follow the /function scheme/ (camelCase), or the+-- /data scheme/ (underscore_separators).+--+-- Objects here are functions as they take size param, but they+-- expected to be redefined elsewhere (possibly at fixed size) +-- with the re-definitions used in /user code/ rather than these +-- primitives. +-- +-- Using camelCase here, then under_score in the DocText versions+-- adds double the function signatures to the Haddock docs.+--+-- Also - should the names encode start pos, or can all start +-- positions be center?+--++--+-- TikZ uses o to indicated /circled/.+--+-- Using o as a prefix for /open/ i.e. stroked has the problem +-- that there aren\'t any other characters ideographic for filled+-- or filled stroked.+--++-- scircle+-- fcircle+-- fscircle++++-- | Stroked circle.+-- +-- Start pos - center.+--+scircle :: InterpretUnit u => u -> LocGraphic u+scircle radius = dcDisk DRAW_STROKE radius++-- | Filled circle.+-- +-- Start pos - center.+--+fcircle :: InterpretUnit u => u -> LocGraphic u+fcircle radius = dcDisk DRAW_FILL radius++-- | Filled-stroked circle.+-- +-- Start pos - center.+--+fscircle :: InterpretUnit u => u -> LocGraphic u+fscircle radius = dcDisk DRAW_FILL_STROKE radius+++++-- | Stroked square.+-- +-- Start pos - center.+--+ssquare :: (Fractional u, InterpretUnit u) => u -> LocGraphic u+ssquare w = renderAnaTrail CSTROKE $ rectangleTrail w w +++-- | Filled square.+-- +-- Start pos - center.+--+fsquare :: (Fractional u, InterpretUnit u) => u -> LocGraphic u+fsquare w = renderAnaTrail CFILL $ rectangleTrail w w +++-- | Filled-stroked square.+-- +-- Start pos - center.+--+fssquare :: (Fractional u, InterpretUnit u) => u -> LocGraphic u+fssquare w = renderAnaTrail CFILL_STROKE $ rectangleTrail w w ++++-- | Implementation.+--+lslice :: (Real u, Floating u, InterpretUnit u) + => DrawMode -> u -> LocGraphic u+lslice mode radius = moveStart (go_left $ 0.5 * radius) lwedge+ where+ lwedge = supplyIncline 0 $ wedge mode radius quarter_pi++-- | Stroked left slice (wedge).+-- +-- Start pos - ....+--+sleft_slice :: (Real u, Floating u, InterpretUnit u) + => u -> LocGraphic u+sleft_slice = lslice DRAW_STROKE+++-- | Filled left slice (wedge).+-- +-- Start pos - ....+--+fleft_slice :: (Real u, Floating u, InterpretUnit u) + => u -> LocGraphic u+fleft_slice = lslice DRAW_FILL+++-- | Filled-stroked left slice (wedge).+-- +-- Start pos - ....+--+fsleft_slice :: (Real u, Floating u, InterpretUnit u) + => u -> LocGraphic u+fsleft_slice = lslice DRAW_FILL_STROKE++++-- | Implementation.+--+rslice :: (Real u, Floating u, InterpretUnit u) + => DrawMode -> u -> LocGraphic u+rslice mode radius = moveStart (go_right $ 0.5 * radius) rwedge+ where+ rwedge = supplyIncline pi $ wedge mode radius quarter_pi+++-- | Stroked right slice (wedge).+-- +-- Start pos - ....+--+sright_slice :: (Real u, Floating u, InterpretUnit u) + => u -> LocGraphic u+sright_slice = rslice DRAW_STROKE+++-- | Filled right slice (wedge).+-- +-- Start pos - ....+--+fright_slice :: (Real u, Floating u, InterpretUnit u) + => u -> LocGraphic u+fright_slice = rslice DRAW_FILL+++-- | Filled-stroked right slice (wedge).+-- +-- Start pos - ....+--+fsright_slice :: (Real u, Floating u, InterpretUnit u) + => u -> LocGraphic u+fsright_slice = rslice DRAW_FILL_STROKE++++-- | Implementation.+--+left_tri :: (Fractional u, InterpretUnit u) + => PathMode -> u -> LocGraphic u+left_tri mode w = + renderAnaTrail mode $ anaCatTrail (go_left $ 0.5 * w)+ $ line_r <> vbase <> line_l+ where+ hh = 0.40 * w+ line_r = catline $ vec w hh+ vbase = catline $ go_down $ 2*hh+ line_l = catline $ vec (-w) hh+++-- | Stroked left triangle.+-- +-- Start pos - ....+--+sleft_triangle :: (Real u, Floating u, InterpretUnit u) + => u -> LocGraphic u+sleft_triangle = left_tri CSTROKE+++-- | Filled left triangle.+-- +-- Start pos - ....+--+fleft_triangle :: (Real u, Floating u, InterpretUnit u) + => u -> LocGraphic u+fleft_triangle = left_tri CFILL+++-- | Filled-stroked left triangle.+-- +-- Start pos - ....+--+fsleft_triangle :: (Real u, Floating u, InterpretUnit u) + => u -> LocGraphic u+fsleft_triangle = left_tri CFILL_STROKE+++-- | Implementation+--+right_tri :: (Fractional u, InterpretUnit u) + => PathMode -> u -> LocGraphic u+right_tri mode w = + renderAnaTrail mode $ anaCatTrail (go_right $ 0.5 * w)+ $ line_l <> vbase <> line_r+ where+ hh = 0.40 * w+ line_l = catline $ vec (-w) hh+ vbase = catline $ go_down $ 2*hh+ line_r = catline $ vec w hh+++-- | Stroked right triangle.+-- +-- Start pos - ....+--+sright_triangle :: (Real u, Floating u, InterpretUnit u) + => u -> LocGraphic u+sright_triangle = right_tri CSTROKE+++-- | Filled right triangle.+-- +-- Start pos - ....+--+fright_triangle :: (Real u, Floating u, InterpretUnit u) + => u -> LocGraphic u+fright_triangle = right_tri CFILL+++-- | Filled-stroked right triangle.+-- +-- Start pos - ....+--+fsright_triangle :: (Real u, Floating u, InterpretUnit u) + => u -> LocGraphic u+fsright_triangle = right_tri CFILL_STROKE++++-- | Note this looks horrible for chars with descenders.+--+ochar :: (Fractional u, InterpretUnit u) + => EscapedChar -> LocGraphic u+ochar esc = char1 <> circ1+ where+ char1 = runPosObject CENTER $ posEscChar esc+ circ1 = localize (set_line_width 0.75) $ capHeight >>= \h -> scircle (0.85 * h)++ocharUpright :: (Fractional u, InterpretUnit u) + => EscapedChar -> LocGraphic u+ocharUpright esc = char1 <> circ1+ where+ char1 = runPosObject CENTER $ posEscCharUpright esc+ circ1 = localize (set_line_width 0.75) $ capHeight >>= \h -> scircle (0.85 * h)++ocharDescender :: (Fractional u, InterpretUnit u) + => EscapedChar -> LocGraphic u+ocharDescender esc = char1 <> circ1+ where+ char1 = fmap abs descender >>= \dy -> + moveStart (go_up dy) $ runPosObject CENTER $ posEscCharUpright esc+ circ1 = localize (set_line_width 0.75) $ capHeight >>= \h -> scircle (0.85 * h)+++ocurrency :: (Floating u, InterpretUnit u) + => u -> LocGraphic u +ocurrency ra = scircle ra <> lne <> lnw <> lsw <> lse+ where+ ra3 = 0.33 * ra+ lne = moveStart (go_north_east ra) $ locStraightLine $ go_north_east ra3+ lnw = moveStart (go_north_west ra) $ locStraightLine $ go_north_west ra3+ lsw = moveStart (go_south_west ra) $ locStraightLine $ go_south_west ra3+ lse = moveStart (go_south_east ra) $ locStraightLine $ go_south_east ra3++++empty_box :: (Fractional u, InterpretUnit u) => u -> LocGraphic u+empty_box w = renderAnaTrail CSTROKE $ rectangleTrail w w++hbar :: (Fractional u, InterpretUnit u) => u -> LocGraphic u+hbar u = + renderAnaTrail OSTROKE $ anaCatTrail (go_left $ 0.5 * u) $ trail_right u++vbar :: (Fractional u, InterpretUnit u) => u -> LocGraphic u+vbar u = + renderAnaTrail OSTROKE $ anaCatTrail (go_down $ 0.5 * u) $ trail_up u++dbl_hbar :: (Fractional u, InterpretUnit u) => u -> LocGraphic u+dbl_hbar u = line1 <> line2+ where+ line1 = moveStart (go_up $ 0.1 * u) $ hbar u+ line2 = moveStart (go_down $ 0.1 * u) $ hbar u+++dbl_vbar :: (Fractional u, InterpretUnit u) => u -> LocGraphic u+dbl_vbar u = line1 <> line2+ where+ line1 = moveStart (go_left $ 0.1 * u) $ vbar u+ line2 = moveStart (go_right $ 0.1 * u) $ vbar u
− src/Wumpus/Drawing/Chains.hs
@@ -1,29 +0,0 @@-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Drawing.Chains--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : stephen.tetley@gmail.com--- Stability : unstable--- Portability : GHC ------ Shim module.------ WARNING - very unstable.--------------------------------------------------------------------------------------module Wumpus.Drawing.Chains- (- module Wumpus.Drawing.Chains.Base- , module Wumpus.Drawing.Chains.Derived- -- ) where---import Wumpus.Drawing.Chains.Base-import Wumpus.Drawing.Chains.Derived
− src/Wumpus/Drawing/Chains/Base.hs
@@ -1,117 +0,0 @@-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Drawing.Chains.Base--- Copyright : (c) Stephen Tetley 2010-2011--- License : BSD3------ Maintainer : stephen.tetley@gmail.com--- Stability : unstable--- Portability : GHC ------ Generate points in an iterated chain.------ \*\* WARNING \*\* - unstable. Names are not so good, also --- Wumpus-Basic has a @chain1@ operator...--------------------------------------------------------------------------------------module Wumpus.Drawing.Chains.Base- (-- Chain- , LocChain- , unchain- , zipchain- , zipchainWith-- , unchainTD- , zipchainTD- , zipchainWithTD---- ) where--import Wumpus.Basic.Kernel -- package: wumpus-basic--import Wumpus.Core -- package: wumpus-core------ | A 'Chain' is a list of points. The list is often expected to --- be inifinte, but if it was a Stream it would loose the ability--- to use list comprehensions.--- -type Chain u = [Point2 u]----- | A LocChain is a function from a starting point to a 'Chain'.--- -type LocChain u = Point2 u -> Chain u----- | Note - commonly a 'Chain' may be infinite, so it is only --- unrolled a finite number of times.----unchain :: Int -> LocGraphic u -> Chain u -> TraceDrawing u ()-unchain i op chn = go i chn- where- go n _ | n <= 0 = return ()- go _ [] = return () - go n (x:xs) = draw (op `at` x) >> go (n-1) xs-------zipchain :: [LocGraphic u] -> Chain u -> TraceDrawing u ()-zipchain (g:gs) (p:ps) = draw (g `at` p) >> zipchain gs ps-zipchain _ _ = return ()---zipchainWith :: (a -> LocGraphic u) -> [a] -> Chain u -> TraceDrawing u ()-zipchainWith op xs chn = go xs chn - where- go (a:as) (p:ps) = draw (op a `at` p) >> go as ps- go _ _ = return ()------ | Variant of 'unchain' where the drawing argument is a --- @TraceDrawing@ not a @LocGraphic@. ----unchainTD :: Int -> (Point2 u -> TraceDrawing u ()) -> Chain u -> TraceDrawing u ()-unchainTD i op chn = go i chn- where- go n _ | n <= 0 = return ()- go _ [] = return () - go n (x:xs) = (op x) >> go (n-1) xs---zipchainTD :: [Point2 u -> TraceDrawing u ()] -> Chain u -> TraceDrawing u ()-zipchainTD (g:gs) (p:ps) = g p >> zipchainTD gs ps-zipchainTD _ _ = return ()----zipchainWithTD :: (a -> Point2 u -> TraceDrawing u ()) -> [a] -> Chain u -> TraceDrawing u ()-zipchainWithTD op xs chn = go xs chn - where- go (a:as) (p:ps) = op a p >> go as ps- go _ _ = return ()------ Notes - something like TikZ\'s chains could possibly be --- achieved with a Reader monad (@local@ initially seems better --- for \"state change\" than @set@ as local models a stack). ------ It\'s almost tempting to put point-supply directly in the--- Trace monad so that TikZ style chaining is transparent.--- (The argument against is: how compatible this would be with--- the Turtle monad for example?).----
− src/Wumpus/Drawing/Chains/Derived.hs
@@ -1,126 +0,0 @@-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Drawing.Chains.Derived--- Copyright : (c) Stephen Tetley 2010-2011--- License : BSD3------ Maintainer : stephen.tetley@gmail.com--- Stability : unstable--- Portability : GHC ------ Generate points in an iterated chain.------ WARNING - very unstable.--------------------------------------------------------------------------------------module Wumpus.Drawing.Chains.Derived- (- - tableDown- , tableRight-- , horizontal- , vertical-- , horizontals- , verticals-- ) where--import Wumpus.Drawing.Chains.Base--import Wumpus.Basic.Kernel -- package: wumpus-basic------------------------------------------------------------------------------------- Tables---- Note - for the minor runtime cost, pairing the row_width and --- row_height should make the API more /memorable/...----- | 'tableDown' : @ num_rows * (row_width, row_height) -> LocChain @------ The table grows down and right, the implicit initial point is --- @top-left@.----tableDown :: Num u => Int -> (u,u) -> LocChain u-tableDown n (rw,rh) pt = map fn ints- where- ints = iterate (+1) 0- fn i = let (x,y) = i `divMod` n - in displace (rw * fromIntegral x) (rh * fromIntegral (-y)) pt----- | 'tableRight' : @ num_cols * row_width * row_height -> LocChain @------ The table grows right and down, the implicit initial point is --- @top-left@.------ This chain is infinite.----tableRight :: Num u => Int -> (u,u) -> LocChain u-tableRight n (rw,rh) pt = map fn ints- where- ints = iterate (+1) 0- fn i = let (y,x) = i `divMod` n - in displace (rw * fromIntegral x) (rh * fromIntegral (-y)) pt-------- | 'horizontal' : @ horizontal_dist -> LocChain @------ The chain grows right by the supplied increment.------ This chain is infinite.------ \*\* WARNING \*\* - name due to be changed. Current name is --- too general for this function. ----horizontal :: Num u => u -> LocChain u-horizontal dx = iterate (displaceH dx)----- | 'vertical' : @ vertical_dist -> LocChain @------ The chain grows up by the supplied increment.------ This chain is infinite.------ \*\* WARNING \*\* - name due to be changed. Current name is --- too general for this function. ----vertical :: Num u => u -> LocChain u-vertical dy = iterate (displaceV dy) ----- | 'horizontals' : @ [horizontal_dist] -> LocChain @------ This is a @scanl@ successive displacing the start point.------ This chain is finite (for finite input list).------ \*\* WARNING \*\* - name due to be changed. Current name is --- too general for this function. ----horizontals :: Num u => [u] -> LocChain u-horizontals xs = \pt -> scanl (flip displaceH) pt xs ----- | 'verticals' : @ [vertical_dist] -> LocChain @------ This is a @scanl@ successive displacing the start point.------ This chain is finite (for finite input list).------ \*\* WARNING \*\* - name due to be changed. Current name is --- too general for this function. ----verticals :: Num u => [u] -> LocChain u-verticals ys = \pt -> scanl (flip displaceV) pt ys--
+ src/Wumpus/Drawing/Connectors.hs view
@@ -0,0 +1,29 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Connectors+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Shim module for Connectors.+-- +-- \*\* WARNING \*\* - this is due to change...+--+--------------------------------------------------------------------------------++module Wumpus.Drawing.Connectors+ ( + module Wumpus.Drawing.Connectors.Arrowheads+ , module Wumpus.Drawing.Connectors.Base+ , module Wumpus.Drawing.Connectors.ConnectorProps++ ) where++import Wumpus.Drawing.Connectors.Arrowheads+import Wumpus.Drawing.Connectors.Base+import Wumpus.Drawing.Connectors.ConnectorProps
+ src/Wumpus/Drawing/Connectors/Arrowheads.hs view
@@ -0,0 +1,453 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Connectors.Arrowheads+-- Copyright : (c) Stephen Tetley 2011-2012+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Arrowheads.+--+-- \*\* WARNING \*\* - naming scheme due to change.+--+--------------------------------------------------------------------------------++module Wumpus.Drawing.Connectors.Arrowheads+ (++ tri90+ , tri60+ , tri45+ , otri90+ , otri60+ , otri45++ , revtri90+ , revtri60+ , revtri45+ , orevtri90+ , orevtri60+ , orevtri45++ , barb90+ , barb60+ , barb45+ , revbarb90+ , revbarb60+ , revbarb45++ , perp+ , bracket++ , diskTip+ , odiskTip++ , squareTip+ , osquareTip++ , diamondTip+ , odiamondTip++ , diamondWideTip+ , odiamondWideTip++ , curveTip+ , revcurveTip++ ) where+++import Wumpus.Drawing.Basis.InclineTrails+import Wumpus.Drawing.Connectors.Base++import Wumpus.Basic.Kernel -- package: wumpus-basic++import Wumpus.Core -- package: wumpus-core++-- import Data.AffineSpace -- package: vector-space+import Data.VectorSpace++import Data.Monoid+++++++-- | Arrow tips are drawn with a solid line even if the connector+-- line is dashed (tips also override round corners)++solid_stroke_tip :: DrawingContextF+solid_stroke_tip = solid_line -- reset_drawing_metrics++++type TrailGen = Radian -> AnaTrail En+++fillTrailTip :: TrailGen -> LocThetaGraphic En+fillTrailTip gen_pt = + localize fill_use_stroke_colour $ promoteLocTheta $ \pt theta ->+ supplyLoc pt $ renderAnaTrail CFILL $ gen_pt theta++closedTrailTip :: TrailGen -> LocThetaGraphic En+closedTrailTip gen_pt = + localize solid_stroke_tip $ promoteLocTheta $ \pt theta ->+ supplyLoc pt $ renderAnaTrail CSTROKE $ gen_pt theta+++openTrailTip :: TrailGen -> LocThetaGraphic En+openTrailTip gen_pt = + localize solid_stroke_tip $ promoteLocTheta $ \pt theta ->+ supplyLoc pt $ renderAnaTrail OSTROKE $ gen_pt theta++++++-- | All three lines are stated.+--+closedTriTrail :: Radian -> TrailGen+closedTriTrail ang = \theta ->+ modifyAna (\v1 -> v1 ^+^ theta_left 1 theta) $+ incline_triangle ang (avec theta 1)++++filledTri :: Radian -> ArrowTip+filledTri ang = + ArrowTip+ { retract_distance = 1+ , tip_half_len = 0.5+ , tip_deco = fillTrailTip $ closedTriTrail ang+ }++--+-- DESIGN NOTE +--+-- Naming scheme should change.+--+-- The \"wumpus way\" of naming seems to be coalescing to favour +-- underscore separated names rather than camelCase for /data/ +-- objects. +--+-- These data objects can still be functions but they are +-- distinguished by drawing differently - roughly speaking+-- the difference between a barb tip and a triangle tip is +-- \"what they draw not what they mean\".+--++-- | Filled triangle - apex is 90 deg.+--+tri90 :: ArrowTip+tri90 = filledTri ang90++-- | Filled triangle - apex is 60 deg.+--+tri60 :: ArrowTip+tri60 = filledTri ang60++-- | Filled triangle - apex is 45 deg.+--+tri45 :: ArrowTip+tri45 = filledTri ang45+++strokedClosedTri :: Radian -> ArrowTip+strokedClosedTri ang = + ArrowTip+ { retract_distance = 1+ , tip_half_len = 0.5+ , tip_deco = closedTrailTip $ closedTriTrail ang+ }+++otri90 :: ArrowTip+otri90 = strokedClosedTri ang90++otri60 :: ArrowTip+otri60 = strokedClosedTri ang60++otri45 :: ArrowTip+otri45 = strokedClosedTri ang45+++-- | All three lines are stated.+--+revClosedTriSpec :: Radian -> TrailGen+revClosedTriSpec ang = \theta -> incline_triangle ang (avec theta (-1))+++filledRevTri :: Radian -> ArrowTip+filledRevTri ang = + ArrowTip+ { retract_distance = 0.5+ , tip_half_len = 0.5+ , tip_deco = fillTrailTip $ revClosedTriSpec ang+ }+++revtri90 :: ArrowTip+revtri90 = filledRevTri ang90++revtri60 :: ArrowTip+revtri60 = filledRevTri ang60++revtri45 :: ArrowTip+revtri45 = filledRevTri ang45+++strokedClosedRevTri :: Radian -> ArrowTip+strokedClosedRevTri ang = + ArrowTip+ { retract_distance = 1+ , tip_half_len = 0.5+ , tip_deco = closedTrailTip $ revClosedTriSpec ang+ }++orevtri90 :: ArrowTip+orevtri90 = strokedClosedRevTri ang90++orevtri60 :: ArrowTip+orevtri60 = strokedClosedRevTri ang60++orevtri45 :: ArrowTip+orevtri45 = strokedClosedRevTri ang45+++barbSpec :: Radian -> TrailGen+barbSpec ang = \theta -> + modifyAna (\v1 -> v1 ^+^ avec theta (-1)) $ incline_barb ang (avec theta 1)+++strokedBarb :: Radian -> ArrowTip+strokedBarb ang = + ArrowTip+ { retract_distance = 0+ , tip_half_len = 0.5+ , tip_deco = openTrailTip $ barbSpec ang+ }++barb90 :: ArrowTip+barb90 = strokedBarb ang90++barb60 :: ArrowTip+barb60 = strokedBarb ang60++barb45 :: ArrowTip+barb45 = strokedBarb ang45+++revBarbSpec :: Radian -> TrailGen+revBarbSpec ang = \theta -> incline_barb ang (avec theta (-1))++strokedRevBarb :: Radian -> ArrowTip+strokedRevBarb ang = + ArrowTip+ { retract_distance = 1+ , tip_half_len = 0.5+ , tip_deco = openTrailTip $ revBarbSpec ang + }+++revbarb90 :: ArrowTip+revbarb90 = strokedRevBarb ang90++revbarb60 :: ArrowTip+revbarb60 = strokedRevBarb ang60++revbarb45 :: ArrowTip+revbarb45 = strokedRevBarb ang45+++perpSpec :: TrailGen+perpSpec ang = + anaCatTrail (theta_up 0.5 ang) $ trail_theta_down 1 ang++perp :: ArrowTip+perp = + ArrowTip+ { retract_distance = 0+ , tip_half_len = 0+ , tip_deco = openTrailTip perpSpec+ }+++bracketSpec :: TrailGen+bracketSpec ang = anaCatTrail (orthoVec (-0.5) 0.5 ang) catt+ where+ catt = mconcat [ trail_theta_right 0.5 ang+ , trail_theta_down 1.0 ang+ , trail_theta_left 0.5 ang + ]+++bracket :: ArrowTip+bracket = + ArrowTip+ { retract_distance = 0.0+ , tip_half_len = 0.5+ , tip_deco = openTrailTip bracketSpec+ }++diskBody :: DrawMode -> Radian -> LocGraphic En+diskBody mode theta = + localize fill_use_stroke_colour $ moveStart vback $ dcDisk mode 0.5+ where+ vback = theta_left 0.5 theta++diskTip :: ArrowTip+diskTip = + ArrowTip+ { retract_distance = 0.5+ , tip_half_len = 0.5+ , tip_deco = promoteLocTheta $ \pt theta -> + diskBody DRAW_FILL theta `at` pt+ }+++odiskTip :: ArrowTip+odiskTip = + ArrowTip+ { retract_distance = 1+ , tip_half_len = 0.5+ , tip_deco = promoteLocTheta $ \pt theta -> + diskBody DRAW_STROKE theta `at` pt+ }+++-- | Note - need to draw square East-West rather than West-East+-- hence the base_width is negative.+--+squareSpec :: TrailGen+squareSpec theta = incline_square $ avec theta (-1)+++squareTip :: ArrowTip+squareTip = + ArrowTip+ { retract_distance = 1+ , tip_half_len = 0.5+ , tip_deco = fillTrailTip squareSpec+ }+ ++osquareTip :: ArrowTip+osquareTip = + ArrowTip+ { retract_distance = 1+ , tip_half_len = 0.5+ , tip_deco = closedTrailTip squareSpec+ }+++-- | squareSpec:+--+-- > +-- > a+-- > / \+-- > ...w..v..o+-- > \ / +-- > b+--+diamondSpec :: En -> TrailGen+diamondSpec w theta = incline_diamond 1 $ avec theta (-w)++diamondTip :: ArrowTip+diamondTip = + ArrowTip+ { retract_distance = 0.5+ , tip_half_len = 0.5+ , tip_deco = fillTrailTip $ diamondSpec 1+ }++odiamondTip :: ArrowTip+odiamondTip = + ArrowTip+ { retract_distance = 1+ , tip_half_len = 0.5+ , tip_deco = closedTrailTip $ diamondSpec 1+ }+++diamondWideTip :: ArrowTip+diamondWideTip = + ArrowTip+ { retract_distance = 1.0+ , tip_half_len = 1.0+ , tip_deco = fillTrailTip $ diamondSpec 2+ }++odiamondWideTip :: ArrowTip+odiamondWideTip = + ArrowTip+ { retract_distance = 2.0+ , tip_half_len = 1.0+ , tip_deco = closedTrailTip $ diamondSpec 2+ }+++curveTipSpec :: TrailGen+curveTipSpec theta = + anaCatTrail dv $ vectrapCCW v1 <> vectrapCCW v2+ where+ dv = orthoVec (-1.0) 0.75 theta -- back and up+ v1 = orthoVec 1.0 (-0.75) theta -- fwd and down+ v2 = orthoVec (-1.0) (-0.75) theta -- back and down+++vectrapCW :: (Real u, Floating u) => Vec2 u -> CatTrail u+vectrapCW v1 = trapCurve CW w h quarter_pi ang+ where+ w = vlength v1+ h = w / 4+ ang = vdirection v1+++vectrapCCW :: (Real u, Floating u) => Vec2 u -> CatTrail u+vectrapCCW v1 = trapCurve CCW w h quarter_pi ang+ where+ w = vlength v1+ h = w / 4+ ang = vdirection v1+++curveTip :: ArrowTip+curveTip = + ArrowTip+ { retract_distance = 0+ , tip_half_len = 0.5+ , tip_deco = body+ }+ where+ body = localize (cap_round . join_round . solid_stroke_tip) $ + openTrailTip curveTipSpec++++curveTipRevSpec :: TrailGen+curveTipRevSpec theta = + anaCatTrail dv $ vectrapCW v1 <> vectrapCW v2+ where+ dv = orthoVec 0.0 0.75 theta -- just up+ v1 = orthoVec (-1.0) (-0.75) theta -- back and down+ v2 = orthoVec 1.0 (-0.75) theta -- fwd and down++++revcurveTip :: ArrowTip+revcurveTip = + ArrowTip+ { retract_distance = 1+ , tip_half_len = 0.5+ , tip_deco = body+ }+ where+ body = localize (cap_round . join_round . solid_stroke_tip) $ + openTrailTip curveTipRevSpec++
+ src/Wumpus/Drawing/Connectors/Base.hs view
@@ -0,0 +1,168 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Connectors.Base+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Connectors...+--+--------------------------------------------------------------------------------++module Wumpus.Drawing.Connectors.Base+ ( +++ ConnectorPathQuery+ , SpacingProjection++ , ArrowTip(..)+ , ArrowConnector++ , ConnectorConfig(..)+ , ConnectorPathSpec(..)+ , renderConnectorConfig++ , arrowDecoratePath++ , leftArrowConnector+ , rightArrowConnector+ , uniformArrowConnector++ ) where++import Wumpus.Drawing.Connectors.ConnectorProps+import Wumpus.Drawing.Paths++import Wumpus.Basic.Kernel -- package: wumpus-basic++import Wumpus.Core -- package: wumpus-core++import Data.Monoid+++++type SpacingProjection u = + ConnectorProps -> Point2 u -> Point2 u -> Query u (Point2 u)++++-- | The type of Connectors - a query from start and end point +-- returning an AbsPath.+--+type ConnectorPathQuery u = ConnectorQuery u (AbsPath u)++++-- | Arrowhead /algorithm/ - the components of an arrowhead.+-- +-- Retract distance is rather vague - depending on the arrowhead+-- it may represent a flush join between the path and the tip+-- or a join that uses the z-order (tip over path) to create the +-- join.+--+-- \*\* WARNING \*\* - pending revision...+--+data ArrowTip = ArrowTip+ { retract_distance :: En+ , tip_half_len :: En+ , tip_deco :: LocThetaGraphic En+ }+++newtype ConnectorPathSpec u = ConnectorPathSpec { + getConnectorPathSpec :: ConnectorProps -> ConnectorPathQuery u }++-- | total_path is the path before accounting for arrow +-- retract distances.+--+data ConnectorConfig u = ConnectorConfig+ { conn_arrowl :: Maybe ArrowTip+ , conn_arrowr :: Maybe ArrowTip+ , conn_path_spec :: ConnectorPathSpec u + }++++-- Ideally there should be a plus operation to combine tips +-- allowing double tips.+-- ++type ArrowConnector u = ConnectorImage u (AbsPath u)+++++-- | NOTE - the prefix /render/ needs (re-) consideration...+-- +-- If it is a good prefix other functions e.g. drawPath should +-- use render rather than draw.+--+renderConnectorConfig :: (Real u, Floating u, InterpretUnit u)+ => ConnectorProps+ -> ConnectorConfig u+ -> ConnectorImage u (AbsPath u)+renderConnectorConfig props (ConnectorConfig mbl mbr pspec) = + promoteConn $ \src dst -> + liftQuery (qapplyConn path_spec src dst) >>= \tot_path -> + connectorSrcSpace props >>= \sepl -> + connectorDstSpace props >>= \sepr ->+ arrowDecoratePath mbl mbr $ shortenL sepl $ shortenR sepr tot_path+ where+ path_spec = getConnectorPathSpec pspec props++++arrowDecoratePath :: (Real u, Floating u, InterpretUnit u) + => Maybe ArrowTip -> Maybe ArrowTip -> (AbsPath u) + -> Image u (AbsPath u)+arrowDecoratePath mbl mbr initial_path = + uconvertCtx1 (maybe 0 retract_distance mbl) >>= \retl -> + uconvertCtx1 (maybe 0 retract_distance mbr) >>= \retr -> + let (p1,theta1) = atstart initial_path+ (p2,theta2) = atend initial_path+ new_path = shortenL retl $ shortenR retr initial_path+ arrl = mbTip p1 (pi + theta1) mbl+ arrr = mbTip p2 theta2 mbr+ in replaceAns initial_path $ + decorate ZABOVE (renderPath OSTROKE new_path) (arrl `mappend` arrr)+ where+ mbTip pt ang = maybe emptyImage (supplyLocTheta pt ang . uconvF . tip_deco)+++-- | Shorthand...+--+leftArrowConnector :: (Real u, Floating u, InterpretUnit u)+ => ConnectorProps -> ConnectorPathSpec u -> ArrowTip+ -> ConnectorImage u (AbsPath u)+leftArrowConnector props cpath tip = renderConnectorConfig props cfg + where+ cfg = ConnectorConfig { conn_arrowl = Just tip + , conn_arrowr = Nothing+ , conn_path_spec = cpath }++rightArrowConnector :: (Real u, Floating u, InterpretUnit u)+ => ConnectorProps -> ConnectorPathSpec u -> ArrowTip+ -> ConnectorImage u (AbsPath u)+rightArrowConnector props cpath tip = renderConnectorConfig props cfg + where+ cfg = ConnectorConfig { conn_arrowl = Nothing+ , conn_arrowr = Just tip+ , conn_path_spec = cpath }+ +uniformArrowConnector :: (Real u, Floating u, InterpretUnit u)+ => ConnectorProps -> ConnectorPathSpec u -> ArrowTip+ -> ConnectorImage u (AbsPath u)+uniformArrowConnector props cpath tip = renderConnectorConfig props cfg + where+ cfg = ConnectorConfig { conn_arrowl = Just tip+ , conn_arrowr = Just tip+ , conn_path_spec = cpath }+ +
+ src/Wumpus/Drawing/Connectors/BoxConnectors.hs view
@@ -0,0 +1,116 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Connectors.BoxConnectors+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Box connectors+--+--------------------------------------------------------------------------------++module Wumpus.Drawing.Connectors.BoxConnectors+ ( + ConnectorBox+ , ConnectorBoxSpec(..)+ , renderConnectorBoxSpec++ , conn_box+ , conn_tube+ , conn_chamf_box +++ ) where++import Wumpus.Drawing.Basis.InclineTrails+import Wumpus.Drawing.Connectors.ConnectorProps++import Wumpus.Basic.Kernel -- package: wumpus-basic++import Wumpus.Core -- package: wumpus-core++import Data.VectorSpace -- package: vector-space+++-- NOTE - boxes seem to only support stroke otherwise+-- they would obliterate what they connect.++++-- | The type of BoxConnectors - a query from start and end point +-- to a Path. +-- +-- Note - unlike a @Connector@, a BoxConnnector is expected to be +-- closed, then filled, stroked or bordered.+--+type ConnectorBox u = ConnectorGraphic u++newtype ConnectorBoxSpec u = ConnectorBoxSpec { + getConnectorBoxSpec :: ConnectorProps -> ConnectorBox u }+++renderConnectorBoxSpec :: (Real u, Floating u, InterpretUnit u)+ => ConnectorProps+ -> ConnectorBoxSpec u+ -> ConnectorBox u+renderConnectorBoxSpec props spec = + getConnectorBoxSpec spec props++++++--+-- DESIGN NOTE - boxes (probably) should not use source and dest+-- separators.+--++boxConnector :: (Floating u, Ord u, InterpretUnit u) + => (ConnectorProps -> Point2 u -> Point2 u -> Image u a) + -> ConnectorBoxSpec u+boxConnector mf = ConnectorBoxSpec $ \props -> + promoteConn $ \p0 p1 -> ignoreAns $ mf props p0 p1 +++adaptAnaTrail :: (Real u, Floating u, Ord u, InterpretUnit u) + => (u -> Vec2 u -> AnaTrail u) + -> ConnectorBoxSpec u+adaptAnaTrail fn = boxConnector $ \props p0 p1 ->+ connectorBoxHalfSize props >>= \sz ->+ let v0 = pvec p0 p1 + v1 = v0 ^+^ avec (vdirection v0) (2*sz)+ vinit = avec (vdirection v0) (-sz)+ in applyLoc (renderAnaTrail CSTROKE $ fn sz v1) (displace vinit p0)+ +++-- | Draw a stroked, rectangular box around the connector points.+--+-- The rectangle will be inclined to the line.+--+conn_box :: (Real u, Floating u, InterpretUnit u) + => ConnectorBoxSpec u+conn_box = adaptAnaTrail (\sz -> incline_rect (2 * sz))+++-- | Draw a stroked, tube around the connector points.+--+-- The tube will be inclined to the line.+--+conn_tube :: (Real u, Floating u, InterpretUnit u) + => ConnectorBoxSpec u+conn_tube = adaptAnaTrail (\sz -> incline_tube (2 * sz))++-- | Draw a stroked, chamfered box around the connector points.+--+-- The tube will be inclined to the line.+--+conn_chamf_box :: (Real u, Floating u, InterpretUnit u) + => ConnectorBoxSpec u+conn_chamf_box = adaptAnaTrail (\sz -> incline_chamf_rect (2 * sz))+
+ src/Wumpus/Drawing/Connectors/ConnectorPaths.hs view
@@ -0,0 +1,563 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Connectors.ConnectorPaths+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Primitive connectors+--+--------------------------------------------------------------------------------++module Wumpus.Drawing.Connectors.ConnectorPaths+ ( ++ conn_line++ , conna_arc+ , connb_arc++ , conn_hdiagh+ , conn_vdiagv+ + , conn_diagh+ , conn_diagv++ , conn_hdiag+ , conn_vdiag++ , conna_bar+ , connb_bar+ + , conna_flam+ , connb_flam++ , conna_orthohbar+ , connb_orthohbar++ , conna_orthovbar+ , connb_orthovbar++ , conna_right+ , connb_right++ , conn_hrr+ , conn_rrh+ , conn_vrr+ , conn_rrv++ , conna_loop+ , connb_loop++ , conn_hbezier+ , conn_vbezier++ ) where++import Wumpus.Drawing.Basis.InclineTrails+import Wumpus.Drawing.Connectors.Base+import Wumpus.Drawing.Connectors.ConnectorProps+import Wumpus.Drawing.Paths++import Wumpus.Basic.Kernel -- package: wumpus-basic++import Wumpus.Core -- package: wumpus-core++import Data.AffineSpace -- package: vector-space++import Control.Applicative+++++-- | Build as Path as a CatTrail between two points.+--+catConnector :: (Floating u, Ord u, InterpretUnit u, Tolerance u) + => (ConnectorProps -> Point2 u -> Point2 u -> Query u (CatTrail u)) + -> ConnectorPathSpec u+catConnector mf = ConnectorPathSpec $ \props -> + qpromoteConn $ \p0 p1 -> catTrailPath p0 <$> mf props p0 p1 +++horizontally :: (Real u, Floating u) + => (Vec2 u -> a) -> (Vec2 u -> a) -> Vec2 u -> a+horizontally rightf leftf v1 = + case horizontalDirection $ vdirection v1 of+ RIGHTWARDS -> rightf v1+ LEFTWARDS -> leftf v1++-- | Straight line connector.+--+conn_line :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => ConnectorPathSpec u+conn_line = catConnector $ \_ p0 p1 -> pure $ catline $ pvec p0 p1+++++-- | Form an arc connector.+-- +-- If the conn_arc_angle in the Drawing context is positive the arc+-- will be formed /above/ the straight line joining the points. +-- If the angle is negative it will be drawn below. +-- +-- The notion of /above/ is respective to the line direction, of +-- course.+-- +-- TODO - above and below versions...+--+conna_arc :: (Real u, Floating u, Ord u, InterpretUnit u, Tolerance u) + => ConnectorPathSpec u+conna_arc = connArcBody (vtriCurve CW) (vtriCurve CCW)++-- | Below version of 'conna_arc'.+--+connb_arc :: (Real u, Floating u, Ord u, InterpretUnit u, Tolerance u) + => ConnectorPathSpec u+connb_arc = connArcBody (vtriCurve CCW) (vtriCurve CW)+++connArcBody :: (Real u, Floating u, Ord u, InterpretUnit u, Tolerance u) + => (u -> Vec2 u -> CatTrail u) + -> (u -> Vec2 u -> CatTrail u) + -> ConnectorPathSpec u+connArcBody rightf leftf = catConnector $ \props p0 p1 -> + let arc_ang = conn_arc_ang props + v1 = pvec p0 p1+ h = (0.5 * vlength v1) * (fromRadian $ tan arc_ang)+ in return $ horizontally (rightf h) (leftf h) v1 +++++-- | Horizontal-diagonal-horizontal connector.+--+-- > --@+-- > /+-- > o--+-- +-- Horizontal /arms/ are drawn from the start and end points, a+-- diagonal segment joins the arms. +-- +conn_hdiagh :: (Real u, Floating u, Tolerance u, InterpretUnit u)+ => ConnectorPathSpec u+conn_hdiagh = catConnector $ \props p0 p1 -> + connectorLegs props >>= \(src_leg, dst_leg) ->+ return $ trail_hdiagh src_leg dst_leg $ pvec p0 p1+++-- probably want trail_hdiagh as a library function++-- | Vertical-diagonal-vertical connector.+--+-- > @+-- > |+-- > \+-- > |+-- > o+--+-- Vertical /arms/ are drawn from the start and end points, a+-- diagonal segment joins the arms. +-- +conn_vdiagv :: (Real u, Floating u, Tolerance u, InterpretUnit u)+ => ConnectorPathSpec u+conn_vdiagv = catConnector $ \props p0 p1 -> + connectorLegs props >>= \(src_leg, dst_leg) ->+ return $ trail_vdiagv src_leg dst_leg $ pvec p0 p1+++-- | Diagonal-horizontal connector.+--+-- > --@+-- > /+-- > o+-- +-- Restricted variant of 'hconndiag' - a diagonal segment is drawn +-- from the start point joining a horizontal arm drawn from the +-- end point+-- +conn_diagh :: (Real u, Floating u, Tolerance u, InterpretUnit u)+ => ConnectorPathSpec u+conn_diagh = catConnector $ \props p0 p1 -> + connectorLegs props >>= \(_, dst_leg) ->+ return $ trail_diagh dst_leg $ pvec p0 p1+++-- | Diagonal-vertical connector.+--+-- > @+-- > |+-- > /+-- > o+--+-- Restricted variant of 'vconndiag' - a diagonal segment is drawn +-- from the start point joining a vertical arm drawn from the end +-- point.+-- +conn_diagv :: (Real u, Floating u, Tolerance u, InterpretUnit u)+ => ConnectorPathSpec u+conn_diagv = catConnector $ \props p0 p1 -> + connectorLegs props >>= \(_, dst_leg) ->+ return $ trail_diagv dst_leg $ pvec p0 p1++++-- | Horizontal-diagonal connector.+--+-- > @+-- > /+-- > o--+--+-- Restricted variant of 'hconndiag' - a horizontal arm is drawn+-- from the start point joining a diagonal segment drawn from the +-- end point.+-- +conn_hdiag :: (Real u, Floating u, Tolerance u, InterpretUnit u)+ => ConnectorPathSpec u+conn_hdiag = catConnector $ \props p0 p1 -> + connectorLegs props >>= \(src_leg, _) ->+ return $ trail_hdiag src_leg $ pvec p0 p1+++-- | Vertical-diagonal connector.+--+-- > @+-- > /+-- > |+-- > o+--+-- Restricted variant of 'vconndiag' - a horizontal arm is drawn+-- from the start point joining a vertical segment drawn from the +-- end point.+-- +conn_vdiag :: (Real u, Floating u, Tolerance u, InterpretUnit u)+ => ConnectorPathSpec u+conn_vdiag = catConnector $ \props p0 p1 -> + connectorLegs props >>= \(src_leg, _) ->+ return $ trail_vdiag src_leg $ pvec p0 p1++++-- DESIGN NOTE - should the concept of /above/ and /below/ use +-- ClockDirection instead?+--+++-- | Bar connector.+--+-- > ,----, +-- > | |+-- > o @ +--+-- The bar is drawn /above/ the points.+--+conna_bar :: (Real u, Floating u, Tolerance u, InterpretUnit u) + => ConnectorPathSpec u+conna_bar = connBarBody (trail_perp_bar2 CW) (trail_perp_bar2 CCW)+++-- | Bar connector.+-- +-- > o @ +-- > | |+-- > '----' +--+-- The bar is drawn /below/ the points.+--+connb_bar :: (Real u, Floating u, Tolerance u, InterpretUnit u) + => ConnectorPathSpec u+connb_bar = connBarBody (trail_perp_bar2 CCW) (trail_perp_bar2 CW)+++connBarBody :: (Real u, Floating u, Tolerance u, InterpretUnit u) + => (u -> u -> Vec2 u -> CatTrail u) + -> (u -> u -> Vec2 u -> CatTrail u) + -> ConnectorPathSpec u+connBarBody rightf leftf = catConnector $ \props p0 p1 ->+ connectorLegs props >>= \(src, dst) ->+ return $ horizontally (rightf src dst) (leftf src dst) $ pvec p0 p1++++-- | /Flam/ connector.+--+-- > ,- '+-- > ,- | +-- > | |+-- > o @ +--+-- The bar is drawn /above/ the points.+--+conna_flam :: (Real u, Floating u, Tolerance u, InterpretUnit u) + => ConnectorPathSpec u+conna_flam = connFlamBody (trail_vflam CW) (trail_vflam CCW)++-- | /Flam/ connector - bleow.+--+connb_flam :: (Real u, Floating u, Tolerance u, InterpretUnit u) + => ConnectorPathSpec u+connb_flam = connFlamBody (trail_vflam CCW) (trail_vflam CW) +++connFlamBody :: (Real u, Floating u, Tolerance u, InterpretUnit u) + => (u -> u -> Vec2 u -> CatTrail u) + -> (u -> u -> Vec2 u -> CatTrail u) + -> ConnectorPathSpec u+connFlamBody rightf leftf = catConnector $ \props p0 p1 ->+ connectorLegs props >>= \(src,dst) ->+ return $ horizontally (rightf src dst) (leftf src dst) $ pvec p0 p1+++++-- | Bar connector - always orthonormal .+--+-- > +-- > ,----, +-- > | |+-- > o @ +--+-- The bar is drawn /above/ the points.+--+conna_orthohbar :: (Real u, Floating u, Tolerance u, InterpretUnit u) + => ConnectorPathSpec u+conna_orthohbar = connOrthobarBody (trail_ortho_hbar CW) (trail_ortho_hbar CCW)++-- | Bar connector orthonormal - below.+--+connb_orthohbar :: (Real u, Floating u, Tolerance u, InterpretUnit u) + => ConnectorPathSpec u+connb_orthohbar = connOrthobarBody (trail_ortho_hbar CCW) (trail_ortho_hbar CW)++++connOrthobarBody :: (Real u, Floating u, Tolerance u, InterpretUnit u) + => (u -> Vec2 u -> CatTrail u)+ -> (u -> Vec2 u -> CatTrail u)+ -> ConnectorPathSpec u+connOrthobarBody rightf leftf = catConnector $ \props p0 p1 ->+ connectorLoopSize props >>= \looph ->+ return $ horizontally (rightf looph) (leftf looph) $ pvec p0 p1+++-- | Bar connector - always orthonormal.+--+-- > +-- > ,--- o +-- > | +-- > '--- @ +-- > +--+-- The bar is drawn /left/ of the points.+--+conna_orthovbar :: (Real u, Floating u, Tolerance u, InterpretUnit u) + => ConnectorPathSpec u+conna_orthovbar = connOrthobarBody (trail_ortho_vbar CW) (trail_ortho_vbar CCW)++-- | Bar connector orthonormal - right of the points.+--+connb_orthovbar :: (Real u, Floating u, Tolerance u, InterpretUnit u) + => ConnectorPathSpec u+connb_orthovbar = connOrthobarBody (trail_ortho_vbar CCW) (trail_ortho_vbar CW)++++-- | Right angle connector.+-- +-- > ,----@ +-- > | +-- > o +--+-- The bar is drawn /above/ the points.+--+conna_right :: (Real u, Floating u, Tolerance u, InterpretUnit u) + => ConnectorPathSpec u+conna_right = catConnector $ \_ p0 p1 ->+ return $ trail_vright $ pvec p0 p1+++-- | Right angle connector.+-- +-- > @ +-- > |+-- > o----' +--+-- The bar is drawn /below/ the points.+--+connb_right :: (Real u, Floating u, Tolerance u, InterpretUnit u) + => ConnectorPathSpec u+connb_right = catConnector $ \_ p0 p1 -> + return $ trail_hright $ pvec p0 p1+++++-- | Connector with two horizontal segments and a joining +-- vertical segment.+--+-- > ,--@+-- > |+-- > o----' +--+-- The length of the first horizontal segment is the source arm +-- length. The length of the final segment is the remaining +-- horizontal distance. +--+conn_hrr :: (Real u, Floating u, Tolerance u, InterpretUnit u) + => ConnectorPathSpec u+conn_hrr = catConnector $ \props p0 p1 ->+ connectorLegs props >>= \(src_leg,_) ->+ return $ trail_hrr src_leg $ pvec p0 p1+++-- | Connector with two horizontal segements and a joining +-- vertical segment.+--+-- > ,----@+-- > |+-- > o--' +--+-- The length of the final horizontal segment is the destination +-- arm length. The length of the initial segment is the remaining+-- horizontal distance. +--+conn_rrh :: (Real u, Floating u, Tolerance u, InterpretUnit u) + => ConnectorPathSpec u+conn_rrh = catConnector $ \props p0 p1 ->+ connectorLegs props >>= \(_,dst_leg) ->+ return $ trail_rrh dst_leg $ pvec p0 p1+++-- | Connector with two right angles...+--+-- > @+-- > |+-- > ,----'+-- > |+-- > o +--+-- The length of the first vertical segment is the source arm +-- length. The length of the final segment is the remaining +-- vertical distance. +--+conn_vrr :: (Real u, Floating u, Tolerance u, InterpretUnit u) + => ConnectorPathSpec u+conn_vrr = catConnector $ \props p0 p1 ->+ connectorLegs props >>= \(src_leg,_) ->+ return $ trail_vrr src_leg $ pvec p0 p1+++-- | Connector with two right angles...+--+-- > @+-- > |+-- > ,----'+-- > |+-- > o +--+-- The length of the final vertical segment is the destination +-- arm length. The length of the initial segment is the remaining+-- vertical distance. +--+conn_rrv :: (Real u, Floating u, Tolerance u, InterpretUnit u) + => ConnectorPathSpec u+conn_rrv = catConnector $ \props p0 p1 ->+ connectorLegs props >>= \(_,dst_leg) ->+ return $ trail_rrv dst_leg $ pvec p0 p1++++-- | Loop connector.+--+-- > ,---------, +-- > | |+-- > '-o @--'+--+-- The loop is drawn /above/ the points.+--+conna_loop :: (Real u, Floating u, Tolerance u, InterpretUnit u) + => ConnectorPathSpec u+conna_loop = connLoopBody (trail_rect_loop CW) (trail_rect_loop CCW)+ ++-- | Loop connector.+--+-- > ,-o @--, +-- > | |+-- > '---------'+--+-- The loop is drawn /below/ the points.+--+connb_loop :: (Real u, Floating u, Tolerance u, InterpretUnit u) + => ConnectorPathSpec u+connb_loop = connLoopBody (trail_rect_loop CCW) (trail_rect_loop CW)+++connLoopBody :: (Real u, Floating u, Tolerance u, InterpretUnit u) + => (u -> u -> u -> Vec2 u -> CatTrail u) + -> (u -> u -> u -> Vec2 u -> CatTrail u) + -> ConnectorPathSpec u+connLoopBody rightf leftf = catConnector $ \props p0 p1 -> + connectorLegs props >>= \(src,dst) ->+ connectorLoopSize props >>= \looph ->+ return $ horizontally (rightf src dst looph) (leftf src dst looph) + $ pvec p0 p1++++-- | Bezier curve connector - the control points are positioned +-- horizontally respective to the source and dest.+--+-- > *--@ +-- > . +-- > . +-- > o--* +--+-- Note - the source and dest arm lengths are doubled, generally +-- this produces nicer curves.+-- +conn_hbezier :: (Real u, Floating u, InterpretUnit u, Tolerance u)+ => ConnectorPathSpec u+conn_hbezier = ConnectorPathSpec $ \props -> + qpromoteConn $ \p0 p1 -> + fmap (\(a,b) -> (2*a,2*b)) (connectorArms props) >>= \(src_arm,dst_arm) ->+ case horizontalDirection $ vdirection $ pvec p0 p1 of+ RIGHTWARDS -> right p0 p1 src_arm dst_arm+ _ -> left p0 p1 src_arm dst_arm+ where+ right p0 p1 h0 h1 = return $ curve1 p0 (p0 .+^ hvec h0) (p1 .-^ hvec h1) p1++ left p0 p1 h0 h1 = return $ curve1 p0 (p0 .-^ hvec h0) (p1 .+^ hvec h1) p1+++-- | Bezier curve connector - the control points are positioned +-- vertically respective to the source and dest.+--+-- > @ +-- > .| +-- > * . *+-- > |.+-- > o+--+-- Note - the source and dest arm lengths are doubled, generally +-- this produces nicer curves.+--+conn_vbezier :: (Real u, Floating u, InterpretUnit u, Tolerance u)+ => ConnectorPathSpec u+conn_vbezier = ConnectorPathSpec $ \props -> + qpromoteConn $ \p0 p1 -> + fmap (\(a,b) -> (2*a,2*b)) (connectorArms props) >>= \(src_arm,dst_arm) ->+ case verticalDirection $ vdirection $ pvec p0 p1 of+ UPWARDS -> up p0 p1 src_arm dst_arm+ _ -> down p0 p1 src_arm dst_arm+ where+ up p0 p1 v0 v1 = return $ curve1 p0 (p0 .+^ vvec v0) (p1 .-^ vvec v1) p1+ down p0 p1 v0 v1 = return $ curve1 p0 (p0 .-^ vvec v0) (p1 .+^ vvec v1) p1+++
+ src/Wumpus/Drawing/Connectors/ConnectorProps.hs view
@@ -0,0 +1,158 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Connectors.ConnectorProps+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Connectors...+--+--------------------------------------------------------------------------------++module Wumpus.Drawing.Connectors.ConnectorProps+ ( ++ -- * Data types+ ConnectorProps(..)+ , default_connector_props++ -- * Queries+ , connectorSrcSpace+ , connectorDstSpace+ , connectorArms+ , connectorLegs+ , connectorLoopSize+ , connectorBoxHalfSize ++ ) where+++import Wumpus.Basic.Kernel -- package: wumpus-basic++import Wumpus.Core -- package: wumpus-core+++import Control.Applicative++++-- | ConnectorProps control the drawing of connectors in +-- Wumpus-Drawing.+--+-- > conn_src_space :: Em+-- > conn_dst_space :: Em+--+-- Source and destination offsets - these offset the drawing of+-- the connector perpendicular to the direction of line formed +-- between the connector points (a positive offset is drawn above, +-- a negative offset below). The main use of offsets is to draw+-- parallel line connectors.+--+-- > conn_arc_ang :: Radian +--+-- Control the /bend/ of an arc connector.+-- +-- > conn_src_arm :: Em+-- > conn_dst_arm :: Em +--+-- Control the /arm/ length of a jointed connector - arms are the +-- initial segments of the connector. +--+-- > conn_loop_size :: Em+--+-- Control the /height/ of a loop connector. +--+-- > conn_box_halfsize :: Em+-- +-- Control the size of a connector box. Connector boxes are +-- drawn with the exterior lines projected out from the connector+-- points a halfsize above and below.+-- +data ConnectorProps = ConnectorProps+ { conn_src_space :: !Em+ , conn_dst_space :: !Em+ , conn_arc_ang :: !Radian+ , conn_src_arm :: !Em+ , conn_dst_arm :: !Em+ , conn_loop_size :: !Em+ , conn_box_halfsize :: !Em+ }+ deriving (Eq,Show)+++-- | Default connector properties.+--+-- > conn_src_sep: 0+-- > conn_dst_sep: 0+-- > conn_arc_ang: pi / 12+-- > conn_src_arm: 1+-- > conn_dst_arm: 1+-- > conn_loop_size: 2 +-- > conn_box_half_size: 2+--+-- Arc angle is 15deg - quite shallow.+--+default_connector_props :: ConnectorProps+default_connector_props = + ConnectorProps { conn_src_space = 0+ , conn_dst_space = 0+ , conn_arc_ang = pi / 12+ , conn_src_arm = 1+ , conn_dst_arm = 1+ , conn_loop_size = 2 + , conn_box_halfsize = 1+ }+++++--------------------------------------------------------------------------------+-- Queries+++connectorSrcSpace :: (DrawingCtxM m, InterpretUnit u) + => ConnectorProps -> m u+connectorSrcSpace props = + (\sz -> uconvert1 sz $ conn_src_space props) <$> pointSize+++connectorDstSpace :: (DrawingCtxM m, InterpretUnit u) + => ConnectorProps -> m u+connectorDstSpace props = + (\sz -> uconvert1 sz $ conn_dst_space props) <$> pointSize+++connectorArms :: (DrawingCtxM m, InterpretUnit u) + => ConnectorProps -> m (u,u)+connectorArms props = + (\sz -> ( uconvert1 sz $ conn_src_arm props+ , uconvert1 sz $ conn_dst_arm props) )+ <$> pointSize++-- | /legs/ are Arms + spacing.+--+connectorLegs :: (DrawingCtxM m, InterpretUnit u) + => ConnectorProps -> m (u,u)+connectorLegs props = + (\sz -> ( uconvert1 sz $ conn_src_space props + conn_src_arm props+ , uconvert1 sz $ conn_dst_space props + conn_dst_arm props ) )+ <$> pointSize++++connectorLoopSize :: (DrawingCtxM m, InterpretUnit u) + => ConnectorProps -> m u+connectorLoopSize props = + (\sz -> uconvert1 sz $ conn_loop_size props) <$> pointSize+++connectorBoxHalfSize :: (DrawingCtxM m, InterpretUnit u) + => ConnectorProps -> m u+connectorBoxHalfSize props = + (\sz -> uconvert1 sz $ conn_box_halfsize props) <$> pointSize+
src/Wumpus/Drawing/Dots/AnchorDots.hs view
@@ -1,6 +1,4 @@ {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE ExistentialQuantification #-}-{-# LANGUAGE FlexibleContexts #-} {-# OPTIONS -Wall #-} --------------------------------------------------------------------------------@@ -31,10 +29,18 @@ , DDotLocImage -- * Dots with anchor points+ , smallDisk+ , largeDisk++ , smallCirc+ , largeCirc+++ , dotNone , dotChar , dotText- , dotHLine- , dotVLine+ , dotHBar+ , dotVBar , dotX , dotPlus , dotCross@@ -57,12 +63,11 @@ ) where -import Wumpus.Drawing.Geometry.Intersection-import Wumpus.Drawing.Geometry.Paths-import Wumpus.Drawing.Dots.Marks-import Wumpus.Drawing.Text.LRText+import Wumpus.Drawing.Dots.SimpleDots ( MarkSize )+import qualified Wumpus.Drawing.Dots.SimpleDots as SD+import Wumpus.Drawing.Paths.Intersection -import Wumpus.Basic.Kernel -- package: wumpus-basic+import Wumpus.Basic.Kernel -- package: wumpus-basic import Wumpus.Core -- package: wumpus-core @@ -71,23 +76,26 @@ import Control.Applicative --- An existential thing that supports anchors.--- This means any dot can retun the same (opaque) structure------ But it does mean that which anchor class are supported is --- fixed - the datatype needs a field for each one.--- Supporting north, southeast etc. will also be tedious...----data DotAnchor u = forall s. - DotAnchor { center_anchor :: Point2 u- , radial_anchor :: Radian -> Point2 u- , cardinal_anchor :: Cardinal -> Point2 u } -data Cardinal = NN | NE | EE | SE | SS | SW | WW | NW- deriving (Eq,Show) +-- | All dots return the same thing a 'DotAnchor' which supports +-- the same (limited) see of anchors.+--+data DotAnchor u = DotAnchor + { center_anchor :: Point2 u+ , radial_anchor :: Radian -> Point2 u+ , cardinal_anchor :: Cardinal -> Point2 u + } type instance DUnit (DotAnchor u) = u +instance Num u => Translate (DotAnchor u) where+ translate x y (DotAnchor ctr radialF cardinalF) = + DotAnchor { center_anchor = translate x y ctr+ , radial_anchor = translate x y . radialF + , cardinal_anchor = translate x y . cardinalF + }++ instance CenterAnchor (DotAnchor u) where center (DotAnchor ca _ _) = ca @@ -95,117 +103,153 @@ radialAnchor theta (DotAnchor _ ra _) = ra theta instance CardinalAnchor (DotAnchor u) where- north (DotAnchor _ _ c1) = c1 NN- south (DotAnchor _ _ c1) = c1 SS- east (DotAnchor _ _ c1) = c1 EE- west (DotAnchor _ _ c1) = c1 WW+ north (DotAnchor _ _ c1) = c1 NORTH+ south (DotAnchor _ _ c1) = c1 SOUTH+ east (DotAnchor _ _ c1) = c1 EAST+ west (DotAnchor _ _ c1) = c1 WEST instance CardinalAnchor2 (DotAnchor u) where- northeast (DotAnchor _ _ c1) = c1 NE- southeast (DotAnchor _ _ c1) = c1 SE- southwest (DotAnchor _ _ c1) = c1 SW- northwest (DotAnchor _ _ c1) = c1 NW+ northeast (DotAnchor _ _ c1) = c1 NORTH_EAST+ southeast (DotAnchor _ _ c1) = c1 SOUTH_EAST+ southwest (DotAnchor _ _ c1) = c1 SOUTH_WEST+ northwest (DotAnchor _ _ c1) = c1 NORTH_WEST radialCardinal :: Floating u => u -> Point2 u -> Cardinal -> Point2 u-radialCardinal rad ctr NN = ctr .+^ (avec (pi/2) rad) -radialCardinal rad ctr NE = ctr .+^ (avec (pi/4) rad) -radialCardinal rad ctr EE = ctr .+^ (avec 0 rad) -radialCardinal rad ctr SE = ctr .+^ (avec (7/4 * pi) rad) -radialCardinal rad ctr SS = ctr .+^ (avec (6/4 * pi) rad) -radialCardinal rad ctr SW = ctr .+^ (avec (5/4 * pi) rad) -radialCardinal rad ctr WW = ctr .+^ (avec pi rad) -radialCardinal rad ctr NW = ctr .+^ (avec (3/4 * pi) rad) +radialCardinal rad ctr NORTH = ctr .+^ (avec (pi/2) rad) +radialCardinal rad ctr NORTH_EAST = ctr .+^ (avec (pi/4) rad) +radialCardinal rad ctr EAST = ctr .+^ (avec 0 rad) +radialCardinal rad ctr SOUTH_EAST = ctr .+^ (avec (7/4 * pi) rad) +radialCardinal rad ctr SOUTH = ctr .+^ (avec (6/4 * pi) rad) +radialCardinal rad ctr SOUTH_WEST = ctr .+^ (avec (5/4 * pi) rad) +radialCardinal rad ctr WEST = ctr .+^ (avec pi rad) +radialCardinal rad ctr NORTH_WEST = ctr .+^ (avec (3/4 * pi) rad) -- Rectangle cardinal points are at \"middles and corners\". -- rectCardinal :: Floating u => u -> u -> Point2 u -> Cardinal -> Point2 u-rectCardinal _ hh ctr NN = ctr .+^ (vvec hh) -rectCardinal hw hh ctr NE = ctr .+^ (vec hw hh) -rectCardinal hw _ ctr EE = ctr .+^ (hvec hw) -rectCardinal hw hh ctr SE = ctr .+^ (vec hw (-hh)) -rectCardinal _ hh ctr SS = ctr .+^ (vvec (-hh)) -rectCardinal hw hh ctr SW = ctr .+^ (vec (-hw) (-hh) )-rectCardinal hw _ ctr WW = ctr .+^ (hvec (-hw)) -rectCardinal hw hh ctr NW = ctr .+^ (vec (-hw) hh) +rectCardinal _ hh ctr NORTH = ctr .+^ (vvec hh) +rectCardinal hw hh ctr NORTH_EAST = ctr .+^ (vec hw hh) +rectCardinal hw _ ctr EAST = ctr .+^ (hvec hw) +rectCardinal hw hh ctr SOUTH_EAST = ctr .+^ (vec hw (-hh)) +rectCardinal _ hh ctr SOUTH = ctr .+^ (vvec (-hh)) +rectCardinal hw hh ctr SOUTH_WEST = ctr .+^ (vec (-hw) (-hh) )+rectCardinal hw _ ctr WEST = ctr .+^ (hvec (-hw)) +rectCardinal hw hh ctr NORTH_WEST = ctr .+^ (vec (-hw) hh) -polyCardinal :: Floating u => (Radian -> Point2 u) -> Cardinal -> Point2 u-polyCardinal f NN = f (0.5 * pi)-polyCardinal f NE = f (0.25 * pi) -polyCardinal f EE = f 0 -polyCardinal f SE = f (1.75 * pi) -polyCardinal f SS = f (1.5 * pi) -polyCardinal f SW = f (1.25 * pi)-polyCardinal f WW = f pi -polyCardinal f NW = f (0.75 * pi) +-- | All anchors are the center!+--+zeroAnchor :: Point2 u -> DotAnchor u+zeroAnchor ctr = + DotAnchor { center_anchor = ctr+ , radial_anchor = const ctr + , cardinal_anchor = const ctr } -rectangleAnchor :: (Real u, Floating u) => u -> u -> Point2 u -> DotAnchor u+rectangleAnchor :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> u -> Point2 u -> DotAnchor u rectangleAnchor hw hh ctr = DotAnchor { center_anchor = ctr , radial_anchor = fn , cardinal_anchor = rectCardinal hw hh ctr } where- fn theta = maybe ctr id $ findIntersect ctr theta - $ rectangleLines ctr hw hh-+ fn theta = let mb_v1 = rectangleRadialIntersect (2*hw) (2*hh) theta+ in displace (maybe zeroVec id mb_v1) ctr -polygonAnchor :: (Real u, Floating u) => [Point2 u] -> Point2 u -> DotAnchor u-polygonAnchor ps ctr = +triangleAnchor :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> Point2 u -> DotAnchor u+triangleAnchor hh ctr = DotAnchor { center_anchor = ctr , radial_anchor = fn - , cardinal_anchor = polyCardinal fn }+ , cardinal_anchor = radialCardinal hh ctr } where- fn theta = maybe ctr id $ findIntersect ctr theta $ polygonLines ps+ fn theta = let mb_v1 = isoscelesTriangleRadialIntersect (2*hh) (2*hh) theta+ in displace (maybe zeroVec id mb_v1) ctr +circleAnchor :: Floating u => u -> Point2 u -> DotAnchor u+circleAnchor rad ctr = + DotAnchor { center_anchor = ctr+ , radial_anchor = fn + , cardinal_anchor = radialCardinal rad ctr }+ where+ fn theta = displace (avec theta rad) ctr -bboxRectAnchor :: (Real u, Floating u) => BoundingBox u -> DotAnchor u+++bboxRectAnchor :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => BoundingBox u -> DotAnchor u bboxRectAnchor (BBox bl@(P2 x1 y1) (P2 x2 y2)) = let hw = 0.5 * (x2 - x1) hh = 0.5 * (y2 - y1) in rectangleAnchor hw hh (bl .+^ vec hw hh) -rectangleLDO :: (Real u, Floating u) - => u -> u -> LocDrawingInfo u (DotAnchor u)-rectangleLDO w h = - promoteR1 $ \pt -> pure $ rectangleAnchor (w*0.5) (h*0.5) pt +zeroLDO :: InterpretUnit u => LocQuery u (DotAnchor u)+zeroLDO = qpromoteLoc $ \pt -> return $ zeroAnchor pt -circleAnchor :: Floating u => u -> Point2 u -> DotAnchor u-circleAnchor rad ctr = DotAnchor ctr - (\theta -> ctr .+^ (avec theta rad))- (radialCardinal rad ctr)+rectangleLDO :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => MarkSize -> MarkSize -> LocQuery u (DotAnchor u)+rectangleLDO w h = qpromoteLoc $ \pt -> + (\uw uh -> rectangleAnchor (uw*0.5) (uh*0.5) pt) + <$> uconvertCtx1 w <*> uconvertCtx1 h -circleLDO :: (Floating u, FromPtSize u) => LocDrawingInfo u (DotAnchor u)-circleLDO = - promoteR1 $ \pt -> - markHeight >>= \diam -> pure $ circleAnchor (diam * 0.5) pt --- This might be better taking a function: ctr -> poly_points--- ...----polygonLDO :: (Real u, Floating u, FromPtSize u) - => (u -> Point2 u -> [Point2 u]) -> LocDrawingInfo u (DotAnchor u)-polygonLDO mk = - promoteR1 $ \ctr -> - markHeight >>= \h -> let ps = mk h ctr in pure $ polygonAnchor ps ctr+circleLDO :: (Floating u, InterpretUnit u) + => MarkSize -> LocQuery u (DotAnchor u)+circleLDO rad = qpromoteLoc $ \pt -> + uconvertCtx1 rad >>= \urad -> pure $ circleAnchor urad pt +-- Probably better just using bounding circle for polygons +-- If you really care about anchors use shapes+-- ++-- Triangle probably benefits proper calculation...++triangleLDO :: (Real u, Floating u, Tolerance u, InterpretUnit u) + => MarkSize -> LocQuery u (DotAnchor u)+triangleLDO h = qpromoteLoc $ \pt -> + (\uh -> triangleAnchor (uh*0.5) pt) + <$> uconvertCtx1 h++ -------------------------------------------------------------------------------- -type DotLocImage u = LocImage u (DotAnchor u) +type DotLocImage u = LocImage u (DotAnchor u) type DDotLocImage = DotLocImage Double -dotChar :: (Floating u, Real u, FromPtSize u) => Char -> DotLocImage u++dotNone :: InterpretUnit u => DotLocImage u+dotNone = intoLocImage zeroLDO SD.dotNone++++smallDisk :: (Floating u, Real u, InterpretUnit u) => DotLocImage u+smallDisk = intoLocImage (circleLDO 0.25) SD.smallDisk+++largeDisk :: (Floating u, Real u, InterpretUnit u) => DotLocImage u+largeDisk = intoLocImage (circleLDO 1.00) SD.largeDisk++smallCirc :: (Floating u, Real u, InterpretUnit u) => DotLocImage u+smallCirc = intoLocImage (circleLDO 0.25) SD.smallCirc+++largeCirc :: (Floating u, Real u, InterpretUnit u) => DotLocImage u+largeCirc = intoLocImage (circleLDO 1.00) SD.largeCirc+++dotChar :: (Floating u, Real u, InterpretUnit u, Tolerance u) + => Char -> DotLocImage u dotChar ch = dotText [ch] @@ -216,71 +260,83 @@ -- -dotText :: (Floating u, Real u, FromPtSize u) => String -> DotLocImage u -dotText ss = fmap (bimapL bboxRectAnchor) (ctrCenterLine ss)+dotText :: (Floating u, Real u, InterpretUnit u, Tolerance u) + => String -> DotLocImage u +dotText ss = + fmap bboxRectAnchor $ runPosObjectBBox CENTER $ posText ss +-- Note - maybe Wumpus-Basic should have a @swapAns@ function? -dotHLine :: (Floating u, FromPtSize u) => DotLocImage u-dotHLine = intoLocImage circleLDO markHLine +dotHBar :: (Floating u, InterpretUnit u) => DotLocImage u+dotHBar = intoLocImage (circleLDO 0.5) SD.dotHBar -dotVLine :: (Floating u, FromPtSize u) => DotLocImage u-dotVLine = intoLocImage circleLDO markVLine +dotVBar :: (Floating u, InterpretUnit u) => DotLocImage u+dotVBar = intoLocImage (circleLDO 0.5) SD.dotVBar -dotX :: (Floating u, FromPtSize u) => DotLocImage u-dotX = intoLocImage circleLDO markX -dotPlus :: (Floating u, FromPtSize u) => DotLocImage u-dotPlus = intoLocImage circleLDO markPlus+dotX :: (Floating u, InterpretUnit u) => DotLocImage u+dotX = intoLocImage (circleLDO 0.5) SD.dotX -dotCross :: (Floating u, FromPtSize u) => DotLocImage u-dotCross = intoLocImage circleLDO markCross+dotPlus :: (Floating u, InterpretUnit u) => DotLocImage u+dotPlus = intoLocImage (circleLDO 0.5) SD.dotPlus -dotDiamond :: (Floating u, FromPtSize u) => DotLocImage u-dotDiamond = intoLocImage circleLDO markDiamond+dotCross :: (Floating u, InterpretUnit u) => DotLocImage u+dotCross = intoLocImage (circleLDO 0.5) SD.dotCross -dotFDiamond :: (Floating u, FromPtSize u) => DotLocImage u-dotFDiamond = intoLocImage circleLDO markFDiamond+dotDiamond :: (Floating u, InterpretUnit u) => DotLocImage u+dotDiamond = intoLocImage (circleLDO 0.5) SD.dotDiamond +dotFDiamond :: (Floating u, InterpretUnit u) => DotLocImage u+dotFDiamond = intoLocImage (circleLDO 0.5) SD.dotFDiamond -dotDisk :: (Floating u, FromPtSize u) => DotLocImage u-dotDisk = intoLocImage circleLDO markDisk +dotDisk :: (Floating u, InterpretUnit u) => DotLocImage u+dotDisk = intoLocImage (circleLDO 0.5) SD.dotDisk -dotSquare :: (Floating u, Real u, FromPtSize u) => DotLocImage u-dotSquare = - lift0R1 markHeight >>= \h -> intoLocImage (rectangleLDO h h) markSquare +dotSquare :: (Floating u, Real u, InterpretUnit u, Tolerance u) + => DotLocImage u+dotSquare = intoLocImage (rectangleLDO 1 1) SD.dotSquare -dotCircle :: (Floating u, FromPtSize u) => DotLocImage u-dotCircle = intoLocImage circleLDO markCircle +dotCircle :: (Floating u, InterpretUnit u) => DotLocImage u+dotCircle = intoLocImage (circleLDO 0.5) SD.dotCircle -dotPentagon :: (Floating u, FromPtSize u) => DotLocImage u-dotPentagon = intoLocImage circleLDO markPentagon -dotStar :: (Floating u, FromPtSize u) => DotLocImage u-dotStar = intoLocImage circleLDO markStar+dotPentagon :: (Floating u, InterpretUnit u) => DotLocImage u+dotPentagon = intoLocImage (circleLDO 0.5) SD.dotPentagon +dotStar :: (Floating u, Ord u, InterpretUnit u, Tolerance u) + => DotLocImage u+dotStar = intoLocImage (circleLDO 0.5) SD.dotStar -dotAsterisk :: (Floating u, FromPtSize u) => DotLocImage u-dotAsterisk = intoLocImage circleLDO markAsterisk -dotOPlus :: (Floating u, FromPtSize u) => DotLocImage u-dotOPlus = intoLocImage circleLDO markOPlus+dotAsterisk :: (Floating u, InterpretUnit u) => DotLocImage u+dotAsterisk = intoLocImage (circleLDO 0.5) SD.dotAsterisk -dotOCross :: (Floating u, FromPtSize u) => DotLocImage u-dotOCross = intoLocImage circleLDO markOCross+dotOPlus :: (Floating u, InterpretUnit u) => DotLocImage u+dotOPlus = intoLocImage (circleLDO 0.5) SD.dotOPlus -dotFOCross :: (Floating u, FromPtSize u) => DotLocImage u-dotFOCross = intoLocImage circleLDO markFOCross+dotOCross :: (Floating u, InterpretUnit u) => DotLocImage u+dotOCross = intoLocImage (circleLDO 0.5) SD.dotOCross +dotFOCross :: (Floating u, InterpretUnit u) => DotLocImage u+dotFOCross = intoLocImage (circleLDO 0.5) SD.dotFOCross -dotTriangle :: (Real u, Floating u, FromPtSize u) => DotLocImage u-dotTriangle = intoLocImage (polygonLDO fn) markTriangle- where - fn h ctr = let (bl,br,top) = equilateralTrianglePoints h ctr in [bl,br,top]++dotTriangle :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => DotLocImage u+dotTriangle = intoLocImage (triangleLDO 1) SD.dotTriangle+++intoLocImage :: InterpretUnit u + => LocQuery u a -> LocImage u z -> LocImage u a+intoLocImage ma gf = promoteLoc $ \pt -> + askDC >>= \ctx -> + let ans = runLocQuery ctx pt ma+ in replaceAns ans $ applyLoc gf pt
− src/Wumpus/Drawing/Dots/Marks.hs
@@ -1,243 +0,0 @@-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Drawing.Dots.Marks--- Copyright : (c) Stephen Tetley 2010-2011--- License : BSD3------ Maintainer : Stephen Tetley <stephen.tetley@gmail.com>--- Stability : highly unstable--- Portability : GHC------ Marks - dots without anchor handles.------ The text and char marks need loaded glyph metrics for proper --- centering. ------ \*\* WARNING \*\* - names are expected to change - filled and--- background-filled marks need a naming convention.--- -----------------------------------------------------------------------------------module Wumpus.Drawing.Dots.Marks- ( --- -- * Marks- markChar- , markText-- , markHLine- , markVLine- , markX- , markPlus- , markCross- , markDiamond- , markFDiamond- , markBDiamond - , markDisk- , markSquare- , markCircle - , markPentagon- , markStar- , markAsterisk- , markOPlus- , markOCross- , markFOCross- , markTriangle-- ) where---import Wumpus.Drawing.Geometry.Paths-import Wumpus.Drawing.Text.LRText--import Wumpus.Basic.Kernel -- package: wumpus-basic--import Wumpus.Core -- package: wumpus-core--import Data.AffineSpace -- package: vector-space-import Data.VectorSpace--import Control.Applicative---- Marks should be the height of a lower-case letter...---- NOTES------ TikZ has both stroked and bordered (filled and outline-stroked)--- marks e.g. square and square*--------infixr 9 `renderPathWith`--renderPathWith :: LocDrawingInfo u (PrimPath u) - -> (PrimPath u -> Graphic u) - -> LocGraphic u-renderPathWith m k = m >>= (lift0R1 . k)----markChar :: (Real u, Floating u, FromPtSize u) => Char -> LocGraphic u-markChar ch = markText [ch]-----markText :: (Real u, Floating u, FromPtSize u) => String -> LocGraphic u-markText ss = fmap (replaceL uNil) $ ctrCenterLine ss------- | Supplied point is the center.----axialLine :: Fractional u => Vec2 u -> LocGraphic u-axialLine v = moveStartPoint (\ctr -> ctr .-^ (0.5 *^ v)) (straightLine v)---markHLine :: (Fractional u, FromPtSize u) => LocGraphic u -markHLine = lift0R1 markHeight >>= \h -> axialLine (hvec h)---markVLine :: (Fractional u, FromPtSize u) => LocGraphic u -markVLine = lift0R1 markHeight >>= \h -> axialLine (vvec h) ---markX :: (Fractional u, FromPtSize u) => LocGraphic u-markX = lift0R1 markHeight >>= mkX - where- mkX h = let w = 0.75 * h- in axialLine (vec w h) `oplus` axialLine (vec (-w) h)----markPlus :: (Fractional u, FromPtSize u) => LocGraphic u-markPlus = markVLine `oplus` markHLine---markCross :: (Floating u, FromPtSize u) => LocGraphic u-markCross = markHeight >>= mkCross- where- mkCross h = axialLine (avec ang h) `oplus` axialLine (avec (-ang) h)- ang = pi*0.25 ---- Note - height is extended slightly to look good...--pathDiamond :: (Fractional u, FromPtSize u) - => LocDrawingInfo u (PrimPath u)-pathDiamond = - promoteR1 $ \pt -> - markHeight >>= \h -> let cp = diamondCoordPath (0.5*h) (0.66*h) - in return $ coordinatePrimPath pt cp------ closedStroke :: (a -> ctx -> prim) --- pathDiamond :: (ctx -> pt -> a)--- ans :: (ctx -> pt -> prim)--markDiamond :: (Fractional u, FromPtSize u) => LocGraphic u-markDiamond = pathDiamond `renderPathWith` closedStroke--markFDiamond :: (Fractional u, FromPtSize u) => LocGraphic u-markFDiamond = pathDiamond `renderPathWith` filledPath----- Note - the (const . fn) composition doesn\'t /tell/ much about--- what is going on - though obviously it can be decoded - make --- the function obvious to the second argument. --- --- A named combinator might be better.-----markBDiamond :: (Fractional u, FromPtSize u) => LocGraphic u-markBDiamond = pathDiamond `renderPathWith` borderedPath----- | Note disk is filled.----markDisk :: (Fractional u, FromPtSize u) => LocGraphic u-markDisk = lift0R1 markHalfHeight >>= filledDisk ----markSquare :: (Fractional u, FromPtSize u) => LocGraphic u-markSquare = - lift0R1 markHeight >>= \h -> - let d = 0.5*(-h) in moveStartPoint (displace d d) $ strokedRectangle h h- ---markCircle :: (Fractional u, FromPtSize u) => LocGraphic u-markCircle = lift0R1 markHalfHeight >>= strokedDisk ---markBCircle :: (Fractional u, FromPtSize u) => LocGraphic u-markBCircle = lift0R1 markHalfHeight >>= borderedDisk ----markPentagon :: (Floating u, FromPtSize u) => LocGraphic u-markPentagon = - promoteR1 $ \pt -> - markHeight >>= \h -> closedStroke $ pentagonPath pt (0.5*h)- where- pentagonPath pt hh = coordinatePrimPath pt $ polygonCoordPath 5 hh-- ---markStar :: (Floating u, FromPtSize u) => LocGraphic u -markStar = lift0R1 markHeight >>= \h -> starLines (0.5*h)--starLines :: Floating u => u -> LocGraphic u-starLines hh = - promoteR1 $ \ctr -> let cp = polygonCoordPath 5 hh- in step $ map (fn ctr) $ cp ctr- where- fn p0 p1 = openStroke $ primPath p0 [lineTo p1]- step (x:xs) = oconcat x xs- step _ = error "starLines - unreachable"---markAsterisk :: (Floating u, FromPtSize u) => LocGraphic u-markAsterisk = lift0R1 markHeight >>= asteriskLines--asteriskLines :: Floating u => u -> LocGraphic u-asteriskLines h = lineF1 `oplus` lineF2 `oplus` lineF3- where- ang = (pi*2) / 6- lineF1 = axialLine (vvec h)- lineF2 = axialLine (avec ((pi*0.5) + ang) h)- lineF3 = axialLine (avec ((pi*0.5) + ang + ang) h)---markOPlus :: (Fractional u, FromPtSize u) => LocGraphic u-markOPlus = markCircle `oplus` markPlus---markOCross :: (Floating u, FromPtSize u) => LocGraphic u-markOCross = markCircle `oplus` markCross---markFOCross :: (Floating u, FromPtSize u) => LocGraphic u-markFOCross = markCross `oplus` markBCircle ----- bkCircle :: (Fractional u, FromPtSize u) => LocGraphic u--- bkCircle = disk (fillAttr attr) (0.5*markHeight attr) ----markTriangle :: (Floating u, FromPtSize u) => LocGraphic u-markTriangle = tripath `renderPathWith` closedStroke- where- tripath = promoteR1 $ \pt -> - markHeight >>= \h -> let cp = equilateralTriangleCoordPath h - in pure $ coordinatePrimPath pt cp-
+ src/Wumpus/Drawing/Dots/SimpleDots.hs view
@@ -0,0 +1,281 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Dots.SimpleDots+-- Copyright : (c) Stephen Tetley 2011-2012+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Simple dots - no anchor handles.+-- +-- Use these where you just want to draw Dots, and do not need+-- connectors between them. +--+-- The text and char marks need loaded glyph metrics for proper +-- centering. +--+-- \*\* WARNING \*\* - names are expected to change - filled and+-- background-filled marks need a naming convention.+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Dots.SimpleDots+ ( ++ -- * Unit for marks (0.75 the font size)+ MarkSize++ , smallDisk+ , largeDisk+ , smallCirc+ , largeCirc ++++ -- * Dots+ , dotNone++ , dotChar+ , dotText+ , dotEscChar+ , dotEscText+++ , dotHBar+ , dotVBar+ , dotX+ , dotPlus+ , dotCross+ , dotDiamond+ , dotFDiamond+ , dotBDiamond + , dotDisk+ , dotSquare+ , dotCircle + , dotPentagon+ , dotStar+ , dotAsterisk+ , dotOPlus+ , dotOCross+ , dotFOCross+ , dotTriangle++ ) where++import Wumpus.Drawing.Basis.ShapeTrails+import Wumpus.Drawing.Basis.Symbols++import Wumpus.Basic.Kernel ++import Wumpus.Core -- package: wumpus-core++import Data.VectorSpace -- package: vector-space++import Data.Monoid++-- Marks should be the height of a lower-case letter...++-- NOTES+--+-- TikZ has both stroked and bordered (filled and outline-stroked)+-- marks e.g. square and square*+--++-- Cap height is a good size for Dots.+++-- | MarkUnit is a contextual unit like 'Em' and 'En'.+-- +-- It is 3\/4 of the current font size.+--+newtype MarkSize = MarkSize { getMarkSize :: Double }+ deriving (Eq,Ord,Num,Floating,Fractional,Real,RealFrac,RealFloat)++instance Show MarkSize where+ showsPrec p d = showsPrec p (getMarkSize d)+++instance InterpretUnit MarkSize where+ normalize sz a = (realToFrac a) * 0.75 * fromIntegral sz+ dinterp sz d = (4/3) * (realToFrac d) / (fromIntegral sz)++instance Tolerance MarkSize where + eq_tolerance = 0.001+ length_tolerance = 0.01+++umark :: InterpretUnit u => LocGraphic MarkSize -> LocGraphic u+umark = uconvF+++-- | Filled disk - radius 0.25 MarkSize.+--+smallDisk :: InterpretUnit u => LocGraphic u+smallDisk = umark $ dcDisk DRAW_FILL 0.25+++-- | Filled disk - radius 1.0 MarkSize.+--+largeDisk :: InterpretUnit u => LocGraphic u+largeDisk = umark $ dcDisk DRAW_FILL 1+++-- | Stroked disk (circle) - radius 0.25 MarkSize.+--+smallCirc :: InterpretUnit u => LocGraphic u+smallCirc = umark $ scircle 0.25+++-- | Stroked disk (circle) - radius 1.0 MarkSize.+--+largeCirc :: InterpretUnit u => LocGraphic u+largeCirc = umark $ scircle 1+++-- possibly:+-- szCirc :: u -> LocGraphic u++dotNone :: InterpretUnit u => LocGraphic u+dotNone = emptyLocImage++dotChar :: (Real u, Floating u, InterpretUnit u) => Char -> LocGraphic u+dotChar ch = dotText [ch]++++dotText :: (Real u, Floating u, InterpretUnit u) => String -> LocGraphic u+dotText ss = ignoreAns $ runPosObject CENTER $ posText ss++dotEscChar :: (Real u, Floating u, InterpretUnit u) + => EscapedChar -> LocGraphic u+dotEscChar = dotEscText . wrapEscChar++dotEscText :: (Real u, Floating u, InterpretUnit u) + => EscapedText -> LocGraphic u+dotEscText esc = ignoreAns $ runPosObject CENTER $ posEscText esc++-- TODO - need Upright versions of dots...++++-- | Supplied point is the center.+--+axialLine :: (Fractional u, InterpretUnit u) => Vec2 u -> LocGraphic u+axialLine v = moveStart (negateV (0.5 *^ v)) (locStraightLine v)+++dotHBar :: (Fractional u, InterpretUnit u) => LocGraphic u +dotHBar = umark $ hbar 1+++dotVBar :: (Fractional u, InterpretUnit u) => LocGraphic u +dotVBar = umark $ vbar 1 +++dotX :: (Fractional u, InterpretUnit u) => LocGraphic u+dotX = umark $ axialLine (vec 0.75 1) `mappend` axialLine (vec (-0.75) 1)++++dotPlus :: (Fractional u, InterpretUnit u) => LocGraphic u+dotPlus = dotVBar `mappend` dotHBar+++dotCross :: (Floating u, InterpretUnit u) => LocGraphic u+dotCross = + umark $ axialLine (avec ang 1) `mappend` axialLine (avec (-ang) 1)+ where + ang = pi*0.25 ++++++dotDiamond :: (Fractional u, InterpretUnit u) => LocGraphic u+dotDiamond = umark $ renderAnaTrail CSTROKE (diamondTrail 0.5 0.66)++dotFDiamond :: (Fractional u, InterpretUnit u) => LocGraphic u+dotFDiamond = umark $ renderAnaTrail CFILL (diamondTrail 0.5 0.66)++++dotBDiamond :: (Fractional u, InterpretUnit u) => LocGraphic u+dotBDiamond = umark $ renderAnaTrail CFILL_STROKE (diamondTrail 0.5 0.66)+++-- | Note disk is filled.+--+dotDisk :: (Fractional u, InterpretUnit u) => LocGraphic u+dotDisk = umark $ dcDisk DRAW_FILL 0.5++++dotSquare :: (Fractional u, InterpretUnit u) => LocGraphic u+dotSquare = umark $ renderAnaTrail CSTROKE (rectangleTrail 1 1)+++dotCircle :: (Fractional u, InterpretUnit u) => LocGraphic u+dotCircle = umark $ scircle 0.5+++dotBCircle :: (Fractional u, InterpretUnit u) => LocGraphic u+dotBCircle = umark $ dcDisk DRAW_FILL_STROKE 0.5++++dotPentagon :: (Floating u, InterpretUnit u) => LocGraphic u+dotPentagon = umark $ renderAnaTrail CSTROKE (polygonTrail 5 0.5)+ +++dotStar :: (Floating u, Ord u, InterpretUnit u, Tolerance u) + => LocGraphic u +dotStar = umark $ starLines 0.5++starLines :: (Floating u, Ord u, InterpretUnit u, Tolerance u) + => u -> LocGraphic u+starLines hh = promoteLoc $ \ctr ->+ let alg = polygonTrail 5 hh+ in liftQuery (qapplyLoc (anaTrailPoints alg) ctr) >>= \ps -> + step $ map (fn ctr) ps+ where+ fn p0 p1 = straightLine p0 p1+ step (x:xs) = mconcat $ x:xs+ step _ = error "starLines - unreachable"++dotAsterisk :: (Floating u, InterpretUnit u) => LocGraphic u+dotAsterisk = umark $ asteriskLines 1++asteriskLines :: (Floating u, InterpretUnit u) => u -> LocGraphic u+asteriskLines h = lineF1 `mappend` lineF2 `mappend` lineF3+ where+ ang = (pi*2) / 6+ lineF1 = axialLine (vvec h)+ lineF2 = axialLine (avec ((pi*0.5) + ang) h)+ lineF3 = axialLine (avec ((pi*0.5) + ang + ang) h)+++dotOPlus :: (Fractional u, InterpretUnit u) => LocGraphic u+dotOPlus = dotCircle `mappend` dotPlus+++dotOCross :: (Floating u, InterpretUnit u) => LocGraphic u+dotOCross = dotCircle `mappend` dotCross+++dotFOCross :: (Floating u, InterpretUnit u) => LocGraphic u+dotFOCross = dotBCircle `mappend` dotCross+++-- bkCircle :: (Fractional u, InterpretUnit u) => LocGraphic u+-- bkCircle = disk (fillAttr attr) (0.5*markHeight attr) ++++dotTriangle :: (Floating u, InterpretUnit u) => LocGraphic u+dotTriangle = umark $ renderAnaTrail CSTROKE $ isosceles_triangle_trail 1 1
+ src/Wumpus/Drawing/Extras/Axes.hs view
@@ -0,0 +1,75 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Extras.Axes+-- Copyright : (c) Stephen Tetley 2011-2012+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Drawing grids+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Extras.Axes+ ( + + orthontAxes+ + , horizontalLabels+ , verticalLabels++ ) where++import Wumpus.Drawing.Connectors+import qualified Wumpus.Drawing.Connectors.ConnectorPaths as C++import Wumpus.Basic.Kernel -- package: wumpus-basic+import Wumpus.Core -- package: wumpus-core++import Data.Monoid++-- Note - axes need labels working out...+++-- | Simple orthonormal axes using snap grid units.+--+orthontAxes :: (Real u, Floating u, InterpretUnit u, Tolerance u)+ => (Int,Int) -> (Int,Int) -> LocGraphic u+orthontAxes (xl,xr) (yl,yr) = promoteLoc $ \(P2 x y) -> + snapmove (1,1) >>= \(V2 uw uh) ->+ let conn1 = ignoreAns conn_line+ xPtl = P2 (x - (uw * fromIntegral xl)) y+ xPtr = P2 (x + (uw * fromIntegral xr)) y+ yPtl = P2 x (y - (uh * fromIntegral yl))+ yPtr = P2 x (y + (uh * fromIntegral yr))+ in localize cap_square $ ignoreAns (connect conn1 xPtl xPtr) + `mappend` ignoreAns (connect conn1 yPtl yPtr)++++horizontalLabels :: (Num a, Show a, Fractional u, InterpretUnit u) + => RectAddress -> [a] -> LocGraphic u +horizontalLabels addr ns = + snapmove (1,1) >>= \(V2 uw _) -> ignoreAns (distribH uw $ map mf ns)+ where+ mf n = runPosObject addr $ posTextUpright $ show n+++verticalLabels :: (Num a, Show a, Fractional u, InterpretUnit u) + => RectAddress -> [a] -> LocGraphic u +verticalLabels addr ns = + snapmove (1,1) >>= \(V2 _ uh) -> ignoreAns (distribV uh $ map mf ns)+ where+ mf n = runPosObject addr $ posTextUpright $ show n++++-- Cf. Parsec\'s Token module - remake with same name...+--+conn_line :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => ArrowConnector u+conn_line = rightArrowConnector default_connector_props C.conn_line barb45
+ src/Wumpus/Drawing/Extras/Clip.hs view
@@ -0,0 +1,46 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Extras.Clip+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Clipping paths.+--+-- Note - at the moment there is nothing much to this module.+-- Ideally, clipping would be defined in Wumpus-Basic, but clipping+-- needs a higher level path object than Wumpus-Basic provides.+-- +-- \*\* WARNING \*\* names need improving.+--+--------------------------------------------------------------------------------++module Wumpus.Drawing.Extras.Clip+ ( + + locClip++ ) where+++import Wumpus.Drawing.Paths++import Wumpus.Basic.Kernel -- package: wumpus-basic++++-- | Clip a LocGraphic.+-- +-- \*\* WARNING \*\* - AbsPath (coordinate-specific) is the wrong +-- object to clip a LocGraphic (coordinate-free). +--+locClip :: InterpretUnit u => AbsPath u -> LocGraphic u -> LocGraphic u+locClip absp gf = promoteLoc $ \pt -> + liftQuery (toPrimPath absp) >>= \pp -> clipImage pp (gf `at` pt)++
+ src/Wumpus/Drawing/Extras/Grids.hs view
@@ -0,0 +1,210 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Extras.Grids+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Drawing grids+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Extras.Grids+ ( + + GridContextF+ , grid+ , standard_grid+ , dotted_major_grid++ , grid_major_colour+ , grid_major_line_width+ , grid_major_dotnum+ , grid_minor_subdivisions+ , grid_minor_colour+ , grid_minor_line_width+ , grid_minor_dotnum+ , grid_point_size+ , grid_label_colour++ ) where+++import Wumpus.Drawing.Basis.DrawingPrimitives++import Wumpus.Basic.Kernel -- package: wumpus-basic++import Wumpus.Core -- package: wumpus-core+import Wumpus.Core.Colour ( black )++import Data.Monoid+++type GridContextF = GridProps -> GridProps++-- | GridProps control the drawing of grids.+-- +data GridProps = GridProps+ { gp_major_colour :: RGBi+ , gp_major_lnwidth :: Double+ , gp_major_dotnum :: Int+ , gp_minor_subdivs :: Int+ , gp_minor_colour :: RGBi+ , gp_minor_lnwidth :: Double+ , gp_minor_dotnum :: Int+ , gp_point_size :: FontSize+ , gp_label_colour :: RGBi+ }++default_grid_props :: GridProps+default_grid_props = + GridProps { gp_major_colour = grey1+ , gp_major_lnwidth = 1+ , gp_major_dotnum = 0+ , gp_minor_subdivs = 5+ , gp_minor_colour = grey2+ , gp_minor_lnwidth = 0.5+ , gp_minor_dotnum = 0+ , gp_point_size = 10+ , gp_label_colour = black+ }+ where+ grey1 = RGBi 100 100 100+ grey2 = RGBi 150 150 150 +++++standard_grid :: GridContextF+standard_grid = id++dotted_major_grid :: GridContextF+dotted_major_grid = + grid_minor_subdivisions 0 . grid_major_dotnum 2++-- Setters for client code.++grid_major_colour :: RGBi -> GridContextF+grid_major_colour rgb = (\s -> s { gp_major_colour = rgb })++grid_major_line_width :: Double -> GridContextF+grid_major_line_width lw = (\s -> s { gp_major_lnwidth = lw })++grid_major_dotnum :: Int -> GridContextF+grid_major_dotnum n = (\s -> s { gp_major_dotnum = n })++grid_minor_subdivisions :: Int -> GridContextF+grid_minor_subdivisions n = (\s -> s { gp_minor_subdivs = n })++grid_minor_colour :: RGBi -> GridContextF+grid_minor_colour rgb = (\s -> s { gp_minor_colour = rgb })++grid_minor_line_width :: Double -> GridContextF+grid_minor_line_width lw = (\s -> s { gp_minor_lnwidth = lw })++grid_minor_dotnum :: Int -> GridContextF+grid_minor_dotnum n = (\s -> s { gp_minor_dotnum = n })+++grid_point_size :: FontSize -> GridContextF+grid_point_size i = (\s -> s { gp_point_size = i })+++grid_label_colour :: RGBi -> GridContextF+grid_label_colour rgb = (\s -> s { gp_label_colour = rgb })+++++-- Drawing context updaters...++major_line_update :: GridProps -> DrawingContextF+major_line_update (GridProps { gp_major_colour = rgb+ , gp_major_lnwidth = lnwidth+ , gp_major_dotnum = dotnum }) = + lineProps rgb lnwidth dotnum ++minor_line_update :: GridProps -> DrawingContextF+minor_line_update (GridProps { gp_minor_colour = rgb+ , gp_minor_lnwidth = lnwidth+ , gp_minor_dotnum = dotnum }) = + lineProps rgb lnwidth dotnum +++lineProps :: RGBi -> Double -> Int -> DrawingContextF+lineProps rgb lw n + | n < 1 = stroke_colour rgb . set_line_width lw . solid_line + | otherwise = stroke_colour rgb . set_line_width lw . dashesF + where+ dashesF = set_dash_pattern $ Dash 0 [(1,n)]++++--------------------------------------------------------------------------------+++grid :: (Fractional u, InterpretUnit u) + => GridContextF -> Int -> Int -> LocGraphic u +grid upd nx ny = + snapmove (1,1) >>= \(V2 uw uh) ->+ let props = upd default_grid_props+ width = uw * fromIntegral nx+ height = uh * fromIntegral ny+ intrr = gridInterior nx width uw ny height uh props+ rect = localize (major_line_update props) $ + blRectangle DRAW_STROKE width height+ in intrr `mappend` rect++ ++gridInterior :: (Fractional u, InterpretUnit u) + => Int -> u -> u -> Int -> u -> u -> GridProps -> LocGraphic u+gridInterior nx w uw ny h uh props = hlines `mappend` vlines+ where+ hlines = horizontalLines ny w uh props+ vlines = verticalLines nx h uw props+++horizontalLines :: (Fractional u, InterpretUnit u) + => Int -> u -> u -> GridProps -> LocGraphic u+horizontalLines numh w uh props@(GridProps { gp_minor_subdivs = subs })+ | subs > 0 = let dy = uh / (fromIntegral subs)+ n = (numh * subs) - 1+ in moveStart (vvec dy) $ minorMajor n subs (vvec dy) mnr mjr+ | otherwise = moveStart (vvec uh) $ duplicate numh (vvec uh) mjr+ where+ mnr = localize (minor_line_update props) $ horizontalLine w+ mjr = localize (major_line_update props) $ horizontalLine w++++verticalLines :: (Fractional u, InterpretUnit u) + => Int -> u -> u -> GridProps -> LocGraphic u+verticalLines numv h uw props@(GridProps { gp_minor_subdivs = subs })+ | subs > 0 = let dx = uw / (fromIntegral subs)+ n = (numv * subs) - 1+ in moveStart (hvec dx) $ minorMajor n subs (hvec dx) mnr mjr+ | otherwise = moveStart (hvec uw) $ duplicate numv (hvec uw) mjr+ where+ mnr = localize (minor_line_update props) $ verticalLine h+ mjr = localize (major_line_update props) $ verticalLine h+++++minorMajor :: InterpretUnit u + => Int -> Int -> Vec2 u -> LocGraphic u -> LocGraphic u + -> LocGraphic u+minorMajor count alt mv mnr mjr = runLocTrace (step count)+ where+ step n | n <= 0 = ureturn+ | n `mod` alt == 0 = insertl mjr >> moveby mv >> step (n-1)+ | otherwise = insertl mnr >> moveby mv >> step (n-1)+ ++
+ src/Wumpus/Drawing/Extras/Loop.hs view
@@ -0,0 +1,93 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Extras.Loop+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Open loop for a circle (useful for automata diagrams).+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Extras.Loop+ ( ++ loopPath+ , loopTrail++ ) where+++import Wumpus.Drawing.Paths++import Wumpus.Basic.Kernel -- package: wumpus-basic++import Wumpus.Core -- package: wumpus-core+++import Data.AffineSpace -- package: vector-space+++import Data.Monoid++++loopPath :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> Point2 u -> Radian -> AbsPath u+loopPath zradius ctr incl = anaTrailPath ctr $ loopTrail zradius incl+++++-- This is a legacy definition updated to use Trails, hence it is +-- not so clear.++loopTrail :: (Real u, Floating u) => u -> Radian -> AnaTrail u+loopTrail circ_radius incl = + anaCatTrail (pvec zeroPt startl) $ + mconcat [ diffCurve startl cp1 cp2 kitel+ , diffCurve kitel cp3 cp4 top+ , diffCurve top cp5 cp6 kiter+ , diffCurve kiter cp7 cp8 startr+ ]+ where+ hw = 1.25 * circ_radius+ height = 3.8 * circ_radius+ hminor = 2.72 * circ_radius+ hbase = circ_radius / 3+ theta = toRadian $ asin $ hbase / circ_radius+ start_vec = avec (circularModulo $ incl - quarter_pi) (0.26 * circ_radius)+ end_vec = avec (circularModulo $ incl + quarter_pi) (0.26 * circ_radius) + minor_down = negate $ 0.8 * circ_radius + major_up = 0.52 * circ_radius+ top_right = negate $ 0.8 * circ_radius+ top_left = 0.8 * circ_radius++ top = dispParallel height incl zeroPt+ kiter = dispOrtho hminor (-hw) incl zeroPt+ kitel = dispOrtho hminor hw incl zeroPt+ + startr = zeroPt .+^ avec (circularModulo $ incl - theta) circ_radius+ startl = zeroPt .+^ avec (circularModulo $ incl + theta) circ_radius++ -- quadrant III+ cp1 = startl .+^ end_vec + cp2 = dispParallel minor_down incl kitel++ -- quadrant II + cp3 = dispParallel major_up incl kitel+ cp4 = dispPerpendicular top_left incl top++ -- quadrant I+ cp5 = dispPerpendicular top_right incl top+ cp6 = dispParallel major_up incl kiter++ -- quadrant IV + cp7 = dispParallel minor_down incl kiter+ cp8 = startr .+^ start_vec+
− src/Wumpus/Drawing/Geometry/Intersection.hs
@@ -1,173 +0,0 @@-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Drawing.Geometry.Intersection--- Copyright : (c) Stephen Tetley 2010-2011--- License : BSD3------ Maintainer : Stephen Tetley <stephen.tetley@gmail.com>--- Stability : highly unstable--- Portability : GHC------ Intersection of line to line and line to plane--- --- \*\* - WARNING \*\* - half baked. --------------------------------------------------------------------------------------module Wumpus.Drawing.Geometry.Intersection- ( - LineSegment(..)- , PointSlope- , pointSlope- , LineEqn- , lineEqn- , toLineEqn- , findIntersect- , intersection-- , rectangleLines- , polygonLines- , langle-- ) - where--import Wumpus.Core -- package: wumpus-core--import Data.AffineSpace -- package: vector-space-import Data.VectorSpace----- WARNING - This module is not very good (neither particularly --- robust, nor efficient).--- --- I really need to find an algorithm that does this properly.-----data LineSegment u = LS (Point2 u) (Point2 u)- deriving (Eq,Ord,Show)---data PointSlope u = PointSlope - { _point_slope_point :: Point2 u- , _point_slope_slope :: u- }- deriving (Eq,Show)--pointSlope :: Fractional u => Point2 u -> Radian -> PointSlope u -pointSlope pt theta = PointSlope pt (fromRadian $ tan theta)----- | Line in equational form, i.e. @Ax + By + C = 0@.-data LineEqn u = LineEqn - { _line_eqn_A :: !u- , _line_eqn_B :: !u- , _line_eqn_C :: !u - }- deriving (Eq,Show)--lineEqn :: Num u => Point2 u -> Point2 u -> LineEqn u-lineEqn (P2 x1 y1) (P2 x2 y2) = LineEqn a b c - where- a = y1 - y2- b = x2 - x1- c = (x1*y2) - (x2*y1)---toLineEqn :: Num u => PointSlope u -> LineEqn u-toLineEqn (PointSlope (P2 x0 y0) m) = LineEqn m (-1) ((-m) * x0 + y0)-----data IntersectionResult u = Intersects u u | Contained | NoIntersect- deriving (Eq,Show)----- Note the uses a /plane/ so is susceptible to picking the --- wrong quadrant...----findIntersect :: (Floating u, Real u, Ord u)- => Point2 u -> Radian -> [LineSegment u] -> Maybe (Point2 u)-findIntersect ctr ang0 = step - where- theta = circularModulo ang0- eqn = toLineEqn $ pointSlope ctr theta- step [] = Nothing- step (x:xs) = case intersection x eqn of - Just pt | quadrantCheck theta ctr pt -> Just pt- _ -> step xs---quadrantCheck :: (Real u, Floating u) - => Radian -> Point2 u -> Point2 u -> Bool-quadrantCheck theta ctr pt = theta == langle ctr pt--intersection :: (Fractional u, Ord u) - => LineSegment u -> LineEqn u -> Maybe (Point2 u)-intersection ls@(LS p q) eqn = case intersect1 ls eqn of- Intersects fp fq -> let t = fp / (fp-fq) in Just $ affineComb p q t - Contained -> Just p- NoIntersect -> Nothing----intersect1 :: (Num u, Ord u) - => LineSegment u -> LineEqn u -> IntersectionResult u-intersect1 (LS p q) eqn = - if inters fp fq then Intersects fp fq- else if contained fp fq then Contained else NoIntersect- where- inters a b = (a < 0 && b >= 0) || (a > 0 && b <= 0)- contained a b = a == 0 && b == 0- fp = lineF p eqn- fq = lineF q eqn- -lineF :: Num u => Point2 u -> LineEqn u -> u-lineF (P2 x y) (LineEqn a b c) = a*x + b*y + c--affineComb :: Num u => Point2 u -> Point2 u -> u -> Point2 u-affineComb p q t = p .+^ t *^ (q .-. p)-----rectangleLines :: Num u => Point2 u -> u -> u -> [LineSegment u]-rectangleLines ctr hw hh = [LS br tr, LS tr tl, LS tl bl, LS bl br]- where- br = ctr .+^ (vec hw (-hh))- tr = ctr .+^ (vec hw hh)- tl = ctr .+^ (vec (-hw) hh)- bl = ctr .+^ (vec (-hw) (-hh))---polygonLines :: [Point2 u] -> [LineSegment u]-polygonLines [] = error "polygonLines - emptyList"-polygonLines (x:xs) = step x xs - where- step a [] = [LS a x]- step a (b:bs) = LS a b : step b bs------ | Calculate the counter-clockwise angle between two points --- and the x-axis.----langle :: (Floating u, Real u) => Point2 u -> Point2 u -> Radian-langle (P2 x1 y1) (P2 x2 y2) = step (x2 - x1) (y2 - y1)- where- -- north-east quadrant - step x y | pve x && pve y = toRadian $ atan (y/x) - - -- north-west quadrant- step x y | pve y = pi - (toRadian $ atan (y / abs x))-- -- south-east quadrant- step x y | pve x = (2*pi) - (toRadian $ atan (abs y / x)) -- -- otherwise... south-west quadrant- step x y = pi + (toRadian $ atan (y/x))-- pve a = signum a >= 0
− src/Wumpus/Drawing/Geometry/Paths.hs
@@ -1,138 +0,0 @@-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Drawing.Geometry.Paths--- Copyright : (c) Stephen Tetley 2010-2011--- License : BSD3------ Maintainer : Stephen Tetley <stephen.tetley@gmail.com>--- Stability : highly unstable--- Portability : GHC------ Paths for /elementary/ shapes - rectangles...--- --- \*\* - WARNING \*\* - half baked. --------------------------------------------------------------------------------------module Wumpus.Drawing.Geometry.Paths- ( - LocCoordPath- , coordinatePrimPath-- , rectangleCoordPath- , diamondCoordPath- , polygonCoordPath- , isoscelesTriangleCoordPath- , isoscelesTrianglePoints- , equilateralTriangleCoordPath- , equilateralTrianglePoints- ) - where--import Wumpus.Core -- package: wumpus-core--import Data.AffineSpace -- package: vector-space---import Data.List ( unfoldr )----- | A functional type from /initial point/ to point list.----type LocCoordPath u = Point2 u -> [Point2 u]----- Note - extraction needs a naming scheme - extractFROM or --- extractTO? - in either case this might be queuing up --- name-clash problems.------ The Path data type will also need a similar function...---- -coordinatePrimPath :: Num u => Point2 u -> LocCoordPath u -> PrimPath u-coordinatePrimPath pt fn = go (fn pt)- where- go ps@(_:_) = vertexPath ps- go [] = emptyPath pt -- fallback----- NOTE - These functions need changing to generate LocCoordPaths...---- | Supplied point is /bottom-left/, subsequenct points are --- counter-clockise so [ bl, br, tr, tl ] .----rectangleCoordPath :: Num u => u -> u -> LocCoordPath u-rectangleCoordPath w h bl = [ bl, br, tr, tl ]- where- br = bl .+^ hvec w- tr = br .+^ vvec h- tl = bl .+^ vvec h ------ | 'diamondPath' : @ half_width * half_height * center_point -> PrimPath @----diamondCoordPath :: Num u => u -> u -> LocCoordPath u-diamondCoordPath hw hh ctr = [ s,e,n,w ]- where- s = ctr .+^ vvec (-hh)- e = ctr .+^ hvec hw- n = ctr .+^ vvec hh- w = ctr .+^ hvec (-hw)- ---- | 'polygonCoordPath' : @ num_points * radius * center -> [point] @ ----polygonCoordPath :: Floating u => Int -> u -> LocCoordPath u-polygonCoordPath n radius ctr = unfoldr phi (0,(pi*0.5))- where- theta = (pi*2) / fromIntegral n- - phi (i,ang) | i < n = Just (ctr .+^ avec ang radius, (i+1,ang+theta))- | otherwise = Nothing------ | @isocelesTriangle bw h pt@------ Supplied point is the centriod of the triangle. This has a --- nicer visual balance than using half-height.----isoscelesTriangleCoordPath :: Floating u => u -> u -> LocCoordPath u-isoscelesTriangleCoordPath bw h ctr = [bl,br,top]- where- (bl,br,top) = isoscelesTrianglePoints bw h ctr----- | @isocelesTriangle bw h pt@------ Supplied point is the centriod of the triangle. This has a --- nicer visual balance than using half-height.----isoscelesTrianglePoints :: Floating u - => u -> u -> Point2 u -> (Point2 u, Point2 u, Point2 u)-isoscelesTrianglePoints bw h ctr = (bl, br, top) - where- hw = 0.5*bw - theta = atan $ h / hw- centroid_h = hw * tan (0.5*theta)- top = ctr .+^ vvec (h - centroid_h)- br = ctr .+^ V2 hw (-centroid_h)- bl = ctr .+^ V2 (-hw) (-centroid_h)----- | @ side_length * ctr -> [Points] @----equilateralTriangleCoordPath :: Floating u => u -> LocCoordPath u-equilateralTriangleCoordPath sl ctr = [bl, br, top] - where- (bl,br,top) = equilateralTrianglePoints sl ctr--equilateralTrianglePoints :: Floating u - => u -> Point2 u -> (Point2 u, Point2 u, Point2 u)-equilateralTrianglePoints sl = isoscelesTrianglePoints sl h- where- h = sl * sin (pi/3)-
src/Wumpus/Drawing/Paths.hs view
@@ -3,29 +3,28 @@ -------------------------------------------------------------------------------- -- | -- Module : Wumpus.Drawing.Paths--- Copyright : (c) Stephen Tetley 2010+-- Copyright : (c) Stephen Tetley 2011 -- License : BSD3 -- -- Maintainer : Stephen Tetley <stephen.tetley@gmail.com> -- Stability : highly unstable -- Portability : GHC ----- Shim import module for Paths.+-- Shim import module for the Absolute Path modules.+-- -- -------------------------------------------------------------------------------- -module Wumpus.Drawing.Paths +module Wumpus.Drawing.Paths ( module Wumpus.Drawing.Paths.Base- , module Wumpus.Drawing.Paths.Connectors- , module Wumpus.Drawing.Paths.ControlPoints- , module Wumpus.Drawing.Paths.MonadicConstruction+ , module Wumpus.Drawing.Paths.PathBuilder+ , module Wumpus.Drawing.Paths.Vamps ) where import Wumpus.Drawing.Paths.Base-import Wumpus.Drawing.Paths.Connectors-import Wumpus.Drawing.Paths.ControlPoints-import Wumpus.Drawing.Paths.MonadicConstruction+import Wumpus.Drawing.Paths.PathBuilder+import Wumpus.Drawing.Paths.Vamps
src/Wumpus/Drawing/Paths/Base.hs view
@@ -5,646 +5,1099 @@ -------------------------------------------------------------------------------- -- | -- Module : Wumpus.Drawing.Paths.Base--- Copyright : (c) Stephen Tetley 2010-2011--- License : BSD3------ Maintainer : Stephen Tetley <stephen.tetley@gmail.com>--- Stability : highly unstable--- Portability : GHC------ Extended path type - more amenable for complex drawings than--- the type in Wumpus-Core.------ \*\* WARNING \*\* this module is an experiment, and may --- change significantly or even be dropped from future revisions.--- -----------------------------------------------------------------------------------module Wumpus.Drawing.Paths.Base- ( -- Path- , DPath- , length- , append- , pconcat- , line- , curve- , pivot- , traceLinePoints- , traceCurvePoints- , curveByAngles-- , toPrimPath -- , tipL- , tipR-- , shortenPath- , shortenL- , shortenR- , directionL- , directionR-- , midway- , midway_- , atstart- , atstart_- , atend- , atend_-- , PathViewL(..)- , DPathViewL- , PathViewR(..)- , DPathViewR- , PathSegment(..)- , DPathSegment- , pathViewL- , pathViewR--- , roundTrail- , roundInterior-- ) where---import Wumpus.Drawing.Geometry.Intersection ( langle )---- package: wumpus-basic-import Wumpus.Basic.Utils.JoinList ( JoinList, ViewL(..), viewl- , ViewR(..), viewr, cons, snoc, join )-import qualified Wumpus.Basic.Utils.JoinList as JL--import Wumpus.Core -- package: wumpus-core--import Data.AffineSpace-import Data.VectorSpace--import Data.List ( foldl' ) --import Prelude hiding ( length )--data Path u = Path { _path_length :: u - , _path_start :: Point2 u- , _path_elements :: JoinList (PathSeg u)- , _path_end :: Point2 u- }- deriving (Eq,Show)--type DPath = Path Double---- Annotating each segment with length is \*\* good \*\*.--- Makes it much more efficient to find the midway point.------ But what do we do about the start point:------ a) put it in the segment - too much info in the type, allows --- consistency problems vis-a-vis gaps in the path.------ b) leave it out - too little info in the type, allows --- consistency problems with length.------ Option (a) is probably most convenient espcially as the --- constructors won\'t be exported.---- Annotation is length...--- -data PathSeg u = LineSeg { _line_length :: u - , _line_start :: Point2 u- , _line_end :: Point2 u- }- | CurveSeg { _curve_length :: u - , _curve_start :: Point2 u- , _ctrl_pt_one :: Point2 u- , _ctrl_pt_two :: Point2 u- , _curve_end :: Point2 u- }- deriving (Eq,Show)---type instance DUnit (Path u) = u-type instance DUnit (PathSeg u) = u---infixr 1 `append`--length :: Num u => Path u -> u-length (Path u _ _ _) = u--append :: Floating u => Path u -> Path u -> Path u-append (Path len1 start1 se1 end1) (Path len2 start2 se2 end2) - | end1 == start2 = Path (len1+len2) start1 (se1 `join` se2) end2 - | otherwise = let joint = lineSegment end1 start2- total_len = len1 + len2 + segmentLength joint- in Path total_len start1 (se1 `join` (cons joint se2)) end2 ---- CAUTION - @append@ is using Floating Point equality to see if--- points are equal...---pconcat :: Floating u => Path u -> [Path u] -> Path u-pconcat p0 ps = foldl' append p0 ps--segmentLength :: PathSeg u -> u-segmentLength (LineSeg u _ _) = u-segmentLength (CurveSeg u _ _ _ _) = u---segmentStart :: PathSeg u -> Point2 u-segmentStart (LineSeg _ p0 _) = p0-segmentStart (CurveSeg _ p0 _ _ _) = p0--segmentEnd :: PathSeg u -> Point2 u-segmentEnd (LineSeg _ _ p1) = p1-segmentEnd (CurveSeg _ _ _ _ p3) = p3-----lineSegment :: Floating u => Point2 u -> Point2 u -> PathSeg u -lineSegment p0 p1 = let v = vlength $ pvec p0 p1 in LineSeg v p0 p1---pathZero :: Floating u => Point2 u -> Path u -pathZero p0 = Path 0 p0 JL.empty p0- --line :: Floating u => Point2 u -> Point2 u -> Path u -line p0 p1 = let v = vlength $ pvec p0 p1 - in Path v p0 (JL.one $ LineSeg v p0 p1) p1- --curve :: (Floating u, Ord u)- => Point2 u -> Point2 u -> Point2 u -> Point2 u -> Path u -curve p0 p1 p2 p3 = let v = curveLength p0 p1 p2 p3- in Path v p0 (JL.one $ CurveSeg v p0 p1 p2 p3) p3---- | A draw a /straight line/ of length 0 at the supplied point. ------ This is /might/ be useful in concatenating curved paths--- as it introduces and extra control point.--- -pivot :: Floating u => Point2 u -> Path u -pivot p0 = Path 0 p0 (JL.one $ LineSeg 0 p0 p0) p0----- | 'traceLinePoints' throws a runtime error if the supplied list--- is empty. ----traceLinePoints :: Floating u => [Point2 u] -> Path u-traceLinePoints [] = error "traceLinePoints - empty point list."-traceLinePoints [a] = line a a-traceLinePoints (a:b:xs) = step (line a b) b xs- where- step acc _ [] = acc- step acc e (y:ys) = step (acc `append` line e y) y ys----- | 'traceCurvePoints' consumes 4 points from the list on the --- intial step (start, control1, control2, end) then steps --- through the list taking 3 points at a time thereafter--- (control1,control2, end). Leftover points are discarded. --- --- 'traceCurvePoints' throws a runtime error if the supplied list--- is has less than 4 elements (start, control1, control2, end). ----traceCurvePoints :: (Floating u, Ord u) => [Point2 u] -> Path u-traceCurvePoints (a:b:c:d:xs) = step (curve a b c d) d xs- where- step acc p0 (x:y:z:zs) = step (acc `append` curve p0 x y z) z zs- step acc _ _ = acc--traceCurvePoints _ = error "tracePointsCurve - less than 4 elems."---curveByAngles :: (Floating u, Ord u) - => Point2 u -> Radian -> Radian -> Point2 u -> Path u-curveByAngles start cin cout end = curve start (start .+^ v1) (end .+^ v2) end- where- sz = 0.375 * (vlength $ pvec start end)- v1 = avec cin sz- v2 = avec cout sz------ | Turn a Path into an ordinary PrimPath.------ Assumes path is properly formed - i.e. end point of one --- segment is the same point as the start point of the next--- segment.----toPrimPath :: Num u => Path u -> PrimPath u-toPrimPath (Path _ start segs _) = step1 $ viewl segs- where- step1 EmptyL = emptyPath start- step1 (e :< se) = let (p0,a) = seg1 e in - primPath p0 $ a : step2 (viewl se)-- step2 EmptyL = []- step2 (e :< se) = seg2 e : step2 (viewl se)- - seg1 (LineSeg _ p0 p1) = (p0, lineTo p1)- seg1 (CurveSeg _ p0 p1 p2 p3) = (p0, curveTo p1 p2 p3)- - seg2 (LineSeg _ _ p1) = lineTo p1- seg2 (CurveSeg _ _ p1 p2 p3) = curveTo p1 p2 p3--------------------------------------------------------------------------------------- Curve length--data StrictCurve u = Curve !(Point2 u) !(Point2 u) !(Point2 u) !(Point2 u)--curveLength :: (Floating u, Ord u) - => Point2 u -> Point2 u -> Point2 u -> Point2 u -> u-curveLength p0 p1 p2 p3 = gravesenLength 0.1 $ Curve p0 p1 p2 p3----- | Jens Gravesen\'s bezier arc-length approximation. ------ Note this implementation is parametrized on error tolerance.----gravesenLength :: (Floating u, Ord u) => u -> StrictCurve u -> u-gravesenLength err_tol crv = step crv where- step c = let l1 = ctrlPolyLength c- l0 = cordLength c- in if l1-l0 > err_tol- then let (a,b) = subdivide c in step a + step b- else 0.5*l0 + 0.5*l1---ctrlPolyLength :: Floating u => StrictCurve u -> u-ctrlPolyLength (Curve p0 p1 p2 p3) = len p0 p1 + len p1 p2 + len p2 p3- where- len pa pb = vlength $ pvec pa pb--cordLength :: Floating u => StrictCurve u -> u-cordLength (Curve p0 _ _ p3) = vlength $ pvec p0 p3----- | mid-point between two points----pointMidpoint :: Fractional u => Point2 u -> Point2 u -> Point2 u-pointMidpoint p0 p1 = p0 .+^ v1 ^/ 2 where v1 = p1 .-. p0----- | Curve subdivision via de Casteljau\'s algorithm.----subdivide :: Fractional u - => StrictCurve u -> (StrictCurve u, StrictCurve u)-subdivide (Curve p0 p1 p2 p3) =- (Curve p0 p01 p012 p0123, Curve p0123 p123 p23 p3)- where- p01 = pointMidpoint p0 p1- p12 = pointMidpoint p1 p2- p23 = pointMidpoint p2 p3- p012 = pointMidpoint p01 p12- p123 = pointMidpoint p12 p23- p0123 = pointMidpoint p012 p123---- | subdivide with an affine weight along the line...----subdividet :: Real u- => u -> StrictCurve u -> (StrictCurve u, StrictCurve u)-subdividet t (Curve p0 p1 p2 p3) = - (Curve p0 p01 p012 p0123, Curve p0123 p123 p23 p3)- where- p01 = affineCombination t p0 p1- p12 = affineCombination t p1 p2- p23 = affineCombination t p2 p3- p012 = affineCombination t p01 p12- p123 = affineCombination t p12 p23- p0123 = affineCombination t p012 p123--affineCombination :: Real u => u -> Point2 u -> Point2 u -> Point2 u-affineCombination a p1 p2 = p1 .+^ a *^ (p2 .-. p1)------------------------------------------------------------------------------------- tips --tipL :: Path u -> Point2 u-tipL (Path _ sp _ _) = sp---tipR :: Path u -> Point2 u-tipR (Path _ _ _ ep) = ep------- | 'sortenPath' : @ left_dist * right_dist * path -> Path @----shortenPath :: (Real u , Floating u) => u -> u -> Path u -> Path u-shortenPath l r = shortenL l . shortenR r -------------------------------------------------------------------------------------- shorten from the left...---- | Note - shortening a line from the left by --- greater-than-or-equal its length is operationally equivalent --- to making a zero-length line at the end point.----shortenL :: (Real u, Floating u) => u -> Path u -> Path u-shortenL n (Path u _ segs ep) - | n >= u = line ep ep- | otherwise = step n (viewl segs)- where- step _ EmptyL = line ep ep -- should be unreachable- step d (e :< se) = let z = segmentLength e in- case compare d z of- GT -> step (d-z) (viewl se)- EQ -> makeLeftPath (u-n) se ep- LT -> let e1 = shortenSegL d e- in Path (u-n) (segmentStart e1) (e1 `cons` se) ep---makeLeftPath :: Floating u => u -> JoinList (PathSeg u) -> Point2 u -> Path u-makeLeftPath u se ep = - case viewl se of- EmptyL -> line ep ep- (e :< _) -> Path u (segmentStart e) se ep---shortenSegL :: (Real u, Floating u) => u -> PathSeg u -> PathSeg u-shortenSegL n (LineSeg u p0 p1) = - LineSeg (u-n) (shortenLineL n p0 p1) p1--shortenSegL n (CurveSeg u p0 p1 p2 p3) = - let (Curve p0' p1' p2' p3') = snd $ subdividet (n/u) (Curve p0 p1 p2 p3)- in CurveSeg (u-n) p0' p1' p2' p3'---shortenLineL :: (Real u, Floating u) - => u -> Point2 u -> Point2 u -> Point2 u-shortenLineL n p0 p1 = p0 .+^ v- where- v0 = p1 .-. p0- v = avec (vdirection v0) n--------------------------------------------------------------------------------------- shorten from the right ...- --- | Note - shortening a line from the right by --- greater-than-or-equal its length is operationally equivalent --- to making a zero-length line at the start point.----shortenR :: (Real u, Floating u) => u -> Path u -> Path u-shortenR n (Path u sp segs _) - | n >= u = line sp sp- | otherwise = step n (viewr segs)- where- step _ EmptyR = line sp sp -- should be unreachable- step d (se :> e) = let z = segmentLength e in- case compare d z of- GT -> step (d-z) (viewr se)- EQ -> makeRightPath n sp se- LT -> let e1 = shortenSegR d e- in Path (u-n) sp (se `snoc` e1) (segmentEnd e1)- --makeRightPath :: Floating u => u -> Point2 u -> JoinList (PathSeg u) -> Path u-makeRightPath u sp se = - case viewr se of- EmptyR -> line sp sp- (_ :> e) -> Path u sp se (segmentEnd e)----shortenSegR :: (Real u, Floating u) => u -> PathSeg u -> PathSeg u-shortenSegR n (LineSeg u p0 p1) = - LineSeg (u-n) p0 (shortenLineR n p0 p1) --shortenSegR n (CurveSeg u p0 p1 p2 p3) = - let (Curve p0' p1' p2' p3') = fst $ subdividet ((u-n)/u) (Curve p0 p1 p2 p3)- in CurveSeg (u-n) p0' p1' p2' p3'---shortenLineR :: (Real u, Floating u) - => u -> Point2 u -> Point2 u -> Point2 u-shortenLineR n p0 p1 = p1 .+^ v- where- v0 = p0 .-. p1- v = avec (vdirection v0) n---------------------------------------------------------------------------------------- line direction---- | Direction of empty path is considered to be 0.----directionL :: (Real u, Floating u) => Path u -> Radian-directionL (Path _ _ se _) = step $ viewl se- where- step (LineSeg _ p0 p1 :< _) = lineDirection p1 p0 -- 1-to-0- step (CurveSeg _ p0 p1 _ _ :< _) = lineDirection p1 p0- step _ = 0 -- should be unreachable----- | Direction of empty path is considered to be 0.----directionR :: (Real u, Floating u) => Path u -> Radian-directionR (Path _ _ se _) = step $ viewr se- where- step (_ :> LineSeg _ p0 p1) = lineDirection p0 p1- step (_ :> CurveSeg _ _ _ p2 p3) = lineDirection p2 p3- step _ = 0 -- should be unreachable ------------------------------------------------------------------------------------------ Return direction as well because the calculation is expensive...----midway :: (Real u, Floating u) => Path u -> (Point2 u, Radian)-midway pa@(Path u sp _ _) - | u == 0 = (sp,0)- | otherwise = let pa1 = shortenR (u/2) pa in (tipR pa1, directionR pa1)---- Just the midway point.----midway_ :: (Real u, Floating u) => Path u -> Point2 u-midway_ = fst . midway---atstart :: (Real u, Floating u) => Path u -> (Point2 u, Radian)-atstart pa@(Path _ sp _ _) = (sp, directionL pa)--atstart_ :: Path u -> Point2 u-atstart_ (Path _ sp _ _) = sp---atend :: (Real u, Floating u) => Path u -> (Point2 u, Radian)-atend pa@(Path _ _ _ ep) = (ep, directionR pa)- --atend_ :: Path u -> Point2 u-atend_ (Path _ _ _ ep) = ep----- nearstart, nearend, verynear ...-------------------------------------------------------------------------------------data PathViewL u = PathOneL (PathSegment u)- | PathSegment u :<< Path u- deriving (Eq,Show) --type DPathViewL = PathViewL Double--data PathViewR u = PathOneR (PathSegment u)- | Path u :>> PathSegment u- deriving (Eq,Show) --type DPathViewR = PathViewR Double---data PathSegment u = Line1 (Point2 u) (Point2 u)- | Curve1 (Point2 u) (Point2 u) (Point2 u) (Point2 u)- deriving (Eq,Show) --type DPathSegment = PathSegment Double--type instance DUnit (PathViewL u) = u-type instance DUnit (PathViewR u) = u-type instance DUnit (PathSegment u) = u--pathViewL :: Num u => Path u -> PathViewL u-pathViewL (Path u _ segs ep) = go (viewl segs)- where- go EmptyL = error "pathViewL - (not) unreachable."- - go (LineSeg v p0 p1 :< se)- | JL.null se = PathOneL (Line1 p0 p1)- | otherwise = Line1 p0 p1 :<< Path (u-v) p1 se ep-- go (CurveSeg v p0 p1 p2 p3 :< se) - | JL.null se = PathOneL (Curve1 p0 p1 p2 p3)- | otherwise = Curve1 p0 p1 p2 p3 :<< Path (u-v) p3 se ep---pathViewR :: Num u => Path u -> PathViewR u-pathViewR (Path u _ segs ep) = go (viewr segs)- where- go EmptyR = error "pathViewR - (not) unreachable."-- go (se :> LineSeg v p0 p1) - | JL.null se = PathOneR (Line1 p0 p1)- | otherwise = Path (u-v) p1 se ep :>> Line1 p0 p1-- go (se :> CurveSeg v p0 p1 p2 p3) - | JL.null se = PathOneR (Curve1 p0 p1 p2 p3)- | otherwise = Path (u-v) p3 se ep :>> Curve1 p0 p1 p2 p3---------------------------------------------------------------------------------------- Round corners---- | The length of the control-point vector wants to be slighly --- longer than half of /d/ (d - being the distance between the --- /truncated/ points and the corner).----cornerCurve :: (Real u, Floating u) - => Point2 u -> Point2 u -> Point2 u -> Path u-cornerCurve p1 p2 p3 = curve p1 cp1 cp2 p3- where- len1 = 0.6 * (vlength $ pvec p1 p2)- len2 = 0.6 * (vlength $ pvec p3 p2)- cp1 = p1 .+^ (avec (langle p1 p2) len1)- cp2 = p3 .+^ (avec (langle p3 p2) len2)----- | 'roundTrail' : @ rounding_distance * [point] -> Path @------ Build a path from the list of vertices, all the interior --- corners are rounded by the rounding distance \*and\* final --- round corner is created \"incorporating\" the start point (as --- the start point becomes a rounded corner the actual path will --- not intersect it). --- --- It is expected that this function will be used to create round --- cornered shapes.--- --- 'roundTrail' throws a runtime error if the input list is empty. --- If the list has one element /the null path/ is built, if the --- list has two elements a straight line is built.----roundTrail :: (Real u, Floating u) - => u -> [Point2 u] -> Path u -roundTrail _ [] = error "roundTrail - empty list."-roundTrail _ [a] = pathZero a-roundTrail _ [a,b] = line a b-roundTrail u (start:b:c:xs) = step (lineCurveTrail u start b c) (b:c:xs)- where- step acc (m:n:o:ps) = step (acc `append` lineCurveTrail u m n o) (n:o:ps)- step acc [n,o] = acc `append` lineCurveTrail u n o start- `append` lineCurveTrail u o start b - step acc _ = acc------ | Two parts - line and corner curve...------ Note - the starting point is moved, this function is for --- closed, rounded paths.----lineCurveTrail :: (Real u, Floating u) - => u -> Point2 u -> Point2 u -> Point2 u -> Path u-lineCurveTrail u a b c = line p1 p2 `append` cornerCurve p2 b p3- where- p1 = a .+^ (avec (vdirection $ pvec a b) u)- p2 = b .+^ (avec (vdirection $ pvec b a) u)- p3 = b .+^ (avec (vdirection $ pvec b c) u)----- | 'roundInterior' : @ rounding_distance * [point] -> Path @------ Build a path from the list of vertices, all the interior --- corners are rounded by the rounding distance. Unlike --- 'roundTrail' there is no /loop around/ to the start point,--- and start path will begin exactly on the start point and end --- exactly on the end point. --- --- 'roundInterior' throws a runtime error if the input list is --- empty. If the list has one element /the null path/ is built, --- if the list has two elements a straight line is built.----roundInterior :: (Real u, Floating u) - => u -> [Point2 u] -> Path u -roundInterior _ [] = error "roundEveryInterior - empty list."-roundInterior _ [a] = pathZero a-roundInterior _ [a,b] = line a b-roundInterior u (start:b:c:xs) = let (path1,p1) = lineCurveInter1 u start b c- in step path1 (p1:c:xs)- where- step acc (m:n:o:ps) = let (seg2,p1) = lineCurveInter1 u m n o- in step (acc `append` seg2) (p1:o:ps)- step acc [n,o] = acc `append` line n o- step acc _ = acc------ | Two parts - line and corner curve...------ Note - draws a straight line from the starting point - this is --- the first step of an interior (non-closed) rounded path----lineCurveInter1 :: (Real u, Floating u) - => u -> Point2 u -> Point2 u -> Point2 u -> (Path u, Point2 u)-lineCurveInter1 u a b c = - (line a p2 `append` cornerCurve p2 b p3, p3)- where- p2 = b .+^ (avec (vdirection $ pvec b a) u)- p3 = b .+^ (avec (vdirection $ pvec b c) u)- +-- Copyright : (c) Stephen Tetley 2010-2012+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Absolute path type - this should be more amenable for building +-- complex drawings than the PrimPath type in Wumpus-Core.+--+-- Note - there is no concatenation (i.e. no Monoid instance),+-- this is because concatenating \*\* absolute \*\* paths has no +-- obvious interpretation - draw a join between the paths, move+-- the second path to start where the first ends...+--+-- Use @CatTrail@ from Wumpus-Basic if you need a relative-path +-- like object that supports concatenation, then convert it in a +-- final step to an @AbsPath@.+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Paths.Base+ ( ++ -- * Absolute path type+ AbsPath+ , DAbsPath+++ -- * Construction+ , emptyPath+ , line1+ , curve1+ , vertexPath+ , curvePath+ , controlCurve++ , vectorPath+ , vectorPathTheta++ , anaTrailPath+ , catTrailPath++ -- * Queries+ , null+ , length++ -- * Concat and extension+ , snocLine+ , snocLineTo+ , snocCurve+ , snocCurveTo+++ -- * Conversion+ , toPrimPath++ , renderPath+ , renderPath_++ -- * Shortening+ , shortenPath+ , shortenL+ , shortenR++ -- * Tips and inclination+ , tipL+ , tipR+ , inclinationL+ , inclinationR++ , isBezierL+ , isBezierR+++ -- * Path anchors+ , midway+ , midway_+ , atstart+ , atstart_+ , atend+ , atend_+++ -- * Views+ , PathViewL(..)+ , DPathViewL+ , PathViewR(..)+ , DPathViewR+ , PathSegment(..)+ , DPathSegment+ , pathViewL+ , pathViewR++ , optimizeLines++ , roundExterior+ , roundInterior++ , deBezier+ , pathMajorPoints+ , pathAllPoints++ -- * Path division+ , pathdiv+++ ) where++import Wumpus.Drawing.Basis.BezierCurve++import Wumpus.Basic.Kernel+import Wumpus.Basic.Utils.JoinList ( JoinList, ViewL(..), viewl+ , ViewR(..), viewr, cons, snoc )+import qualified Wumpus.Basic.Utils.JoinList as JL++import Wumpus.Core -- package: wumpus-core++import Data.AffineSpace -- package: vector-space+import Data.VectorSpace++import Data.Monoid+import qualified Data.Traversable as T++import Prelude hiding ( null, length )++--+-- Design Note+-- +-- Wumpus has no relative path type. An AbsPath is expected to be +-- an /answer/ from some function like an anchor point or a +-- bounding box.+--+-- Wumpus didn\'t always work on this premise, some of the code +-- below may contradict this...+--++++++-- | Absolute path data type.+--+data AbsPath u = AbsPath + { _abs_path_length :: u + , _abs_path_start :: Point2 u+ , _abs_path_elements :: JoinList (AbsPathSeg u)+ , _abs_path_end :: Point2 u+ }+ deriving (Eq,Show)++type instance DUnit (AbsPath u) = u+++type DAbsPath = AbsPath Double++-- Annotating each segment with length is \*\* good \*\*.+-- Makes it much more efficient to find the midway point.+--+-- But what do we do about the start point:+--+-- a) put it in the segment - too much info in the type, allows +-- consistency problems vis-a-vis gaps in the path.+--+-- b) leave it out - too little info in the type, allows +-- consistency problems with length.+--+-- Option (a) is probably most convenient espcially as the +-- constructors won\'t be exported.++-- Annotation is length...+-- +data AbsPathSeg u = AbsLineSeg u (Vec2 u)+ | AbsCurveSeg u (Vec2 u) (Vec2 u) (Vec2 u)+ deriving (Eq,Show)+++type instance DUnit (AbsPathSeg u) = u++-- +-- DESIGN NOTE+--+-- No monoid instance. AbsPaths dont support empty or even concat +-- naturally.+--+-- Concat is troublesome because AbsPaths are always located +-- within a frame.+--+-- They can support:+--+-- a. Concat with a join between the end of the first path and the+-- start of the second.+-- +-- b. Shape preserving concat - the second path is moved so it +-- continues from the end point of the first path.+--+-- It is conceptually simpler to think about extension (adding to +-- the path tip) rather than concatenation. +--++--------------------------------------------------------------------------------++instance Functor AbsPath where+ fmap f (AbsPath u sp ls ep) = + AbsPath (f u) (fmap f sp) (fmap (fmap f) ls) (fmap f ep)++instance Functor AbsPathSeg where+ fmap f (AbsLineSeg u v1) = + AbsLineSeg (f u) (fmap f v1)++ fmap f (AbsCurveSeg u v1 v2 v3) = + AbsCurveSeg (f u) (fmap f v1) (fmap f v2) (fmap f v3)++++--------------------------------------------------------------------------------+-- Translate+++-- | AbsPathSegments are build from vectors so they do not+-- respond to translation.+--+instance Num u => Translate (AbsPathSeg u) where+ translate _ _ s1 = s1+++instance (Floating u, Ord u, Tolerance u) => Scale (AbsPathSeg u) where+ scale sx sy (AbsLineSeg _ v1) = absLineSeg $ scale sx sy v1+ scale sx sy (AbsCurveSeg _ v1 v2 v3) = + absCurveSeg (scale sx sy v1) (scale sx sy v2) (scale sx sy v3)++++-- | Translate is cheap on AbsPath it just moves the start and+-- end points. The path itself is otherwise built from vectors+-- so it doesn\'t respond to translation (translate == id).+--+instance Num u => Translate (AbsPath u) where+ translate x y (AbsPath len sp se ep) = + AbsPath len (translate x y sp) se (translate x y ep)++++-- | This is expensive on paths - needs a traversal.+--+instance (Floating u, Ord u, Tolerance u) => Scale (AbsPath u) where+ scale sx sy = rebuildPath (scale sx sy) (scale sx sy)+ (\v1 v2 v3 -> ( scale sx sy v1+ , scale sx sy v2+ , scale sx sy v3 ))+++-- | This is expensive on paths - needs a traversal.+--+instance (Real u, Floating u, Ord u, Tolerance u) => Rotate (AbsPath u) where+ rotate ang = rebuildPath (rotate ang) (rotate ang)+ (\v1 v2 v3 -> ( rotate ang v1+ , rotate ang v2+ , rotate ang v3 ))++-- | This is expensive on paths - needs a traversal.+--+instance (Real u, Floating u, Ord u, Tolerance u) => RotateAbout (AbsPath u) where+ rotateAbout ang pt = + rebuildPath (rotateAbout ang pt) (rotateAbout ang pt)+ (\v1 v2 v3 -> ( rotateAbout ang pt v1+ , rotateAbout ang pt v2+ , rotateAbout ang pt v3 ))+++++rebuildPath :: (Floating u, Ord u, Tolerance u) + => (Point2 u -> Point2 u) + -> (Vec2 u -> Vec2 u) + -> (Vec2 u -> Vec2 u -> Vec2 u -> (Vec2 u, Vec2 u, Vec2 u))+ -> AbsPath u + -> AbsPath u+rebuildPath pointf linef curvef (AbsPath _ sp segs _) = + step (emptyPath $ pointf sp) (viewl segs)+ where+ step ac EmptyL = ac++ step ac (AbsLineSeg _ v1 :< xs) = + step (snocLine ac $ linef v1) (viewl xs) ++ step ac (AbsCurveSeg _ v1 v2 v3 :< xs) = + step (snocCurve ac $ curvef v1 v2 v3) (viewl xs) ++--------------------------------------------------------------------------------+-- Construction++absLineSeg :: Floating u => Vec2 u -> AbsPathSeg u+absLineSeg v1 = AbsLineSeg (vlength v1) v1++absCurveSeg :: (Floating u, Ord u, Tolerance u) + => Vec2 u -> Vec2 u -> Vec2 u -> AbsPathSeg u+absCurveSeg v1 v2 v3 = + AbsCurveSeg (bezierLength $ vbezierCurve v1 v2 v3 zeroPt) v1 v2 v3++-- | Create the empty path.+-- +-- Note - an absolute path needs /locating/ and cannot be built +-- without a start point. Figuratively, the empty path is a path+-- from the start point to the end point.+--+-- Thus AbsPath operates as a semigroup but not a monoid.+--+emptyPath :: Floating u => Point2 u -> AbsPath u+emptyPath = zeroPath+++-- | Create an absolute path as a straight line between the +-- supplied points.+--+line1 :: Floating u => Point2 u -> Point2 u -> AbsPath u +line1 p0 p1 = AbsPath len p0 (JL.one s1) p1+ where+ s1@(AbsLineSeg len _) = absLineSeg $ pvec p0 p1++-- | Create an absolute path from a single cubic Bezier curve.+--+curve1 :: (Floating u, Ord u, Tolerance u)+ => Point2 u -> Point2 u -> Point2 u -> Point2 u -> AbsPath u +curve1 p0 p1 p2 p3 = + AbsPath len p0 (JL.one $ AbsCurveSeg len v1 v2 v3) p3+ where+ v1 = pvec p0 p1+ v2 = pvec p1 p2+ v3 = pvec p2 p3+ len = bezierLength (BezierCurve p0 p1 p2 p3)+++-- | 'vertexPath' throws a runtime error if the supplied list+-- is empty. +--+vertexPath :: (Floating u, Ord u, Tolerance u) + => [Point2 u] -> AbsPath u+vertexPath [] = error "traceLinePoints - empty point list."+vertexPath [a] = line1 a a+vertexPath (a:b:xs) = step (line1 a b) xs+ where+ step acc [] = acc+ step acc (y:ys) = step (snocLineTo acc y) ys++++-- | 'curvePath' consumes 4 points from the list on the +-- intial step (start, control1, control2, end) then steps +-- through the list taking 3 points at a time thereafter+-- (control1,control2, end). Leftover points are discarded. +-- +-- 'curvePath' throws a runtime error if the supplied list+-- is has less than 4 elements (start, control1, control2, end). +--+curvePath :: (Floating u, Ord u, Tolerance u) + => [Point2 u] -> AbsPath u+curvePath (a:b:c:d:xs) = step (curve1 a b c d) xs+ where+ step acc (x:y:z:zs) = step (snocCurveTo acc (x,y,z)) zs+ step acc _ = acc++curvePath _ = error "curvePath - less than 4 elems."++++-- NOTE - need a proper arc path builder.++++-- | This is not an arc...+-- +controlCurve :: (Floating u, Ord u, Tolerance u) + => Point2 u -> Radian -> Radian -> Point2 u -> AbsPath u+controlCurve start cin cout end = + curve1 start (start .+^ v1) (end .+^ v2) end+ where+ sz = 0.375 * (vlength $ pvec start end)+ v1 = avec cin sz+ v2 = avec cout sz++++++vectorPath :: (Floating u, Ord u, Tolerance u) + => [Vec2 u] -> Point2 u -> AbsPath u+vectorPath vecs p0 = vertexPath $ p0 : step p0 vecs+ where+ step _ [] = []+ step pt (v1:vs) = let p1 = pt .+^ v1 in p1 : step p1 vs +++vectorPathTheta :: (Real u, Floating u, Tolerance u) + => [Vec2 u] -> Radian -> Point2 u -> AbsPath u +vectorPathTheta vs ang = vectorPath $ map (rotate ang) vs+++anaTrailPath :: (Floating u, Ord u, Tolerance u) + => Point2 u -> AnaTrail u -> AbsPath u+anaTrailPath pt trl = + let (v1,ss) = destrAnaTrail trl in step (emptyPath $ pt .+^ v1) ss+ where+ step ac [] = ac+ step ac (TLine v1:xs) = step (ac `snocLine` v1) xs+ step ac (TCurve v1 v2 v3:xs) = step (snocCurve ac (v1,v2,v3)) xs+ +catTrailPath :: (Floating u, Ord u, Tolerance u) + => Point2 u -> CatTrail u -> AbsPath u+catTrailPath pt trl = step (emptyPath pt) $ destrCatTrail trl+ where+ step ac [] = ac+ step ac (TLine v1:xs) = step (ac `snocLine` v1) xs+ step ac (TCurve v1 v2 v3:xs) = step (snocCurve ac (v1,v2,v3)) xs+ ++--------------------------------------------------------------------------------+-- Queries++-- | Is the path empty?+--+null :: AbsPath u -> Bool+null = JL.null . _abs_path_elements++-- | Length of the Path.+--+-- Length is the length of the path as it is drawn, it is not a +-- count of the number or path segments.+--+-- Length is cached so this operation is cheap - though this puts+-- a tax on the build operations. +-- +length :: Num u => AbsPath u -> u+length (AbsPath u _ _ _) = u+++--------------------------------------------------------------------------------+-- Extension++infixl 5 `snocLine`+++-- | Extend the path with a straight line segment from the +-- end-point defined by the supplied vector.+--+-- > infixl 5 `snocLine`+--+snocLine :: Floating u => AbsPath u -> Vec2 u -> AbsPath u+snocLine (AbsPath u sp se ep) v1 = + let u1 = vlength v1 + tail_line = AbsLineSeg u1 v1+ in AbsPath (u + u1) sp (JL.snoc se tail_line) (ep .+^ v1)++++infixl 5 `snocLineTo`+++-- | Extend the path with a straight line segment from the +-- end-point to the supplied point.+--+-- > infixl 5 `snocLineTo`+--+snocLineTo :: Floating u => AbsPath u -> Point2 u -> AbsPath u+snocLineTo (AbsPath u sp se1 ep) p1 = AbsPath (u + len) sp (snoc se1 s1) p1+ where+ s1@(AbsLineSeg len _) = lineSegment ep p1+++infixl 5 `snocCurve`++-- | Extend the path from the end-point with a Bezier curve +-- segment formed by the supplied points.+--+-- > infixl 5 `snocCurve`+-- +snocCurve :: (Floating u, Ord u, Tolerance u)+ => AbsPath u -> (Vec2 u, Vec2 u, Vec2 u) -> AbsPath u+snocCurve absp@(AbsPath _ _ _ ep) (v1,v2,v3) = snocCurveTo absp (p1,p2,p3)+ where+ p1 = ep .+^ v1+ p2 = p1 .+^ v2+ p3 = p2 .+^ v3+ ++infixl 5 `snocCurveTo`+++-- | Extend the path from the end-point with a Bezier curve +-- segment formed by the supplied points.+--+-- > infixl 5 `snocCurveTo`+-- +snocCurveTo :: (Floating u, Ord u, Tolerance u)+ => AbsPath u -> (Point2 u, Point2 u, Point2 u) -> AbsPath u+snocCurveTo (AbsPath u sp se1 ep) (p1,p2,p3) = + AbsPath (u + len) sp (snoc se1 s1) p3+ where+ s1@(AbsCurveSeg len _ _ _) = curveSegment ep p1 p2 p3+ ++-------------------------------------------------------------------------------- ++++segmentLength :: AbsPathSeg u -> u+segmentLength (AbsLineSeg u _) = u+segmentLength (AbsCurveSeg u _ _ _) = u+++segmentVector :: Num u => AbsPathSeg u -> Vec2 u+segmentVector (AbsLineSeg _ v1) = v1+segmentVector (AbsCurveSeg _ v1 v2 v3) = v1 ^+^ v2 ^+^ v3++++-- | Helper - construct a line segment.+-- +lineSegment :: Floating u => Point2 u -> Point2 u -> AbsPathSeg u +lineSegment p0 p1 = lineSegmentV $ pvec p0 p1++-- | Helper - construct a line segment.+-- +lineSegmentV :: Floating u => Vec2 u -> AbsPathSeg u +lineSegmentV v1 = AbsLineSeg (vlength v1) v1+++-- | Helper - construct a curve segment.+-- +curveSegment :: (Floating u, Ord u, Tolerance u) + => Point2 u -> Point2 u -> Point2 u -> Point2 u -> AbsPathSeg u +curveSegment p0 p1 p2 p3 = AbsCurveSeg len v1 v2 v3+ where+ len = bezierLength (BezierCurve p0 p1 p2 p3)+ v1 = pvec p0 p1+ v2 = pvec p1 p2+ v3 = pvec p2 p3+++-- | Helper - construct the /empty/ but located path.+-- +zeroPath :: Floating u => Point2 u -> AbsPath u +zeroPath p0 = AbsPath 0 p0 JL.empty p0+ +++renderPath :: InterpretUnit u + => PathMode -> AbsPath u -> Image u (AbsPath u)+renderPath mode rp = replaceAns rp $ + liftQuery (toPrimPath rp) >>= dcPath mode+++renderPath_ :: InterpretUnit u + => PathMode -> AbsPath u -> Graphic u+renderPath_ mode rp = liftQuery (toPrimPath rp) >>= dcPath mode+++-- | Turn a Path into an ordinary PrimPath.+--+-- Assumes path is properly formed - i.e. end point of one +-- segment is the same point as the start point of the next+-- segment.+--+toPrimPath :: InterpretUnit u => AbsPath u -> Query u PrimPath+toPrimPath (AbsPath _ start segs _) = + uconvertCtxF start >>= \dstart -> + T.mapM uconvertCtxF segs >>= \dsegs ->+ return $ step1 dstart dsegs+ where+ step1 p0 se | JL.null se = emptyPrimPath p0+ | otherwise = absPrimPath p0 $ step2 p0 (viewl se)++ step2 _ EmptyL = []+ step2 pt (e :< se) = let (p1,s) = mkSeg pt e+ in s : step2 p1 (viewl se)+ + mkSeg p0 (AbsLineSeg _ v1) = let p1 = p0 .+^ v1+ in (p1, absLineTo p1)+ mkSeg p0 (AbsCurveSeg _ v1 v2 v3) = let p1 = p0 .+^ v1+ p2 = p1 .+^ v2+ p3 = p2 .+^ v3+ in (p3, absCurveTo p1 p2 p3)+++++++-- | 'sortenPath' : @ left_dist * right_dist * path -> Path @+--+shortenPath :: (Real u , Floating u) + => u -> u -> AbsPath u -> AbsPath u+shortenPath l r = shortenL l . shortenR r +++--------------------------------------------------------------------------------+-- shorten from the left...++-- | Note - shortening a line from the left by +-- greater-than-or-equal its length is operationally equivalent +-- to making a zero-length line at the end point.+--+shortenL :: (Real u, Floating u) => u -> AbsPath u -> AbsPath u+shortenL n path1@(AbsPath u startp segs ep) + | n < 0 = path1+ | n >= u = AbsPath 0 ep mempty ep + | otherwise = step n startp (viewl segs)+ where+ step _ _ EmptyL = AbsPath 0 ep mempty ep + step d sp (e :< se) = let z = segmentLength e+ snext = sp .+^ segmentVector e+ in case compare d z of+ GT -> step (d-z) snext (viewl se)+ EQ -> makeLeftPath (u-n) snext se ep+ LT -> let (spart,e1) = shortenSegL d sp e+ in AbsPath (u-n) spart (e1 `cons` se) ep+++makeLeftPath :: Floating u + => u -> Point2 u -> JoinList (AbsPathSeg u) -> Point2 u + -> AbsPath u+makeLeftPath u sp se ep | JL.null se = line1 sp ep+ | otherwise = AbsPath u sp se ep++++shortenSegL :: (Real u, Floating u) + => u -> Point2 u -> AbsPathSeg u -> (Point2 u, AbsPathSeg u)+shortenSegL n sp (AbsLineSeg u v1) = + let v2 = shortenVec n v1 + sp' = sp .+^ avec (vdirection v1) n+ in (sp', AbsLineSeg (u-n) v2)++shortenSegL n sp (AbsCurveSeg u v1 v2 v3) = + (q0, AbsCurveSeg (u-n) (pvec q0 q1) (pvec q1 q2) (pvec q2 q3))+ where+ (BezierCurve q0 q1 q2 q3) = let p1 = sp .+^ v1+ p2 = p1 .+^ v2+ p3 = p2 .+^ v3+ in snd $ subdividet (n/u) + (BezierCurve sp p1 p2 p3)+ +++shortenVec :: (Real u, Floating u) => u -> Vec2 u -> Vec2 u+shortenVec n v0 = v0 ^-^ v+ where+ v = avec (vdirection v0) n+++--------------------------------------------------------------------------------+-- shorten from the right ...+ +-- | Note - shortening a line from the right by +-- greater-than-or-equal its length is operationally equivalent +-- to making a zero-length line at the start point.+--+shortenR :: (Real u, Floating u) => u -> AbsPath u -> AbsPath u+shortenR n path1@(AbsPath u sp segs endpt) + | n < 0 = path1+ | n >= u = AbsPath 0 sp mempty sp + | otherwise = step n (viewr segs) endpt+ where+ step _ EmptyR _ = AbsPath 0 sp mempty sp + step d (se :> e) ep = let z = segmentLength e + enext = ep .-^ segmentVector e+ in case compare d z of+ GT -> step (d-z) (viewr se) enext+ EQ -> makeRightPath n sp se enext+ LT -> let (e1,epart) = shortenSegR d e ep+ in AbsPath (u-n) sp (se `snoc` e1) epart+ ++makeRightPath :: Floating u + => u -> Point2 u -> JoinList (AbsPathSeg u) -> Point2 u + -> AbsPath u+makeRightPath u sp se ep | JL.null se = line1 sp ep+ | otherwise = AbsPath u sp se ep+++shortenSegR :: (Real u, Floating u) + => u -> AbsPathSeg u -> Point2 u -> (AbsPathSeg u, Point2 u)+shortenSegR n (AbsLineSeg u v1) ep = + let v2 = shortenVec n v1 + ep' = ep .-^ avec (vdirection v1) n+ in (AbsLineSeg (u-n) v2, ep')++shortenSegR n (AbsCurveSeg u v1 v2 v3) ep = + (AbsCurveSeg (u-n) (pvec q0 q1) (pvec q1 q2) (pvec q2 q3), q3)+ where+ (BezierCurve q0 q1 q2 q3) = let p2 = ep .-^ v3+ p1 = p2 .-^ v2+ p0 = p1 .-^ v1+ in fst $ subdividet ((u-n)/u) + (BezierCurve p0 p1 p2 ep)+ +++++--------------------------------------------------------------------------------+-- tips ++tipL :: AbsPath u -> Point2 u+tipL (AbsPath _ sp _ _) = sp+++tipR :: AbsPath u -> Point2 u+tipR (AbsPath _ _ _ ep) = ep+++--------------------------------------------------------------------------------+-- line direction++++-- | Direction of empty path is considered to be 0.+--+inclinationL :: (Real u, Floating u) => AbsPath u -> Radian+inclinationL (AbsPath _ _ se _) = step $ viewl se+ where+ step (AbsLineSeg _ v1 :< _) = vdirection v1+ step (AbsCurveSeg _ v1 _ _ :< _) = vdirection v1+ step _ = 0+++-- | Direction of empty path is considered to be 0.+--+inclinationR :: (Real u, Floating u) => AbsPath u -> Radian+inclinationR (AbsPath _ _ se _) = step $ viewr se+ where+ step (_ :> AbsLineSeg _ v1) = vdirection v1+ step (_ :> AbsCurveSeg _ _ _ v3) = vdirection v3+ step _ = 0+++ +-- | Is the left tip a Bezier curve?+--+isBezierL :: AbsPath u -> Bool+isBezierL (AbsPath _ _ se _) = step $ viewl se+ where+ step (AbsCurveSeg _ _ _ _ :< _) = True + step _ = False+++ +-- | Is the right tip a Bezier curve?+-- +isBezierR :: AbsPath u -> Bool+isBezierR (AbsPath _ _ se _) = step $ viewr se+ where+ step (_ :> AbsCurveSeg _ _ _ _) = True + step _ = False+++--------------------------------------------------------------------------------+++-- Return direction as well because the calculation is expensive...+--+midway :: (Real u, Floating u) => AbsPath u -> (Point2 u, Radian)+midway pa@(AbsPath u sp _ _) + | u == 0 = (sp,0)+ | otherwise = let pa1 = shortenR (u/2) pa in (tipR pa1, inclinationR pa1)++-- Just the midway point.+--+midway_ :: (Real u, Floating u) => AbsPath u -> Point2 u+midway_ = fst . midway+++atstart :: (Real u, Floating u) => AbsPath u -> (Point2 u, Radian)+atstart pa@(AbsPath _ sp _ _) = (sp, inclinationL pa)++atstart_ :: AbsPath u -> Point2 u+atstart_ (AbsPath _ sp _ _) = sp+++atend :: (Real u, Floating u) => AbsPath u -> (Point2 u, Radian)+atend pa@(AbsPath _ _ _ ep) = (ep, inclinationR pa)+ ++atend_ :: AbsPath u -> Point2 u+atend_ (AbsPath _ _ _ ep) = ep+++-- nearstart, nearend, verynear ...+++++--------------------------------------------------------------------------------+-- Path Views cf. Data.Sequence+++infixr 5 :<<+infixl 5 :>>++data PathViewL u = EmptyPathL+ | PathSegment u :<< AbsPath u+ deriving (Eq,Show) ++type instance DUnit (PathViewL u) = u ++type DPathViewL = PathViewL Double++data PathViewR u = EmptyPathR+ | AbsPath u :>> PathSegment u+ deriving (Eq,Show) ++type instance DUnit (PathViewR u) = u ++type DPathViewR = PathViewR Double++++-- | PathSegments are annotated with length.+--+--+data PathSegment u = LineSeg u (Point2 u) (Point2 u)+ | CurveSeg u (Point2 u) (Point2 u) (Point2 u) (Point2 u)+ deriving (Eq,Show) ++type instance DUnit (PathSegment u) = u +++type DPathSegment = PathSegment Double+++--------------------------------------------------------------------------------++instance Functor PathSegment where+ fmap f (LineSeg d p0 p1) = LineSeg (f d) (fmap f p0) (fmap f p1)+ fmap f (CurveSeg d p0 p1 p2 p3) = + CurveSeg (f d) (fmap f p0) (fmap f p1) (fmap f p2) (fmap f p3)++instance Functor PathViewL where+ fmap _ EmptyPathL = EmptyPathL+ fmap f (a :<< as) = fmap f a :<< fmap f as++instance Functor PathViewR where+ fmap _ EmptyPathR = EmptyPathR+ fmap f (as :>> a) = fmap f as :>> fmap f a++--------------------------------------------------------------------------------++++pathViewL :: Num u => AbsPath u -> PathViewL u+pathViewL (AbsPath len sp segs ep) = go (viewl segs)+ where+ go EmptyL = EmptyPathL+ + go (AbsLineSeg d v1 :< se) =+ let p1 = sp .+^ v1 in LineSeg d sp p1 :<< AbsPath (len - d) p1 se ep++ go (AbsCurveSeg d v1 v2 v3 :< se) =+ let p1 = sp .+^ v1+ p2 = p1 .+^ v2+ p3 = p2 .+^ v3+ in CurveSeg d sp p1 p2 p3 :<< AbsPath (len - d) p3 se ep+++pathViewR :: Num u => AbsPath u -> PathViewR u+pathViewR (AbsPath len sp segs ep) = go (viewr segs)+ where+ go EmptyR = EmptyPathR ++ go (se :> AbsLineSeg d v1) = + let p0 = ep .-^ v1 in AbsPath (len - d) sp se p0 :>> LineSeg d p0 ep++ go (se :> AbsCurveSeg d v1 v2 v3) = + let p2 = ep .-^ v3+ p1 = p2 .-^ v2+ p0 = p1 .-^ v1+ in AbsPath (len - d) sp se p0 :>> CurveSeg d p0 p1 p2 ep++++--------------------------------------------------------------------------------+++-- Path should be same length afterwards.++optimizeLines :: (Real u, Floating u, Ord u, Tolerance u) + => AbsPath u -> AbsPath u+optimizeLines (AbsPath _ sp0 segs _) =+ outer (zeroPath sp0) (viewl segs)+ where+ outer acc (AbsLineSeg _ v1 :< se) = + inner acc (vdirection v1) v1 (viewl se)++ outer acc (AbsCurveSeg u v1 v2 v3 :< se) = + outer (snocC acc u v1 v2 v3) (viewl se)++ outer acc EmptyL = acc++ inner acc d0 v0 (AbsLineSeg _ v1 :< se) =+ let d1 = vdirection v1+ in if (d0 == vdirection v1) + then inner acc d1 (v0 ^+^ v1) (viewl se)+ else inner (snocV acc v0) d1 v1 (viewl se)++ inner acc _ v0 (AbsCurveSeg u v1 v2 v3 :< se) = + let acc1 = snocC (snocV acc v0) u v1 v2 v3+ in outer acc1 (viewl se)++ inner acc _ v0 EmptyL = snocV acc v0++ snocC (AbsPath u sp se ep) u1 v1 v2 v3 = + let tail_curve = AbsCurveSeg u1 v1 v2 v3 + vtotal = v1 ^+^ v2 ^+^ v3 + in AbsPath (u+u1) sp (JL.snoc se tail_curve) (ep .+^ vtotal)++ snocV (AbsPath u sp se ep) v1 =+ let u1 = vlength v1+ tail_line = AbsLineSeg u1 v1+ in AbsPath (u+u1) sp (JL.snoc se tail_line) (ep .+^ v1)+++--------------------------------------------------------------------------------+-- Round corners+++segToCatTrail :: AbsPathSeg u -> CatTrail u+segToCatTrail (AbsLineSeg _ v1) = catline v1+segToCatTrail (AbsCurveSeg _ v1 v2 v3) = catcurve v1 v2 v3++-- | Because lookahead of 2 makes the rounding algos easier we+-- decons to a list...+--+segmentListL :: AbsPath u -> (Point2 u, [AbsPathSeg u])+segmentListL (AbsPath _ sp se _) = (sp, JL.toList se)++-- | Round interior corners of a Path.+--+-- The path is treated as open - the start of the initial and end+-- of the final segments are not rounded. Only straight line to +-- straight line joins are rounded, joins to or from Bezier +-- curves are not rounded.+-- +-- Caution - all path segments are expected to be longer than+-- 2x the round corner length, though this is not checked..+--+roundInterior :: (Real u, Floating u,Tolerance u) + => u -> AbsPath u -> AbsPath u+roundInterior du pth0 = catTrailPath pt ctrail+ where+ (pt,ss) = segmentListL pth0+ ctrail = roundInteriorCat du ss+++roundInteriorCat :: (Real u, Floating u) => u -> [AbsPathSeg u] -> CatTrail u+roundInteriorCat _ [] = mempty+roundInteriorCat du (z:zs) = step mempty z zs+ where+ step ac (AbsLineSeg _ v1) (AbsLineSeg _ v2:xs) = + let (x,k) = roundAB du v1 v2 in step (ac `mappend` x) k xs++ step ac prev (x:xs) = + let tprev = segToCatTrail prev in step (ac `mappend` tprev) x xs++ step ac prev [] = ac `mappend` segToCatTrail prev++roundAB :: (Real u, Floating u) + => u -> Vec2 u -> Vec2 u -> (CatTrail u, AbsPathSeg u)+roundAB du v1 v2 = + (catline v1' `mappend` tcurve, lineSegmentV v2')+ where+ v1' = shortenVec du v1+ v2' = shortenVec du v2++ tv1 = avec (vdirection v1) du+ tv2 = avec (vdirection v2) du+ base_vec = tv1 ^+^ tv2+ bw = vlength base_vec+ h = sqrt $ pow2 du - (pow2 $ 0.5 * bw)+ clockd = clockDirection v1 v2+ tcurve = triCurve clockd bw (-h) (vdirection base_vec)+ + -- note the (-h) in tricurve is wrong, we need to account + -- for CCW or CW properly...++pow2 :: Num a => a -> a+pow2 x = x ^ (2::Int)++-- Form corners inside a /triangle/ Bezier.+++-- | Round a \"closed\" path. +--+-- Caution - all path sgements are expected to be longer than+-- 2x the round corner length, though this is not checked..+--+roundExterior :: (Real u, Floating u, Tolerance u) + => u -> AbsPath u -> AbsPath u+roundExterior du pth0 + | isBezierR pth0 || isBezierL pth0 = roundInterior du pth0+ | otherwise = catTrailPath pt ctrail+ where+ (pt,ss) = segmentListL pth0+ ctrail = roundExteriorCat du ss+++roundExteriorCat :: (Real u, Floating u) => u -> [AbsPathSeg u] -> CatTrail u+roundExteriorCat _ [] = mempty++roundExteriorCat _ (AbsCurveSeg _ _ _ _ : _) = + error "runExteriorCat - unreachable."++roundExteriorCat du (z@(AbsLineSeg _ v0):zs) = step0 z zs+ where+ step0 (AbsLineSeg _ v1) xs = + let seg0 = lineSegmentV $ shortenVec du v1 in step1 mempty seg0 xs++ step0 _ _ = error "roundExteriorCat - unreachable 1."+ + step1 ac (AbsLineSeg _ v1) (AbsLineSeg _ v2:xs) = + let (x,k) = roundAB du v1 v2 in step1 (ac `mappend` x) k xs++ step1 ac prev (x:xs) = + let tprev = segToCatTrail prev in step1 (ac `mappend` tprev) x xs++ step1 ac (AbsLineSeg _ v1) [] = + let (t,_) = roundAB du v1 v0 in ac `mappend` t++ step1 _ _ [] = + error "roundExteriorCat - unreachable 2."+++-- | Redraw an 'AbsPath' replacing the Bezier curves with three +-- lines along the control vectors.+--+deBezier :: Floating u => AbsPath u -> AbsPath u+deBezier (AbsPath _ sp segs _) = + step (emptyPath sp) (viewl segs)+ where+ step ac EmptyL = ac++ step ac (AbsLineSeg _ v1 :< xs) = + step (ac `snocLine` v1) (viewl xs) ++ step ac (AbsCurveSeg _ v1 v2 v3 :< xs) = + step (ac `snocLine` v1 `snocLine` v2 `snocLine` v3) (viewl xs) ++-- | This does not extract the control points of Bezier curves.+-- +pathMajorPoints :: Num u => AbsPath u -> [Point2 u]+pathMajorPoints (AbsPath _ sp segs _) = sp : step sp (viewl segs)+ where+ step _ EmptyL = []++ step pt (AbsLineSeg _ v1 :< xs) = + let p1 = pt .+^ v1 in p1 : step p1 (viewl xs) ++ step pt (AbsCurveSeg _ v1 v2 v3 :< xs) = + let p1 = pt .+^ v1 in p1 : step (p1 .+^ (v2 ^+^ v3)) (viewl xs) + ++-- | This extracts the control points of Bezier curves.+-- +pathAllPoints :: Num u => AbsPath u -> [Point2 u]+pathAllPoints (AbsPath _ sp segs _) = sp : step sp (viewl segs)+ where+ step _ EmptyL = []+ + step pt (AbsLineSeg _ v1 :< xs) = + let p1 = pt .+^ v1 in p1 : step p1 (viewl xs) ++ step pt (AbsCurveSeg _ v1 v2 v3 :< xs) = + let p1 = pt .+^ v1 + p2 = p1 .+^ v2+ p3 = p2 .+^ v3+ in p1 : p2 : p3 : step p3 (viewl xs) ++++--------------------------------------------------------------------------------+-- Path division+++-- | Divide a path returning intermediate points and direction+--+-- Args are initial-prefix, division size, trailing size.+-- +-- Generation is stopped if the remainder of the path is shorter+-- than the trailing size.+--+pathdiv :: (Real u, Floating u) + => u -> u -> u -> AbsPath u -> [(Point2 u, Radian)]+pathdiv ana sz end = step . shortenL ana + where+ step pth | length pth < end = []+ | otherwise = atstart pth : step (shortenL sz pth)+
− src/Wumpus/Drawing/Paths/Connectors.hs
@@ -1,247 +0,0 @@-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Drawing.Paths.Connectors--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : Stephen Tetley <stephen.tetley@gmail.com>--- Stability : highly unstable--- Portability : GHC------ Library of connector paths...------ \*\* WARNING \*\* this module is experimental and may change --- significantly in future revisions.--- -----------------------------------------------------------------------------------module Wumpus.Drawing.Paths.Connectors- ( -- ConnectorPath- , DConnectorPath- , sconnect-- , connLine-- , connRightVH- , connRightHV- , connRightVHV- , connRightHVH-- , connIsosceles- , connIsosceles2- , connLightningBolt-- , connIsoscelesCurve- , connSquareCurve- , connUSquareCurve-- , connTrapezoidCurve- , connZSquareCurve- , connUZSquareCurve-- ) where--import Wumpus.Drawing.Paths.Base-import Wumpus.Drawing.Paths.ControlPoints--import Wumpus.Basic.Kernel -- package: wumpus-basic-import Wumpus.Core -- package: wumpus-core--import Data.AffineSpace -- package: vector-space--import Control.Applicative-import Prelude hiding ( length )----- | Note - a ConnectorPath is not drawn automatically, it is a--- @ConnectorCF@ not a @ConnectorGraphic@ or @ConnectorImage@.----type ConnectorPath u = ConnectorCF u (Path u)--type DConnectorPath = ConnectorPath Double------ Maybe this should be ConnectorPath u -> ConnectorImage u (Path u) instead?--- This would be closer to the new shapes...----sconnect :: Num u - => ConnectorPath u -> Point2 u -> Point2 u -> Image u (Path u)-sconnect mf p0 p1 = - connect mf p0 p1 >>= \cpath -> - intoImage (pure cpath) (openStroke $ toPrimPath cpath) - ---- | Build the path with interior round corners.--- -roundCornerPath :: (Real u, Floating u, FromPtSize u) - => [Point2 u] -> CF (Path u)-roundCornerPath xs = getRoundCornerSize >>= \sz -> - if sz == 0 then return (traceLinePoints xs) - else return (roundInterior sz xs)---------------------------------------------------------------------------------------- | Connect with a straight line.----connLine :: Floating u => ConnectorPath u-connLine = promoteR2 $ \p0 p1 -> pure $ line p0 p1------ | Right-angled connector - go vertical, then go horizontal.----connRightVH :: (Real u, Floating u, FromPtSize u) => ConnectorPath u-connRightVH = promoteR2 $ \ p0@(P2 x0 _) p1@(P2 _ y1) ->- let mid = P2 x0 y1 in roundCornerPath [p0, mid, p1]------ | Right-angled connector - go horizontal, then go vertical.----connRightHV :: (Real u, Floating u, FromPtSize u) - => ConnectorPath u-connRightHV = promoteR2 $ \ p0@(P2 _ y0) p1@(P2 x1 _) -> - let mid = P2 x1 y0 in roundCornerPath [p0, mid, p1]---- | Right-angled connector - go vertical for the supplied --- distance, go horizontal, go vertical again for the --- remaining distance.--- -connRightVHV :: (Real u, Floating u, FromPtSize u) - => u -> ConnectorPath u-connRightVHV v = promoteR2 $ \ p0@(P2 x0 _) p1@(P2 x1 _) ->- let a0 = p0 .+^ vvec v- a1 = a0 .+^ hvec (x1 - x0)- in roundCornerPath [p0, a0, a1, p1]----- | Right-angled connector - go horizontal for the supplied --- distance, go verical, go horizontal again for the --- remaining distance.--- -connRightHVH :: (Real u, Floating u, FromPtSize u) - => u -> ConnectorPath u-connRightHVH h = promoteR2 $ \ p0@(P2 _ y0) p1@(P2 _ y1) -> - let a0 = p0 .+^ hvec h- a1 = a0 .+^ vvec (y1 - y0)- in roundCornerPath [p0,a0,a1,p1]----- | /Triangular/ joint.--- --- @u@ is the altitude of the triangle.----connIsosceles :: (Real u, Floating u, FromPtSize u) - => u -> ConnectorPath u -connIsosceles dy = promoteR2 $ \ p0 p1 -> - let mid_pt = midpointIsosceles dy p0 p1- in roundCornerPath [p0, mid_pt, p1]- - ------ | Double /triangular/ joint.--- --- @u@ is the altitude of the triangle.----connIsosceles2 :: (Real u, Floating u, FromPtSize u)- => u -> ConnectorPath u -connIsosceles2 u = promoteR2 $ \ p0 p1 -> - let (cp0,cp1) = dblpointIsosceles u p0 p1- in roundCornerPath [ p0, cp0, cp1, p1 ]------ | /Lightning bolt/ joint - a two joint connector with an /axis/--- perpendicular to the connector direction.--- --- @u@ is the half length of the of the axis.----connLightningBolt :: (Real u, Floating u, FromPtSize u) - => u -> ConnectorPath u -connLightningBolt u = promoteR2 $ \ p0 p1 -> - let cp0 = midpointIsosceles u p0 p1- cp1 = midpointIsosceles (-u) p0 p1- in roundCornerPath [ p0, cp0, cp1, p1 ]---------------------------------------------------------------------------------------- | Form a curve inside an isosceles triangle. ------ The two Bezier control points take the same point - the--- altitude of the triangle. The curve tends to be quite shallow--- relative to the altitude.------ @u@ is the altitude of the triangle.----connIsoscelesCurve :: (Real u, Floating u) => u -> ConnectorPath u -connIsoscelesCurve u = promoteR2 $ \ p0 p1 ->- let control_pt = midpointIsosceles u p0 p1- in pure $ traceCurvePoints [p0, control_pt, control_pt, p1]- - ---- | Form a curve inside a square. ------ The two Bezier control points take the /top/ corners. The--- curve tends to be very deep.--- -connSquareCurve :: (Real u, Floating u) => ConnectorPath u -connSquareCurve = promoteR2 $ \ p0 p1 ->- let (cp0,cp1) = squareFromBasePoints p0 p1- in pure $ traceCurvePoints [p0, cp0, cp1, p1]------ | Form a curve inside a square. ------ As per 'connSquareCurve' but the curve is drawn /underneath/--- the line formed between the start and end points.--- --- (Underneath is modulo the direction, of course).--- -connUSquareCurve :: (Real u, Floating u) => ConnectorPath u -connUSquareCurve = promoteR2 $ \ p0 p1 -> - let (cp0,cp1) = usquareFromBasePoints p0 p1- in pure $ traceCurvePoints [p0, cp0, cp1, p1]----- | altitude * ratio_to_base ------ Form a curve inside a trapeziod.--- -connTrapezoidCurve :: (Real u, Floating u) => u -> u -> ConnectorPath u -connTrapezoidCurve u ratio_to_base = promoteR2 $ \p0 p1 -> - let (cp0,cp1) = trapezoidFromBasePoints u ratio_to_base p0 p1- in pure $ traceCurvePoints [p0, cp0, cp1, p1]----- | Make a curve within a square, following the corner points as--- a Z.----connZSquareCurve :: (Real u, Floating u) => ConnectorPath u -connZSquareCurve = promoteR2 $ \p0 p1 -> - let (cp0,cp1) = squareFromCornerPoints p0 p1- in pure $ traceCurvePoints [p0,cp0,cp1,p1]---- | Make a curve within a square, following the corner points as--- a Z.------ The order of tracing flips the control points, so this is an--- /underneath/ version of 'connZSquareCurve'.--- -connUZSquareCurve :: (Real u, Floating u) => ConnectorPath u -connUZSquareCurve = promoteR2 $ \ p0 p1 -> - let (cp0,cp1) = squareFromCornerPoints p0 p1 - in pure $ traceCurvePoints [p0,cp1,cp0,p1]-
− src/Wumpus/Drawing/Paths/ControlPoints.hs
@@ -1,189 +0,0 @@-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Drawing.Paths.ControlPoints--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : Stephen Tetley <stephen.tetley@gmail.com>--- Stability : highly unstable--- Portability : GHC------ Collection of point manufacturing functions.------ \*\* WARNING \*\* this module is experimental and may change --- significantly in future revisions.--- -----------------------------------------------------------------------------------module Wumpus.Drawing.Paths.ControlPoints- ( -- midpointIsosceles- , dblpointIsosceles-- , rectangleFromBasePoints- , squareFromBasePoints- , usquareFromBasePoints-- , trapezoidFromBasePoints-- , squareFromCornerPoints-- ) where--import Wumpus.Basic.Kernel--import Wumpus.Core -- package: wumpus-core--import Data.AffineSpace -- package: vector-space------- | 'midpointIsosceles' : --- @ altitude * start_pt * end_pt -> mid_pt @------ Triangular midpoint.--- --- @u@ is the altitude of the triangle - negative values of u --- form the triangle below the line.----midpointIsosceles :: (Real u, Floating u) - => u -> Point2 u -> Point2 u -> Point2 u-midpointIsosceles u p1@(P2 x1 y1) p2@(P2 x2 y2) = - mid_pt .+^ avec perp_ang u- where- mid_pt = P2 (x1 + 0.5*(x2-x1)) (y1 + 0.5*(y2-y1))- perp_ang = (pi*0.5) + vdirection (pvec p1 p2) ------ | 'dblpointIsosceles' : --- @ altitude * start_pt * end_pt * (third_pt, two_thirds_pt) @--- --- Double triangular joint - one joint at a third of the line--- length, the other at two thirds.--- -dblpointIsosceles :: (Real u, Floating u) - => u -> Point2 u -> Point2 u -> (Point2 u, Point2 u) -dblpointIsosceles u p1@(P2 x1 y1) p2@(P2 x2 y2) = - (mid1 .+^ avec perp_ang u, mid2 .-^ avec perp_ang u)- where- mid1 = P2 (x1 + 0.33*(x2-x1)) (y1 + 0.33*(y2-y1))- mid2 = P2 (x1 + 0.66*(x2-x1)) (y1 + 0.66*(y2-y1))- perp_ang = (pi*0.5) + vdirection (pvec p1 p2) ----------------------------------------------------------------------------------------- | 'rectangleFromBasePoints' : --- @ altitude * start_pt * end_pt * (top_left, top_right) @--- --- Control points forming a rectangle. ------ The two manufactured control points form the top corners, --- so the supplied points map as @start_point == bottom_left@ and --- @end_point == bottom_right@.----rectangleFromBasePoints :: (Real u, Floating u) - => u -> Point2 u -> Point2 u -> (Point2 u, Point2 u)-rectangleFromBasePoints u p1 p2 = (cp1, cp2)- where- base_vec = pvec p1 p2- theta = vdirection base_vec- cp1 = displacePerpendicular u theta p1- cp2 = displacePerpendicular u theta p2----- | 'squareFromBasePoints' : --- @ start_pt -> end_pt -> (top_left, top_right) @--- --- Control points forming a square - side_len derived from the --- distance between start and end points.------ The two manufactured control points form the top corners, --- so the supplied points map as @start_point == bottom_left@ and --- @end_point == bottom_right@.----squareFromBasePoints :: (Real u, Floating u) - => Point2 u -> Point2 u -> (Point2 u, Point2 u)-squareFromBasePoints p1 p2 = rectangleFromBasePoints side_len p1 p2- where- side_len = vlength $ pvec p1 p2----- | 'usquareFromBasePoints' : --- @ start_pt -> end_pt -> (bottom_left, bottom_right) @--- --- Control points forming a square - side_len derived from the --- distance between start and end points.------ As per 'squareFromBasePoints' but the square is drawn --- /underneath/ the line formed between the start and end points.--- (Underneath is modulo the direction, of course).------ The two manufactured control points form the /bottom/ corners, --- so the supplied points map as @start_point == top_left@ and --- @end_point == top_right@.--- -usquareFromBasePoints :: (Real u, Floating u) - => Point2 u -> Point2 u -> (Point2 u, Point2 u)-usquareFromBasePoints p1 p2 = rectangleFromBasePoints side_len p1 p2- where- side_len = negate $ vlength $ pvec p1 p2-------- | 'trapezoidFromBasePoints' : --- @ altitude * ratio_to_base * start_pt * end_pt -> (top_left, top_right) @------ Control points form an isosceles trapezoid.------ The two manufactured control points form the top corners, --- so the supplied points map as @start_point == bottom_left@ and --- @end_point == bottom_right@.--- -trapezoidFromBasePoints :: (Real u, Floating u) - => u -> u -> Point2 u -> Point2 u - -> (Point2 u, Point2 u) -trapezoidFromBasePoints u ratio_to_base p1 p2 = (cp1, cp2)- where- base_vec = pvec p1 p2- base_len = vlength base_vec- theta = vdirection base_vec- half_ulen = 0.5 * ratio_to_base * base_len- base_mid = displaceParallel (0.5 * base_len) theta p1- ubase_mid = displacePerpendicular u theta base_mid- cp1 = displaceParallel (-half_ulen) theta ubase_mid- cp2 = displaceParallel half_ulen theta ubase_mid------- | 'squareFromCornerPoints' : --- @ altitude * start_pt * end_pt * (top_left, bottom_right) @--- --- Control points forming a square bisected by the line from --- start_pt to end_pt. ------ The two manufactured control points form the top_left and--- bottom_right corners, so the supplied points map as --- @start_point == bottom_left@ and @end_point == top_right@.----squareFromCornerPoints :: (Real u, Floating u) - => Point2 u -> Point2 u -> (Point2 u, Point2 u) -squareFromCornerPoints p1 p2 = (cp1, cp2)- where- base_vec = pvec p1 p2- half_len = 0.5 * (vlength base_vec)- theta = vdirection base_vec- base_mid = displaceParallel half_len theta p1- cp1 = displacePerpendicular half_len theta base_mid- cp2 = displacePerpendicular (-half_len) theta base_mid-
+ src/Wumpus/Drawing/Paths/Illustrate.hs view
@@ -0,0 +1,61 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Paths.Illustrate+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Illustrate a path - show the construction of its Bezier curves.+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Paths.Illustrate+ ( ++ path_as_control_box+ , path_with_control_points+ + ) where++import Wumpus.Drawing.Paths.Base++import Wumpus.Basic.Kernel++import Wumpus.Core -- package: wumpus-core+import Wumpus.Core.Colour+import Data.Monoid++++grey1 :: RGBi+grey1 = RGBi 200 200 200++-- | Illustrate the control points as a /boxed/ path - Bezier +-- curves are replaced with straight lines spanning the +-- control points.+--+path_as_control_box :: (Floating u, InterpretUnit u) => AbsPath u -> Graphic u+path_as_control_box path1 = pic1 `mappend` pic2+ where+ pic1 = localize (set_line_width 8 . stroke_colour grey1) $+ renderPath_ OSTROKE path1+ pic2 = localize (set_line_width 1 . stroke_colour black) $+ renderPath_ OSTROKE $ deBezier path1+++path_with_control_points :: (Floating u, InterpretUnit u) => AbsPath u -> Graphic u+path_with_control_points path1 = pic1 `mappend` pic2+ where+ pic1 = localize (fill_colour grey1) $+ mconcat $ map (disk1 `at`) $ pathAllPoints path1++ pic2 = localize (set_line_width 1 . stroke_colour black) $+ renderPath_ OSTROKE path1++ disk1 = dcDisk DRAW_FILL 3+
+ src/Wumpus/Drawing/Paths/Intersection.hs view
@@ -0,0 +1,318 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Paths.Intersection+-- Copyright : (c) Stephen Tetley 2011-2012+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Intersection of Paths with (infinite) lines.+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Paths.Intersection+ ( ++ Line(..)+ , inclinedLine+ , vectorLine+ , Ray(..)+ , inclinedRay++ , lineLineIntersection+ , linePathIntersection+ , linePathSegmentIntersection+ , rayPathIntersection+ , rayPathSegmentIntersection++ , rectangleRadialIntersect+ , isoscelesTriangleRadialIntersect++ ) where++import Wumpus.Drawing.Basis.BezierCurve+import Wumpus.Drawing.Basis.ShapeTrails+import Wumpus.Drawing.Paths.Base++import Wumpus.Basic.Kernel -- package: wumpus-basic+import Wumpus.Core -- package: wumpus-core++import Data.AffineSpace -- package: vector-space++--------------------------------------------------------------------------------+++--+-- Private types - LineEquation, 2x2 matrix and bezier curve.+--+-- Although these types are /general/ exposing them just leads +-- to a bloated API.+--+-- If these types are fund to be more useful - they can go into +-- @Drawing.Basis@.+--+++-- | Line in equational form, i.e. @Ax + By + C = 0@.+--+data LineEquation u = LineEquation + { _line_eqn_A :: !u+ , _line_eqn_B :: !u+ , _line_eqn_C :: !u + }+ deriving (Eq,Show)++type instance DUnit (LineEquation u) = u+++-- | 'lineEquation' : @ point1 * point2 -> LineEquation @+-- +-- Construct a line in equational form bisecting the supplied +-- points.+--+lineEquation :: Num u => Point2 u -> Point2 u -> LineEquation u+lineEquation (P2 x1 y1) (P2 x2 y2) = LineEquation a b c + where+ a = y1 - y2+ b = x2 - x1+ c = (x1*y2) - (x2*y1)++-- | 2x2 matrix, considered to be in row-major form.+-- +-- > (M2'2 a b+-- > c d)+--+-- ++data Matrix2'2 u = M2'2 !u !u !u !u+ deriving (Eq)++type instance DUnit (Matrix2'2 u) = u+++-- | Determinant of a 2x2 matrix.+--+det2'2 :: Num u => Matrix2'2 u -> u+det2'2 (M2'2 a b c d) = a*d - b*c++++++--------------------------------------------------------------------------------++-- | Infinite line represented by two points.+--+data Line u = Line (Point2 u) (Point2 u)+ deriving (Eq,Show)++type instance DUnit (Line u) = u++++-- | 'inclinedLine' : @ point * ang -> Line @+--+++-- | Make an infinite line passing through the supplied point +-- inclined by @ang@.+--+inclinedLine :: Floating u => Point2 u -> Radian -> Line u+inclinedLine radial_ogin ang = Line radial_ogin (radial_ogin .+^ avec ang 100)++vectorLine :: Num u => Vec2 u -> Point2 u -> Line u+vectorLine v1 p0 = Line p0 (p0 .+^ v1)+++-- | A 'Ray' extends from the first point, through the second to+-- infinity.+--+-- ('Line' extends to infinity in both directions.+--+data Ray u = Ray (Point2 u) (Point2 u) + deriving (Eq,Show)++type instance DUnit (Ray u) = u+++-- | Make an infinite ray starting from the supplied point +-- inclined by @ang@.+--+inclinedRay :: Floating u => Point2 u -> Radian -> Ray u+inclinedRay ray_ogin ang = Ray ray_ogin (ray_ogin .+^ avec ang 100)+ +++++pointOnLineSeg :: (Real u, Floating u, Ord u, Tolerance u) + => Point2 u -> (Point2 u, Point2 u) -> Bool+pointOnLineSeg pt (p0,p1) + | pt == p0 || pt == p1 = True+ | otherwise = + vdirection v1 == vdirection v0 && vlength v1 `tLTE` vlength v0+ where+ v0 = pvec p0 p1+ v1 = pvec p0 pt+ ++-- | 'interLineLine' : @ line1 * line2 -> Maybe Point @+-- +-- Find the intersection of two lines, if there is one. +--+-- Lines are infinite they are represented by points on them, +-- they are not line segments.+--+-- An answer of @Nothing@ may indicate either the lines coincide+-- or the are parallel.+--+lineLineIntersection :: (Fractional u, Ord u, Tolerance u)+ => Line u -> Line u -> Maybe (Point2 u)+lineLineIntersection (Line p1 p2) (Line q1 q2) = + if det_co `tEQ` 0 then Nothing + else Just $ P2 (det_xm / det_co) (det_ym / det_co)+ where+ -- Ax + By + C = 0+ LineEquation a1 b1 c1 = lineEquation p1 p2+ LineEquation a2 b2 c2 = lineEquation q1 q2++ coeffM = M2'2 a1 b1 a2 b2+ det_co = det2'2 coeffM++ xM = M2'2 (negate c1) b1 (negate c2) b2+ det_xm = det2'2 xM++ yM = M2'2 a1 (negate c1) a2 (negate c2)+ det_ym = det2'2 yM+++linePathIntersection :: (Real u, Floating u, Ord u, Tolerance u) + => Line u -> AbsPath u -> Maybe (Point2 u)+linePathIntersection ln = step . pathViewL+ where+ step EmptyPathL = Nothing+ step (a :<< bs) = let ans = linePathSegmentIntersection ln a+ in case ans of+ Nothing -> step (pathViewL bs)+ _ -> ans++linePathSegmentIntersection :: (Real u, Floating u, Ord u, Tolerance u) + => Line u -> PathSegment u -> Maybe (Point2 u)+linePathSegmentIntersection ln1 (LineSeg _ p0 p1) = + mbWithin p0 p1 $ lineLineIntersection ln1 (Line p0 p1)++linePathSegmentIntersection (Line pa pb) (CurveSeg _ p0 p1 p2 p3) = + lineEqnCurveIntersection (lineEquation pa pb) (BezierCurve p0 p1 p2 p3)+++mbWithin :: (Real u, Floating u, Ord u, Tolerance u) + => Point2 u -> Point2 u -> Maybe (Point2 u) -> Maybe (Point2 u)+mbWithin p0 p1 mb = mb >>= \pt -> + if pointOnLineSeg pt (p0,p1) then Just pt else Nothing++lineEqnCurveIntersection :: (Floating u, Ord u, Tolerance u) + => LineEquation u -> BezierCurve u -> Maybe (Point2 u)+lineEqnCurveIntersection eqnline c0 = step c0+ where+ step c = case cut eqnline c of+ Left pt -> Just pt -- cut at start or end+ Right False -> Nothing+ Right True -> let (a,b) = subdivide c+ in case step a of+ Just pt -> Just pt+ Nothing -> step b+++rayPathIntersection :: (Real u, Floating u, Ord u, Tolerance u) + => Ray u -> AbsPath u -> Maybe (Point2 u)+rayPathIntersection ry = step . pathViewL+ where+ step EmptyPathL = Nothing+ step (a :<< bs) = let ans = rayPathSegmentIntersection ry a+ in case ans of+ Nothing -> step (pathViewL bs)+ _ -> ans++rayPathSegmentIntersection :: (Real u, Floating u, Ord u, Tolerance u) + => Ray u -> PathSegment u -> Maybe (Point2 u)+rayPathSegmentIntersection (Ray p0 p1) seg = + test =<< linePathSegmentIntersection (Line p0 p1) seg+ where+ test pt = if vdirection (pvec p0 p1) == vdirection (pvec p0 pt)+ then Just pt else Nothing+ +-- | Is the curve cut by the line? +--+-- The curve might cut at the start or end points - which is good+-- as it saves performing a subdivision, but it makes the return +-- type a bit involved.+--+cut :: (Floating u , Ord u, Tolerance u)+ => LineEquation u -> BezierCurve u -> Either (Point2 u) Bool+cut eqnline (BezierCurve p0 p1 p2 p3) = + if d0 `tEQ` 0 then Left p0 else+ if d3 `tEQ` 0 then Left p3 else+ let ds = [d0,d1,d2,d3] in Right $ not $ all pve ds || all nve ds+ where+ pve = (>= 0)+ nve = (< 0)+ d0 = pointLineDistance p0 eqnline + d1 = pointLineDistance p1 eqnline + d2 = pointLineDistance p2 eqnline + d3 = pointLineDistance p3 eqnline +++++-- | 'pointLineDistance' : @ point -> line -> Distance @+--+-- Find the distance from a point to a line in equational form+-- using this formula:+-- +-- > P(u,v) +-- > L: Ax + By + C = 0+-- >+-- > (A*u) + (B*v) + C +-- > -----------------+-- > sqrt $ (A^2) +(B^2)+--+-- A positive distance indicates the point is above the line, +-- negative indicates below.+--+pointLineDistance :: Floating u => Point2 u -> LineEquation u -> u+pointLineDistance (P2 u v) (LineEquation a b c) = + ((a*u) + (b*v) + c) / base+ where+ base = sqrt $ (a^two) + (b^two)+ two :: Integer+ two = 2+++--------------------------------------------------------------------------------+-- Intersections on common shapes++-- | Answer is vector from center.+--+rectangleRadialIntersect :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> u -> Radian -> Maybe (Vec2 u) +rectangleRadialIntersect w h ang = + fmap (pvec zeroPt) $ rayPathIntersection (inclinedRay zeroPt ang) rp + where+ rp = anaTrailPath zeroPt $ rectangle_trail w h++++-- | Answer is vector from centroid.+--+isoscelesTriangleRadialIntersect :: (Real u, Floating u+ , InterpretUnit u, Tolerance u) + => u -> u -> Radian -> Maybe (Vec2 u) +isoscelesTriangleRadialIntersect bw h ang = + fmap (pvec zeroPt) $ rayPathIntersection (inclinedRay zeroPt ang) rp + where+ rp = anaTrailPath zeroPt $ isosceles_triangle_trail bw h
− src/Wumpus/Drawing/Paths/MonadicConstruction.hs
@@ -1,160 +0,0 @@-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Drawing.Paths.MonadicConstruction--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : Stephen Tetley <stephen.tetley@gmail.com>--- Stability : highly unstable--- Portability : GHC------ Build paths monadically.------ \*\* WARNING \*\* this module is an experiment, and may --- change significantly or even be dropped from future revisions.--- -----------------------------------------------------------------------------------module Wumpus.Drawing.Paths.MonadicConstruction- ( -- PathM- , runPath- , execPath-- , tip-- , lineto- , rlineto- , hline- , vline-- , bezierto- , curveto-- , verticalHorizontal- , horizontalVertical-- ) where--import Wumpus.Basic.Utils.HList-import Wumpus.Drawing.Paths.Base--import Wumpus.Core -- package: wumpus-core--import Data.AffineSpace -- package: vector-space--import Control.Applicative-import Data.List----- Are connectors and paths quite different things?------ It looks like they are - connectors always know start and end --- points.-------- State monad version is quite good - it ameliorates the problem--- of joining to the end point of an empty path...--data St u = St- { current_point :: Point2 u - , path_acc :: H (Path u)- }---newtype PathM u a = PathM { getPathM :: St u -> (a,St u) }---instance Functor (PathM u) where- fmap f mf = PathM $ \s -> let (a,s1) = getPathM mf s in (f a,s1)---instance Applicative (PathM u) where- pure a = PathM $ \s -> (a,s)- mf <*> ma = PathM $ \s -> let (f,s1) = getPathM mf s- (a,s2) = getPathM ma s1- in (f a,s2)--instance Monad (PathM u) where- return a = PathM $ \s -> (a,s)- m >>= k = PathM $ \s -> let (a,s1) = getPathM m s in- (getPathM . k) a s1------ Running the path is (probably) agnostic to the DrawingCtx.----runPath :: Floating u => Point2 u -> PathM u a -> (a, Path u)-runPath start mf = - let (a,s') = getPathM mf s in (a, post $ toListH $ path_acc s')- where- s = St { current_point = start- , path_acc = emptyH- }- post [] = line start start- post (x:xs) = foldl' append x xs --execPath :: Floating u => Point2 u -> PathM u a -> Path u-execPath start mf = snd $ runPath start mf--snocline :: Floating u => Vec2 u -> PathM u ()-snocline v = PathM $ \(St pt ac) -> let ep = pt .+^ v - in ((), St ep (ac `snocH` line pt ep))---tip :: PathM u (Point2 u)-tip = PathM $ \s -> (current_point s,s)---lineto :: Floating u => Point2 u -> PathM u ()-lineto pt = PathM $ \(St p0 ac) -> ((), St pt (ac `snocH` line p0 pt))--rlineto :: Floating u => Vec2 u -> PathM u ()-rlineto (V2 dx dy) = tip >>= \(P2 x y) -> lineto (P2 (x+dx) (y+dy))---hline :: Floating u => u -> PathM u ()-hline len = snocline (hvec len) --vline :: Floating u => u -> PathM u ()-vline len = snocline (vvec len) ----bezierto :: (Floating u, Ord u) - => Point2 u -> Point2 u -> Point2 u -> PathM u ()-bezierto c1 c2 ep = PathM $ \(St p0 ac) -> - ((), St ep (ac `snocH` curve p0 c1 c2 ep))-----------curveto :: (Floating u, Ord u) - => Radian -> Radian -> Point2 u -> PathM u ()-curveto cin cout end = PathM $ \(St p0 ac) -> - let seg = curveByAngles p0 cin cout end - ac1 = ac `snocH` seg- end1 = tipR seg- in ((), St end1 ac1) -----verticalHorizontal :: Floating u => Point2 u -> PathM u ()-verticalHorizontal (P2 x y) = - tip >>= \(P2 x0 _) -> lineto (P2 x0 y) >> lineto (P2 x y)--horizontalVertical :: Floating u => Point2 u -> PathM u ()-horizontalVertical (P2 x y) = - tip >>= \(P2 _ y0) -> lineto (P2 x y0) >> lineto (P2 x y)-
+ src/Wumpus/Drawing/Paths/PathBuilder.hs view
@@ -0,0 +1,470 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Paths.PathBuilder+-- Copyright : (c) Stephen Tetley 2011-2012+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Build relative paths monadically.+--+-- \*\* WARNING \*\* this module is an experiment, and may +-- change significantly or even be dropped from future revisions.+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Paths.PathBuilder+ ( +++ GenPathSpec+ , PathSpec+ , Vamp(..)+++ , runGenPathSpec+ , execGenPathSpec+ , evalGenPathSpec+ , stripGenPathSpec++ , runPathSpec+ , runPathSpec_++ , runPivot+++ , penline+ , pencurve+ + + , breakPath+ , hpenline+ , vpenline+ , apenline++ , penlines+ , pathmoves++ , vamp+ , cycleSubPath+ , updatePen+ + ) where++import Wumpus.Drawing.Paths.Base++import Wumpus.Basic.Kernel -- package: wumpus-basic++import Wumpus.Core -- package: wumpus-core+++import Data.AffineSpace -- package: vector-space+import Data.VectorSpace++import Control.Applicative+import Control.Monad+import Data.Monoid+import Prelude hiding ( null, cycle, lines )++-- +-- TODO - possibly we need two drawing contexts one for the pen +-- and one for the decoration trace.+-- +-- Alternatively PathSt should have a local DrawingContext for the +-- pen.+--+++-- | Note - a path spec has an immutable start point like +-- @LocDrawing@.+--+-- Effectively a path is draw in a local coordinate system with +-- @(0,0)@ as the origin.+--+newtype GenPathSpec st u a = GenPathSpec { + getGenPathSpec :: DrawingContext -> PathSt st -> (a, PathSt st, CatPrim) }++type instance DUnit (GenPathSpec st u a) = u+type instance UState (GenPathSpec st u a) = st++type PathSpec u a = GenPathSpec () u a+++data PathSt st = PathSt+ { st_active_pen :: ActivePen+ , st_pen_ctx :: DrawingContext+ , st_cumulative_path :: AbsPath Double+ , st_user_state :: st+ }+++-- | Note - this formulation doesn\'t support monoidal append.+-- +-- Information gets lost for this one (we really would want to +-- draw the left-hand-side):+--+-- > PEN_DOWN _ _ `mappend` PEN_UP+--+-- So it has to be part of the state not the writer.+-- +data ActivePen = PEN_UP + | PEN_DOWN (AbsPath Double)+ +++zeroActivePen :: DPoint2 -> ActivePen+zeroActivePen pt = PEN_DOWN (emptyPath pt)+++data Vamp u = Vamp+ { vamp_move :: Vec2 u+ , vamp_conn :: ConnectorGraphic u+ }++type instance DUnit (Vamp u) = u+++--------------------------------------------------------------------------------+-- Instances+++-- Functor++instance Functor (GenPathSpec st u) where+ fmap f ma = GenPathSpec $ \ctx s -> + let (a,s1,w1) = getGenPathSpec ma ctx s + in (f a,s1,w1)+++-- Applicative++instance Applicative (GenPathSpec st u) where+ pure a = GenPathSpec $ \_ s -> (a, s, mempty)+ mf <*> ma = GenPathSpec $ \ctx s -> + let (f,s1,w1) = getGenPathSpec mf ctx s+ (a,s2,w2) = getGenPathSpec ma ctx s1+ in (f a, s2, w1 `mappend` w2)+++-- Monad++instance Monad (GenPathSpec st u) where+ return a = GenPathSpec $ \_ s -> (a, s, mempty)+ ma >>= k = GenPathSpec $ \ctx s -> + let (a,s1,w1) = getGenPathSpec ma ctx s+ (b,s2,w2) = (getGenPathSpec . k) a ctx s1+ in (b, s2, w1 `mappend` w2)+++-- Monoid ++instance Monoid a => Monoid (GenPathSpec st u a) where+ mempty = GenPathSpec $ \_ s -> (mempty, s, mempty)+ ma `mappend` mb = GenPathSpec $ \ctx s -> + let (a,s1,w1) = getGenPathSpec ma ctx s+ (b,s2,w2) = getGenPathSpec mb ctx s1+ in (a `mappend` b, s2, w1 `mappend` w2)+++-- DrawingCtxM++instance DrawingCtxM (GenPathSpec st u) where+ askDC = GenPathSpec $ \ctx s -> (ctx, s, mempty)+ asksDC f = GenPathSpec $ \ctx s -> (f ctx, s, mempty)+ localize upd ma = GenPathSpec $ \ctx s -> + getGenPathSpec ma (upd ctx) s+++-- UserStateM ++instance UserStateM (GenPathSpec st u) where+ getState = GenPathSpec $ \_ s -> + (st_user_state s, s, mempty)+ setState ust = GenPathSpec $ \_ s -> + ((), s {st_user_state = ust} , mempty)+ updateState upd = GenPathSpec $ \_ s -> + let ust = st_user_state s+ in ((), s {st_user_state = upd ust}, mempty)++++-- Note - all these need to peek at the cumulative path++-- LocationM++instance InterpretUnit u => LocationM (GenPathSpec st u) where+ location = locationImpl+++-- CursorM ++instance InterpretUnit u => CursorM (GenPathSpec st u) where+ moveby = movebyImpl+++-- InsertlM++instance InterpretUnit u => InsertlM (GenPathSpec st u) where+ insertl = insertlImpl+++ +--------------------------------------------------------------------------------+-- Run functions++runGenPathSpec :: InterpretUnit u + => st -> PathMode -> GenPathSpec st u a + -> LocImage u (a, st, AbsPath u)+runGenPathSpec st mode ma = promoteLoc $ \pt -> + askDC >>= \ctx ->+ let P2 dx dy = normalizeF (dc_font_size ctx) pt+ st_zero = PathSt (zeroActivePen zeroPt) ctx (emptyPath zeroPt) st+ (a,s1,w1) = getGenPathSpec ma ctx st_zero+ dpath = translate dx dy $ st_cumulative_path s1+ upath = dinterpF (dc_font_size ctx) dpath+ pctx = st_pen_ctx s1+ (_,w2) = runImage pctx (renderActivePen mode $ st_active_pen s1) + wfinal = cpmove (V2 dx dy) $ w1 `mappend` w2+ in replaceAns (a, st_user_state s1, upath) $ primGraphic wfinal+++-- Note - eval and exec return the AbsPath this is as-per RWS+-- which returns @w@ for execRWS (s,w) and evalRWS (a,w)+--++evalGenPathSpec :: InterpretUnit u+ => st -> PathMode -> GenPathSpec st u a+ -> LocImage u (a, AbsPath u)+evalGenPathSpec st mode ma = + (\(a,_,w) -> (a,w)) <$> runGenPathSpec st mode ma++ ++execGenPathSpec :: InterpretUnit u+ => st -> PathMode -> GenPathSpec st u a+ -> LocImage u (st, AbsPath u)+execGenPathSpec st mode ma =+ (\(_,s,w) -> (s,w)) <$> runGenPathSpec st mode ma++++stripGenPathSpec :: InterpretUnit u+ => st -> PathMode -> GenPathSpec st u a+ -> LocQuery u (a, st, AbsPath u)+stripGenPathSpec st mode ma = stripLocImage $ runGenPathSpec st mode ma+++runPathSpec :: InterpretUnit u+ => PathMode -> PathSpec u a -> LocImage u (a, AbsPath u)+runPathSpec mode ma = evalGenPathSpec () mode ma++runPathSpec_ :: InterpretUnit u+ => PathMode -> PathSpec u a -> LocGraphic u+runPathSpec_ mode ma = ignoreAns $ evalGenPathSpec () mode ma++++-- Monad run function nomenclature:+--+-- > run - both+-- > eval - answer (no state)+-- > exec - state (no answer)+-- +-- Note RWS always returns the @w@.+--+-- For Wumpus:+-- +-- > run - monadic answer, and the writer /construction/+-- > eval - just the monadic answer+-- > exec - just the writer /construction/.+--+-- In all case the CatPrim inside the LocImage may contain +-- additional graphics.+--+-- Client code can use @ignoreAns@ to generate a @LocGraphic@+-- from the @LocImage@.++++-- | Helper.+--+renderActivePen :: PathMode -> ActivePen -> DGraphic +renderActivePen _ PEN_UP = mempty+renderActivePen mode (PEN_DOWN abs_path) = renderPath_ mode abs_path++++++++-- | Form a \"pivot path\" drawing from two path specifications.+-- The start point of the drawing is the pivot formed by joining+-- the paths.+--+runPivot :: (Floating u, InterpretUnit u)+ => PathSpec u a -> PathSpec u a -> LocGraphic u+runPivot ma mb = promoteLoc $ \pt -> + askDC >>= \ctx ->+ let dpt = normalizeF (dc_font_size ctx) pt+ st_zero = PathSt (zeroActivePen zeroPt) ctx (emptyPath zeroPt) ()+ (p1,s1,w1) = getGenPathSpec mz ctx st_zero+ dp1 = normalizeF (dc_font_size ctx) p1+ v1 = pvec dpt dp1+ pctx = st_pen_ctx s1+ (_,w2) = runImage pctx $ renderActivePen OSTROKE $ st_active_pen s1+ wfinal = w1 `mappend` w2+ in primGraphic $ cpmove (negateV v1) wfinal+ where+ mz = ma >> location >>= \pt -> mb >> return pt+++--------------------------------------------------------------------------------+-- operations+++locationImpl :: InterpretUnit u => GenPathSpec st u (Point2 u)+locationImpl = GenPathSpec $ \ctx s ->+ let pt = tipR $ st_cumulative_path s + upt = dinterpF (dc_font_size ctx) pt+ in (upt, s, mempty)+++-- | 'extendPaths' extends both the @cumulative_path@ and the +-- @active_pen@. If the pen is up it, changes to a pendown.+--+extendPaths :: DVec2 -> PathSt st -> PathSt st+extendPaths v1 s@(PathSt { st_cumulative_path = cp+ , st_active_pen = pen} ) = + s { st_cumulative_path = snocLine cp v1, st_active_pen = upd pen }+ where+ upd PEN_UP = let pt = tipR cp in PEN_DOWN $ line1 pt (pt .+^ v1)+ upd (PEN_DOWN absp) = PEN_DOWN $ snocLine absp v1+++++-- | Extend the path with a line, drawn by the pen.+-- +penline :: InterpretUnit u => Vec2 u -> GenPathSpec st u ()+penline v1 = GenPathSpec $ \ctx s -> + let sz = dc_font_size ctx+ dv1 = normalizeF sz v1+ in ((), extendPaths dv1 s, mempty)++++-- | @extendPenC@ causes a pendown.+--+extendPathsC :: DVec2 -> DVec2 -> DVec2 -> PathSt st -> PathSt st+extendPathsC v1 v2 v3 s@(PathSt { st_cumulative_path = cp+ , st_active_pen = pen} ) = + s { st_cumulative_path = snocCurve cp (v1,v2,v3), st_active_pen = upd pen }+ where+ upd PEN_UP = let p0 = tipR cp + p1 = p0 .+^ v1+ p2 = p1 .+^ v2+ p3 = p2 .+^ v3+ in PEN_DOWN $ curve1 p0 p1 p2 p3+ upd (PEN_DOWN absp) = PEN_DOWN $ snocCurve absp (v1,v2,v3)++++-- | Extend the path with a curve, drawn by the pen.+-- +pencurve :: InterpretUnit u + => Vec2 u -> Vec2 u -> Vec2 u -> GenPathSpec st u ()+pencurve v1 v2 v3 = GenPathSpec $ \ctx s -> + let sz = dc_font_size ctx+ dv1 = normalizeF sz v1+ dv2 = normalizeF sz v2+ dv3 = normalizeF sz v3+ in ((), extendPathsC dv1 dv2 dv3 s, mempty)+++-- | @moveby@ causes a pen up.+--+movebyImpl :: InterpretUnit u => Vec2 u -> GenPathSpec st u ()+movebyImpl v1 = GenPathSpec $ \ctx s@(PathSt {st_pen_ctx = pctx}) ->+ let sz = dc_font_size ctx+ dv1 = normalizeF sz v1+ (_,w1) = runImage pctx $ renderActivePen OSTROKE $ st_active_pen s+ cpath = snocLine (st_cumulative_path s) dv1+ in ((), s { st_active_pen = PEN_UP, st_cumulative_path = cpath }, w1)+++breakPath :: InterpretUnit u => GenPathSpec st u ()+breakPath = movebyImpl (V2 0 0)++hpenline :: InterpretUnit u => u -> GenPathSpec st u ()+hpenline dx = penline (hvec dx)++vpenline :: InterpretUnit u => u -> GenPathSpec st u ()+vpenline dy = penline (vvec dy)+++apenline :: (Floating u, InterpretUnit u) + => Radian -> u -> GenPathSpec st u ()+apenline ang d = penline (avec ang d)++penlines :: InterpretUnit u => [Vec2 u] -> GenPathSpec st u ()+penlines = mapM_ penline++pathmoves :: InterpretUnit u => [Vec2 u] -> GenPathSpec st u ()+pathmoves = mapM_ moveby++++insertlImpl :: InterpretUnit u + => LocImage u a -> GenPathSpec st u a+insertlImpl gf = GenPathSpec $ \ctx s ->+ let upt = dinterpF (dc_font_size ctx) (tipR $ st_cumulative_path s)+ (a,wcp) = runLocImage ctx upt gf+ in (a, s, wcp)++++vamp :: InterpretUnit u => Vamp u -> GenPathSpec st u ()+vamp (Vamp v1 conn) = GenPathSpec $ \ctx s@(PathSt {st_pen_ctx = pctx}) ->+ let sz = dc_font_size ctx+ dv1 = normalizeF sz v1+ (_,w1) = runImage pctx $ renderActivePen OSTROKE $ st_active_pen s+ upt = dinterpF sz (tipR $ st_cumulative_path s)+ (_,w2) = runConnectorImage ctx upt (upt .+^ v1) conn+ cpath = snocLine (st_cumulative_path s) dv1+ in ((), s { st_active_pen = PEN_UP, st_cumulative_path = cpath }+ , w1 `mappend` w2)+++cycleSubPath :: DrawMode -> GenPathSpec st u ()+cycleSubPath mode = GenPathSpec $ \_ s@(PathSt {st_pen_ctx = pctx}) ->+ let (_,w1) = runImage pctx $ renderActivePen (fn mode) (st_active_pen s)+ in ((), s { st_active_pen = PEN_UP }, w1)+ where+ fn DRAW_STROKE = CSTROKE+ fn DRAW_FILL = CFILL+ fn DRAW_FILL_STROKE = CFILL_STROKE++++-- Design note +--+-- Should pen changing be @local@ style vis the Reader monad or a +-- state change with the State monad?+-- +-- Now switched to state change.+--+++-- | Note - updates the pen but doesn\'t draw, the final path+-- will be drawing with the last updated context.+--+updatePen :: DrawingContextF -> GenPathSpec st u ()+updatePen upd = GenPathSpec $ \_ s@(PathSt { st_pen_ctx = pctx}) ->+ ((), s { st_pen_ctx = upd pctx}, mempty )+
+ src/Wumpus/Drawing/Paths/Vamps.hs view
@@ -0,0 +1,53 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Paths.Vamps+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Library of vamps (currently small).+--+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Paths.Vamps+ ( ++ squareWE++ ) where++import Wumpus.Drawing.Paths.PathBuilder ( Vamp(..) )+import Wumpus.Drawing.Paths.Base++import Wumpus.Basic.Kernel++import Wumpus.Core -- package: wumpus-core+++-- TODO - library of useful / illustrative vamps (circle, square etc.)++++-- +--+squareWE :: (Real u, Floating u, Ord u, Tolerance u, InterpretUnit u) + => u -> Vamp u+squareWE diam = Vamp { vamp_move = hvec diam+ , vamp_conn = conn }+ where+ conn = promoteConn $ \p1 p2 -> + let dir = vdirection $ pvec p1 p2+ in renderPath_ CSTROKE $ vectorPathTheta path1 dir p1++ hdiam = 0.5 * diam + path1 = [ vvec hdiam, hvec diam, vvec (-diam), hvec (-diam) ]+++-- Drawing a cirle picks the half point on the vamp_move vector...+
src/Wumpus/Drawing/Shapes.hs view
@@ -20,12 +20,29 @@ , module Wumpus.Drawing.Shapes.Circle , module Wumpus.Drawing.Shapes.Diamond , module Wumpus.Drawing.Shapes.Ellipse+ , module Wumpus.Drawing.Shapes.InvSemicircle+ , module Wumpus.Drawing.Shapes.InvSemiellipse+ , module Wumpus.Drawing.Shapes.InvTriangle+ , module Wumpus.Drawing.Shapes.Parallelogram , module Wumpus.Drawing.Shapes.Rectangle-+ , module Wumpus.Drawing.Shapes.Semicircle+ , module Wumpus.Drawing.Shapes.Semiellipse+ , module Wumpus.Drawing.Shapes.Trapezium+ , module Wumpus.Drawing.Shapes.Triangle ) where import Wumpus.Drawing.Shapes.Base import Wumpus.Drawing.Shapes.Circle import Wumpus.Drawing.Shapes.Diamond import Wumpus.Drawing.Shapes.Ellipse+import Wumpus.Drawing.Shapes.InvSemicircle+import Wumpus.Drawing.Shapes.InvSemiellipse+import Wumpus.Drawing.Shapes.InvTriangle+import Wumpus.Drawing.Shapes.Parallelogram import Wumpus.Drawing.Shapes.Rectangle+import Wumpus.Drawing.Shapes.Semicircle+import Wumpus.Drawing.Shapes.Semiellipse+import Wumpus.Drawing.Shapes.Trapezium+import Wumpus.Drawing.Shapes.Triangle++
src/Wumpus/Drawing/Shapes/Base.hs view
@@ -1,10 +1,9 @@ {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-} {-# OPTIONS -Wall #-} -------------------------------------------------------------------------------- -- |--- Module : Wumpus.Drawing.Shapes.Base2+-- Module : Wumpus.Drawing.Shapes.Base -- Copyright : (c) Stephen Tetley 2010-2011 -- License : BSD3 --@@ -21,69 +20,199 @@ ( - LocShape- , intoLocShape+ Shape+ , DShape++ , shapeMap+ , makeShape , strokedShape+ , dblStrokedShape , filledShape , borderedShape+ , rstrokedShape+ , rfilledShape+ , rborderedShape , roundCornerShapePath + , updatePathAngle+ , setDecoration+ , ShapeCTM , makeShapeCTM , ctmCenter , ctmAngle- , projectPoint-- + , ctmLocale+ , projectFromCtr ) where -import Wumpus.Basic.Kernel import Wumpus.Drawing.Paths +import Wumpus.Basic.Kernel -- package: wumpus-basic+ import Wumpus.Core -- package: wumpus-core+import Wumpus.Core.Colour ( white ) +import Data.AffineSpace -- package: vector-space import Control.Applicative +-- import Data.Traversable ( Traversable )+-- import qualified Data.Traversable as T -type LocShape u a = LocCF u (a, Path u)+-- | Shape is a record of three /LocTheta/ functions - +-- functions /from Point and Angle to answer/. +--+-- The @shape_path_fun@ returns a path. When the Shape is drawn, +-- the rendering function (@strokedShape@, etc.) uses the path for +-- drawing and returns the polymorphic answer @a@ of the +-- @shape_ans_fun@. Lastly the @shape_decoration@ function can +-- instantiated to add decoration (e.g. text) to the Shape as it +-- is rendered.+--+-- The @a@ of the @shape_ans_fun@ represents some concrete shape +-- object (e.g. a Rectangle, Triangle etc.). Crucial for shape +-- objects is that they support Anchors - this allows connectors +-- to address specific locations on the Shape border so +-- \"node and link\" diagrams can be made easily.+--+data Shape t u = Shape + { shape_ans_fun :: LocThetaQuery u (t u)+ , shape_path_fun :: LocThetaQuery u (AbsPath u) + , shape_decoration :: LocThetaGraphic u+ } +type instance DUnit (Shape t u) = u -intoLocShape :: LocCF u a -> LocCF u (Path u) -> LocCF u (a,Path u)-intoLocShape = liftA2 (,)+type DShape t = Shape t Double -strokedShape :: Num u => LocShape u a -> LocImage u a-strokedShape mf = - promoteR1 $ \pt -> - (mf `at` pt) >>= \(a,spath) -> - intoImage (pure a) (closedStroke $ toPrimPath spath) +-------------------------------------------------------------------------------- -filledShape :: Num u => LocShape u a -> LocImage u a-filledShape mf = - promoteR1 $ \pt -> - (mf `at` pt) >>= \(a,spath) -> - intoImage (pure a) (filledPath $ toPrimPath spath)+shapeMap :: InterpretUnit u + => (t u -> t' u) -> Shape t u -> Shape t' u+shapeMap f = (\s sf -> s { shape_ans_fun = qpromoteLocTheta $ \pt ang -> + fmap f $ qapplyLocTheta sf pt ang }) + <*> shape_ans_fun -borderedShape :: Num u => LocShape u a -> LocImage u a-borderedShape mf = - promoteR1 $ \pt -> - (mf `at` pt) >>= \(a,spath) -> - intoImage (pure a) (borderedPath $ toPrimPath spath)+-- Note - there are no instances of Applicative, Monad, +-- DrawingCtxM... so Shapes cannot have localized drawing props.+--+-- @localize@ must be performed in the context of @strokeShape@, +-- @fillShape@ etc.+-- +++--------------------------------------------------------------------------------+++makeShape :: InterpretUnit u+ => LocThetaQuery u (t u) -> LocThetaQuery u (AbsPath u) -> Shape t u+makeShape f g = Shape { shape_ans_fun = f+ , shape_path_fun = g+ , shape_decoration = emptyLocThetaImage+ }+++++strokedShape :: InterpretUnit u => Shape t u -> LocImage u (t u)+strokedShape = shapeToLoc (dcClosedPath DRAW_STROKE)+++-- | Note - this is simplistic double stroking - draw a background +-- line with triple thickness and draw a white line on top.+--+-- I think this is what TikZ does, but it works better for TikZ +-- where the extra thickness seems to be accounted for by the +-- anchors. For Wumpus, arrows cut into the outside black line.+--+-- Probably Wumpus should calculate two paths instead.+--+dblStrokedShape :: InterpretUnit u => Shape t u -> LocImage u (t u)+dblStrokedShape sh = decorateAbove back fore + where+ img = shapeToLoc (dcClosedPath DRAW_STROKE) sh+ back = getLineWidth >>= \lw ->+ localize (set_line_width $ lw * 3.0) img+ fore = ignoreAns $ localize (stroke_colour white) img++++filledShape :: InterpretUnit u => Shape t u -> LocImage u (t u)+filledShape = shapeToLoc (dcClosedPath DRAW_FILL)+++borderedShape :: InterpretUnit u => Shape t u -> LocImage u (t u)+borderedShape = shapeToLoc (dcClosedPath DRAW_FILL_STROKE)+++shapeToLoc :: InterpretUnit u+ => (PrimPath -> Graphic u) -> Shape t u -> LocImage u (t u)+shapeToLoc drawF sh = promoteLoc $ \pt -> + applyLocTheta (liftLocThetaQuery $ shape_ans_fun sh) pt 0 >>= \a -> + applyLocTheta (liftLocThetaQuery $ shape_path_fun sh) pt 0 >>= \spath -> + let g2 = atIncline (shape_decoration sh) pt 0 + in replaceAns a (decorateAbove g2 $ liftQuery (toPrimPath spath) >>= drawF)++++rstrokedShape :: InterpretUnit u => Shape t u -> LocThetaImage u (t u)+rstrokedShape = shapeToLocTheta (dcClosedPath DRAW_STROKE)+++rfilledShape :: InterpretUnit u => Shape t u -> LocThetaImage u (t u)+rfilledShape = shapeToLocTheta (dcClosedPath DRAW_FILL)+++rborderedShape :: InterpretUnit u => Shape t u -> LocThetaImage u (t u)+rborderedShape = shapeToLocTheta (dcClosedPath DRAW_FILL_STROKE)+++shapeToLocTheta :: InterpretUnit u+ => (PrimPath -> Graphic u) -> Shape t u -> LocThetaImage u (t u)+shapeToLocTheta drawF sh = promoteLocTheta $ \pt theta -> + applyLocTheta (liftLocThetaQuery $ shape_ans_fun sh) pt theta >>= \a -> + applyLocTheta (liftLocThetaQuery $ shape_path_fun sh) pt theta >>= \spath -> + let g2 = atIncline (shape_decoration sh) pt theta+ in replaceAns a $ decorateAbove g2 (liftQuery (toPrimPath spath) >>= drawF)+++ -- | Draw the shape path with round corners. -- -roundCornerShapePath :: (Real u, Floating u, FromPtSize u) - => [Point2 u] -> CF (Path u)-roundCornerShapePath xs = getRoundCornerSize >>= \sz -> - if sz == 0 then return (traceLinePoints xs) - else return (roundTrail sz xs)+roundCornerShapePath :: (Real u, Floating u, InterpretUnit u, Tolerance u)+ => u -> [Point2 u] -> Query u (AbsPath u)+roundCornerShapePath sz xs = + if sz `tEQ` 0 then return (vertexPath xs) + else return (roundExterior sz $ vertexPath xs) +-- | The path angle can be modified. This allows /inverse/ +-- versions of shapes (e.g. InvTriangle) to be made by+-- wrapping a base Shape but rotating the path prior to drawing +-- it.+-- +-- Only the Path needs rotating, the decoration takes the original +-- angle. The anchors are typically implemented by rotating the +-- correspoding anchor of the wrapped Shape about its center.+-- +updatePathAngle :: InterpretUnit u + => (Radian -> Radian) -> Shape t u -> Shape t u+updatePathAngle f = + (\s fi -> s { shape_path_fun = qpromoteLocTheta $ \pt ang -> + qapplyLocTheta fi pt (mvTheta ang) })+ <*> shape_path_fun+ where+ mvTheta = circularModulo . f +setDecoration :: LocThetaGraphic u -> Shape t u -> Shape t u+setDecoration gf = (\s -> s { shape_decoration = gf }) ++ -------------------------------------------------------------------------------- -- CTM @@ -93,33 +222,50 @@ data ShapeCTM u = ShapeCTM { ctm_center :: Point2 u- , ctm_scale_x :: !u- , ctm_scale_y :: !u+ , ctm_scale_x :: !Double+ , ctm_scale_y :: !Double , ctm_rotation :: Radian } deriving (Eq,Ord,Show) - type instance DUnit (ShapeCTM u) = u -makeShapeCTM :: Num u => Point2 u -> ShapeCTM u-makeShapeCTM pt = ShapeCTM { ctm_center = pt- , ctm_scale_x = 1- , ctm_scale_y = 1- , ctm_rotation = 0 } +instance Functor ShapeCTM where+ fmap f = (\s i -> s { ctm_center = fmap f i }) <*> ctm_center +makeShapeCTM :: Point2 u -> Radian -> ShapeCTM u+makeShapeCTM pt ang = ShapeCTM { ctm_center = pt+ , ctm_scale_x = 1+ , ctm_scale_y = 1+ , ctm_rotation = ang } -instance Num u => Scale (ShapeCTM u) where- scale sx sy = (\s x y -> s { ctm_scale_x = x*sx, ctm_scale_y = y*sy })- <*> ctm_scale_x <*> ctm_scale_y +ctmCenter :: ShapeCTM u -> Point2 u+ctmCenter = ctm_center -instance Rotate (ShapeCTM u) where- rotate ang = (\s i -> s { ctm_rotation = circularModulo $ i+ang })- <*> ctm_rotation+ctmAngle :: ShapeCTM u -> Radian+ctmAngle = ctm_rotation +ctmLocale :: ShapeCTM u -> (Point2 u, Radian)+ctmLocale ctm = (ctm_center ctm, ctm_rotation ctm)+++instance (Fractional u) => Scale (ShapeCTM u) where+ scale sx sy = (\s x y pt -> s { ctm_scale_x = x*sx+ , ctm_scale_y = y*sy + , ctm_center = scale sx sy pt })+ <*> ctm_scale_x <*> ctm_scale_y <*> ctm_center+++instance (Real u, Floating u) => Rotate (ShapeCTM u) where+ rotate ang = (\s i pt -> let ctr = rotate ang pt+ in s { ctm_rotation = circularModulo $ i+ang+ , ctm_center = ctr })+ <*> ctm_rotation <*> ctm_center++ instance (Real u, Floating u) => RotateAbout (ShapeCTM u) where rotateAbout ang pt = (\s ctr i -> s { ctm_rotation = circularModulo $ i+ang@@ -127,25 +273,18 @@ <*> ctm_center <*> ctm_rotation -instance Num u => Translate (ShapeCTM u) where- translate dx dy = (\s (P2 x y) -> s { ctm_center = P2 (x+dx) (y+dy) })- <*> ctm_center-+instance (Num u) => Translate (ShapeCTM u) where+ translate dx dy = + (\s i -> s { ctm_center = translate dx dy i })+ <*> ctm_center -ctmCenter :: ShapeCTM u -> Point2 u-ctmCenter = ctm_center--ctmAngle :: ShapeCTM u -> Radian-ctmAngle = ctm_rotation--+projectFromCtr :: (Real u, Floating u) => Vec2 u -> ShapeCTM u -> Anchor u+projectFromCtr v (ShapeCTM { ctm_center = ctr+ , ctm_scale_x = sx+ , ctm_scale_y = sy+ , ctm_rotation = theta }) = + let v1 = rotate theta $ scale sx sy $ v in ctr .+^ v1 -projectPoint :: (Real u, Floating u) => Point2 u -> ShapeCTM u -> Point2 u-projectPoint (P2 x y) (ShapeCTM { ctm_center = (P2 dx dy)- , ctm_scale_x = sx- , ctm_scale_y = sy- , ctm_rotation = theta }) =- translate dx dy $ rotate theta $ P2 (sx*x) (sy*y)
src/Wumpus/Drawing/Shapes/Circle.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-} {-# OPTIONS -Wall #-} --------------------------------------------------------------------------------@@ -32,7 +31,6 @@ import Wumpus.Core -- package: wumpus-core -import Data.AffineSpace -- package: vector-space import Control.Applicative @@ -45,49 +43,56 @@ { circ_ctm :: ShapeCTM u , circ_radius :: !u }- deriving (Eq,Show)- -type DCircle = Circle Double -type instance DUnit (Circle u) = u+type instance DUnit (Circle u) = u +type DCircle = Circle Double +instance Functor Circle where+ fmap f (Circle ctm r) = Circle (fmap f ctm) (f r) -mapCircleCTM :: (ShapeCTM u -> ShapeCTM u) -> Circle u -> Circle u-mapCircleCTM f = (\s i -> s { circ_ctm = f i }) <*> circ_ctm+--------------------------------------------------------------------------------+-- Affine trans -instance Num u => Scale (Circle u) where- scale sx sy = mapCircleCTM (scale sx sy)+mapCTM :: (ShapeCTM u -> ShapeCTM u) -> Circle u -> Circle u+mapCTM f = (\s i -> s { circ_ctm = f i }) <*> circ_ctm -instance Rotate (Circle u) where- rotate ang = mapCircleCTM (rotate ang)- +instance (Real u, Floating u) => Rotate (Circle u) where+ rotate ang = mapCTM (rotate ang)+ instance (Real u, Floating u) => RotateAbout (Circle u) where- rotateAbout ang pt = mapCircleCTM (rotateAbout ang pt)+ rotateAbout ang pt = mapCTM (rotateAbout ang pt) +instance Fractional u => Scale (Circle u) where+ scale sx sy = mapCTM (scale sx sy) instance Num u => Translate (Circle u) where- translate dx dy = mapCircleCTM (translate dx dy)+ translate dx dy = mapCTM (translate dx dy) +--------------------------------------------------------------------------------+-- Anchors -runCircle :: (u -> ShapeCTM u -> a) -> Circle u -> a-runCircle fn (Circle { circ_ctm = ctm, circ_radius = radius }) = - fn radius ctm+runDisplaceCenter :: (Real u, Floating u)+ => (u -> Vec2 u) -> Circle u -> Anchor u+runDisplaceCenter fn (Circle { circ_ctm = ctm+ , circ_radius = radius }) = + projectFromCtr (fn radius) ctm +-- Anchors look like they need ctx... instance (Real u, Floating u) => CenterAnchor (Circle u) where- center = runCircle (\_ -> ctmCenter)+ center = runDisplaceCenter $ \_ -> V2 0 0 instance (Real u, Floating u) => CardinalAnchor (Circle u) where- north = runCircle $ \r -> projectPoint $ P2 0 r- south = runCircle $ \r -> projectPoint $ P2 0 (-r)- east = runCircle $ \r -> projectPoint $ P2 r 0- west = runCircle $ \r -> projectPoint $ P2 (-r) 0+ north = runDisplaceCenter $ \r -> V2 0 r+ south = runDisplaceCenter $ \r -> V2 0 (-r)+ east = runDisplaceCenter $ \r -> V2 r 0+ west = runDisplaceCenter $ \r -> V2 (-r) 0 instance (Real u, Floating u) => CardinalAnchor2 (Circle u) where@@ -98,29 +103,35 @@ instance (Real u, Floating u) => RadialAnchor (Circle u) where- radialAnchor theta = runCircle $ \r -> projectPoint $ zeroPt .+^ avec theta r-+ radialAnchor ang = runDisplaceCenter $ \r -> avec ang r +--------------------------------------------------------------------------------+-- Construction --- | 'circle' : @ radius -> shape @+-- | 'circle' : @ radius -> Shape @ ---circle :: (Real u, Floating u) => u -> LocShape u (Circle u)-circle radius = intoLocShape (mkCircle radius) (mkCirclePath radius)+circle :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> Shape Circle u+circle radius = makeShape (mkCircle radius) (mkCirclePath radius) -mkCircle :: Num u => u -> LocCF u (Circle u)-mkCircle radius = promoteR1 $ \ctr -> - pure $ Circle { circ_ctm = makeShapeCTM ctr, circ_radius = radius }-+mkCircle :: InterpretUnit u => u -> LocThetaQuery u (Circle u)+mkCircle radius = qpromoteLocTheta $ \ctr theta -> + pure $ Circle { circ_ctm = makeShapeCTM ctr theta+ , circ_radius = radius + } -mkCirclePath :: (Floating u, Ord u) => u -> LocCF u (Path u)-mkCirclePath radius = promoteR1 $ \ctr -> - pure $ traceCurvePoints $ bezierCircle 2 radius ctr +-- Rotation (theta) can be ignored.+--+mkCirclePath :: (Floating u, Ord u, InterpretUnit u, Tolerance u)+ => u -> LocThetaQuery u (AbsPath u)+mkCirclePath radius = qpromoteLocTheta $ \ctr _ ->+ pure $ curvePath $ bezierCircle radius ctr
src/Wumpus/Drawing/Shapes/Diamond.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-} {-# OPTIONS -Wall #-} --------------------------------------------------------------------------------@@ -12,7 +11,7 @@ -- Stability : highly unstable -- Portability : GHC ----- Simple shapes - rectangle, circle diamond, ellipse.+-- Diamond (rhombus). -- -------------------------------------------------------------------------------- @@ -26,17 +25,17 @@ ) where -import Wumpus.Drawing.Geometry.Intersection-import Wumpus.Drawing.Geometry.Paths++import Wumpus.Drawing.Basis.ShapeTrails+import Wumpus.Drawing.Basis.Geometry import Wumpus.Drawing.Paths+import Wumpus.Drawing.Paths.Intersection import Wumpus.Drawing.Shapes.Base import Wumpus.Basic.Kernel -- package: wumpus-basic import Wumpus.Core -- package: wumpus-core -import Data.AffineSpace -- package: vector-space -import Data.VectorSpace import Control.Applicative @@ -53,46 +52,72 @@ , dia_hh :: !u } +type instance DUnit (Diamond u) = u+ type DDiamond = Diamond Double -type instance DUnit (Diamond u) = u +instance Functor Diamond where+ fmap f (Diamond ctm hw hh) = Diamond (fmap f ctm) (f hw) (f hh) -mapDiamondCTM :: (ShapeCTM u -> ShapeCTM u) -> Diamond u -> Diamond u-mapDiamondCTM f = (\s i -> s { dia_ctm = f i }) <*> dia_ctm+--------------------------------------------------------------------------------+-- Affine trans -instance Num u => Scale (Diamond u) where- scale sx sy = mapDiamondCTM (scale sx sy)+mapCTM :: (ShapeCTM u -> ShapeCTM u) -> Diamond u -> Diamond u+mapCTM f = (\s i -> s { dia_ctm = f i }) <*> dia_ctm -instance Rotate (Diamond u) where- rotate ang = mapDiamondCTM (rotate ang)- +instance (Real u, Floating u) => Rotate (Diamond u) where+ rotate ang = mapCTM (rotate ang)+ instance (Real u, Floating u) => RotateAbout (Diamond u) where- rotateAbout ang pt = mapDiamondCTM (rotateAbout ang pt)+ rotateAbout ang pt = mapCTM (rotateAbout ang pt) +instance Fractional u => Scale (Diamond u) where+ scale sx sy = mapCTM (scale sx sy) instance Num u => Translate (Diamond u) where- translate dx dy = mapDiamondCTM (translate dx dy)+ translate dx dy = mapCTM (translate dx dy) +--------------------------------------------------------------------------------+-- Anchors -runDiamond :: (u -> u -> ShapeCTM u -> a) -> Diamond u -> a-runDiamond fm (Diamond { dia_ctm = ctm, dia_hw = hw, dia_hh = hh }) = - fm hw hh ctm+-- | 'runDisplaceCenter' : @ ( half_width +-- * half_height -> Vec ) * diamond -> Point @+--+runDisplaceCenter :: (Real u, Floating u)+ => (u -> u -> Vec2 u) -> Diamond u -> Anchor u+runDisplaceCenter fn (Diamond { dia_ctm = ctm+ , dia_hw = hw+ , dia_hh = hh }) = + projectFromCtr (fn hw hh) ctm instance (Real u, Floating u) => CenterAnchor (Diamond u) where- center = runDiamond (\_ _ -> ctmCenter)+ center = runDisplaceCenter $ \_ _ -> V2 0 0 +instance (Real u, Floating u) => ApexAnchor (Diamond u) where+ apex = runDisplaceCenter $ \_ hh -> V2 0 hh++instance (Real u, Floating u) => + SideMidpointAnchor (Diamond u) where+ sideMidpoint n a = step (n `mod` 4) + where+ step 1 = midpoint (north a) (west a)+ step 2 = midpoint (west a) (south a)+ step 3 = midpoint (south a) (east a)+ step _ = midpoint (east a) (north a)++ instance (Real u, Floating u) => CardinalAnchor (Diamond u) where- north = runDiamond $ \_ hh -> projectPoint $ P2 0 hh- south = runDiamond $ \_ hh -> projectPoint $ P2 0 (-hh)- east = runDiamond $ \hw _ -> projectPoint $ P2 hw 0- west = runDiamond $ \hw _ -> projectPoint $ P2 (-hw) 0+ north = apex+ south = runDisplaceCenter $ \_ hh -> V2 0 (-hh)+ east = runDisplaceCenter $ \hw _ -> V2 hw 0+ west = runDisplaceCenter $ \hw _ -> V2 (-hw) 0 -instance (Real u, Floating u, Fractional u) => CardinalAnchor2 (Diamond u) where+instance (Real u, Floating u) => CardinalAnchor2 (Diamond u) where northeast x = midpoint (north x) (east x) southeast x = midpoint (south x) (east x) southwest x = midpoint (south x) (west x)@@ -100,53 +125,43 @@ -instance (Real u, Floating u) => RadialAnchor (Diamond u) where- radialAnchor = diamondIntersect+instance (Real u, Floating u, InterpretUnit u, Tolerance u) => + RadialAnchor (Diamond u) where+ radialAnchor ang = runDisplaceCenter $ \hw hh -> + maybe zeroVec id $ diamondRadialAnchor hw hh ang --- Utils.Intersection needs improving... --diamondIntersect :: (Real u, Floating u) - => Radian -> Diamond u -> Point2 u-diamondIntersect theta (Diamond { dia_ctm = ctm, dia_hw = hw, dia_hh = hh }) = - let ps = diamondPoints hw hh ctm - ctr = ctmCenter ctm- in maybe ctr id $ findIntersect ctr theta $ polygonLines ps- ---midpoint :: Fractional u => Point2 u -> Point2 u -> Point2 u-midpoint p1 p2 = let v = 0.5 *^ pvec p1 p2 in p1 .+^ v+diamondRadialAnchor :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> u -> Radian -> Maybe (Vec2 u) +diamondRadialAnchor hw hh ang = + fmap (pvec zeroPt) $ rayPathIntersection (inclinedRay zeroPt ang) rp + where+ rp = anaTrailPath zeroPt $ diamond_trail (2*hw) (2*hh) +--------------------------------------------------------------------------------+-- Construction -- | 'diamond' : @ half_width * half_height -> shape @ -- -- Note - args might change to tull_width and full_height... ---diamond :: (Real u, Floating u, FromPtSize u) - => u -> u -> LocShape u (Diamond u)-diamond hw hh = intoLocShape (mkDiamond hw hh) (mkDiamondPath hw hh)---mkDiamond :: Num u => u -> u -> LocCF u (Diamond u)-mkDiamond hw hh = promoteR1 $ \ctr -> - pure $ Diamond { dia_ctm = makeShapeCTM ctr, dia_hw = hw, dia_hh = hh }+diamond :: (Real u, Floating u, InterpretUnit u, Tolerance u)+ => u -> u -> Shape Diamond u+diamond hw hh = makeShape (mkDiamond hw hh) (mkDiamondPath hw hh) -mkDiamondPath :: (Real u, Floating u, FromPtSize u) - => u -> u -> LocCF u (Path u)-mkDiamondPath hw hh = promoteR1 $ \ctr -> - roundCornerShapePath $ diamondCoordPath hw hh ctr+mkDiamond :: InterpretUnit u => u -> u -> LocThetaQuery u (Diamond u)+mkDiamond hw hh = qpromoteLocTheta $ \ctr theta -> + pure $ Diamond { dia_ctm = makeShapeCTM ctr theta+ , dia_hw = hw+ , dia_hh = hh + } -diamondPoints :: (Real u, Floating u) => u -> u -> ShapeCTM u -> [Point2 u]-diamondPoints hw hh ctm = map (projectPoint `flip` ctm) [ s, e, n, w ]- where- s = P2 0 (-hh)- e = P2 hw 0- n = P2 0 hh- w = P2 (-hw) 0 -+mkDiamondPath :: (Real u, Floating u, InterpretUnit u, Tolerance u)+ => u -> u -> LocThetaQuery u (AbsPath u)+mkDiamondPath hw hh = qpromoteLocTheta $ \ctr theta -> + return $ anaTrailPath ctr $ rdiamond_trail (2*hw) (2*hh) theta
src/Wumpus/Drawing/Shapes/Ellipse.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-} {-# OPTIONS -Wall #-} --------------------------------------------------------------------------------@@ -28,6 +27,7 @@ ) where +import Wumpus.Drawing.Basis.ShapeTrails import Wumpus.Drawing.Paths import Wumpus.Drawing.Shapes.Base @@ -35,7 +35,6 @@ import Wumpus.Core -- package: wumpus-core -import Data.AffineSpace -- package: vector-space import Control.Applicative @@ -47,55 +46,73 @@ -- Ellipse + data Ellipse u = Ellipse- { ell_ctm :: ShapeCTM u + { ell_ctm :: ShapeCTM u , ell_rx :: !u , ell_ry :: !u } -type DEllipse = Ellipse Double- type instance DUnit (Ellipse u) = u +type DEllipse = Ellipse Double -mapEllipseCTM :: (ShapeCTM u -> ShapeCTM u) -> Ellipse u -> Ellipse u-mapEllipseCTM f = (\s i -> s { ell_ctm = f i }) <*> ell_ctm+instance Functor Ellipse where+ fmap f (Ellipse ctm rx ry) = Ellipse (fmap f ctm) (f rx) (f ry) -instance Num u => Scale (Ellipse u) where- scale sx sy = mapEllipseCTM (scale sx sy) +--------------------------------------------------------------------------------+-- Affine trans -instance Rotate (Ellipse u) where- rotate ang = mapEllipseCTM (rotate ang)- +mapCTM :: (ShapeCTM u -> ShapeCTM u) -> Ellipse u -> Ellipse u+mapCTM f = (\s i -> s { ell_ctm = f i }) <*> ell_ctm ++instance (Real u, Floating u) => Rotate (Ellipse u) where+ rotate ang = mapCTM (rotate ang)+ instance (Real u, Floating u) => RotateAbout (Ellipse u) where- rotateAbout ang pt = mapEllipseCTM (rotateAbout ang pt)+ rotateAbout ang pt = mapCTM (rotateAbout ang pt) +instance Fractional u => Scale (Ellipse u) where+ scale sx sy = mapCTM (scale sx sy) -instance Num u => Translate (Ellipse u) where- translate dx dy = mapEllipseCTM (translate dx dy)+instance InterpretUnit u => Translate (Ellipse u) where+ translate dx dy = mapCTM (translate dx dy) -runEllipse :: (u -> u -> ShapeCTM u -> a) -> Ellipse u -> a-runEllipse fn (Ellipse { ell_ctm = ctm, ell_rx = rx, ell_ry = ry }) = - fn rx ry ctm+--------------------------------------------------------------------------------+-- Anchors +-- Note - this is monadic for Ellipse... +runDisplaceCenter :: (Real u, Floating u)+ => (u -> u -> Vec2 u) -> Ellipse u -> Anchor u+runDisplaceCenter fn (Ellipse { ell_ctm = ctm+ , ell_rx = rx+ , ell_ry = ry }) = + projectFromCtr (fn rx ry) ctm+++-- NOTE - the Affine instances provided by Wumpus-Core for Point +-- and Vector may be contradictory for Wumpus-Basic.++ -- | x_radius is the unit length. ---scaleEll :: (Scale t, Fractional u, u ~ DUnit t) => u -> u -> t -> t-scaleEll rx ry = scale 1 (ry/rx) +scaleEll :: (Real u, Fractional u)+ => u -> u -> Vec2 u -> Vec2 u+scaleEll rx ry v = let rat = realToFrac (ry/rx) in scale 1 rat v instance (Real u, Floating u) => CenterAnchor (Ellipse u) where- center = runEllipse $ \_ _ -> ctmCenter+ center = runDisplaceCenter $ \_ _ -> V2 0 0 instance (Real u, Floating u) => RadialAnchor (Ellipse u) where- radialAnchor theta = runEllipse $ \rx ry -> - projectPoint $ scaleEll rx ry $ zeroPt .+^ avec theta rx+ radialAnchor theta = runDisplaceCenter $ \rx ry -> + scaleEll rx ry $ avec theta rx instance (Real u, Floating u) => CardinalAnchor (Ellipse u) where@@ -112,23 +129,27 @@ northwest = radialAnchor (0.75*pi) -+--------------------------------------------------------------------------------+-- Construction -- | 'ellipse' : @ x_radii * y_radii -> shape @ ---ellipse :: (Floating u, Ord u) => u -> u -> LocShape u (Ellipse u)-ellipse rx ry = - intoLocShape (mkEllipse rx ry) (mkEllipsePath rx ry)+ellipse :: (Real u, Floating u, Ord u, InterpretUnit u, Tolerance u) + => u -> u -> Shape Ellipse u+ellipse rx ry = makeShape (mkEllipse rx ry) (mkEllipsePath rx ry) -mkEllipse :: Num u => u -> u -> LocCF u (Ellipse u)-mkEllipse rx ry = promoteR1 $ \ctr -> - pure $ Ellipse { ell_ctm = makeShapeCTM ctr, ell_rx = rx, ell_ry = ry }+mkEllipse :: InterpretUnit u => u -> u -> LocThetaQuery u (Ellipse u)+mkEllipse rx ry = qpromoteLocTheta $ \ctr theta -> + pure $ Ellipse { ell_ctm = makeShapeCTM ctr theta+ , ell_rx = rx+ , ell_ry = ry + } --- This is wrong...----mkEllipsePath :: (Floating u, Ord u) => u -> u -> LocCF u (Path u)-mkEllipsePath rx ry = promoteR1 $ \(P2 x y) -> - pure $ traceCurvePoints $ map (translate x y . scaleEll rx ry) - $ bezierCircle 2 rx zeroPt++mkEllipsePath :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> u -> LocThetaQuery u (AbsPath u)+mkEllipsePath rx ry = qpromoteLocTheta $ \ctr theta -> + return $ anaTrailPath ctr $ rellipse_trail rx ry theta+
+ src/Wumpus/Drawing/Shapes/InvSemicircle.hs view
@@ -0,0 +1,126 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Shapes.InvSemicircle+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Inverse semicircle. +-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Shapes.InvSemicircle+ ( ++ InvSemicircle+ , DInvSemicircle+ , invsemicircle++ ) where++import Wumpus.Drawing.Shapes.Base+import Wumpus.Drawing.Shapes.Semicircle++import Wumpus.Basic.Kernel -- package: wumpus-basic++import Wumpus.Core -- package: wumpus-core+++++--------------------------------------------------------------------------------+-- Inverse semicircle++newtype InvSemicircle u = InvSemicircle { getInvSemicircle :: Semicircle u }++type instance DUnit (InvSemicircle u) = u+ +type DInvSemicircle = InvSemicircle Double+++instance Functor InvSemicircle where+ fmap f = InvSemicircle . fmap f . getInvSemicircle++--------------------------------------------------------------------------------+-- Affine trans++mapInner :: (Semicircle u -> Semicircle u) + -> InvSemicircle u + -> InvSemicircle u+mapInner f = InvSemicircle . f . getInvSemicircle+++instance (Real u, Floating u) => Rotate (InvSemicircle u) where+ rotate ang = mapInner (rotate ang)+ +instance (Real u, Floating u) => RotateAbout (InvSemicircle u) where+ rotateAbout ang pt = mapInner (rotateAbout ang pt)++instance Fractional u => Scale (InvSemicircle u) where+ scale sx sy = mapInner (scale sx sy)++instance InterpretUnit u => Translate (InvSemicircle u) where+ translate dx dy = mapInner (translate dx dy)++++--------------------------------------------------------------------------------+-- Anchors++runRotateAnchor :: (Real u, Floating u) + => (Semicircle u -> Anchor u) -> InvSemicircle u -> Anchor u+runRotateAnchor f (InvSemicircle a) = + let ctr = center a in rotateAbout pi ctr (f a)+++instance (Real u, Floating u) => + CenterAnchor (InvSemicircle u) where+ center = center . getInvSemicircle++instance (Real u, Floating u) => + ApexAnchor (InvSemicircle u) where+ apex = runRotateAnchor apex++instance (Real u, Floating u) => + TopCornerAnchor (InvSemicircle u) where+ topLeftCorner = runRotateAnchor bottomRightCorner+ topRightCorner = runRotateAnchor bottomLeftCorner++instance (Real u, Floating u) => + CardinalAnchor (InvSemicircle u) where+ north = runRotateAnchor south+ south = runRotateAnchor north+ east = runRotateAnchor west+ west = runRotateAnchor east+++instance (Real u, Floating u, InterpretUnit u, Tolerance u) => + CardinalAnchor2 (InvSemicircle u) where+ northeast = runRotateAnchor southwest+ southeast = runRotateAnchor northwest+ southwest = runRotateAnchor northeast+ northwest = runRotateAnchor southeast++++instance (Real u, Floating u, InterpretUnit u, Tolerance u) => + RadialAnchor (InvSemicircle u) where+ radialAnchor theta = + runRotateAnchor (radialAnchor $ circularModulo $ pi+theta)+++--------------------------------------------------------------------------------+-- Construction++-- | 'invsemicircle' : @ radius -> Shape @+--+invsemicircle :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> Shape InvSemicircle u+invsemicircle radius = + shapeMap InvSemicircle $ updatePathAngle (+ pi) $ semicircle radius
+ src/Wumpus/Drawing/Shapes/InvSemiellipse.hs view
@@ -0,0 +1,127 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Shapes.InvSemiellipse+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Inverse semiellipse. +-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Shapes.InvSemiellipse+ ( ++ InvSemiellipse+ , DInvSemiellipse+ , invsemiellipse++ ) where++import Wumpus.Drawing.Shapes.Base+import Wumpus.Drawing.Shapes.Semiellipse++import Wumpus.Basic.Kernel -- package: wumpus-basic++import Wumpus.Core -- package: wumpus-core+++++--------------------------------------------------------------------------------+-- Inverse semiellipse++newtype InvSemiellipse u = InvSemiellipse { getInvSemiellipse :: Semiellipse u }++type instance DUnit (InvSemiellipse u) = u+ +type DInvSemiellipse = InvSemiellipse Double++++instance Functor InvSemiellipse where+ fmap f = InvSemiellipse . fmap f . getInvSemiellipse++--------------------------------------------------------------------------------+-- Affine trans++mapInner :: (Semiellipse u -> Semiellipse u) + -> InvSemiellipse u + -> InvSemiellipse u+mapInner f = InvSemiellipse . f . getInvSemiellipse++instance (Real u, Floating u) => Rotate (InvSemiellipse u) where+ rotate ang = mapInner (rotate ang)+ +instance (Real u, Floating u) => RotateAbout (InvSemiellipse u) where+ rotateAbout ang pt = mapInner (rotateAbout ang pt)++instance Fractional u => Scale (InvSemiellipse u) where+ scale sx sy = mapInner (scale sx sy)++instance InterpretUnit u => Translate (InvSemiellipse u) where+ translate dx dy = mapInner (translate dx dy)++++--------------------------------------------------------------------------------+-- Anchors++runRotateAnchor :: (Real u, Floating u) + => (Semiellipse u -> Anchor u) -> InvSemiellipse u -> Anchor u+runRotateAnchor f (InvSemiellipse a) =+ let ctr = center a in rotateAbout pi ctr (f a)++++instance (Real u, Floating u, Tolerance u) => + CenterAnchor (InvSemiellipse u) where+ center = center . getInvSemiellipse++instance (Real u, Floating u, Tolerance u) => + ApexAnchor (InvSemiellipse u) where+ apex = runRotateAnchor apex++instance (Real u, Floating u, Tolerance u) => + TopCornerAnchor (InvSemiellipse u) where+ topLeftCorner = runRotateAnchor bottomRightCorner+ topRightCorner = runRotateAnchor bottomLeftCorner++instance (Real u, Floating u, InterpretUnit u, Tolerance u) => + CardinalAnchor (InvSemiellipse u) where+ north = runRotateAnchor south+ south = runRotateAnchor north+ east = runRotateAnchor west+ west = runRotateAnchor east+++instance (Real u, Floating u, InterpretUnit u, Tolerance u) => + CardinalAnchor2 (InvSemiellipse u) where+ northeast = runRotateAnchor southwest+ southeast = runRotateAnchor northwest+ southwest = runRotateAnchor northeast+ northwest = runRotateAnchor southeast++++instance (Real u, Floating u, InterpretUnit u, Tolerance u) => + RadialAnchor (InvSemiellipse u) where+ radialAnchor theta = + runRotateAnchor (radialAnchor $ circularModulo $ pi+theta)+++--------------------------------------------------------------------------------+-- Construction++-- | 'invsemiellipse' : @ rx * ry -> Shape @+--+invsemiellipse :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> u -> Shape InvSemiellipse u+invsemiellipse rx ry = + shapeMap InvSemiellipse $ updatePathAngle (+ pi) $ semiellipse rx ry
+ src/Wumpus/Drawing/Shapes/InvTriangle.hs view
@@ -0,0 +1,151 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Shapes.InvTriangle+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Inverse version of the Triangle shape.+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Shapes.InvTriangle+ ( ++ InvTriangle+ , DInvTriangle+ , invtriangle+++ ) where++import Wumpus.Drawing.Basis.Geometry+import Wumpus.Drawing.Shapes.Base+import Wumpus.Drawing.Shapes.Triangle++import Wumpus.Basic.Kernel -- package: wumpus-basic++import Wumpus.Core -- package: wumpus-core++++-- Datatype++newtype InvTriangle u = InvTriangle { getInvTriangle :: Triangle u }++type instance DUnit (InvTriangle u) = u+++type DInvTriangle = InvTriangle Double+++instance Functor InvTriangle where+ fmap f = InvTriangle . fmap f . getInvTriangle+++--------------------------------------------------------------------------------+-- Affine trans++mapInner :: (Triangle u -> Triangle u) -> InvTriangle u -> InvTriangle u+mapInner f = InvTriangle . f . getInvTriangle ++instance (Real u, Floating u) => Rotate (InvTriangle u) where+ rotate ang = mapInner (rotate ang)+ +instance (Real u, Floating u) => RotateAbout (InvTriangle u) where+ rotateAbout ang pt = mapInner (rotateAbout ang pt)++instance Fractional u => Scale (InvTriangle u) where+ scale sx sy = mapInner (scale sx sy)++instance InterpretUnit u => Translate (InvTriangle u) where+ translate dx dy = mapInner (translate dx dy)+++--------------------------------------------------------------------------------+-- Anchors++-- Anchors should be rotated about the center by pi...++runRotateAnchor :: (Real u, Floating u) + => (Triangle u -> Anchor u) -> InvTriangle u -> Anchor u+runRotateAnchor f (InvTriangle a) =+ let ctr = center a in rotateAbout pi ctr (f a)+++instance (Real u, Floating u) => + CenterAnchor (InvTriangle u) where+ center = center . getInvTriangle+++-- apex is same on InvTriangle as regular triangle++instance (Real u, Floating u) => + ApexAnchor (InvTriangle u) where+ apex = runRotateAnchor apex++-- Top corners are bottom corners of the wrapped triangle.+--+instance (Real u, Floating u) => + TopCornerAnchor (InvTriangle u) where+ topLeftCorner = runRotateAnchor bottomRightCorner+ topRightCorner = runRotateAnchor bottomLeftCorner+++-- Use established points on the InvTrangle - don\'t delegate to +-- the base Triangle.+--+instance (Real u, Floating u) => + SideMidpointAnchor (InvTriangle u) where+ sideMidpoint n a = step (n `mod` 3) + where+ step 1 = midpoint (topRightCorner a) (topLeftCorner a)+ step 2 = midpoint (topLeftCorner a) (apex a)+ step _ = midpoint (apex a) (topRightCorner a)++++-- east and west should be parallel to the centroid.+--++instance (Real u, Floating u) => + CardinalAnchor (InvTriangle u) where+ north = runRotateAnchor south+ south = runRotateAnchor north+ east = runRotateAnchor west+ west = runRotateAnchor east+++instance (Real u, Floating u, InterpretUnit u, Tolerance u) => + CardinalAnchor2 (InvTriangle u) where+ northeast = runRotateAnchor southwest+ southeast = runRotateAnchor northwest+ southwest = runRotateAnchor northeast+ northwest = runRotateAnchor southeast++++instance (Real u, Floating u, InterpretUnit u, Tolerance u) => + RadialAnchor (InvTriangle u) where+ radialAnchor theta = runRotateAnchor (radialAnchor $ circularModulo $ pi+theta)++--------------------------------------------------------------------------------+-- Construction++-- | 'invtriangle' : @ top_base_width * height -> Triangle @+--+--+invtriangle :: (Real u, Floating u, InterpretUnit u, Tolerance u)+ => u -> u -> Shape InvTriangle u+invtriangle bw h = + shapeMap InvTriangle $ updatePathAngle (+ pi) $ triangle bw h+ +++
+ src/Wumpus/Drawing/Shapes/Parallelogram.hs view
@@ -0,0 +1,240 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Shapes.Parallelogram+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Parallelogram.+--+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Shapes.Parallelogram+ ( ++ Parallelogram+ , DParallelogram+ , parallelogram+ , zparallelogram+++ ) where++import Wumpus.Drawing.Basis.Geometry+import Wumpus.Drawing.Basis.ShapeTrails+import Wumpus.Drawing.Paths+import Wumpus.Drawing.Paths.Intersection+import Wumpus.Drawing.Shapes.Base+++import Wumpus.Basic.Kernel -- package: wumpus-basic++import Wumpus.Core -- package: wumpus-core++import Data.VectorSpace -- package: vector-space++import Control.Applicative+++++--------------------------------------------------------------------------------+-- Parallelogram++-- | A Paralleogram.+--+data Parallelogram u = Parallelogram + { pll_ctm :: ShapeCTM u+ , pll_base_width :: !u+ , pll_height :: !u+ , pll_base_left_ang :: Radian+ }++type instance DUnit (Parallelogram u) = u+++type DParallelogram = Parallelogram Double+++instance Functor Parallelogram where+ fmap f (Parallelogram ctm bw h lang) = + Parallelogram (fmap f ctm) (f bw) (f h) lang++++--------------------------------------------------------------------------------+-- Affine trans++mapCTM :: (ShapeCTM u -> ShapeCTM u) -> Parallelogram u -> Parallelogram u+mapCTM f = (\s i -> s { pll_ctm = f i }) <*> pll_ctm+++instance (Real u, Floating u) => Rotate (Parallelogram u) where+ rotate ang = mapCTM (rotate ang)+ +instance (Real u, Floating u) => RotateAbout (Parallelogram u) where+ rotateAbout ang pt = mapCTM (rotateAbout ang pt)++instance Fractional u => Scale (Parallelogram u) where+ scale sx sy = mapCTM (scale sx sy)++instance InterpretUnit u => Translate (Parallelogram u) where+ translate dx dy = mapCTM (translate dx dy)++--------------------------------------------------------------------------------+-- Anchors++-- | 'runDisplaceCenter' : @ ( base_width+-- * height +-- * base_minor+-- -> Vec ) * parallelogram -> Point @+--+runDisplaceCenter :: (Real u, Floating u)+ => (u -> u -> Radian -> Vec2 u) -> Parallelogram u -> Anchor u+runDisplaceCenter fn (Parallelogram { pll_ctm = ctm+ , pll_base_width = bw+ , pll_height = h + , pll_base_left_ang = lang }) =+ projectFromCtr (fn bw h lang) ctm+++runDisplaceCenterHalves :: (Real u, Floating u)+ => (u -> u -> Radian -> Vec2 u) + -> Parallelogram u + -> Anchor u+runDisplaceCenterHalves fn = + runDisplaceCenter $ \bw h bl_ang -> fn (0.5*bw) (0.5*h) bl_ang+++instance (Real u, Floating u) => + CenterAnchor (Parallelogram u) where+ center = runDisplaceCenter $ \_ _ _ -> V2 0 0+++-- | WARNING - WRONG...++-- top anchors swap the base minor and major...+--++instance (Real u, Floating u) => + TopCornerAnchor (Parallelogram u) where+ topLeftCorner = runDisplaceCenterHalves $ \hw hh lang -> + let hypo = hh / (fromRadian $ sin lang) in hvec (-hw) ^+^ avec lang hypo++ topRightCorner = runDisplaceCenterHalves $ \hw hh lang ->+ let hypo = hh / (fromRadian $ sin lang) in hvec hw ^+^ avec lang hypo++instance (Real u, Floating u) => + BottomCornerAnchor (Parallelogram u) where+ bottomLeftCorner = runDisplaceCenterHalves $ \hw hh lang ->+ let hypo = hh / (fromRadian $ sin lang) in hvec (-hw) ^+^ avec lang (-hypo)++ bottomRightCorner = runDisplaceCenterHalves $ \hw hh lang -> + let hypo = hh / (fromRadian $ sin lang) in hvec hw ^+^ avec lang (-hypo)++++instance (Real u, Floating u) => + SideMidpointAnchor (Parallelogram u) where+ sideMidpoint n a = step (n `mod` 4) + where+ step 1 = midpoint (topRightCorner a) (topLeftCorner a)+ step 2 = midpoint (topLeftCorner a) (bottomLeftCorner a)+ step 3 = midpoint (bottomLeftCorner a) (bottomRightCorner a)+ step _ = midpoint (bottomRightCorner a) (topRightCorner a)++++instance (Real u, Floating u, InterpretUnit u, Tolerance u) => + CardinalAnchor (Parallelogram u) where+ north = radialAnchor (0.5 * pi)+ south = radialAnchor (1.5 * pi)+ east = radialAnchor 0+ west = radialAnchor pi+++instance (Real u, Floating u, InterpretUnit u, Tolerance u) => + CardinalAnchor2 (Parallelogram u) where+ northeast = radialAnchor (0.25*pi)+ southeast = radialAnchor (1.75*pi)+ southwest = radialAnchor (1.25*pi)+ northwest = radialAnchor (0.75*pi)++++instance (Real u, Floating u, InterpretUnit u, Tolerance u) => + RadialAnchor (Parallelogram u) where+ radialAnchor ang = runDisplaceCenter $ \bw h bl_ang-> + maybe zeroVec id $ pllRadialAnchor bw h bl_ang ang++++-- | Note - it is not worth changing this to a quadrantAlg.+--+-- There are pathological parallelograms that the current +-- QuadrantAlg code cannot handle, and a better abstraction is+-- needed (rather than better implementation of QuadrantAlg).+--+pllRadialAnchor :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> u -> Radian -> Radian -> Maybe (Vec2 u)+pllRadialAnchor bw h bl_ang ang =+ fmap (pvec zeroPt) $ rayPathIntersection (inclinedRay zeroPt ang) rp + where+ rp = anaTrailPath zeroPt $ parallelogram_trail bw h bl_ang+++--------------------------------------------------------------------------------+-- Construction+++-- | 'parallelogram' : @ width * height * bottom_left_ang -> Parallelogram @+--+--+parallelogram :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> u -> Radian -> Shape Parallelogram u+parallelogram bw h lang =+ makeShape (mkParallelogram bw h lang) (mkParallelogramPath bw h lang)+++-- | 'zparallelogram' : @ base_width * height -> Parallelogram @+--+--+zparallelogram :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> u -> Shape Parallelogram u+zparallelogram bw h = parallelogram bw h ang+ where+ ang = d2r (60::Double)+++--------------------------------------------------------------------------------+++mkParallelogram :: (Real u, Fractional u, InterpretUnit u, Tolerance u) + => u -> u -> Radian -> LocThetaQuery u (Parallelogram u)+mkParallelogram bw h bl_ang = qpromoteLocTheta $ \ctr theta -> + pure $ Parallelogram { pll_ctm = makeShapeCTM ctr theta+ , pll_base_width = bw+ , pll_height = h+ , pll_base_left_ang = bl_ang+ }+++++mkParallelogramPath :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> u -> Radian -> LocThetaQuery u (AbsPath u)+mkParallelogramPath bw h bl_ang = qpromoteLocTheta $ \ctr theta -> + return $ anaTrailPath ctr $ rparallelogram_trail bw h bl_ang theta++ ++++
src/Wumpus/Drawing/Shapes/Rectangle.hs view
@@ -1,5 +1,4 @@ {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-} {-# OPTIONS -Wall #-} --------------------------------------------------------------------------------@@ -13,6 +12,9 @@ -- Portability : GHC -- -- Rectangle shape.+--+-- Note - CardinalAnchor2 (northeast etc.) point to their radial +-- positions (this is a change since earlier versions). -- -------------------------------------------------------------------------------- @@ -25,12 +27,12 @@ ) where -import Wumpus.Drawing.Geometry.Intersection-import Wumpus.Drawing.Geometry.Paths+import Wumpus.Drawing.Basis.ShapeTrails import Wumpus.Drawing.Paths+import Wumpus.Drawing.Paths.Intersection import Wumpus.Drawing.Shapes.Base -import Wumpus.Basic.Kernel -- package: wumpus-basic+import Wumpus.Basic.Kernel import Wumpus.Core -- package: wumpus-core @@ -39,8 +41,7 @@ ------------------------------------------------------------------------------------ Rectangle+-- Data type data Rectangle u = Rectangle { rect_ctm :: ShapeCTM u@@ -49,88 +50,120 @@ } deriving (Eq,Ord,Show) +type instance DUnit (Rectangle u) = u+ type DRectangle = Rectangle Double -type instance DUnit (Rectangle u) = u+instance Functor Rectangle where+ fmap f (Rectangle ctm hw hh) = Rectangle (fmap f ctm) (f hw) (f hh) +--------------------------------------------------------------------------------+-- Affine trans -mapRectangleCTM :: (ShapeCTM u -> ShapeCTM u) -> Rectangle u -> Rectangle u-mapRectangleCTM f = (\s i -> s { rect_ctm = f i }) <*> rect_ctm+mapCTM :: (ShapeCTM u -> ShapeCTM u) -> Rectangle u -> Rectangle u+mapCTM f = (\s i -> s { rect_ctm = f i }) <*> rect_ctm -instance Num u => Scale (Rectangle u) where- scale sx sy = mapRectangleCTM (scale sx sy) -instance Rotate (Rectangle u) where- rotate ang = mapRectangleCTM (rotate ang)- -+instance (Real u, Floating u) => Rotate (Rectangle u) where+ rotate ang = mapCTM (rotate ang)+ instance (Real u, Floating u) => RotateAbout (Rectangle u) where- rotateAbout ang pt = mapRectangleCTM (rotateAbout ang pt)+ rotateAbout ang pt = mapCTM (rotateAbout ang pt) +instance Fractional u => Scale (Rectangle u) where+ scale sx sy = mapCTM (scale sx sy) -instance Num u => Translate (Rectangle u) where- translate dx dy = mapRectangleCTM (translate dx dy)+instance InterpretUnit u => Translate (Rectangle u) where+ translate dx dy = mapCTM (translate dx dy) +--------------------------------------------------------------------------------+-- Anchors -runRectangle :: (u -> u -> ShapeCTM u -> a) -> Rectangle u -> a-runRectangle fn (Rectangle { rect_ctm = ctm, rect_hw = hw, rect_hh = hh }) = - fn hw hh ctm+-- | 'runDisplaceCenter' : @ ( half_width+-- * half_height -> Vec ) * rectangle -> Point @+--+runDisplaceCenter :: (Real u, Floating u) + => (u -> u -> Vec2 u) -> Rectangle u -> Anchor u+runDisplaceCenter fn (Rectangle { rect_ctm = ctm+ , rect_hw = hw+ , rect_hh = hh }) = + projectFromCtr (fn hw hh) ctm -instance (Real u, Floating u) => CenterAnchor (Rectangle u) where- center = runRectangle (\ _ _ -> ctmCenter) -instance (Real u, Floating u) => CardinalAnchor (Rectangle u) where- north = runRectangle $ \_ hh -> projectPoint $ P2 0 hh- south = runRectangle $ \_ hh -> projectPoint $ P2 0 (-hh)- east = runRectangle $ \hw _ -> projectPoint $ P2 hw 0- west = runRectangle $ \hw _ -> projectPoint $ P2 (-hw) 0+instance (Real u, Floating u) => + CenterAnchor (Rectangle u) where+ center = runDisplaceCenter $ \_ _ -> V2 0 0 -instance (Real u, Floating u) => CardinalAnchor2 (Rectangle u) where- northeast = runRectangle $ \hw hh -> projectPoint $ P2 hw hh- southeast = runRectangle $ \hw hh -> projectPoint $ P2 hw (-hh)- southwest = runRectangle $ \hw hh -> projectPoint $ P2 (-hw) (-hh)- northwest = runRectangle $ \hw hh -> projectPoint $ P2 (-hw) hh+instance (Real u, Floating u) => + TopCornerAnchor (Rectangle u) where+ topLeftCorner = runDisplaceCenter $ \hw hh -> V2 (-hw) hh+ topRightCorner = runDisplaceCenter $ \hw hh -> V2 hw hh +instance (Real u, Floating u) => + BottomCornerAnchor (Rectangle u) where+ bottomLeftCorner = runDisplaceCenter $ \hw hh -> V2 (-hw) (-hh)+ bottomRightCorner = runDisplaceCenter $ \hw hh -> V2 hw (-hh) -instance (Real u, Floating u) => RadialAnchor (Rectangle u) where- radialAnchor theta = runRectangle $ \hw hh -> - projectPoint $ rectangleIntersect hw hh theta+instance (Real u, Floating u) => + SideMidpointAnchor (Rectangle u) where+ sideMidpoint n a = step (n `mod` 4) + where+ step 1 = north a+ step 2 = west a+ step 3 = south a+ step _ = east a --- Note - the answer needs projecting with the CTM...----rectangleIntersect :: (Real u, Floating u) - => u -> u -> Radian -> Point2 u-rectangleIntersect hw hh theta = - maybe zeroPt id $ findIntersect zeroPt theta $ rectangleLines zeroPt hw hh +instance (Real u, Floating u) => + CardinalAnchor (Rectangle u) where+ north = runDisplaceCenter $ \_ hh -> V2 0 hh+ south = runDisplaceCenter $ \_ hh -> V2 0 (-hh)+ east = runDisplaceCenter $ \hw _ -> V2 hw 0+ west = runDisplaceCenter $ \hw _ -> V2 (-hw) 0 +instance (Real u, Floating u, InterpretUnit u, Tolerance u) => + CardinalAnchor2 (Rectangle u) where+ northeast = radialAnchor (0.25*pi)+ southeast = radialAnchor (1.75*pi)+ southwest = radialAnchor (1.25*pi)+ northwest = radialAnchor (0.75*pi) ++instance (Real u, Floating u, InterpretUnit u, Tolerance u) => + RadialAnchor (Rectangle u) where+ radialAnchor ang = runDisplaceCenter $ \hw hh -> + maybe zeroVec id $ rectangleRadialIntersect (2*hw) (2*hh) ang+++--------------------------------------------------------------------------------+-- Construction++ -- | 'rectangle' : @ width * height -> shape @ ---rectangle :: (Real u, Floating u, FromPtSize u) - => u -> u -> LocShape u (Rectangle u)+rectangle :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> u -> Shape Rectangle u rectangle w h = - intoLocShape (mkRectangle (0.5*w) (0.5*h))- (mkRectPath (0.5*w) (0.5*h))+ makeShape (mkRectangle (0.5*w) (0.5*h)) (mkRectPath (0.5*w) (0.5*h)) -mkRectangle :: Num u => u -> u -> LocCF u (Rectangle u)-mkRectangle hw hh = promoteR1 $ \ctr -> - pure $ Rectangle { rect_ctm = makeShapeCTM ctr+mkRectangle :: InterpretUnit u => u -> u -> LocThetaQuery u (Rectangle u)+mkRectangle hw hh = qpromoteLocTheta $ \ctr theta -> + pure $ Rectangle { rect_ctm = makeShapeCTM ctr theta , rect_hw = hw , rect_hh = hh } +mkRectPath :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> u -> LocThetaQuery u (AbsPath u)+mkRectPath hw hh = qpromoteLocTheta $ \ctr theta -> + return $ anaTrailPath ctr $ rrectangle_trail (2*hw) (2*hh) theta -mkRectPath :: (Real u, Floating u, FromPtSize u) - => u -> u -> LocCF u (Path u)-mkRectPath hw hh = promoteR1 $ \ctr -> - let btm_left = displace (-hw) (-hh) ctr- in roundCornerShapePath $ rectangleCoordPath (2*hw) (2*hh) btm_left- ++
+ src/Wumpus/Drawing/Shapes/Semicircle.hs view
@@ -0,0 +1,193 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Shapes.Semicircle+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Semicircle. +-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Shapes.Semicircle+ ( ++ Semicircle+ , DSemicircle+ , semicircle++ ) where++import Wumpus.Drawing.Basis.ShapeTrails+import Wumpus.Drawing.Paths+import Wumpus.Drawing.Paths.Intersection+import Wumpus.Drawing.Shapes.Base++import Wumpus.Basic.Kernel -- package: wumpus-basic++import Wumpus.Core -- package: wumpus-core+++import Control.Applicative++++--------------------------------------------------------------------------------+-- Datatype++data Semicircle u = Semicircle + { sc_ctm :: ShapeCTM u+ , sc_radius :: !u + }++type instance DUnit (Semicircle u) = u++ +type DSemicircle = Semicircle Double+++instance Functor Semicircle where+ fmap f (Semicircle ctm r) = Semicircle (fmap f ctm) (f r)++++-- | Use the formula:+--+-- > 4r+-- > ---+-- > 3pi+--+-- to get the yminor.+--+++hminor :: Floating u => u -> u+hminor radius = (4 * radius) / (3 * pi)++hmajor :: Floating u => u -> u+hmajor radius = radius - hminor radius+++--------------------------------------------------------------------------------+-- Affine trans++mapCTM :: (ShapeCTM u -> ShapeCTM u) -> Semicircle u -> Semicircle u+mapCTM f = (\s i -> s { sc_ctm = f i }) <*> sc_ctm+++instance (Real u, Floating u) => Rotate (Semicircle u) where+ rotate ang = mapCTM (rotate ang)+ +instance (Real u, Floating u) => RotateAbout (Semicircle u) where+ rotateAbout ang pt = mapCTM (rotateAbout ang pt)++instance Fractional u => Scale (Semicircle u) where+ scale sx sy = mapCTM (scale sx sy)++instance InterpretUnit u => Translate (Semicircle u) where+ translate dx dy = mapCTM (translate dx dy)+++--------------------------------------------------------------------------------+-- Anchors++-- | 'runDisplaceCenter' : @ ( radius+-- * height_minor +-- * height_major -> Vec ) * semicircle -> Point @+--+runDisplaceCenter :: (Real u, Floating u) + => (u -> Vec2 u) -> Semicircle u -> Anchor u+runDisplaceCenter fn (Semicircle { sc_ctm = ctm+ , sc_radius = radius }) = + projectFromCtr (fn radius) ctm+++instance (Real u, Floating u) => + CenterAnchor (Semicircle u) where+ center = runDisplaceCenter $ \_ -> V2 0 0++instance (Real u, Floating u) => + ApexAnchor (Semicircle u) where+ apex = runDisplaceCenter $ \r -> V2 0 (hmajor r)++instance (Real u, Floating u) => + BottomCornerAnchor (Semicircle u) where+ bottomLeftCorner = runDisplaceCenter $ \r -> V2 (-r) (negate $ hminor r)+ bottomRightCorner = runDisplaceCenter $ \r -> V2 r (negate $ hminor r)++instance (Real u, Floating u) => + CardinalAnchor (Semicircle u) where+ north = apex+ south = runDisplaceCenter $ \r -> V2 0 (negate $ hminor r)+ east = runDisplaceCenter $ \r -> let x = pyth r (hminor r) in V2 x 0+ west = runDisplaceCenter $ \r -> let x = pyth r (hminor r) in V2 (-x) 0++-- | Use Pythagoras formula for working out the /east/ and /west/+-- distances. A right-triangle is formed below the centroid, +-- radius is the hypotenuese, hminor is the other side.+--+pyth :: Floating u => u -> u -> u+pyth hyp s1 = sqrt $ pow2 hyp - pow2 s1+ where+ pow2 = (^ (2::Int))+++instance (Real u, Floating u, InterpretUnit u, Tolerance u) => + CardinalAnchor2 (Semicircle u) where+ northeast = radialAnchor (0.25*pi)+ southeast = radialAnchor (1.75*pi)+ southwest = radialAnchor (1.25*pi)+ northwest = radialAnchor (0.75*pi)+++++instance (Real u, Floating u, InterpretUnit u, Tolerance u) => + RadialAnchor (Semicircle u) where+ radialAnchor ang = runDisplaceCenter $ \r ->+ maybe zeroVec id $ semicircleRadialAnchor r ang++++semicircleRadialAnchor :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> Radian -> Maybe (Vec2 u) +semicircleRadialAnchor r ang = + fmap (pvec zeroPt) $ rayPathIntersection (inclinedRay zeroPt ang) rp + where+ rp = anaTrailPath zeroPt $ semicircle_trail r+++--------------------------------------------------------------------------------+-- Construction++-- | 'semicircle' : @ radius -> Shape @+--+semicircle :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> Shape Semicircle u+semicircle radius = makeShape (mkSemicircle radius) (mkSemicirclePath radius)+ +++mkSemicircle :: InterpretUnit u+ => u -> LocThetaQuery u (Semicircle u)+mkSemicircle radius = qpromoteLocTheta $ \ctr theta -> + pure $ Semicircle { sc_ctm = makeShapeCTM ctr theta+ , sc_radius = radius+ }++++-- TODO - need to check other shapes to see if the are deriving +-- the center properly...+--+mkSemicirclePath :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> LocThetaQuery u (AbsPath u)+mkSemicirclePath radius = qpromoteLocTheta $ \ctr theta ->+ return $ anaTrailPath ctr $ rsemicircle_trail radius theta+
+ src/Wumpus/Drawing/Shapes/Semiellipse.hs view
@@ -0,0 +1,176 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Shapes.Semiellipse+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Semiellipse.+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Shapes.Semiellipse+ ( ++ Semiellipse+ , DSemiellipse+ , semiellipse++ ) where++import Wumpus.Drawing.Basis.ShapeTrails+import Wumpus.Drawing.Paths+import Wumpus.Drawing.Paths.Intersection+import Wumpus.Drawing.Shapes.Base++import Wumpus.Basic.Kernel -- package: wumpus-basic++import Wumpus.Core -- package: wumpus-core++import Control.Applicative++++--------------------------------------------------------------------------------+-- Datatype++data Semiellipse u = Semiellipse + { se_ctm :: ShapeCTM u+ , se_rx :: !u + , se_ry :: !u+ }++type instance DUnit (Semiellipse u) = u+++ +type DSemiellipse = Semiellipse Double++instance Functor Semiellipse where+ fmap f (Semiellipse ctm rx ry) = Semiellipse (fmap f ctm) (f rx) (f ry)++++ryminor :: Floating u => u -> u+ryminor ry = (4 * ry) / (3 * pi)++rymajor :: Floating u => u -> u+rymajor ry = ry - ryminor ry+++--------------------------------------------------------------------------------+-- Affine trans++mapCTM :: (ShapeCTM u -> ShapeCTM u) -> Semiellipse u -> Semiellipse u+mapCTM f = (\s i -> s { se_ctm = f i }) <*> se_ctm+++instance (Real u, Floating u) => Rotate (Semiellipse u) where+ rotate ang = mapCTM (rotate ang)+ +instance (Real u, Floating u) => RotateAbout (Semiellipse u) where+ rotateAbout ang pt = mapCTM (rotateAbout ang pt)++instance Fractional u => Scale (Semiellipse u) where+ scale sx sy = mapCTM (scale sx sy)++instance InterpretUnit u => Translate (Semiellipse u) where+ translate dx dy = mapCTM (translate dx dy)+++--------------------------------------------------------------------------------+-- Anchors+++-- | 'runDisplaceCenter' : @ ( rx+-- * ry +-- * ry_minor +-- * ry_major -> Vec ) * semiellipse -> Point @+--+runDisplaceCenter :: (Real u, Floating u) + => (u -> u -> Vec2 u) -> Semiellipse u -> Anchor u+runDisplaceCenter fn (Semiellipse { se_ctm = ctm+ , se_rx = rx+ , se_ry = ry }) = + projectFromCtr (fn rx ry) ctm+++++instance (Real u, Floating u) => + CenterAnchor (Semiellipse u) where+ center = runDisplaceCenter $ \_ _ -> V2 0 0++instance (Real u, Floating u, Tolerance u) => + ApexAnchor (Semiellipse u) where+ apex = runDisplaceCenter $ \_ ry -> V2 0 (rymajor ry)++instance (Real u, Floating u) => + BottomCornerAnchor (Semiellipse u) where+ bottomLeftCorner = runDisplaceCenter $ \rx ry -> V2 (-rx) (negate $ ryminor ry)+ bottomRightCorner = runDisplaceCenter $ \rx ry -> V2 rx (negate $ ryminor ry)+++instance (Real u, Floating u, InterpretUnit u, Tolerance u) => + CardinalAnchor (Semiellipse u) where+ north = apex+ south = runDisplaceCenter $ \_ ry -> V2 0 (negate $ ryminor ry)+ east = radialAnchor 0+ west = radialAnchor pi++instance (Real u, Floating u, InterpretUnit u, Tolerance u) => + CardinalAnchor2 (Semiellipse u) where+ northeast = radialAnchor (0.25*pi)+ southeast = radialAnchor (1.75*pi)+ southwest = radialAnchor (1.25*pi)+ northwest = radialAnchor (0.75*pi)++++instance (Real u, Floating u, InterpretUnit u, Tolerance u) => + RadialAnchor (Semiellipse u) where+ radialAnchor ang = runDisplaceCenter $ \rx ry ->+ maybe zeroVec id $ semiellipseRadialAnchor rx ry ang+++semiellipseRadialAnchor :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> u -> Radian -> Maybe (Vec2 u) +semiellipseRadialAnchor rx ry ang = + fmap (pvec zeroPt) $ rayPathIntersection (inclinedRay zeroPt ang) rp + where+ rp = anaTrailPath zeroPt $ semiellipse_trail rx ry+++--------------------------------------------------------------------------------+-- Construction+++-- | 'semiellipse' : @ x_radius * y_radius -> Shape @+--+semiellipse :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> u -> Shape Semiellipse u+semiellipse rx ry = makeShape (mkSemiellipse rx ry) (mkSemiellipsePath rx ry)+ ++++mkSemiellipse :: InterpretUnit u + => u -> u -> LocThetaQuery u (Semiellipse u)+mkSemiellipse rx ry = qpromoteLocTheta $ \ctr theta -> + pure $ Semiellipse { se_ctm = makeShapeCTM ctr theta+ , se_rx = rx+ , se_ry = ry+ }+++mkSemiellipsePath :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> u -> LocThetaQuery u (AbsPath u)+mkSemiellipsePath rx ry = qpromoteLocTheta $ \ctr theta ->+ return $ anaTrailPath ctr $ rsemiellipse_trail rx ry theta+
+ src/Wumpus/Drawing/Shapes/Trapezium.hs view
@@ -0,0 +1,220 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Shapes.Trapezium+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Isoceles Trapezium.+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Shapes.Trapezium+ ( ++ Trapezium+ , DTrapezium+ , trapezium+++ ) where++import Wumpus.Drawing.Basis.ShapeTrails+import Wumpus.Drawing.Paths+import Wumpus.Drawing.Paths.Intersection+import Wumpus.Drawing.Shapes.Base++import Wumpus.Basic.Kernel -- package: wumpus-basic++import Wumpus.Core -- package: wumpus-core++++import Control.Applicative+++++--------------------------------------------------------------------------------+-- Trapezium++-- | A trapezium.+--+data Trapezium u = Trapezium + { tz_ctm :: ShapeCTM u+ , tz_base_width :: !u+ , tz_top_width :: !u+ , tz_height :: !u+ , tz_bottom_left_ang :: Radian + }++type instance DUnit (Trapezium u) = u++type DTrapezium = Trapezium Double++instance Functor Trapezium where+ fmap f (Trapezium ctm bw tw h ang) = + Trapezium (fmap f ctm) (f bw) (f tw) (f h) ang++--------------------------------------------------------------------------------+-- Affine trans++mapCTM :: (ShapeCTM u -> ShapeCTM u) -> Trapezium u -> Trapezium u+mapCTM f = (\s i -> s { tz_ctm = f i }) <*> tz_ctm+++instance (Real u, Floating u) => Rotate (Trapezium u) where+ rotate ang = mapCTM (rotate ang)+ +instance (Real u, Floating u) => RotateAbout (Trapezium u) where+ rotateAbout ang pt = mapCTM (rotateAbout ang pt)++instance Fractional u => Scale (Trapezium u) where+ scale sx sy = mapCTM (scale sx sy)++instance InterpretUnit u => Translate (Trapezium u) where+ translate dx dy = mapCTM (translate dx dy)+++--------------------------------------------------------------------------------+-- Anchors+++-- | 'runDisplaceCenter' : @ ( base_width +-- * height+-- * bl_ang -> Vec ) * trapezium -> Point @+--+runDisplaceCenter :: (Real u, Floating u)+ => (u -> u -> Radian -> Vec2 u) + -> Trapezium u -> Anchor u+runDisplaceCenter fn (Trapezium { tz_ctm = ctm+ , tz_base_width = bw+ , tz_height = h + , tz_bottom_left_ang = bl_ang }) =+ projectFromCtr (fn bw h bl_ang) ctm+++-- | 'runDisplaceCenterHalves' : @ ( half_base_width +-- * half_top_width+-- * half_height -> Vec ) * trapezium -> Point @+--+runDisplaceCenterHalves :: (Real u, Floating u)+ => (u -> u -> u -> Vec2 u) + -> Trapezium u -> Anchor u+runDisplaceCenterHalves fn (Trapezium { tz_ctm = ctm+ , tz_base_width = bw+ , tz_top_width = tw+ , tz_height = h }) =+ projectFromCtr (fn (0.5 * bw) (0.5 * tw) (0.5 * h)) ctm+++instance (Real u, Floating u) => + CenterAnchor (Trapezium u) where+ center = runDisplaceCenter $ \_ _ _ -> V2 0 0++++instance (Real u, Floating u) => + BottomCornerAnchor (Trapezium u) where+ bottomLeftCorner = runDisplaceCenterHalves $ \hbw _ hh -> V2 (-hbw) (-hh)+ bottomRightCorner = runDisplaceCenterHalves $ \hbw _ hh -> V2 hbw (-hh)++++++instance (Real u, Floating u) => + TopCornerAnchor (Trapezium u) where+ topLeftCorner = runDisplaceCenterHalves $ \_ htw hh -> V2 (-htw) hh+ topRightCorner = runDisplaceCenterHalves $ \_ htw hh -> V2 htw hh+++instance (Real u, Floating u, Tolerance u) => + SideMidpointAnchor (Trapezium u) where+ sideMidpoint n a = step (n `mod` 4) + where+ step 1 = north a+ step 2 = west a+ step 3 = south a+ step _ = east a++++instance (Real u, Floating u, Tolerance u) => + CardinalAnchor (Trapezium u) where+ north = radialAnchor half_pi+ south = radialAnchor (1.5 * pi)+ east = radialAnchor 0+ west = radialAnchor pi+++instance (Real u, Floating u, Tolerance u) => + CardinalAnchor2 (Trapezium u) where+ northeast = radialAnchor (0.25*pi)+ southeast = radialAnchor (1.75*pi)+ southwest = radialAnchor (1.25*pi)+ northwest = radialAnchor (0.75*pi)++++instance (Real u, Floating u, Tolerance u) => + RadialAnchor (Trapezium u) where+ radialAnchor ang = runDisplaceCenter $ \bw h bl_ang -> + maybe zeroVec id $ trapeziumRadialAnchor bw h bl_ang ang+++-- +trapeziumRadialAnchor :: (Real u, Floating u, Tolerance u) + => u -> u -> Radian -> Radian -> Maybe (Vec2 u)+trapeziumRadialAnchor bw h bl_ang ang =+ fmap (pvec zeroPt) $ rayPathIntersection (inclinedRay zeroPt ang) rp + where+ rp = anaTrailPath zeroPt $ trapezium_trail bw h bl_ang+++ +--------------------------------------------------------------------------------+-- Construction+++-- | 'trapezium' : @ base_width * height * bottom_left_ang * +-- bottom_right_ang -> Shape @+--+--+trapezium :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> u -> Radian -> Shape Trapezium u+trapezium bw h base_ang = + makeShape (mkTrapezium bw h base_ang) (mkTrapeziumPath bw h base_ang)+++++--------------------------------------------------------------------------------+++mkTrapezium :: (Real u, Fractional u, InterpretUnit u) + => u -> u -> Radian -> LocThetaQuery u (Trapezium u)+mkTrapezium bw h base_ang = qpromoteLocTheta $ \ctr theta -> + pure $ Trapezium { tz_ctm = makeShapeCTM ctr theta+ , tz_base_width = bw+ , tz_top_width = tw+ , tz_height = h+ , tz_bottom_left_ang = base_ang+ }+ where+ base_minor = h / (fromRadian $ tan base_ang)+ tw = bw - (2 * base_minor)+++mkTrapeziumPath :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> u -> Radian -> LocThetaQuery u (AbsPath u)+mkTrapeziumPath bw h bl_ang = qpromoteLocTheta $ \ctr theta -> + return $ anaTrailPath ctr $ rtrapezium_trail bw h bl_ang theta++
+ src/Wumpus/Drawing/Shapes/Triangle.hs view
@@ -0,0 +1,203 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Shapes.Triangle+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Isosceles triangle.+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Shapes.Triangle+ ( ++ Triangle+ , DTriangle+ , triangle++ ) where++import Wumpus.Drawing.Basis.ShapeTrails+import Wumpus.Drawing.Basis.Geometry+import Wumpus.Drawing.Paths+import Wumpus.Drawing.Paths.Intersection+import Wumpus.Drawing.Shapes.Base++import Wumpus.Basic.Kernel -- package: wumpus-basic++import Wumpus.Core -- package: wumpus-core+++import Control.Applicative+++++-- Datatype++-- | An isosceles triangle, oriented /upwards/.+--+data Triangle u = Triangle + { tri_ctm :: ShapeCTM u+ , tri_base_width :: !u+ , tri_height :: !u+ }++type instance DUnit (Triangle u) = u++type DTriangle = Triangle Double++instance Functor Triangle where+ fmap f (Triangle ctm bw h) = Triangle (fmap f ctm) (f bw) (f h)+++hminor :: Fractional u => u -> u +hminor h = h / 3++hmajor :: Fractional u => u -> u +hmajor h = 2 * (h / 3)++++--------------------------------------------------------------------------------+-- Affine trans++mapCTM :: (ShapeCTM u -> ShapeCTM u) -> Triangle u -> Triangle u+mapCTM f = (\s i -> s { tri_ctm = f i }) <*> tri_ctm+++instance (Real u, Floating u) => Rotate (Triangle u) where+ rotate ang = mapCTM (rotate ang)+ +instance (Real u, Floating u) => RotateAbout (Triangle u) where+ rotateAbout ang pt = mapCTM (rotateAbout ang pt)++instance Fractional u => Scale (Triangle u) where+ scale sx sy = mapCTM (scale sx sy)++instance InterpretUnit u => Translate (Triangle u) where+ translate dx dy = mapCTM (translate dx dy)++--------------------------------------------------------------------------------+-- Anchors++-- | 'runDisplaceCenter' : @ ( half_base_width +-- * height_minor +-- * height_major +-- * base_ang -> Vec ) * traingle -> Point @+--+runDisplaceCenter :: (Real u, Floating u)+ => (u -> u -> Vec2 u) -> Triangle u -> Anchor u+runDisplaceCenter fn (Triangle { tri_ctm = ctm+ , tri_base_width = bw + , tri_height = h }) = + projectFromCtr (fn bw h) ctm+++instance (Real u, Floating u) => + CenterAnchor (Triangle u) where+ center = runDisplaceCenter $ \_ _ -> V2 0 0+++instance (Real u, Floating u) => + ApexAnchor (Triangle u) where+ apex = runDisplaceCenter $ \_ h -> V2 0 (hmajor h)+++instance (Real u, Floating u) => + BottomCornerAnchor (Triangle u) where+ bottomLeftCorner = runDisplaceCenter $ \bw h -> + V2 (negate $ 0.5 * bw) (negate $ hminor h)+ bottomRightCorner = runDisplaceCenter $ \bw h -> + V2 (0.5 * bw) (negate $ hminor h)+++-- east and west should be parallel to the centroid.+--++instance (Real u, Floating u) => + CardinalAnchor (Triangle u) where+ north = runDisplaceCenter $ \_ h -> V2 0 (hmajor h)+ south = runDisplaceCenter $ \_ h -> V2 0 (negate $ hminor h)+ east = runDisplaceCenter $ \bw h -> findEast bw h+ west = runDisplaceCenter $ \bw h -> findWest bw h+++instance (Real u, Floating u) => + SideMidpointAnchor (Triangle u) where+ sideMidpoint n a = step (n `mod` 3) + where+ step 1 = midpoint (apex a) (bottomLeftCorner a)+ step 2 = midpoint (bottomLeftCorner a) (bottomRightCorner a)+ step _ = midpoint (bottomRightCorner a) (apex a)+++findEast :: (Real u, Fractional u) => u -> u -> Vec2 u+findEast bw h = V2 xdist 0+ where+ half_base = 0.5 * bw + base_ang = atan $ toRadian (h / half_base)+ b1 = (hminor h) / (fromRadian $ tan base_ang)+ xdist = (0.5 * bw) - b1++findWest :: (Real u, Fractional u) => u -> u -> Vec2 u+findWest bw h = let (V2 xdist 0) = findEast bw h in V2 (-xdist) 0 ++++instance (Real u, Floating u, InterpretUnit u, Tolerance u) => + CardinalAnchor2 (Triangle u) where+ northeast = radialAnchor (0.25*pi)+ southeast = radialAnchor (1.75*pi)+ southwest = radialAnchor (1.25*pi)+ northwest = radialAnchor (0.75*pi)++++instance (Real u, Floating u, InterpretUnit u, Tolerance u) => + RadialAnchor (Triangle u) where+ radialAnchor ang = runDisplaceCenter $ \bw h -> + maybe zeroVec id $ isoscelesTriangleRadialIntersect bw h ang+++++ +--------------------------------------------------------------------------------+-- Construction++-- | 'triangle' : @ base_width * height -> Shape @+--+--+triangle :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> u -> Shape Triangle u+triangle bw h = makeShape (mkTriangle bw h) (mkTrianglePath bw h)++++++mkTriangle :: (Real u, Fractional u, InterpretUnit u) + => u -> u -> LocThetaQuery u (Triangle u)+mkTriangle bw h = qpromoteLocTheta $ \ctrd theta -> + pure $ Triangle { tri_ctm = makeShapeCTM ctrd theta+ , tri_base_width = bw+ , tri_height = h + }++++++mkTrianglePath :: (Real u, Floating u, InterpretUnit u, Tolerance u) + => u -> u -> LocThetaQuery u (AbsPath u)+mkTrianglePath bw h = qpromoteLocTheta $ \ctr theta -> + return $ anaTrailPath ctr $ risosceles_triangle_trail bw h theta+
+ src/Wumpus/Drawing/Text/Base/DocTextZero.hs view
@@ -0,0 +1,314 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Text.Base.DocTextZero+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : unstable+-- Portability : GHC+--+-- Flexible text type, composable with @pretty-print@ style +-- operators.+-- +-- Direction zero (left-to-right) only.+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Text.Base.DocTextZero+ ( ++ GenDoc+ , Doc + , GenDocGraphic+ , DocGraphic+ , runGenDoc++ , (<+>)+ , blank+ , space+ , string+ , escaped+ , embedPosObject+ + , bold+ , italic+ , boldItalic++ , monospace+ , int + , integer+ , float+ , ffloat++ , strikethrough+ , underline+ , highlight++ ) where+++import Wumpus.Basic.Kernel -- package: wumpus-basic++import Wumpus.Core -- package: wumpus-core++import Control.Applicative+import Data.Monoid+import Numeric++--+-- Design Issue:+--+-- Can user state be added to Doc?+--+-- Easy if PosObject had user state, difficult as it doesn\'t...+--+ +-- | Doc type.+--+newtype GenDoc st u a = GenDoc { getGenDoc :: DocEnv -> GenPosObject st u a } ++type instance DUnit (GenDoc st u a) = u+type instance UState (GenDoc st u a) = st++type GenDocGraphic st u = GenDoc st u (UNil u)++type Doc u a = GenDoc () u a++type DocGraphic u = Doc u (UNil u)+++data DocEnv = DocEnv + { doc_alignment :: VAlign+ , doc_font_family :: FontFamily+ }++instance Functor (GenDoc st u) where+ fmap f ma = GenDoc $ \env -> fmap f $ getGenDoc ma env++instance Applicative (GenDoc st u) where+ pure a = GenDoc $ \_ -> pure a+ mf <*> ma = GenDoc $ \env -> getGenDoc mf env <*> getGenDoc ma env+++instance Monad (GenDoc st u) where+ return a = GenDoc $ \_ -> return a+ ma >>= k = GenDoc $ \env -> getGenDoc ma env >>= \a -> getGenDoc (k a) env++instance (Monoid a, InterpretUnit u) => Monoid (GenDoc st u a) where+ mempty = GenDoc $ \_ -> mempty+ ma `mappend` mb = GenDoc $ \env -> getGenDoc ma env `hconcat` getGenDoc mb env+++instance DrawingCtxM (GenDoc st u) where+ askDC = GenDoc $ \_ -> askDC + asksDC fn = GenDoc $ \_ -> asksDC fn+ localize upd ma = GenDoc $ \env -> localize upd (getGenDoc ma env)+++instance UserStateM (GenDoc st u) where+ getState = GenDoc $ \_ -> getState+ setState s = GenDoc $ \_ -> setState s+ updateState upd = GenDoc $ \_ -> updateState upd+++runGenDoc :: VAlign -> FontFamily -> GenDoc st u a -> GenPosObject st u a+runGenDoc va ff ma = getGenDoc ma env1 + where+ env1 = DocEnv { doc_alignment = va, doc_font_family = ff }++++--------------------------------------------------------------------------------+-- Get vcat vconcat... from the Concat class++instance (Monoid a, Fractional u, InterpretUnit u) => + Concat (GenDoc st u a) where+ hconcat = mappend+ vconcat = vcatImpl++vcatImpl :: (Monoid a, Fractional u, InterpretUnit u) + => GenDoc st u a -> GenDoc st u a -> GenDoc st u a+vcatImpl ma mb = GenDoc $ \env -> + let va = doc_alignment env + in textlineSpace >>= \sep -> + valignSpace va sep (getGenDoc ma env) (getGenDoc mb env)++--------------------------------------------------------------------------------+-- Primitives++infixr 6 <+>++-- | Concatenate two Docs separated with a space.+--+-- (infixr 6)+--+(<+>) :: InterpretUnit u + => GenDocGraphic st u -> GenDocGraphic st u -> GenDocGraphic st u+a <+> b = a `mappend` space `mappend` b ++++blank :: InterpretUnit u => GenDocGraphic st u+blank = GenDoc $ \_ -> posTextPrim (Left "")++space :: InterpretUnit u => GenDocGraphic st u+space = GenDoc $ \_ -> posCharPrim (Left ' ')+++string :: InterpretUnit u => String -> GenDocGraphic st u+string ss = GenDoc $ \_ -> posTextPrim (Left ss)++++escaped :: InterpretUnit u => EscapedText -> GenDocGraphic st u+escaped esc = GenDoc $ \_ -> posTextPrim (Right esc)++embedPosObject :: GenPosObject st u a -> GenDoc st u a+embedPosObject ma = GenDoc $ \_ -> ma++++--------------------------------------------------------------------------------+-- Change font weight++bold :: GenDoc st u a -> GenDoc st u a +bold ma = GenDoc $ \env -> + localize (set_font $ boldWeight $ doc_font_family env)+ (getGenDoc ma env)+++italic :: GenDoc st u a -> GenDoc st u a +italic ma = GenDoc $ \env -> + localize (set_font $ italicWeight $ doc_font_family env)+ (getGenDoc ma env)+++boldItalic :: GenDoc st u a -> GenDoc st u a +boldItalic ma = GenDoc $ \env -> + localize (set_font $ boldItalicWeight $ doc_font_family env)+ (getGenDoc ma env)+++--------------------------------------------------------------------------------+-- Monospace++monospace :: InterpretUnit u + => EscapedChar -> EscapedText -> GenDocGraphic st u+monospace ref_ch esc = GenDoc $ \_ -> + monospaceEscText (vector_x <$> escCharVector ref_ch) esc+++++int :: InterpretUnit u => Int -> GenDocGraphic st u+int i = integer $ fromIntegral i+++integer :: InterpretUnit u => Integer -> GenDocGraphic st u+integer i = monospace (CharLiteral '0') (escapeString $ show i)++++--------------------------------------------------------------------------------+++-- | Specialized version of 'ffloat' - the answer is always +-- rendered at \"full precision\".+--+float :: (RealFloat a, InterpretUnit u) => a -> GenDocGraphic st u+float = ffloat Nothing+++-- | This is equivalent to 'showFFloat' in the Numeric module.+-- +-- Like 'showFFloat', the answer is rendered to supplied +-- precision. @Nothing@ indicated full precision.+--+ffloat :: (RealFloat a, InterpretUnit u) + => (Maybe Int) -> a -> GenDocGraphic st u+ffloat mb d = + monospace (CharLiteral '0') $ escapeString $ ($ "") $ showFFloat mb d+++++++--------------------------------------------------------------------------------+-- Decorate++strikethrough :: (Fractional u, InterpretUnit u) + => GenDoc st u a -> GenDoc st u a+strikethrough = decorateDoc ZABOVE drawStrikethrough ++underline :: (Fractional u, InterpretUnit u) + => GenDoc st u a -> GenDoc st u a+underline = decorateDoc ZABOVE drawUnderline++highlight :: (Fractional u, InterpretUnit u) + => RGBi -> GenDoc st u a -> GenDoc st u a+highlight rgb = decorateDoc ZBELOW (drawBackfill rgb) + +++decorateDoc :: InterpretUnit u + => ZOrder -> (Orientation u -> LocGraphic u) -> GenDoc st u a + -> GenDoc st u a+decorateDoc zdec fn ma = GenDoc $ \env -> + decoratePosObject zdec fn $ getGenDoc ma env+++ ++-- API might be simple if we conditionally apply strikethrough on +-- interpText (possibly including spaces), but never on interpSpace.+--+-- Might want to derive stroke_colour from text_colour and linewidth+-- fromf font size as well...+--+drawStrikethrough :: (Fractional u, InterpretUnit u) + => Orientation u -> LocGraphic u+drawStrikethrough (Orientation xmin xmaj _ ymaj) = + linestyle $ moveStart (vec (-xmin) vpos) ln+ where+ vpos = 0.45 * ymaj+ ln = locStraightLine (hvec $ xmin + xmaj)++++drawUnderline :: (Fractional u, InterpretUnit u) + => Orientation u -> LocGraphic u+drawUnderline (Orientation xmin xmaj _ _) = + underlinePosition >>= \vpos ->+ linestyle $ moveStart (vec (-xmin) vpos) ln+ where+ ln = locStraightLine (hvec $ xmin + xmaj)+++-- | This uses underline_thickness ...+--+linestyle :: LocGraphic u -> LocGraphic u+linestyle mf = + underlineThickness >>= \sz -> + localize (stroke_use_text_colour . set_line_width sz) mf+++-- | Note - quarter margin looks good.+--+drawBackfill :: (Fractional u, InterpretUnit u) + => RGBi -> Orientation u -> LocGraphic u+drawBackfill rgb (Orientation xmin xmaj ymin ymaj) = + textMargin >>= \(dx,dy) -> + let hdx = 0.25 * dx+ hdy = 0.25 * dy + in localize (fill_colour rgb) $ moveStart (mkVec hdx hdy) (mkRect hdx hdy)+ where+ mkVec dx dy = vec (negate $ xmin+dx) (negate $ ymin+dy)+ mkRect dx dy = let w = dx + xmin + xmaj + dx+ h = dy + ymin + ymaj + dy+ in dcRectangle DRAW_FILL w h+
+ src/Wumpus/Drawing/Text/Base/Label.hs view
@@ -0,0 +1,217 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Text.Base.Label+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : unstable+-- Portability : GHC+--+-- Annotation labels.+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Text.Base.Label+ ( ++ locImageLabel++ , label_center_of+ , label_left_of+ , label_right_of+ , label_above+ , label_below++ , connectorPathLabel+ , label_midway_of+ , label_atstart_of+ , label_atend_of++ , centerRelative+ , right_of+ , left_of+ , above_right_of+ , below_right_of+ , above_left_of+ , below_left_of++ ) where+++import Wumpus.Drawing.Paths++import Wumpus.Basic.Kernel -- package: wumpus-basic++import Wumpus.Core -- package: wumpus-core+++type BoundedLocRectGraphic u = RectAddress -> LocImage u (BoundingBox u)+++locImageLabel :: InterpretUnit u + => (a -> Anchor u) + -> RectAddress + -> (RectAddress -> LocImage u (BoundingBox u)) + -> LocImage u a + -> LocImage u a+locImageLabel fn rpos mklabel obj = promoteLoc $ \pt -> + elaborateAbove (obj `at` pt) (\a -> ignoreAns $ mklabel rpos `at` fn a)++++label_center_of :: (InterpretUnit u, CenterAnchor a, u ~ DUnit a) + => BoundedLocRectGraphic u -> LocImage u a -> LocImage u a+label_center_of = locImageLabel center CENTER+++label_left_of :: (InterpretUnit u, CardinalAnchor a, u ~ DUnit a) + => BoundedLocRectGraphic u -> LocImage u a -> LocImage u a+label_left_of = locImageLabel west EE++label_right_of :: (InterpretUnit u, CardinalAnchor a, u ~ DUnit a) + => BoundedLocRectGraphic u -> LocImage u a -> LocImage u a+label_right_of = locImageLabel east WW+++label_above :: (InterpretUnit u, CardinalAnchor a, u ~ DUnit a) + => BoundedLocRectGraphic u -> LocImage u a -> LocImage u a+label_above = locImageLabel north SS+++label_below :: (InterpretUnit u, CardinalAnchor a, u ~ DUnit a) + => BoundedLocRectGraphic u -> LocImage u a -> LocImage u a+label_below = locImageLabel south NN+++++connectorPathLabel :: InterpretUnit u + => (AbsPath u -> Point2 u) + -> RectAddress+ -> BoundedLocRectGraphic u+ -> Image u (AbsPath u) + -> Image u (AbsPath u)+connectorPathLabel fn rpos lbl img = + elaborateAbove img (\a -> ignoreAns $ lbl rpos `at` (fn a))+++label_midway_of :: (Real u, Floating u, InterpretUnit u) + => RectAddress + -> BoundedLocRectGraphic u + -> Image u (AbsPath u) -> Image u (AbsPath u)+label_midway_of = connectorPathLabel midway_+++label_atstart_of :: (Real u, Floating u, InterpretUnit u) + => RectAddress + -> BoundedLocRectGraphic u + -> Image u (AbsPath u) -> Image u (AbsPath u)+label_atstart_of = connectorPathLabel atstart_+++label_atend_of :: (Real u, Floating u, InterpretUnit u) + => RectAddress + -> BoundedLocRectGraphic u+ -> Image u (AbsPath u) -> Image u (AbsPath u)+label_atend_of = connectorPathLabel atend_+++++-- | Absolute units.+-- +centerRelative :: (CenterAnchor a, Fractional u, InterpretUnit u, u ~ DUnit a) + => (Int,Int) -> a -> Query u (Anchor u)+centerRelative coord a = + snapmove coord >>= \v -> return $ displace v (center a)++-- TODO - These are really for Anchors.+--+-- Should the have a separate module or be rolled into the same+-- module as the classes?+--++-- | Value is 1 snap unit right.+--+-- This function should be considered obsolete, pending a +-- re-think.+-- +right_of :: (CenterAnchor a, Fractional u, InterpretUnit u, u ~ DUnit a) + => a -> Query u (Anchor u)+right_of = centerRelative (1,0)++-- | Value is 1 snap move left.+--+-- This function should be considered obsolete, pending a +-- re-think.+-- +left_of :: (CenterAnchor a, Fractional u, InterpretUnit u, u ~ DUnit a) + => a -> Query u (Anchor u)+left_of = centerRelative ((-1),0)++-- | Value is 1 snap move up, 1 snap move right.+--+-- This function should be considered obsolete, pending a +-- re-think.+-- +above_right_of :: (CenterAnchor a, Fractional u, InterpretUnit u, u ~ DUnit a) + => a -> Query u (Anchor u)+above_right_of = centerRelative (1,1)++-- | Value is 1 snap move below, 1 snap move right.+--+-- This function should be considered obsolete, pending a +-- re-think.+-- +below_right_of :: (CenterAnchor a, Fractional u, InterpretUnit u, u ~ DUnit a) + => a -> Query u (Anchor u)+below_right_of = centerRelative (1, (-1))++-- | Value is 1 snap move up, 1 snap move left.+--+-- This function should be considered obsolete, pending a +-- re-think.+-- +above_left_of :: (CenterAnchor a, Fractional u, InterpretUnit u, u ~ DUnit a) + => a -> Query u (Anchor u)+above_left_of = centerRelative ((-1),1)++-- | Value is 1 snap move down, 1 snap move left.+--+-- This function should be considered obsolete, pending a +-- re-think.+-- +below_left_of :: (CenterAnchor a, Fractional u, InterpretUnit u, u ~ DUnit a) + => a -> Query u (Anchor u)+below_left_of = centerRelative ((-1),(-1))+ ++++-- TikZ has label=below etc.+-- +-- This would probably translate to a functions:+-- @labelBelow@+--+++-- Design note - there aren\'t many Images that support anchors,+-- except for LocImages that have been /saturated/ (i.e. applied +-- to a point with @at@).+-- +-- For a saturated Image, getting at the anchors via bind does +-- not seem so bad (indeed, this was the original point of +-- anchors). Obviously it is important to add labels to LocImages+-- (the original point of the label functions) but what about +-- LocThetaImages and LocRectImages. Is it acceptable to /saturate/+-- them to LocImages before labelling them?+-- +-- Connectors support different /anchor-like/ positions so they +-- will different labelling functions.+-- + +
+ src/Wumpus/Drawing/Text/DirectionZero.hs view
@@ -0,0 +1,53 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.DirectionZero+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : unstable+-- Portability : GHC+--+-- Common import module for the Writing Direction 0 modules+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Text.DirectionZero+ ( + module Wumpus.Drawing.Text.Base.DocTextZero++ , textline+ , rtextline+ , multilineText++ ) where++import Wumpus.Drawing.Text.Base.DocTextZero++import Wumpus.Basic.Kernel -- package: wumpus-basic++import Wumpus.Core -- package: wumpus-core++-- | Note - this is likely to be moved...+-- +-- Also, reversed argument order would be more convenient as +-- RectAddress always short but String could be long. +--+textline :: InterpretUnit u => RectAddress -> String -> BoundedLocGraphic u+textline addr ss = runPosObjectBBox addr (posText ss)+++-- | Note - this is likely to be moved too...+--+rtextline :: (Real u, Floating u, InterpretUnit u)+ => Radian -> RectAddress -> String -> BoundedLocGraphic u+rtextline ang addr ss = runPosObjectBBox addr (rposText ang ss)+++multilineText :: (Fractional u, InterpretUnit u)+ => VAlign -> RectAddress -> String -> BoundedLocGraphic u+multilineText va addr ss = runPosObjectBBox addr (multilinePosText va ss)++
+ src/Wumpus/Drawing/Text/DocSymbols.hs view
@@ -0,0 +1,78 @@+{-# OPTIONS -Wall #-}+++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Text.DocSymbols+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : highly unstable+-- Portability : GHC +--+-- Symbols - redefined Basis.Symbols.+-- +--------------------------------------------------------------------------------++module Wumpus.Drawing.Text.DocSymbols+ (++ ocircle+ , small_ocircle++ , empty_box++ , left_slice+ , right_slice++ )+ where++import qualified Wumpus.Drawing.Basis.Symbols as S+import Wumpus.Drawing.Text.Base.DocTextZero+++import Wumpus.Basic.Kernel -- package: wumpus-basic++-- import Wumpus.Core -- package: wumpus-core++import Control.Applicative+++ocircle :: (Fractional u, InterpretUnit u) => GenDocGraphic st u+ocircle = embedPosObject $ makePosObject qy gf+ where+ qy = (\h -> let hh = 0.5 * h in Orientation hh hh 0 h) <$> capHeight+ gf = capHeight >>= \h -> moveStart (go_up $ 0.5 * h) (S.scircle $ 0.5 * h)++++small_ocircle :: (Fractional u, InterpretUnit u) => GenDocGraphic st u+small_ocircle = embedPosObject $ makePosObject qy gf+ where+ qy = (\h -> let hw = 0.33 * h in Orientation hw hw 0 h) <$> capHeight+ gf = capHeight >>= \h -> moveStart (go_up $ 0.33 * h) (S.scircle $ 0.25 * h)++empty_box :: (Fractional u, InterpretUnit u) => GenDocGraphic st u+empty_box = embedPosObject $ makePosObject qy gf+ where+ qy = (\h -> let hw = 0.5 * h in Orientation hw hw 0 h) <$> capHeight+ gf = capHeight >>= \h -> moveStart (go_up $ 0.33 * h) (S.empty_box $ 0.66 * h)++++left_slice :: (Real u, Floating u, InterpretUnit u) => GenDocGraphic st u+left_slice = embedPosObject $ makePosObject qy gf+ where+ qy = (\h -> let hw = 0.66 * h in Orientation hw hw 0 h) <$> capHeight+ gf = capHeight >>= \h -> moveStart (go_up $ 0.33 * h) (S.sleft_slice h)++right_slice :: (Real u, Floating u, InterpretUnit u) => GenDocGraphic st u+right_slice = embedPosObject $ makePosObject qy gf+ where+ qy = (\h -> let hw = 0.66 * h in Orientation hw hw 0 h) <$> capHeight+ gf = capHeight >>= \h -> moveStart (go_up $ 0.33 * h) (S.sright_slice h)++-- More to follow...+
− src/Wumpus/Drawing/Text/LRText.hs
@@ -1,409 +0,0 @@-{-# LANGUAGE TypeFamilies #-}-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Drawing.Text.LRText--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : stephen.tetley@gmail.com--- Stability : unstable--- Portability : GHC------ Left-to-right measured text. The text uses glyph metrics so it --- can be positioned accurately.--- --- \*\* WARNING \*\* - the API for this module needs work. The --- current API is not satisfactory for drawing according to a --- start position (there are other reasonable start positions than --- the ones currently supported - adding them would explode the --- number of definitions).--- -----------------------------------------------------------------------------------module Wumpus.Drawing.Text.LRText- ( -- baseCenterLine- , baseLeftLine- , baseRightLine-- , rbaseCenterLine- , rbaseLeftLine- , rbaseRightLine-- , ctrCenterLine- , baseCenterEscChar-- , multiAlignLeft- , multiAlignCenter- , multiAlignRight-- , rmultiAlignLeft- , rmultiAlignCenter- , rmultiAlignRight- -- ) where---import Wumpus.Basic.Kernel--import Wumpus.Core -- package: wumpus-core-import Wumpus.Core.Text.GlyphIndices--import Data.AffineSpace -- package: vector-space-import Data.VectorSpace--import Control.Applicative-import Data.Char-import qualified Data.Map as Map-import Data.Maybe ----- Note - BoundedLocThetaGraphic is probably an adequate type--- even though the same text will have a different bounding box--- if it is rotated (the sides of the BBox are always parallel to --- the x and y axes even if the text is not parrale to the --- x-axis). --- --- I cannot think of any compelling graphics that need a more --- accurate type. The execption is text cannot have exact anchors --- however this is a moot /if/ text is considered as a labelling --- of an existing rectangle (which may or may not have been --- rotated).--------- One line of multiline text----data OnelineText u = OnelineText EscapedText (AdvanceVec u)----- | max_width * oneline_text -> LocThetaGraphic----type LocThetaDrawOneline u = u -> OnelineText u -> LocThetaGraphic u----- | max_width * oneline_text -> LocThetaGraphic----type BoundedLocThetaOneline u = u -> OnelineText u -> BoundedLocThetaGraphic u----- | Draw one line of left-aligned text, knowing the max_width --- of all the lines of text.------ All left-aligned text is moved left by half the max_width.------ Note - implicit point is baseline-center, this is perhaps --- unintituitive given the functions name but it is an --- advantage for drawing multi-line text.--- -drawLeftAligned :: Floating u => LocThetaDrawOneline u-drawLeftAligned max_width (OnelineText esc _) = - promoteR2 $ \baseline_ctr theta -> - let mv = displaceParallel ((-0.5) * max_width) theta- in apply2R2 (rescapedline esc) (mv baseline_ctr) theta- ----- | Draw one line of center-aligned text. Center aligned text is --- oblivious to the max_width of all the lines of text.------ Each line of center-aligned text is moved left by half its --- advance vector.------ Implicit point is baseline-center.----drawCenterAligned :: Floating u => LocThetaDrawOneline u-drawCenterAligned _ (OnelineText esc av) = - promoteR2 $ \baseline_ctr theta -> - let mv = displaceParallel (negate $ 0.5 * advanceH av) theta- in apply2R2 (rescapedline esc) (mv baseline_ctr) theta- ---- | Draw one line of right-aligned text, knowing the max_width --- of all the lines of text.------ Each right-aligned text line is moved by the width component --- of the advance vector minus half the max width.------ Note - implicit point is baseline-center, this is perhaps --- unintituitive given the functions name but it is an --- advantage for drawing multi-line text.--- -drawRightAligned :: Floating u => LocThetaDrawOneline u-drawRightAligned max_width (OnelineText esc av) = - promoteR2 $ \baseline_ctr theta -> - let mv = displaceParallel ((0.5 * max_width) - advanceH av) theta- in apply2R2 (rescapedline esc) (mv baseline_ctr) theta------- Impilict point is baseline-center.----onelineBBox :: (Real u, Floating u, FromPtSize u) - => OnelineText u -> LocThetaDrawingInfo u (BoundingBox u)-onelineBBox (OnelineText _ av) = - promoteR2 $ \baseline_ctr theta -> - glyphHeightRange >>= \(ymin, ymax) ->- getTextMargin >>= \(xsep, ysep) -> - let hw = 0.5 * advanceH av - btm_left = baseline_ctr .+^ vec (-hw) ymin- top_right = baseline_ctr .+^ vec hw ymax- bbox = expandBB xsep ysep (BBox btm_left top_right)- in pure $ centerOrthoBBox theta bbox- where- expandBB xsep ysep (BBox (P2 x0 y0) (P2 x1 y1)) = - BBox (P2 (x0-xsep) (y0-ysep)) (P2 (x1+xsep) (y1+ysep))- --------- This should have max_width as a param...----makeMoveableLine :: (Real u, Floating u, FromPtSize u) - => LocThetaDrawOneline u - -> BoundedLocThetaOneline u-makeMoveableLine drawF max_width oline =- intoLocThetaImage (onelineBBox oline) (drawF max_width oline)--onelineAlg :: (Real u, Floating u, FromPtSize u) - => DisplaceFun u - -> LocThetaDrawOneline u - -> EscapedText- -> BoundedLocThetaGraphic u-onelineAlg ptMoveF drawF esc = - promoteR2 $ \pt theta -> - onelineEscText esc >>= \ans@(OnelineText _ av) ->- let max_width = advanceH av- move = ptMoveF max_width ans theta - in apply2R2 (makeMoveableLine drawF max_width ans) (move pt) theta-------- | Draw 1 line...------ Impilict point is baseline-left.-----baseLeftLine :: (Real u, Floating u, FromPtSize u) - => String -> BoundedLocGraphic u-baseLeftLine ss = rbaseLeftLine ss `rot` 0------ | Draw 1 line...------ Impilict point is baseline-center.----baseCenterLine :: (Real u, Floating u, FromPtSize u) - => String -> BoundedLocGraphic u-baseCenterLine ss = rbaseCenterLine ss `rot` 0------- | Draw 1 line...------ Impilict point is baseline-right.----baseRightLine :: (Real u, Floating u, FromPtSize u) - => String -> BoundedLocGraphic u-baseRightLine ss = rbaseRightLine ss `rot` 0----rbaseLeftLine :: (Real u, Floating u, FromPtSize u) - => String -> BoundedLocThetaGraphic u-rbaseLeftLine ss = - onelineAlg leftToCenter drawLeftAligned (escapeString ss)---rbaseCenterLine :: (Real u, Floating u, FromPtSize u) - => String -> BoundedLocThetaGraphic u-rbaseCenterLine ss = - onelineAlg centerToCenter drawCenterAligned (escapeString ss)---rbaseRightLine :: (Real u, Floating u, FromPtSize u) - => String -> BoundedLocThetaGraphic u-rbaseRightLine ss = - onelineAlg rightToCenter drawRightAligned (escapeString ss)----- Note - assumes the ymin of the font is 0 or less.----ctrCenterLine :: (Real u, Floating u, FromPtSize u) - => String -> BoundedLocGraphic u-ctrCenterLine ss =- glyphHeightRange >>= \(ymin, ymax) -> - let hh = 0.5 * ymax - ymin in - moveStartPoint (displaceV $ negate $ hh - abs ymin) $ baseCenterLine ss----baseCenterEscChar :: (Real u, Floating u, FromPtSize u) - => EscapedChar -> BoundedLocGraphic u-baseCenterEscChar esc = body `rot` 0- where- body = onelineAlg centerToCenter drawCenterAligned (wrapEscChar esc)--------- | max_width * interim_text * theta -> (Point -> Point)----type DisplaceFun u = u -> OnelineText u -> Radian -> PointDisplace u--centerToCenter :: DisplaceFun u-centerToCenter _ _ _ = id--leftToCenter :: Floating u => DisplaceFun u-leftToCenter max_width _ theta =- displaceParallel (0.5 * max_width) theta--rightToCenter :: Floating u => DisplaceFun u-rightToCenter max_width (OnelineText _ av) theta =- displaceParallel ((0.5 * max_width) - advanceH av) theta-----multiAlignLeft :: (Floating u, Real u, Ord u, FromPtSize u)- => String- -> BoundedLocGraphic u-multiAlignLeft ss = rmultiAlignLeft ss `rot` 0---multiAlignCenter :: (Floating u, Real u, Ord u, FromPtSize u)- => String- -> BoundedLocGraphic u-multiAlignCenter ss = rmultiAlignCenter ss `rot` 0-----multiAlignRight :: (Floating u, Real u, Ord u, FromPtSize u)- => String- -> BoundedLocGraphic u-multiAlignRight ss = rmultiAlignRight ss `rot` 0---rmultiAlignLeft :: (Floating u, Real u, Ord u, FromPtSize u)- => String- -> BoundedLocThetaGraphic u-rmultiAlignLeft = multilineTEXT (makeMoveableLine drawLeftAligned)---rmultiAlignCenter :: (Floating u, Real u, Ord u, FromPtSize u)- => String- -> BoundedLocThetaGraphic u-rmultiAlignCenter = multilineTEXT (makeMoveableLine drawCenterAligned)---rmultiAlignRight :: (Floating u, Real u, Ord u, FromPtSize u)- => String- -> BoundedLocThetaGraphic u-rmultiAlignRight = multilineTEXT (makeMoveableLine drawRightAligned)----multilineTEXT :: (Floating u, Ord u, FromPtSize u)- => BoundedLocThetaOneline u- -> String- -> BoundedLocThetaGraphic u-multilineTEXT _ [] = lift1R2 emptyBoundedLocGraphic-multilineTEXT mf ss = - lift0R2 (linesToInterims ss) >>= \(max_av, itexts) -> - centralPoints (length itexts) >>= \pts -> - zipMultis (advanceH max_av) mf itexts pts--- --zipMultis :: (Ord u, FromPtSize u)- => u- -> BoundedLocThetaOneline u- -> [OnelineText u] -> [Point2 u]- -> BoundedLocThetaGraphic u-zipMultis _ _ [] _ = lift1R2 $ emptyBoundedLocGraphic-zipMultis _ _ _ [] = lift1R2 $ emptyBoundedLocGraphic-zipMultis max_w mf (a:as) (b:bs) = step a b as bs- where- mkGraphic itext pt = promoteR2 $ \_ theta -> - apply2R2 (mf max_w itext) pt theta- step r s (r2:rs) (s2:ss) = liftA2 oplus (mkGraphic r s) (step r2 s2 rs ss)- step r s _ _ = mkGraphic r s------- | @ana@ is an /anacrusis/ factor - if there are even points--- half the baseline_spacing is added to get the top point----centralPoints :: Floating u => Int -> LocThetaDrawingInfo u [Point2 u]-centralPoints n | n < 2 = promoteR2 $ \ctr _ -> return [ctr]- | even n = body (n `div` 2) (0.5*)- | otherwise = body (n `div` 2) (0 *) - where- body halfn ana = promoteR2 $ \ctr theta -> - baselineSpacing >>= \h ->- let y0 = (h * fromIntegral halfn) + ana h- top = displacePerpendicular y0 theta ctr- in pure $ trailPoints n h theta top- ---trailPoints :: Floating u => Int -> u -> Radian -> Point2 u -> [Point2 u]-trailPoints n height theta top = take n $ iterate fn top- where- fn pt = displacePerpendicular (-height) theta pt---------------------------------------------------------------------------------------- This isn't worth the complexity to get down to one traversal...--linesToInterims :: (FromPtSize u, Ord u) - => String -> DrawingInfo (AdvanceVec u, [OnelineText u])-linesToInterims = fmap post . mapM (onelineEscText . escapeString) . lines- where- post xs = let vmax = foldr fn (hvec 0) xs in (vmax,xs)- fn (OnelineText _ av) vmax = avMaxWidth av vmax--avMaxWidth :: Ord u => AdvanceVec u -> AdvanceVec u -> AdvanceVec u-avMaxWidth a@(V2 w1 _) b@(V2 w2 _) = if w2 > w1 then b else a--onelineEscText :: FromPtSize u => EscapedText -> DrawingInfo (OnelineText u)-onelineEscText esc = fmap (OnelineText esc) $ textVector esc----textVector :: FromPtSize u => EscapedText -> DrawingInfo (AdvanceVec u)-textVector esc = - cwLookupTable >>= \table -> - let cs = destrEscapedText id esc - in pure $ foldr (\c v -> v ^+^ (charWidth table c)) (vec 0 0) cs- - ---charWidth :: FromPtSize u - => CharWidthTable u -> EscapedChar -> AdvanceVec u-charWidth fn (CharLiteral c) = fn $ ord c-charWidth fn (CharEscInt i) = fn i-charWidth fn (CharEscName s) = fn ix- where- ix = fromMaybe (-1) $ Map.lookup s ps_glyph_indices
− src/Wumpus/Drawing/Text/SafeFonts.hs
@@ -1,167 +0,0 @@-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Drawing.Text.SafeFonts--- Copyright : (c) Stephen Tetley 2009-2010--- License : BSD3------ Maintainer : Stephen Tetley <stephen.tetley@gmail.com>--- Stability : highly unstable--- Portability : GHC------ Safe to use \"Core 13\" fonts that are expected to be present--- for any PostScript interpreter.------ Note - regrettably Symbol is not safe to use for SVG.------ \*\* WARNING \*\* - this module is in flux due to changes to --- Text encoding in Wumpus-Core and adding font metrics to --- Wumpus-Basic. The code here is likely to be revised.--------------------------------------------------------------------------------------module Wumpus.Drawing.Text.SafeFonts- ( - -- * Times Roman- times_roman- , times_italic- , times_bold- , times_bold_italic-- -- * Helvetica- , helvetica- , helvetica_oblique- , helvetica_bold- , helvetica_bold_oblique-- -- * Courier- , courier- , courier_oblique- , courier_bold- , courier_bold_oblique-- -- * Symbol- , symbol-- ) where----import Wumpus.Core-import Wumpus.Core.Text.StandardEncoding-import Wumpus.Core.Text.Symbol---- Supported fonts are:------ Times-Roman Times-Italic Times-Bold Times-BoldItalic--- Helvetica Helvetica-Oblique Helvetica-Bold Helvetica-Bold-Oblique--- Courier Courier-Oblique Courier-Bold Courier-Bold-Oblique--- Symbol------------------------------------------------------------------------------------- ---- | Times-Roman--- -times_roman :: FontFace-times_roman = - FontFace "Times-Roman" "Times New Roman" SVG_REGULAR standard_encoding---- | Times Italic----times_italic :: FontFace-times_italic = - FontFace "Times-Italic" "Times New Roman" SVG_ITALIC standard_encoding- --- | Times Bold----times_bold :: FontFace-times_bold = - FontFace "Times-Bold" "Times New Roman" SVG_BOLD standard_encoding---- | Times Bold Italic----times_bold_italic :: FontFace-times_bold_italic = FontFace "Times-BoldItalic" - "Times New Roman" - SVG_BOLD_ITALIC - standard_encoding-------------------------------------------------------------------------------------- Helvetica---- | Helvetica ----helvetica :: FontFace-helvetica = FontFace "Helvetica" "Helvetica" SVG_REGULAR standard_encoding----- | Helvetica Oblique----helvetica_oblique :: FontFace-helvetica_oblique = - FontFace "Helvetica-Oblique" "Helvetica" SVG_OBLIQUE standard_encoding---- | Helvetica Bold--- -helvetica_bold :: FontFace-helvetica_bold = - FontFace "Helvetica-Bold" "Helvetica" SVG_BOLD standard_encoding----- | Helvetica Bold Oblique----helvetica_bold_oblique :: FontFace-helvetica_bold_oblique = FontFace "Helvetica-Bold-Oblique" - "Helvetica" - SVG_BOLD_OBLIQUE - standard_encoding---------------------------------------------------------------------------------------- | Courier--- -courier :: FontFace-courier = FontFace "Courier" "Courier New" SVG_REGULAR standard_encoding---- | Courier Oblique--- -courier_oblique :: FontFace-courier_oblique = - FontFace "Courier-Oblique" "Courier New" SVG_OBLIQUE standard_encoding---- | Courier Bold--- -courier_bold :: FontFace-courier_bold = - FontFace "Courier-Bold" "Courier New" SVG_BOLD standard_encoding----- | Courier Bold Oblique--- -courier_bold_oblique :: FontFace-courier_bold_oblique = FontFace "Courier-Bold-Oblique" - "Courier New" - SVG_BOLD_OBLIQUE - standard_encoding------------------------------------------------------------------------------------- Symbol---- | Symbol------ Note - Symbol is intentionally not supported for SVG by some --- renderers (Firefox). Chrome is fine, but the use of symbol --- should be still be avoided for web graphics.--- -symbol :: FontFace-symbol = FontFace "Symbol" "Symbol" SVG_REGULAR symbol_encoding-----
+ src/Wumpus/Drawing/Text/StandardFontDefs.hs view
@@ -0,0 +1,323 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Drawing.Text.StandardFontDefs+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Safe to use \"Core 13\" fonts that are expected to be present+-- for any PostScript interpreter.+--+-- Note - regrettably Symbol is not safe to use for SVG.+--+--------------------------------------------------------------------------------++module Wumpus.Drawing.Text.StandardFontDefs+ ( + -- * Times Roman+ times_roman_family+ + , times_roman+ , times_italic+ , times_bold+ , times_bold_italic++ -- * Helvetica+ , helvetica_family++ , helvetica+ , helvetica_oblique+ , helvetica_bold+ , helvetica_bold_oblique++ -- * Courier+ , courier_family++ , courier+ , courier_oblique+ , courier_bold+ , courier_bold_oblique++ -- * Symbol+ , symbol++ ) where+++import Wumpus.Basic.Kernel -- package: wumpus-basic++import Wumpus.Core -- package: wumpus-core+import Wumpus.Core.Text.StandardEncoding+import Wumpus.Core.Text.Symbol++-- Supported fonts are:+--+-- Times-Roman Times-Italic Times-Bold Times-BoldItalic+-- Helvetica Helvetica-Oblique Helvetica-Bold Helvetica-Bold-Oblique+-- Courier Courier-Oblique Courier-Bold Courier-Bold-Oblique+-- Symbol++--------------------------------------------------------------------------------+-- ++-- | 'FontFamily' definition for Times-Roman.+--+times_roman_family :: FontFamily+times_roman_family = + FontFamily { ff_regular = times_roman+ , ff_bold = Just times_bold+ , ff_italic = Just times_italic+ , ff_bold_italic = Just times_bold_italic+ }+++-- | Times-Roman+-- +times_roman :: FontDef+times_roman = + FontDef { font_def_face = face+ , gs_file_name = "n021003l.afm" -- NimbusRomNo9L-Regu+ , afm_file_name = "Times-Roman.afm"+ }+ where+ face = FontFace { ps_font_name = "Times-Roman" + , svg_font_family = "Times New Roman" + , svg_font_style = SVG_REGULAR + , font_enc_vector = standard_encoding+ }+++-- | Times Italic+--+times_italic :: FontDef+times_italic =+ FontDef { font_def_face = face+ , gs_file_name = "n021023l.afm" -- NimbusRomNo9L-ReguItal+ , afm_file_name = "Times-Italic.afm"+ }+ where+ face = FontFace { ps_font_name = "Times-Italic"+ , svg_font_family = "Times New Roman"+ , svg_font_style = SVG_ITALIC+ , font_enc_vector = standard_encoding+ }+++-- | Times Bold+--+times_bold :: FontDef+times_bold = + FontDef { font_def_face = face+ , gs_file_name = "n021004l.afm" -- NimbusRomNo9L-Medi+ , afm_file_name = "Times-Bold.afm"+ }+ where+ face = FontFace { ps_font_name = "Times-Bold"+ , svg_font_family = "Times New Roman"+ , svg_font_style = SVG_BOLD+ , font_enc_vector = standard_encoding+ }+++-- | Times Bold Italic+--+times_bold_italic :: FontDef+times_bold_italic = + FontDef { font_def_face = face+ , gs_file_name = "n021024l.afm" -- NimbusRomNo9L-MediItal+ , afm_file_name = "Times-BoldItalic.afm"+ }+ where+ face = FontFace { ps_font_name = "Times-BoldItalic"+ , svg_font_family = "Times New Roman"+ , svg_font_style = SVG_BOLD_ITALIC+ , font_enc_vector = standard_encoding+ }+++--------------------------------------------------------------------------------+-- Helvetica++-- | 'FontFamily' definition for Helvetica.+--+helvetica_family :: FontFamily+helvetica_family = + FontFamily { ff_regular = helvetica+ , ff_bold = Just helvetica_bold+ , ff_italic = Just helvetica_oblique+ , ff_bold_italic = Just helvetica_bold_oblique+ }++++-- | Helvetica regular weight.+--+helvetica :: FontDef+helvetica =+ FontDef { font_def_face = face+ , gs_file_name = "n019003l.afm" -- NimbusSanL-Regu+ , afm_file_name = "Helvetica.afm"+ }+ where+ face = FontFace { ps_font_name = "Helvetica"+ , svg_font_family = "Helvetica"+ , svg_font_style = SVG_REGULAR+ , font_enc_vector = standard_encoding+ }+++-- | Helvetica Oblique+--+helvetica_oblique :: FontDef+helvetica_oblique = + FontDef { font_def_face = face+ , gs_file_name = "n019023l.afm" -- NimbusSanL-ReguItal+ , afm_file_name = "Helvetica-Oblique.afm"+ }+ where+ face = FontFace { ps_font_name = "Helvetica-Oblique"+ , svg_font_family = "Helvetica"+ , svg_font_style = SVG_OBLIQUE+ , font_enc_vector = standard_encoding+ }++-- | Helvetica Bold+-- +helvetica_bold :: FontDef+helvetica_bold = + FontDef { font_def_face = face+ , gs_file_name = "n019004l.afm" -- NimbusSanL-Bold+ , afm_file_name = "Helvetica-Bold.afm"+ }+ where+ face = FontFace { ps_font_name = "Helvetica-Bold"+ , svg_font_family = "Helvetica"+ , svg_font_style = SVG_BOLD+ , font_enc_vector = standard_encoding+ }+++-- | Helvetica Bold Oblique+--+helvetica_bold_oblique :: FontDef+helvetica_bold_oblique = + FontDef { font_def_face = face+ , gs_file_name = "n019024l.afm" -- NimbusSanL-BoldItal+ , afm_file_name = "Helvetica-BoldOblique.afm"+ }+ where+ face = FontFace { ps_font_name = "Helvetica-Bold-Oblique"+ , svg_font_family = "Helvetica"+ , svg_font_style = SVG_BOLD_OBLIQUE+ , font_enc_vector = standard_encoding+ }++++--------------------------------------------------------------------------------++-- | 'FontFamily' definition for Courier.+--+courier_family :: FontFamily+courier_family = + FontFamily { ff_regular = courier+ , ff_bold = Just courier_bold+ , ff_italic = Just courier_oblique+ , ff_bold_italic = Just courier_bold_oblique+ }++++-- | Courier+-- +courier :: FontDef+courier = + FontDef { font_def_face = face+ , gs_file_name = "n022003l.afm" -- NimbusMonL-Regu+ , afm_file_name = "Courier.afm"+ }+ where+ face = FontFace { ps_font_name = "Courier"+ , svg_font_family = "Courier New"+ , svg_font_style = SVG_REGULAR+ , font_enc_vector = standard_encoding+ }++-- | Courier Oblique+-- +courier_oblique :: FontDef+courier_oblique = + FontDef { font_def_face = face+ , gs_file_name = "n022023l.afm" -- NimbusMonL-ReguObli+ , afm_file_name = "Courier-Oblique.afm"+ }+ where+ face = FontFace { ps_font_name = "Courier-Oblique"+ , svg_font_family = "Courier New"+ , svg_font_style = SVG_OBLIQUE+ , font_enc_vector = standard_encoding+ }+++-- | Courier Bold+-- +courier_bold :: FontDef+courier_bold =+ FontDef { font_def_face = face+ , gs_file_name = "n022004l.afm" -- NimbusMonL-Bold+ , afm_file_name = "Courier-Bold.afm"+ }+ where+ face = FontFace { ps_font_name = "Courier-Bold"+ , svg_font_family = "Courier New"+ , svg_font_style = SVG_BOLD+ , font_enc_vector = standard_encoding+ }+++-- | Courier Bold Oblique+-- +courier_bold_oblique :: FontDef+courier_bold_oblique = + FontDef { font_def_face = face+ , gs_file_name = "n022024l.afm" -- NimbusMonL-BoldObli+ , afm_file_name = "Courier-BoldOblique.afm"+ }+ where+ face = FontFace { ps_font_name = "Courier-Bold-Oblique"+ , svg_font_family = "Courier New"+ , svg_font_style = SVG_BOLD_OBLIQUE+ , font_enc_vector = standard_encoding+ }++--------------------------------------------------------------------------------+-- Symbol++-- | Symbol+--+-- Note - Symbol is intentionally not supported for SVG by some +-- renderers (Firefox). Chrome is fine, but the use of symbol +-- should be still be avoided for web graphics.+-- +symbol :: FontDef+symbol = + FontDef { font_def_face = face+ , gs_file_name = "s050000l.afm" -- StandardSymL+ , afm_file_name = "Symbol.afm"+ }+ where+ face = FontFace { ps_font_name = "Symbol"+ , svg_font_family = "Symbol"+ , svg_font_style = SVG_REGULAR+ , font_enc_vector = symbol_encoding+ }+++++
− src/Wumpus/Drawing/Turtle/TurtleClass.hs
@@ -1,91 +0,0 @@-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Drawing.Turtle.TurtleClass--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : stephen.tetley@gmail.com--- Stability : unstable--- Portability : GHC ------ Turtle monad and monad transformer.------ The Turtle monad embodies the LOGO style of imperative --- drawing - sending commands to update the a cursor.------ While Wumpus generally aims for a more compositional,--- \"coordinate-free\" style of drawing, some types of diagram --- are more easily expressed in the LOGO style.------ Note - as turtle drawing with Wumpus is a /local effect/, --- there is only one instance of TurtleM. Potentially TurtleM --- will be removed and the functions implemented directly. --------------------------------------------------------------------------------------module Wumpus.Drawing.Turtle.TurtleClass- (-- Coord-- , TurtleM(..)-- , setsLoc- , setsLoc_-- -- * movement- , resetLoc- , moveLeft- , moveRight- , moveUp- , moveDown- , nextLine- -- ) where----type Coord = (Int,Int)---class Monad m => TurtleM m where- getLoc :: m (Int,Int)- setLoc :: (Int,Int) -> m ()- getOrigin :: m (Int,Int)- setOrigin :: (Int,Int) -> m ()----setsLoc :: TurtleM m => (Coord -> (a,Coord)) -> m a-setsLoc f = getLoc >>= \coord -> - let (a,coord') = f coord in setLoc coord' >> return a--setsLoc_ :: TurtleM m => (Coord -> Coord) -> m ()-setsLoc_ f = getLoc >>= \coord -> setLoc (f coord)---resetLoc :: TurtleM m => m ()-resetLoc = getOrigin >>= setLoc---moveRight :: TurtleM m => m ()-moveRight = setsLoc_ $ \(x,y)-> (x+1, y)---moveLeft :: TurtleM m => m ()-moveLeft = setsLoc_ $ \(x,y) -> (x-1,y)--moveUp :: TurtleM m => m ()-moveUp = setsLoc_ $ \(x,y) -> (x,y+1)--moveDown :: TurtleM m => m ()-moveDown = setsLoc_ $ \(x,y) -> (x ,y-1)---nextLine :: TurtleM m => m ()-nextLine = getOrigin >>= \(ox,_) ->- setsLoc_ $ \(_,y) -> (ox,y-1)-
− src/Wumpus/Drawing/Turtle/TurtleMonad.hs
@@ -1,139 +0,0 @@-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE TypeSynonymInstances #-}-{-# LANGUAGE FlexibleInstances #-}-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Drawing.Turtle.TurtleMonad--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : stephen.tetley@gmail.com--- Stability : unstable--- Portability : GHC ------ Turtle monad transformer.------ The Turtle monad embodies the LOGO style of imperative --- drawing - sending commands to update the a cursor.------ While Wumpus generally aims for a more compositional,--- \"coordinate-free\" style of drawing, some types of --- diagram are more easily expressed in the LOGO style.------ Turtle is only a transformer - it is intended to be run within--- a 'Drawing'.--------------------------------------------------------------------------------------module Wumpus.Drawing.Turtle.TurtleMonad- (- -- * Re-exports- module Wumpus.Drawing.Turtle.TurtleClass-- -- * Turtle transformer- , TurtleT- , runTurtleT-- - ) where--import Wumpus.Basic.Kernel-import Wumpus.Drawing.Turtle.TurtleClass----import Control.Applicative-import Control.Monad----- Note - if Turtle is now just a /local effect/ monad is the --- Turtle class still needed? Afterall, there is (probably)--- only ever going to be one instance.------- Turtle is a Reader / State monad--- --- The env is the horizontal and vertical move distances.--- --- The state is the current coordinate and the origin.-----data TurtleState = TurtleState - { _turtle_origin :: (Int,Int)- , _current_coord :: (Int,Int)- }--newtype TurtleT u m a = TurtleT { - getTurtleT :: ScalingContext Int Int u - -> TurtleState - -> m (a, TurtleState) }--type instance MonUnit (TurtleT u m) = u- ----- Functor----instance Monad m => Functor (TurtleT u m) where- fmap f m = TurtleT $ \r s -> getTurtleT m r s >>= \(a,s') ->- return (f a, s')----- Applicative --instance Monad m => Applicative (TurtleT u m) where- pure a = TurtleT $ \_ s -> return (a,s)- mf <*> ma = TurtleT $ \r s -> getTurtleT mf r s >>= \(f,s') ->- getTurtleT ma r s' >>= \(a,s'') ->- return (f a,s'') ----- Monad --instance Monad m => Monad (TurtleT u m) where- return a = TurtleT $ \_ s -> return (a,s)- m >>= k = TurtleT $ \r s -> getTurtleT m r s >>= \(a,s') ->- (getTurtleT . k) a r s' >>= \(b,s'') ->- return (b,s'')-----instance Monad m => TurtleM (TurtleT u m) where- getLoc = TurtleT $ \_ s@(TurtleState _ c) -> return (c,s)- setLoc c = TurtleT $ \_ (TurtleState o _) -> return ((),TurtleState o c)- getOrigin = TurtleT $ \_ s@(TurtleState o _) -> return (o,s)- setOrigin o = TurtleT $ \_ (TurtleState _ c) -> return ((),TurtleState o c)---runTurtleT :: (Monad m, Num u) - => (Int,Int) -> ScalingContext Int Int u -> TurtleT u m a -> m a-runTurtleT ogin cfg mf = getTurtleT mf cfg st0 >>= \(a,_) -> return a- where - st0 = TurtleState ogin ogin ----------------------------------------------------------------------------------------- Cross instances--instance DrawingCtxM m => DrawingCtxM (TurtleT u m) where- askDC = TurtleT $ \_ s -> askDC >>= \ ctx -> return (ctx,s)- localize upd mf = TurtleT $ \r s -> localize upd (getTurtleT mf r s)----- This needs undecidable instances...--instance (Monad m, TraceM m, u ~ MonUnit m) => TraceM (TurtleT u m) where- trace a = TurtleT $ \_ s -> trace a >> return ((),s)---instance (Monad m, u ~ MonUnit m, Num u) => PointSupplyM (TurtleT u m) where- position = TurtleT $ \r s@(TurtleState _ (x,y)) -> return (scalePt r x y,s)-
src/Wumpus/Drawing/VersionNumber.hs view
@@ -3,7 +3,7 @@ -------------------------------------------------------------------------------- -- | -- Module : Wumpus.Drawing.VersionNumber--- Copyright : (c) Stephen Tetley 2010+-- Copyright : (c) Stephen Tetley 2010-2012 -- License : BSD3 -- -- Maintainer : stephen.tetley@gmail.com@@ -23,7 +23,7 @@ -- | Version number ----- > (0,1,0)+-- > (0,9,0) -- wumpus_drawing_version :: (Int,Int,Int)-wumpus_drawing_version = (0,1,0)+wumpus_drawing_version = (0,9,0)
wumpus-drawing.cabal view
@@ -1,5 +1,5 @@ name: wumpus-drawing-version: 0.1.0+version: 0.9.0 license: BSD3 license-file: LICENSE copyright: Stephen Tetley <stephen.tetley@gmail.com>@@ -12,7 +12,7 @@ \*\* WARNING \*\* - this package is sub-alpha. Although many of the drawing objects have been improved since the code was split from Wumpus-Basic, the code is still prototypical. Essentially- this package is a /technology preview/ and not a re-usable + this package is a /technology preview/ and not yet a re-usable library. . NOTE - many of the demos use font metrics. Font metrics for@@ -24,7 +24,7 @@ links below. To run the demos properly you will need one of these sets of metrics. .- Adobe Font techinal notes:+ Adobe Font technical notes: <https://www.adobe.com/devnet/font.html> . Core 14 AFM metrics:@@ -38,22 +38,44 @@ . Changelog: .- v0.1.0:+ v0.8.0 to v0.9.0:+ . + * Updated to work with wumpus-basic-0.24.0. .- * Initial release - this is a split from @Wumpus-Basic@ making - the (very prototypical - read sub-alpha, unstable...) modules - in the @Drawing@ hierarchy a separate package.+ * Removed monoid mappend alias @(<>)@ as it is now defined by+ Data.Monoid. .- * Simplified Chains - chains are now regular lists (though often- infinite). Drawings are made with chains using new zip-like- functions.+ v0.7.0 to v0.8.0: .- * Re-worked Shapes.+ * Changed API to make Connectors. .- * Re-worked Arrow and Arrow Tip types.+ * Added InclineTrails to Drawing.Basis.+ . + * Reworked @Extras.Loop@. .- * Re-worked ConnectorPaths.+ v0.6.0 to v0.7.0: .+ * Changed paths - @RelPath@ has been removed and there are now + only absolute paths. @PathBuilder@ builds absolute paths.+ Pen updating in @PathBuilder@ now works like a State monad + rather than @local@ in a Reader monad.+ .+ * Changed argument order of the run functions to @monadLib@ + style (params * monadic action) rather than @MTL@ style + (monadic action * params).+ .+ * Added Symbols to Drawing.Basis.+ .+ * Added a tube box connector.+ .+ v0.5.0 to v0.6.0:+ .+ * Removed @LocTrace@ and @RefTrace@ from @Wumpus.Drawing.Basis@,+ they are superseded by @LocDrawing@ in Wumpus-Basic.+ . + * Remaned path building operations in @RelPathBuilder@.+ .+ . build-type: Simple stability: highly unstable cabal-version: >= 1.2@@ -63,59 +85,76 @@ LICENSE, demo/ArrowCircuit.hs, demo/Arrowheads.hs,+ demo/Automata.hs, demo/ClipPic.hs, demo/ColourCharts.hs, demo/ColourChartUtils.hs, demo/Connectors.hs demo/DotPic.hs, demo/FeatureModel.hs,- demo/FontLoaderUtils.hs, demo/FontPic.hs, demo/LeftRightText.hs, demo/PetriNet.hs,- demo/PictureCompo.hs,- demo/Symbols.hs,- demo/TableChains.hs+ demo/SingleChar.hs,+ demo/SingleLine.hs,+ demo/SampleShapes.hs,+ demo/Symbols.hs library hs-source-dirs: src build-depends: base < 5, containers >= 0.3 && <= 0.6,- directory >= 1.0 && < 2.0, - filepath >= 1.1 && < 2.0, vector-space >= 0.6 && < 1.0,- wumpus-core >= 0.42.0 && < 0.43.0,- wumpus-basic == 0.15.0+ wumpus-core >= 0.52.0 && < 0.53.0,+ wumpus-basic == 0.24.0 exposed-modules:- Wumpus.Drawing.Arrows,- Wumpus.Drawing.Arrows.Connectors,- Wumpus.Drawing.Arrows.Tips,- Wumpus.Drawing.Chains,- Wumpus.Drawing.Chains.Base,- Wumpus.Drawing.Chains.Derived,+ Wumpus.Drawing.Basis.BezierCurve,+ Wumpus.Drawing.Basis.DrawingPrimitives,+ Wumpus.Drawing.Basis.Geometry,+ Wumpus.Drawing.Basis.InclineTrails,+ Wumpus.Drawing.Basis.ShapeTrails,+ Wumpus.Drawing.Basis.Symbols, Wumpus.Drawing.Colour.SVGColours, Wumpus.Drawing.Colour.X11Colours,+ Wumpus.Drawing.Connectors,+ Wumpus.Drawing.Connectors.Arrowheads,+ Wumpus.Drawing.Connectors.Base,+ Wumpus.Drawing.Connectors.BoxConnectors,+ Wumpus.Drawing.Connectors.ConnectorPaths,+ Wumpus.Drawing.Connectors.ConnectorProps, Wumpus.Drawing.Dots.AnchorDots,- Wumpus.Drawing.Dots.Marks,- Wumpus.Drawing.Geometry.Intersection,- Wumpus.Drawing.Geometry.Paths,+ Wumpus.Drawing.Dots.SimpleDots,+ Wumpus.Drawing.Extras.Axes,+ Wumpus.Drawing.Extras.Clip,+ Wumpus.Drawing.Extras.Grids,+ Wumpus.Drawing.Extras.Loop, Wumpus.Drawing.Paths, Wumpus.Drawing.Paths.Base,- Wumpus.Drawing.Paths.Connectors,- Wumpus.Drawing.Paths.MonadicConstruction,- Wumpus.Drawing.Paths.ControlPoints,+ Wumpus.Drawing.Paths.Illustrate,+ Wumpus.Drawing.Paths.Intersection,+ Wumpus.Drawing.Paths.PathBuilder,+ Wumpus.Drawing.Paths.Vamps, Wumpus.Drawing.Shapes, Wumpus.Drawing.Shapes.Base, Wumpus.Drawing.Shapes.Circle, Wumpus.Drawing.Shapes.Diamond, Wumpus.Drawing.Shapes.Ellipse,+ Wumpus.Drawing.Shapes.InvSemicircle,+ Wumpus.Drawing.Shapes.InvSemiellipse,+ Wumpus.Drawing.Shapes.InvTriangle,+ Wumpus.Drawing.Shapes.Parallelogram, Wumpus.Drawing.Shapes.Rectangle,- Wumpus.Drawing.Text.LRText,- Wumpus.Drawing.Text.SafeFonts,- Wumpus.Drawing.Turtle.TurtleClass,- Wumpus.Drawing.Turtle.TurtleMonad,+ Wumpus.Drawing.Shapes.Trapezium,+ Wumpus.Drawing.Shapes.Semicircle,+ Wumpus.Drawing.Shapes.Semiellipse,+ Wumpus.Drawing.Shapes.Triangle,+ Wumpus.Drawing.Text.Base.DocTextZero,+ Wumpus.Drawing.Text.Base.Label,+ Wumpus.Drawing.Text.DirectionZero,+ Wumpus.Drawing.Text.DocSymbols,+ Wumpus.Drawing.Text.StandardFontDefs, Wumpus.Drawing.VersionNumber other-modules: