wumpus-basic 0.15.0 → 0.24.0
raw patch · 63 files changed
Files
- CHANGES +152/−0
- demo/AltUnits.hs +78/−0
- demo/FontDeltaPic.hs +9/−10
- demo/SimpleAdvGraphic.hs +68/−0
- demo/SimplePosObject.hs +114/−0
- src/Wumpus/Basic/Kernel.hs +58/−20
- src/Wumpus/Basic/Kernel/Base/Anchors.hs +0/−238
- src/Wumpus/Basic/Kernel/Base/BaseDefs.hs +437/−162
- src/Wumpus/Basic/Kernel/Base/ContextFun.hs +0/−550
- src/Wumpus/Basic/Kernel/Base/DrawingContext.hs +201/−74
- src/Wumpus/Basic/Kernel/Base/FontSupport.hs +262/−0
- src/Wumpus/Basic/Kernel/Base/GlyphMetrics.hs +0/−117
- src/Wumpus/Basic/Kernel/Base/QueryDC.hs +212/−164
- src/Wumpus/Basic/Kernel/Base/ScalingContext.hs +0/−104
- src/Wumpus/Basic/Kernel/Base/Units.hs +166/−0
- src/Wumpus/Basic/Kernel/Base/UpdateDC.hs +430/−139
- src/Wumpus/Basic/Kernel/Base/WrappedPrimitive.hs +84/−75
- src/Wumpus/Basic/Kernel/Drawing/Basis.hs +101/−0
- src/Wumpus/Basic/Kernel/Drawing/Chain.hs +463/−0
- src/Wumpus/Basic/Kernel/Drawing/CtxPicture.hs +408/−0
- src/Wumpus/Basic/Kernel/Drawing/LocDrawing.hs +237/−0
- src/Wumpus/Basic/Kernel/Drawing/LocTrace.hs +213/−0
- src/Wumpus/Basic/Kernel/Drawing/PosObject.hs +668/−0
- src/Wumpus/Basic/Kernel/Drawing/TraceDrawing.hs +410/−0
- src/Wumpus/Basic/Kernel/Objects/AdvObject.hs +315/−0
- src/Wumpus/Basic/Kernel/Objects/AdvanceGraphic.hs +0/−106
- src/Wumpus/Basic/Kernel/Objects/Anchors.hs +230/−0
- src/Wumpus/Basic/Kernel/Objects/BaseObjects.hs +0/−191
- src/Wumpus/Basic/Kernel/Objects/Basis.hs +116/−0
- src/Wumpus/Basic/Kernel/Objects/Bounded.hs +119/−66
- src/Wumpus/Basic/Kernel/Objects/Concat.hs +188/−0
- src/Wumpus/Basic/Kernel/Objects/Connector.hs +249/−28
- src/Wumpus/Basic/Kernel/Objects/CtxPicture.hs +0/−563
- src/Wumpus/Basic/Kernel/Objects/Displacement.hs +358/−0
- src/Wumpus/Basic/Kernel/Objects/DrawingPrimitives.hs +614/−0
- src/Wumpus/Basic/Kernel/Objects/Graphic.hs +0/−478
- src/Wumpus/Basic/Kernel/Objects/Image.hs +293/−0
- src/Wumpus/Basic/Kernel/Objects/LocImage.hs +357/−0
- src/Wumpus/Basic/Kernel/Objects/LocThetaImage.hs +294/−0
- src/Wumpus/Basic/Kernel/Objects/Orientation.hs +564/−0
- src/Wumpus/Basic/Kernel/Objects/TraceDrawing.hs +0/−338
- src/Wumpus/Basic/Kernel/Objects/Trail.hs +1042/−0
- src/Wumpus/Basic/System/FontLoader.hs +143/−0
- src/Wumpus/Basic/System/FontLoader/Afm.hs +0/−82
- src/Wumpus/Basic/System/FontLoader/AfmParserBase.hs +225/−0
- src/Wumpus/Basic/System/FontLoader/AfmTopLevel.hs +113/−0
- src/Wumpus/Basic/System/FontLoader/AfmV2Parser.hs +65/−0
- src/Wumpus/Basic/System/FontLoader/AfmV4Dot1Parser.hs +63/−0
- src/Wumpus/Basic/System/FontLoader/Base/AfmParserBase.hs +0/−210
- src/Wumpus/Basic/System/FontLoader/Base/AfmV2Parser.hs +0/−65
- src/Wumpus/Basic/System/FontLoader/Base/AfmV4Dot1Parser.hs +0/−63
- src/Wumpus/Basic/System/FontLoader/Base/Datatypes.hs +0/−178
- src/Wumpus/Basic/System/FontLoader/Base/FontLoadMonad.hs +0/−205
- src/Wumpus/Basic/System/FontLoader/Base/GSFontMap.hs +0/−108
- src/Wumpus/Basic/System/FontLoader/Datatypes.hs +166/−0
- src/Wumpus/Basic/System/FontLoader/FontLoadMonad.hs +218/−0
- src/Wumpus/Basic/System/FontLoader/GSTopLevel.hs +110/−0
- src/Wumpus/Basic/System/FontLoader/GhostScript.hs +0/−86
- src/Wumpus/Basic/Utils/FormatCombinators.hs +0/−430
- src/Wumpus/Basic/Utils/HList.hs +4/−0
- src/Wumpus/Basic/Utils/JoinList.hs +1/−1
- src/Wumpus/Basic/VersionNumber.hs +3/−3
- wumpus-basic.cabal +100/−40
CHANGES view
@@ -1,3 +1,155 @@++0.20.0 to 0.21.0:++ * The code from the @Kernel.Base.UserState@ module has been + moved to the module @Kernel.Drawing.Basis@.++ * Moved @Image@ and @Query@ from @Objects.Basis@ to new module+ @Objects.Image@. Moved @Chain@, @CtxPicture@ and + @TraceDrawing@ into the new @Drawing@ folder.++ * Added @Wumpus.Basic.Kernel.Drawing.LocDrawing@ module.+ + * Rationalized the combinators exported by the drawing objects + @Image@, @LocImage@, @LocThetaImage@ and @Connector@.+ ++0.18.0 to 0.20.0 (no release numbered v0.19.0):++ * Added text advance-vector calculations to @QueryDC@ - + previously they were in Wumpus-Drawing.+ + * Changed argument order of @run@ functions to follow the usual+ style - monadic action first and then dependent parameters + (initial state, reader environment etc.).+ + * Changed representation of Chains to a monadic type with an + explicit operation for next position.++ * Removed @PrimW@ type - pairs are now used instead. ++ * Removed transformer version of @TraceDrawing@. All operations + in TraceDrawing are now specialized to work only on the + @TraceDrawing@ type. Added @UserState@ to TraceDrawing and+ other drawing monads to compensate for the loss of+ expressiveness.++ * Changed the internals of @AdvObject@ and @PosObject@ so they + are more idiomatic - both are now essentially reader-writer + monads. Added PosObject functions for text primitives (moved + from Wumpus-Drawing).++ * Changed the @obliterate@ method of the @Decorate@ class.++ * Removed the Semigroup (OPlus) and Bifunctor classes. They are + no longer so pertinent now that Image, LocImage etc. are + newtypes rather than /ContextFuns/.++ * Changed internals of LocImage, LocThetaImage and Connector + types so the internal points always have Double (PostScript + point) as the unit. This fixes a bug where otherwise changing+ the font size for drawings with Em or En units would break the+ layout.++ * Added @ORIGIN@ to the @RectAddress@ enumeration. I had + overlooked that @BLC /= ORIGIN@.++ * Added underline-position and underline-thickness to the font + metrics read by the font loader.++0.17.0 to 0.18.0:+ + * Major change - removed @ContextFun@ from @Kernel.Base@. Now + the drawing objects (Image, LocImage, Connector...) are + newtype wrapped Reader-Writer monads rather than aliases to + one of the arity family of ContextFuns. Being newtypes gives+ a lot better opportunities for overloading.++ * Changed Displacement API - it is smaller, and the function + naming is more uniform.+ + * Changed @DrawingPimitives@ to use @DrawStyle@.+ + * Moved vertices generating functions from @Geometry.Path@ to + @Geometry.Vertices@.+ +0.16.0 to 0.17.0:++ * Changed representation of ContextFuns - context funs are no + longer an arity family of newtypes. The new representation + is simpler, but some of the opportunities for overloading + have been lost.++ * Substantial round of renaming for the @DrawingContext@ types+ an query and update functions.++ * Re-worked the font metrics and font loader modules. The new+ names should be more systematic. Removed the need for the + GhostScript font modules to match PostScript names to + GhostScript font files. ++ * Superceded the PointSupplyM class by adding snapping grid + scaling to the DrawingContext. + + * Removed @ScalingContext@ module.++ * Made text colour a distinct property in the DrawingContext. + Text is now drawn with the text colour rather than the stroke + colour.++ * Added FontLoadLog to the DrawingContext - this allows access + to the errors recorded during font loading.++0.15.0 to 0.16.0:++ * Moved the Geometry modules from Wumpus-Drawing into + Wumpus-Basic.++ * Re-worked the @CtxPicture@ API, although the current naming + scheme is not satisfactory.++ * Added extra Anchor classes.++ * Added @PosImage@ object - this is a rectangle-framed object + that can be drawn from any of its corners or its center. ++ * Added @CF3@, an arity three /ContextFunction/.++ * Added @Displacement@ module. This defines the @PointDisplace@+ type and provides a library of @PointDisplace@ functions.+ Note - some of the new functions have taken names previously + used for anchor projection functions (@northwards@, + @southwards@, etc.), anchor projections are now build with the + function @projectAnchor@.++ * Added the property @descender@ to the font metrics.++ * Split drawing primitives from type in @Objects.Graphic@, + drawing primitives are now in the module + @Objects.DrawingPrimitives@.++0.14.0 to 0.15.0:+ + * Split previous @Wumpus-Basic@ package into two packages:+ @Wumpus-Basic@ and @Wumpus-Drawing@. This is a pratical move + to separate the developed (although not yet polished) @Kernel@ + and @FontLoader@ code from the prototypical @Drawing@ code.++ * Renamed the @Drawing@ object to @CtxPicture@. Although + @CtxPicture@ is a less pleasant name, it should be less + confusing. A @CtxPicture@ is the essentially the @Picture@ + type from Wumpus-Core with an implicit context - + @ContextPicture@ is simply too long and @CtxPicture@ is + almost jibberish but the previous unrelated name @Drawing@ + was not helpful.++ * @Kernel.Base.ScalingContext@ has been simplified. + ScalingContexts are no longer manipulated via a custom Reader+ monad or transformer as the type signatures were too unwieldy.++ * Added @TextMargin@ to the @DrawingContext@ - Wumpus-Drawing + can now calculate more appealing bounding boxes for text.+ 0.13.0 to 0.14.0: * Re-organised module hierarchy, Wumpus-Basic is now divided
+ demo/AltUnits.hs view
@@ -0,0 +1,78 @@+{-# OPTIONS -Wall #-}+++module AltUnits where++import Wumpus.Basic.Kernel++import Wumpus.Core -- package: wumpus-core++import Data.Monoid+import System.Directory++main :: IO ()+main = do + createDirectoryIfMissing True "./out/"+ let pic1 = runCtxPictureU std_attr drawing01+ writeEPS "./out/alt_units01.eps" pic1+ writeSVG "./out/alt_units01.svg" pic1+++std_attr :: DrawingContext+std_attr = (stroke_colour firebrick . fill_colour linen) $ standardContext 12+++drawing01 :: CtxPicture+drawing01 = drawTracing $ combined+++mf1 :: TraceDrawing Double ()+mf1 = do+ draw $ dblLocGraphic `at` P2 12 0++mf2 :: TraceDrawing Em ()+mf2 = do+ draw $ emLocGraphic `at` P2 1 0++combined :: TraceDrawing Double ()+combined = do + draw $ dblLocGraphic `at` P2 12 108+ drawl (P2 12 72) $ uconvF emLocGraphic+ draw $ uconvF $ emLocGraphic `at` P2 1 3+ drawl (P2 12 0) $ uconvF $ hyperlink haskell_org enLocGraphic++dblLocGraphic :: LocGraphic Double+dblLocGraphic = rect1 `mappend` rect2 + where+ rect1 = dcRectangle DRAW_FILL_STROKE 36 24 + rect2 = moveStart (hvec 36) $ dcRectangle DRAW_FILL_STROKE 60 24 ++++emLocGraphic :: LocGraphic Em+emLocGraphic = localize swap_colours $ rect1 `mappend` rect2 + where+ rect1 = dcRectangle DRAW_FILL_STROKE 3 2 + rect2 = moveStart (hvec 3) $ dcRectangle DRAW_FILL_STROKE 5 2 +++enLocGraphic :: LocGraphic En+enLocGraphic = localize (fill_colour lemon_chiffon) $ rect1 `mappend` rect2 + where+ rect1 = dcRectangle DRAW_FILL_STROKE 6 4+ rect2 = moveStart (hvec 6) $ dcRectangle DRAW_FILL_STROKE 10 4+++haskell_org :: XLink+haskell_org = xlinkhref "http://www.haskell.org"++firebrick :: RGBi+firebrick = RGBi 0xb2 0x22 0x22++lemon_chiffon :: RGBi+lemon_chiffon = RGBi 0xff 0xfa 0xcd++linen :: RGBi+linen = RGBi 0xfa 0xf0 0xe6++
demo/FontDeltaPic.hs view
@@ -16,8 +16,7 @@ main :: IO () main = do createDirectoryIfMissing True "./out/"- putStrLn $ "Note - the SVG optimization that should be used here"- putStrLn $ "has bit-rotted and is not currently in use.."+ putStrLn $ "Check the generated SVG to verify the font attrs are grouped..." -- let pic1 = runCtxPictureU std_attr drawing01 writeEPS "./out/font_delta01.eps" pic1@@ -28,23 +27,23 @@ std_attr = standardContext 24 -drawing01 :: DCtxPicture+drawing01 :: CtxPicture drawing01 = drawTracing $ mf -mf :: (Floating u, FromPtSize u) => TraceDrawing u ()-mf = do +mf :: TraceDrawing Double ()+mf = fontDelta $ do draw $ line1 `at` (P2 0 100) draw $ line2 `at` (P2 0 75) draw $ line3 `at` (P2 0 50) draw $ line4 `at` (P2 0 25) draw $ line5 `at` (P2 0 0) where- line1 = textline "All the lines of this drawing" - line2 = textline "should be grouped within a SVG"- line3 = textline "g-element, from where they"- line4 = textline "inherit the font-family and"- line5 = textline "font-size attributes."+ line1 = dcTextlabel "All the lines of this drawing" + line2 = dcTextlabel "should be grouped within a SVG"+ line3 = dcTextlabel "g-element, from where they"+ line4 = dcTextlabel "inherit the font-family and"+ line5 = dcTextlabel "font-size attributes."
+ demo/SimpleAdvGraphic.hs view
@@ -0,0 +1,68 @@+{-# OPTIONS -Wall #-}+++module SimpleAdvGraphic where++import Wumpus.Basic.Kernel++import Wumpus.Core -- package: wumpus-core++import Control.Applicative+import System.Directory++main :: IO ()+main = do + createDirectoryIfMissing True "./out/"+ let pic1 = runCtxPictureU std_attr drawing01+ writeEPS "./out/simple_adv_graphic01.eps" pic1+ writeSVG "./out/simple_adv_graphic01.svg" pic1+++std_attr :: DrawingContext+std_attr = standardContext 24+++drawing01 :: CtxPicture+drawing01 = drawTracing mf ++++-- Although TraceDrawing can be fully unit polymorphic, it seems +-- always best to specialize as we are stating concrete values+-- (and they will be in some unit).+--++mf :: TraceDrawing Double ()+mf = do+ drawl (P2 0 120) $ + runAdvObject $ evenspace (hvec 10) [text01, text02, text01]++ drawl (P2 0 80) $ + runAdvObject $ advances [text01, text02, text01]++ drawl (P2 0 40) $ + runAdvObject (miniDisk `advance` text01 `advance` miniDisk)++ drawl (P2 0 0) $+ runAdvObject (miniDisk `advance` text02 `advance` miniDisk)+++-- Normally, text calculate the advance vector from the font +-- metrics...+--+text01 :: AdvGraphic Double+text01 = makeAdvObject (pure $ hvec 84) $ dcTextlabel "text01"+ ++text02 :: AdvGraphic Double+text02 = makeAdvObject (pure $ hvec 210) $ dcTextlabel "text number two"+++miniDisk :: AdvGraphic Double+miniDisk = makeAdvObject (pure $ V2 0 0) disk1 + where+ disk1 = localize (fill_colour sienna) $ dcDisk DRAW_FILL 3+++sienna :: RGBi+sienna = RGBi 160 82 45
+ demo/SimplePosObject.hs view
@@ -0,0 +1,114 @@+{-# OPTIONS -Wall #-}+++module SimplePosObject where++import Wumpus.Basic.Kernel++import Wumpus.Core -- package: wumpus-core+import Wumpus.Core.Colour ( red )++import Data.Monoid+import System.Directory++++main :: IO ()+main = do + createDirectoryIfMissing True "./out/"+ let pic1 = runCtxPictureU std_ctx drawing01+ writeEPS "./out/simple_pos_object01.eps" pic1+ writeSVG "./out/simple_pos_object01.svg" pic1+++std_ctx :: DrawingContext+std_ctx = standardContext 24+++drawing01 :: CtxPicture+drawing01 = drawTracing $ localize (fill_colour red) $ mf +++mf :: TraceDrawing Double ()+mf = do+ draw $ testDrawMinor NN `at` (P2 0 400)+ draw $ testDrawMinor SS `at` (P2 75 400)+ draw $ testDrawMinor EE `at` (P2 150 400)+ draw $ testDrawMinor WW `at` (P2 225 400)+ draw $ testDrawMinor NE `at` (P2 0 325)+ draw $ testDrawMinor SE `at` (P2 75 325)+ draw $ testDrawMinor SW `at` (P2 150 325)+ draw $ testDrawMinor NW `at` (P2 225 325)+ draw $ testDrawMinor CENTER `at` (P2 0 250)+ draw $ testDrawMinor BLL `at` (P2 75 250)+ draw $ testDrawMinor BLC `at` (P2 150 250)+ draw $ testDrawMinor BLR `at` (P2 225 250)+++ draw $ testDrawBl NN `at` (P2 0 150)+ draw $ testDrawBl SS `at` (P2 75 150)+ draw $ testDrawBl EE `at` (P2 150 150)+ draw $ testDrawBl WW `at` (P2 225 150)+ draw $ testDrawBl NE `at` (P2 0 75)+ draw $ testDrawBl SE `at` (P2 75 75)+ draw $ testDrawBl SW `at` (P2 150 75)+ draw $ testDrawBl NW `at` (P2 225 75)+ draw $ testDrawBl CENTER `at` (P2 0 0)+ draw $ testDrawBl BLL `at` (P2 75 0)+ draw $ testDrawBl BLC `at` (P2 150 0)+ draw $ testDrawBl BLR `at` (P2 225 0)+ + drawl (P2 300 0) $ runPosObject SW msg+ where+ msg = multilinePosText VALIGN_RIGHT "Red dot represents the\nrectangle address."+++testDrawBl :: RectAddress -> LocGraphic Double+testDrawBl raddr = dcDisk DRAW_FILL 2 `mappend` rectBl raddr+++rectBl :: RectAddress -> LocGraphic Double+rectBl raddr = + ignoreAns $ runPosObject raddr $ makePosObject (return ortt) (mkRectBl w h)+ where+ w = 40 + h = 30+ ortt = Orientation { or_x_minor = 0+ , or_x_major = w+ , or_y_minor = 0+ , or_y_major = h }+ ++-- start-point - bottom left+mkRectBl :: InterpretUnit u => u -> u -> LocGraphic u+mkRectBl w h = dcRectangle DRAW_STROKE w h++++testDrawMinor :: RectAddress -> LocGraphic Double+testDrawMinor raddr = + dcDisk DRAW_FILL 2 `mappend` (ignoreAns $ rectMinor raddr)+++rectMinor :: RectAddress -> LocGraphic Double+rectMinor raddr = + runPosObject raddr $ makePosObject (return ortt) (mkRectMinor m w h)+ where+ m = 10+ w = 40 + h = 30+ ortt = Orientation { or_x_minor = m+ , or_x_major = (w-m)+ , or_y_minor = m+ , or_y_major = (h-m) }+ ++-- start-point - +10 +10+mkRectMinor :: InterpretUnit u => u -> u -> u -> LocGraphic u+mkRectMinor m w h = promoteLoc $ \pt -> + let bl = displace (vec (-m) (-m)) pt+ br = displace (hvec w) bl+ tr = displace (vvec h) br+ tl = displace (vvec h) bl+ in liftQuery (vertexPP [bl, br, tr, tl]) >>= dcClosedPath DRAW_STROKE+
src/Wumpus/Basic/Kernel.hs view
@@ -3,7 +3,7 @@ -------------------------------------------------------------------------------- -- | -- Module : Wumpus.Basic.Kernel--- Copyright : (c) Stephen Tetley 2010+-- Copyright : (c) Stephen Tetley 2010-2011 -- License : BSD3 -- -- Maintainer : stephen.tetley@gmail.com@@ -12,42 +12,80 @@ -- -- Import shim for @Wumpus.Basic.Kernel@ modules. --+-- @Kernel.Base@ - low-level objects, general enumerations, unit +-- and @DrawingContext@ support. @DrawingContext@ is comparative +-- to the /graphics state/ in PostScript, but it is a read-only+-- environment (cf. the Reader monad). Like the Reader monad it +-- supports branching update through @local@ - here called +-- @localize@.+-- +-- @Kernel.Objects@ - \"elementary\" drawing objects, plus some +-- catalogues of named, predefined drawing objects +-- (DrawingPrimitives) and useful operations (named vectors - +-- Displacement). --+-- @Kernel.Drawing@ - \"collective\" drawing objects. @Drawing@ is +-- considered a higher layer than @Objects@, so there should be +-- dependencies only from @Drawing@ to @Objects@.+-- -------------------------------------------------------------------------------- module Wumpus.Basic.Kernel (- module Wumpus.Basic.Kernel.Base.Anchors- , module Wumpus.Basic.Kernel.Base.BaseDefs- , module Wumpus.Basic.Kernel.Base.ContextFun+ module Wumpus.Basic.Kernel.Base.BaseDefs , module Wumpus.Basic.Kernel.Base.DrawingContext- , module Wumpus.Basic.Kernel.Base.GlyphMetrics+ , module Wumpus.Basic.Kernel.Base.FontSupport , module Wumpus.Basic.Kernel.Base.QueryDC- , module Wumpus.Basic.Kernel.Base.ScalingContext+ , module Wumpus.Basic.Kernel.Base.Units , module Wumpus.Basic.Kernel.Base.UpdateDC , module Wumpus.Basic.Kernel.Base.WrappedPrimitive- , module Wumpus.Basic.Kernel.Objects.AdvanceGraphic- , module Wumpus.Basic.Kernel.Objects.BaseObjects+ , module Wumpus.Basic.Kernel.Drawing.Basis+ , module Wumpus.Basic.Kernel.Drawing.Chain+ , module Wumpus.Basic.Kernel.Drawing.CtxPicture+ , module Wumpus.Basic.Kernel.Drawing.LocDrawing+ , module Wumpus.Basic.Kernel.Drawing.LocTrace+ , module Wumpus.Basic.Kernel.Drawing.PosObject+ , module Wumpus.Basic.Kernel.Drawing.TraceDrawing+ , module Wumpus.Basic.Kernel.Objects.AdvObject+ , module Wumpus.Basic.Kernel.Objects.Anchors+ , module Wumpus.Basic.Kernel.Objects.Basis , module Wumpus.Basic.Kernel.Objects.Bounded+ , module Wumpus.Basic.Kernel.Objects.Concat , module Wumpus.Basic.Kernel.Objects.Connector- , module Wumpus.Basic.Kernel.Objects.CtxPicture- , module Wumpus.Basic.Kernel.Objects.Graphic- , module Wumpus.Basic.Kernel.Objects.TraceDrawing+ , module Wumpus.Basic.Kernel.Objects.Displacement+ , module Wumpus.Basic.Kernel.Objects.DrawingPrimitives+ , module Wumpus.Basic.Kernel.Objects.Image+ , module Wumpus.Basic.Kernel.Objects.LocImage+ , module Wumpus.Basic.Kernel.Objects.LocThetaImage+ , module Wumpus.Basic.Kernel.Objects.Orientation+ , module Wumpus.Basic.Kernel.Objects.Trail+ ) where -import Wumpus.Basic.Kernel.Base.Anchors import Wumpus.Basic.Kernel.Base.BaseDefs-import Wumpus.Basic.Kernel.Base.ContextFun import Wumpus.Basic.Kernel.Base.DrawingContext-import Wumpus.Basic.Kernel.Base.GlyphMetrics+import Wumpus.Basic.Kernel.Base.FontSupport import Wumpus.Basic.Kernel.Base.QueryDC-import Wumpus.Basic.Kernel.Base.ScalingContext+import Wumpus.Basic.Kernel.Base.Units import Wumpus.Basic.Kernel.Base.UpdateDC import Wumpus.Basic.Kernel.Base.WrappedPrimitive-import Wumpus.Basic.Kernel.Objects.AdvanceGraphic-import Wumpus.Basic.Kernel.Objects.BaseObjects+import Wumpus.Basic.Kernel.Drawing.Basis+import Wumpus.Basic.Kernel.Drawing.Chain+import Wumpus.Basic.Kernel.Drawing.CtxPicture+import Wumpus.Basic.Kernel.Drawing.LocDrawing+import Wumpus.Basic.Kernel.Drawing.LocTrace+import Wumpus.Basic.Kernel.Drawing.PosObject+import Wumpus.Basic.Kernel.Drawing.TraceDrawing+import Wumpus.Basic.Kernel.Objects.AdvObject+import Wumpus.Basic.Kernel.Objects.Anchors+import Wumpus.Basic.Kernel.Objects.Basis import Wumpus.Basic.Kernel.Objects.Bounded+import Wumpus.Basic.Kernel.Objects.Concat import Wumpus.Basic.Kernel.Objects.Connector-import Wumpus.Basic.Kernel.Objects.CtxPicture-import Wumpus.Basic.Kernel.Objects.Graphic-import Wumpus.Basic.Kernel.Objects.TraceDrawing+import Wumpus.Basic.Kernel.Objects.Displacement+import Wumpus.Basic.Kernel.Objects.DrawingPrimitives+import Wumpus.Basic.Kernel.Objects.Image+import Wumpus.Basic.Kernel.Objects.LocImage+import Wumpus.Basic.Kernel.Objects.LocThetaImage+import Wumpus.Basic.Kernel.Objects.Orientation+import Wumpus.Basic.Kernel.Objects.Trail
− src/Wumpus/Basic/Kernel/Base/Anchors.hs
@@ -1,238 +0,0 @@-{-# LANGUAGE TypeFamilies #-}-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Basic.Kernel.Base.Anchors--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : Stephen Tetley <stephen.tetley@gmail.com>--- Stability : highly unstable--- Portability : GHC------ Anchor points on shapes, bounding boxes, etc.------ Anchors are addressable positions, an examplary use is taking--- anchors on node shapes to get the start and end points for --- connectors in a network (graph) diagram.--- -----------------------------------------------------------------------------------module Wumpus.Basic.Kernel.Base.Anchors- ( -- -- * Anchors- CenterAnchor(..)- , CardinalAnchor(..)- , CardinalAnchor2(..)- , RadialAnchor(..)-- -- * Extended anchor points- , northwards- , southwards- , eastwards- , westwards- , northeastwards- , southeastwards- , southwestwards- , northwestwards-- , radialConnectorPoints-- ) where--import Wumpus.Core -- package: wumpus-core--import Data.AffineSpace -- package: vector-space----- | Center of an object.----class CenterAnchor t where- center :: DUnit t ~ u => t -> Point2 u---- | Cardinal (compass) positions on an object. --- --- Note - in TikZ cardinal anchors are not necessarily at the--- equivalent radial position, for instance reactangle north-east--- is the top-right corner whether or not this is incident at --- 45deg.------ Wumpus generally follows the TikZ convention.----class CardinalAnchor t where- north :: DUnit t ~ u => t -> Point2 u- south :: DUnit t ~ u => t -> Point2 u- east :: DUnit t ~ u => t -> Point2 u- west :: DUnit t ~ u => t -> Point2 u----- | Secondary group of cardinal (compass) positions on an object. --- --- It seems possible that for some objects defining the primary--- compass points (north, south,...) will be straight-forward --- whereas defining the secondary compass points may be --- problemmatic, hence the compass points are split into two --- classes.----class CardinalAnchor2 t where- northeast :: DUnit t ~ u => t -> Point2 u- southeast :: DUnit t ~ u => t -> Point2 u- southwest :: DUnit t ~ u => t -> Point2 u- northwest :: DUnit t ~ u => t -> Point2 u----- | Anchor on a border that can be addressed by an angle.------ The angle is counter-clockwise from the right-horizontal, i.e.--- 0 is /east/.----class RadialAnchor t where- radialAnchor :: DUnit t ~ u => Radian -> t -> Point2 u-----extendPtDist :: (Real u, Floating u) => u -> Point2 u -> Point2 u -> Point2 u-extendPtDist d p1 p2 = let v = pvec p1 p2- ang = vdirection v- len = vlength v- in p1 .+^ avec ang (len+d)----- | 'northwards' : @ dist * object -> Point @--- --- Project the anchor along a line from the center that goes --- through the north anchor. ------ If the distance is zero the answer with be the north anchor.------ If the distance is negative the answer within the object before --- the north anchor.------ If the distance is positive the anchor outside the object.----northwards :: ( Real u, Floating u, CenterAnchor t, CardinalAnchor t- , u ~ DUnit t ) - => u -> t -> Point2 u-northwards u a = extendPtDist u (center a) (north a)----- | 'southwards' : @ dist * object -> Point @--- --- Variant of the function 'northwards', but projecting the line --- southwards from the center of the object.----southwards :: ( Real u, Floating u, CenterAnchor t, CardinalAnchor t- , u ~ DUnit t ) - => u -> t -> Point2 u-southwards u a = extendPtDist u (center a) (south a)----- | 'eastwards' : @ dist * object -> Point @--- --- Variant of the function 'northwards', but projecting the line --- eastwards from the center of the object.----eastwards :: ( Real u, Floating u, CenterAnchor t, CardinalAnchor t- , u ~ DUnit t ) - => u -> t -> Point2 u-eastwards u a = extendPtDist u (center a) (east a)----- | 'westwards' : @ dist * object -> Point @--- --- Variant of the function 'northwards', but projecting the line --- westwards from the center of the object.----westwards :: ( Real u, Floating u, CenterAnchor t, CardinalAnchor t- , u ~ DUnit t ) - => u -> t -> Point2 u-westwards u a = extendPtDist u (center a) (west a)----- | 'northeastwards' : @ dist * object -> Point @--- --- Variant of the function 'northwards', but projecting the line --- northeastwards from the center of the object.----northeastwards :: ( Real u, Floating u, CenterAnchor t, CardinalAnchor2 t- , u ~ DUnit t ) - => u -> t -> Point2 u-northeastwards u a = extendPtDist u (center a) (northeast a)----- | 'southeastwards' : @ dist * object -> Point @--- --- Variant of the function 'northwards', but projecting the line --- southeastwards from the center of the object.----southeastwards :: ( Real u, Floating u, CenterAnchor t, CardinalAnchor2 t- , u ~ DUnit t ) - => u -> t -> Point2 u-southeastwards u a = extendPtDist u (center a) (southeast a)----- | 'southwestwards' : @ dist * object -> Point @--- --- Variant of the function 'northwards', but projecting the line --- southwestwards from the center of the object.----southwestwards :: ( Real u, Floating u, CenterAnchor t, CardinalAnchor2 t- , u ~ DUnit t ) - => u -> t -> Point2 u-southwestwards u a = extendPtDist u (center a) (southwest a)----- | 'northwestwards' : @ dist * object -> Point @--- --- Variant of the function 'northwards', but projecting the line --- northwestwards from the center of the object.----northwestwards :: ( Real u, Floating u, CenterAnchor t, CardinalAnchor2 t- , u ~ DUnit t ) - => u -> t -> Point2 u-northwestwards u a = extendPtDist u (center a) (northwest a)--------------------------------------------------------------------------------------- | 'radialConnectorPoints' : @ object_a * object_b -> (Point_a, Point_b) @------ Find the radial connectors points for objects @a@ and @b@ along--- the line joining their centers.----radialConnectorPoints :: ( Real u, Floating u- , CenterAnchor t1, RadialAnchor t1- , CenterAnchor t2, RadialAnchor t2- , u ~ DUnit t1, DUnit t1 ~ DUnit t2 ) - => t1 -> t2 -> (Point2 u, Point2 u) -radialConnectorPoints a b = (radialAnchor theta a, radialAnchor (theta+pi) b)- where- theta = vdirection $ pvec (center a) (center b)- ------------------------------------------------------------------------------------- Instances --instance Fractional u => CenterAnchor (BoundingBox u) where- center (BBox (P2 xl ylo) (P2 xr yhi)) = P2 x y - where- x = xl+0.5*(xr-xl)- y = ylo+0.5*(yhi-ylo)- --instance Fractional u => CardinalAnchor (BoundingBox u) where- north (BBox (P2 xl _ ) (P2 xr yhi)) = P2 (xl+0.5*(xr-xl)) yhi- south (BBox (P2 xl ylo) (P2 xr _ )) = P2 (xl+0.5*(xr-xl)) ylo- east (BBox (P2 _ ylo) (P2 xr yhi)) = P2 xr (ylo+0.5*(yhi-ylo))- west (BBox (P2 xl ylo) (P2 _ yhi)) = P2 xl (ylo+0.5*(yhi-ylo))---instance Fractional u => CardinalAnchor2 (BoundingBox u) where- northeast (BBox _ ur) = ur- southeast (BBox (P2 _ ylo) (P2 xr _)) = P2 xr ylo- southwest (BBox ll _) = ll- northwest (BBox (P2 xl _) (P2 _ yhi)) = P2 xl yhi -
src/Wumpus/Basic/Kernel/Base/BaseDefs.hs view
@@ -1,13 +1,10 @@ {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeSynonymInstances #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-} {-# OPTIONS -Wall #-} -------------------------------------------------------------------------------- -- | -- Module : Wumpus.Basic.Kernal.Base.BaseDefs--- Copyright : (c) Stephen Tetley 2010+-- Copyright : (c) Stephen Tetley 2010-2012 -- License : BSD3 -- -- Maintainer : stephen.tetley@gmail.com@@ -22,262 +19,540 @@ module Wumpus.Basic.Kernel.Base.BaseDefs ( - -- * A semigroup class- OPlus(..)- , oconcat+ -- * Constants+ quarter_pi+ , half_pi+ , two_pi - -- * A bifunctor class- , Bimap(..)- , replaceL- , replaceR+ , ang180+ , ang150+ , ang120+ , ang90+ , ang60+ , ang45+ , ang30+ , ang15 + -- * Unit phantom type+ , UNil(..)+ , ureturn+ , uvoid++ -- * Non-contextual unit conversion.+ , ScalarUnit(..)++ -- * Unit interpretation with respect to the current Point size+ , InterpretUnit(..)+ , dinterpF+ , normalizeF+ , uconvert1+ , uconvertF++ , intraMapPoint+ , intraMapFunctor+++ -- * KernChar+ , KernChar++ -- * Drawing paths and shapes (closed paths)+ , PathMode(..)+ , DrawMode(..)+ , closedMode++ -- * Drawing layers+ , ZOrder(..) + -- * Alignment , HAlign(..) , VAlign(..) - -- * Advance vector- , AdvanceVec- , advanceH- , advanceV+ -- * Text height+ , TextHeight(..) - -- * Moving points- , PointDisplace- , displace- , displaceVec- , displaceH- , displaceV+ -- * Cardinal (compass) positions+ , Cardinal(..) - , ThetaPointDisplace- , displaceParallel- , displacePerpendicular+ -- * Direction enumeration+ , Direction(..)+ , ClockDirection(..) + , clockDirection + , HDirection(..)+ , horizontalDirection+ , VDirection(..)+ , verticalDirection - -- * Monadic drawing- , MonUnit+ -- * Quadrant enumeration+ , Quadrant(..)+ , quadrant - , PointSupplyM(..)+ -- * Beziers + , bezierArcPoints + , bezierMinorArc+ ++ -- * Misc++ , both+ , monPreRepeatPost+ ) where import Wumpus.Core -- package: wumpus-core + import Data.AffineSpace -- package: vector-space+import Data.VectorSpace -infixr 6 `oplus`+import Control.Applicative+import Data.Monoid --- | A Semigroup class.--- --- The perhaps unusual name is the TeX name for the circled plus --- glyph. It would be nice if there was a semigroup class in the--- Haskell Base libraries... --- -class OPlus t where- oplus :: t -> t -> t -oconcat :: OPlus t => t -> [t] -> t-oconcat t = step t- where- step ac [] = ac- step ac (x:xs) = step (ac `oplus` x) xs -instance OPlus () where- _ `oplus` _ = () -instance OPlus (UNil u) where- _ `oplus` _ = uNil+quarter_pi :: Radian+quarter_pi = 0.25 * pi -instance Ord u => OPlus (BoundingBox u) where- oplus = boundaryUnion+half_pi :: Radian+half_pi = 0.5 * pi -instance OPlus (Primitive u) where- a `oplus` b = primGroup [a,b]+two_pi :: Radian+two_pi = 2.0 * pi -instance (OPlus a, OPlus b) => OPlus (a,b) where- (a,b) `oplus` (a',b') = (a `oplus` a', b `oplus` b') +ang180 :: Radian+ang180 = pi -instance OPlus a => OPlus (r -> a) where- f `oplus` g = \x -> f x `oplus` g x+ang150 :: Radian+ang150 = 5 * ang30 --- The functional instance (r -> a) also covers (r1 -> r2 -> a),--- (r1 -> r2 -> r3 -> a) etc.+ang120 :: Radian +ang120 = 2 * ang60 +ang90 :: Radian+ang90 = pi / 2++ang60 :: Radian+ang60 = pi / 3++ang45 :: Radian +ang45 = pi / 4++ang30 :: Radian+ang30 = pi / 6++ang15 :: Radian+ang15 = pi / 12+++ --------------------------------------------------------------------------------+-- Simple objects wrapped with unit phatom type --- | A Bifunctor class.++-- | The empty data type - i.e. @()@ - wrapped with a phantom unit +-- parameter.+--+data UNil u = UNil deriving (Eq,Ord,Read,Show)++type instance DUnit (UNil u) = u++instance Functor UNil where+ fmap _ UNil= UNil+++instance Monoid (UNil u) where+ mempty = UNil+ _ `mappend` _ = UNil+++++instance Rotate (UNil u) where+ rotate _ = id++instance RotateAbout (UNil u) where+ rotateAbout _ _ = id++instance Scale (UNil u) where+ scale _ _ = id++instance Translate (UNil u) where+ translate _ _ = id++-- | Return a 'UNil' rather than @()@ at the end of sequence of+-- monadic commands.+--+-- Many Wumpus objects are usefully constructed in the +-- @do-notation@, but because Wumpus has to expose the type of +-- the @unit@ to the type checker we must finish the do-block +-- with:+--+-- > ureturn -- --- Again, it would be nice if there was a Bifunctor class in the--- Haskell Base libraries... +-- or: -- -class Bimap f where- bimap :: (a -> p) -> (b -> q) -> f a b -> f p q- bimapL :: (a -> p) -> f a b -> f p b- bimapR :: (b -> q) -> f a b -> f a q+-- > return UNil+--+-- rather than:+--+-- > return ()+--+--+ureturn :: Monad m => m (UNil u)+ureturn = return UNil +-- | 'uvoid' runs a monadic computation and returns @UNil@.+--+uvoid :: Monad m => m a -> m (UNil u)+uvoid ma = ma >> return UNil +--------------------------------------------------------------------------------+-- Non-contextual units -instance Bimap (,) where- bimap f g (a,b) = (f a, g b)- bimapL f (a,b) = (f a, b)- bimapR g (a,b) = (a, g b)+class ScalarUnit a where+ fromPsPoint :: Double -> a + toPsPoint :: a -> Double -instance Bimap Either where- bimap f _ (Left a) = Left (f a)- bimap _ g (Right b) = Right (g b)+instance ScalarUnit Double where+ fromPsPoint = id+ toPsPoint = id - bimapL f (Left a) = Left (f a)- bimapL _ (Right b) = Right b - bimapR _ (Left a) = Left a- bimapR g (Right b) = Right (g b) -replaceL :: Bimap f => p -> f a b -> f p b-replaceL = bimapL . const+--------------------------------------------------------------------------------+-- Interpreting units -replaceR :: Bimap f => q -> f a b -> f a q-replaceR = bimapR . const+-- Units may or may not depend on current font size+-- +class (Eq u, Num u) => InterpretUnit u where+ normalize :: FontSize -> u -> Double+ dinterp :: FontSize -> Double -> u ---------------------------------------------------------------------------------+instance InterpretUnit Double where+ normalize _ = id+ dinterp _ = id --- Alignment+instance InterpretUnit AfmUnit where+ normalize sz = afmValue sz + dinterp sz = afmUnit sz --- | Horizontal alignment - align to the top, center or bottom.++-- | 'dinterp' an object that gives access to its unit at the +-- functor position. ---data HAlign = HTop | HCenter | HBottom- deriving (Enum,Eq,Ord,Show)+dinterpF :: (Functor t, InterpretUnit u) => FontSize -> t Double -> t u+dinterpF sz = fmap (dinterp sz) --- | Vertical alignment - align to the left, center or bottom.++-- | 'normalize' an object that gives access to its unit at the +-- functor position. ---data VAlign = VLeft | VCenter | VRight- deriving (Enum,Eq,Ord,Show)+normalizeF :: (Functor t, InterpretUnit u) => FontSize -> t u -> t Double+normalizeF sz = fmap (normalize sz) ---------------------------------------------------------------------------------+-- | Convert a scalar value from one unit to another.+--+uconvert1 :: (InterpretUnit u, InterpretUnit u1) => FontSize -> u -> u1+uconvert1 sz = dinterp sz . normalize sz --- | Advance vectors provide an idiom for drawing consecutive--- graphics. PostScript uses them to draw left-to-right text - --- each character has an advance vector for the width and --- as characters are drawn they successively displace the start--- point for the next character with their advance vector.+-- | Unit convert an object that gives access to its unit at the+-- Functor position. ----- Type alias for Vec2.+-- In practive this will be \*all\* Image answers. ---type AdvanceVec u = Vec2 u+uconvertF :: (Functor t, InterpretUnit u, InterpretUnit u1) + => FontSize -> t u -> t u1+uconvertF sz = fmap (uconvert1 sz) --- | Extract the horizontal component of an advance vector.++-- Helper for defining Affine instances. This function allows +-- scaling etc to be applied on a Point coerced to a Double then+-- converted back to the original unit. Thus transformations can +-- work in contextual units. ----- For left-to-right latin text, the vertical component of an--- advance vector is expected to be 0. Ingoring it seems --- permissible when drawing text.+intraMapPoint :: InterpretUnit u + => FontSize -> (DPoint2 -> DPoint2) -> Point2 u -> Point2 u+intraMapPoint sz fn (P2 x y) = + let P2 x' y' = fn $ P2 (normalize sz x) (normalize sz y)+ in P2 (dinterp sz x') (dinterp sz y')++++-- Helper for defining Affine instances. This function allows +-- scaling etc to be applied on a Point coerced to a Double then+-- converted back to the original unit. Thus transformations can +-- work in contextual units. ---advanceH :: AdvanceVec u -> u-advanceH (V2 w _) = w+intraMapFunctor :: (Functor f, InterpretUnit u)+ => FontSize -> (f Double -> f Double) -> f u -> f u+intraMapFunctor sz fn ma = dinterpF sz $ fn $ normalizeF sz ma --- | Extract the verticall component of an advance vector.++--------------------------------------------------------------------------------+-- KernChar+++-- | Unit parametric version of KerningChar from Wumpus-Core. ---advanceV :: AdvanceVec u -> u-advanceV (V2 _ h) = h+type KernChar u = (u,EscapedChar) + ----------------------------------------------------------------------------------- Displacing points+-- Drawing closed paths --- | 'PointDisplace' is a type representing functions --- @from Point to Point@.++-- | Draw closed paths. +-- +-- > OSTROKE - open and stroked ----- It is especially useful for building composite graphics where --- one part of the graphic is drawn from a different start point --- to the other part.+-- > CSTROKE - closed and stroke ---type PointDisplace u = Point2 u -> Point2 u+-- > CFILL - closed and filled+--+-- > CFILL_STROKE - closed, the path is filled, its edge is stroked.+--+data PathMode = OSTROKE | CSTROKE | CFILL | CFILL_STROKE+ deriving (Bounded,Enum,Eq,Ord,Show) --- | 'displace' : @ x -> y -> PointDisplace @+++-- | Draw closed paths and shapes. +-- +-- > DRAW_STROKE - closed and stroked ----- Build a combinator to move @Points@ by the supplied @x@ and --- @y@ distances.+-- > DRAW_FILL - closed and filled ---displace :: Num u => u -> u -> PointDisplace u-displace dx dy (P2 x y) = P2 (x+dx) (y+dy)+-- > DRAW_FILL_STROKE - the path is filled, its edge is stroked.+--+data DrawMode = DRAW_STROKE | DRAW_FILL | DRAW_FILL_STROKE+ deriving (Bounded,Enum,Eq,Ord,Show) --- | 'displaceV' : @ (V2 x y) -> PointDisplace @+-- | Interpret a 'DrawMode' for a closed path.+--+closedMode :: DrawMode -> PathMode+closedMode DRAW_STROKE = CSTROKE +closedMode DRAW_FILL = CFILL +closedMode DRAW_FILL_STROKE = CFILL_STROKE++++++-- | Enumerated type for drawing with respect to the z-order.+--+data ZOrder = ZBELOW | ZABOVE+ deriving (Bounded,Enum,Eq,Ord,Show)+++--------------------------------------------------------------------------------++-- Alignment++-- | Horizontal alignment - align to the top, center or bottom.+--+data HAlign = HALIGN_TOP | HALIGN_CENTER | HALIGN_BASE+ deriving (Enum,Eq,Ord,Show)++-- | Vertical alignment - align to the left, center or bottom.+--+data VAlign = VALIGN_LEFT | VALIGN_CENTER | VALIGN_RIGHT+ deriving (Enum,Eq,Ord,Show)+++--------------------------------------------------------------------------------+-- Text height+++-- | Wumpus distinguishes two use-cases for displaying vertically +-- centered text. -- --- Version of 'displace' where the displacement is supplied as--- a vector rather than two parameters.+-- Arbitrary text that is expected to contain lower case letters +-- with descenders, show take the vertical center as the mid-point +-- between the cap height and the descender depth.+--+-- Unfortunately, including the descender depth can produce +-- unbalanced results for text which is not expected to have +-- descenders (e.g. numbers within a bordered box), visually this +-- makes the center too high. -- -displaceVec :: Num u => Vec2 u -> PointDisplace u-displaceVec (V2 dx dy) (P2 x y) = P2 (x+dx) (y+dy)+data TextHeight = JUST_CAP_HEIGHT | CAP_HEIGHT_PLUS_DESCENDER+ deriving (Enum,Eq,Ord,Show) --- | 'displaceH' : @ x -> PointDisplace @--- --- Build a combinator to move @Points@ by horizontally the --- supplied @x@ distance.++--------------------------------------------------------------------------------++-- Compass positions++-- | An enumeratied type representing the compass positions. ---displaceH :: Num u => u -> PointDisplace u-displaceH dx (P2 x y) = P2 (x+dx) y+data Cardinal = NORTH | NORTH_EAST | EAST | SOUTH_EAST + | SOUTH | SOUTH_WEST | WEST | NORTH_WEST+ deriving (Enum,Eq,Ord,Show) --- | 'displaceV' : @ y -> PointDisplace @--- --- Build a combinator to move @Points@ vertically by the supplied --- @y@ distance.++-- | An enumerated type representing horizontal and vertical +-- directions. ---displaceV :: Num u => u -> PointDisplace u-displaceV dy (P2 x y) = P2 x (y+dy)+data Direction = UP | DOWN | LEFT | RIGHT+ deriving (Enum,Eq,Ord,Show) --- | 'ThetaPointDisplace' is a type representing functions --- @from Radian * Point to Point@.+-- | An enumerated type representing horizontal direction. ----- It is useful for building arrowheads which are constructed --- with an implicit angle representing the direction of the line --- at the arrow tip.+data HDirection = LEFTWARDS | RIGHTWARDS+ deriving (Enum,Eq,Ord,Show) +++horizontalDirection :: Radian -> HDirection+horizontalDirection = fn . circularModulo+ where+ fn a | a <= 0.5*pi || a > 1.5*pi = RIGHTWARDS+ | otherwise = LEFTWARDS++-- | An enumerated type representing vertical direction. ---type ThetaPointDisplace u = Radian -> PointDisplace u+data VDirection = UPWARDS | DOWNWARDS+ deriving (Enum,Eq,Ord,Show) +verticalDirection :: Radian -> VDirection+verticalDirection = fn . circularModulo+ where+ fn a | a <= pi = UPWARDS+ | otherwise = DOWNWARDS+ --- | 'displaceParallel' : @ dist -> ThetaPointDisplace @--- --- Build a combinator to move @Points@ in parallel to the --- direction of the implicit angle by the supplied distance --- @dist@. +-- | An enumerated type representing /clock/ directions. ---displaceParallel :: Floating u => u -> ThetaPointDisplace u-displaceParallel d = \theta pt -> pt .+^ avec (circularModulo theta) d+data ClockDirection = CW | CCW+ deriving (Enum,Eq,Ord,Show) --- | 'displaceParallel' : @ dist -> ThetaPointDisplace @--- --- Build a combinator to move @Points@ perpendicular to the --- direction of the implicit angle by the supplied distance --- @dist@. ++-- | Note - behaviour at the continuity (0 deg, 180 deg, ...) is+-- unspecified. ---displacePerpendicular :: Floating u => u -> ThetaPointDisplace u-displacePerpendicular d = - \theta pt -> pt .+^ avec (circularModulo $ theta + (0.5*pi)) d+clockDirection :: (Real u, Floating u) + => Vec2 u -> Vec2 u -> ClockDirection+clockDirection v1 v2 = if a1 < asum then CW else CCW+ where+ a1 = r2d $ vdirection v1+ asum = r2d $ vdirection (v1 ^+^ v2) +-- | An enumerated type representing quadrants.+-- +data Quadrant = QUAD_NE | QUAD_NW | QUAD_SW | QUAD_SE+ deriving (Enum,Eq,Ord,Show) +-- | 'quadrant' : @ ang -> Quadrant @+--+-- Get the quadrant of an angle.+--+quadrant :: Radian -> Quadrant+quadrant = fn . circularModulo+ where+ fn a | a < 0.5*pi = QUAD_NE+ | a < pi = QUAD_NW+ | a < 1.5*pi = QUAD_SW+ | otherwise = QUAD_SE ++ ----------------------------------------------------------------------------------- Monadic drawing+-- Beziers --- | DUnit is always for fully saturated type constructors, so --- (seemingly) an equivalent type family is needed for monads.+kappa :: Floating u => u+kappa = 4 * ((sqrt 2 - 1) / 3) -type family MonUnit m :: * --- | A monad that supplies points, e.g. a turtle monad. +-- | 'bezierArcPoints' : @ apex_angle * radius * inclination * center -> [Point] @ ----- \*\* WARNING \*\* - the idea behind this class is somewhat--- half-baked. It may be revised or even dropped in subsequent--- versions of Wumpus-Basic.+-- > ang should be in the range 0 < ang < 360deg. ---class Monad m => PointSupplyM (m :: * -> *) where- position :: MonUnit m ~ u => m (Point2 u)+-- > if 0 < ang <= 90 returns 4 points+-- > if 90 < ang <= 180 returns 7 points+-- > if 180 < ang <= 270 returns 10 points+-- > if 270 < ang < 360 returns 13 points+--+bezierArcPoints :: Floating u + => Radian -> u -> Radian -> Point2 u -> [Point2 u]+bezierArcPoints ang radius theta pt = go (circularModulo ang)+ where+ go a | a <= half_pi = wedge1 a+ | a <= pi = wedge2 (a/2)+ | a <= 1.5*pi = wedge3 (a/3)+ | otherwise = wedge4 (a/4)+ + wedge1 a = + let (p0,p1,p2,p3) = bezierMinorArc a radius theta pt+ in [p0,p1,p2,p3]++ wedge2 a = + let (p0,p1,p2,p3) = bezierMinorArc a radius theta pt+ (_ ,p4,p5,p6) = bezierMinorArc a radius (theta+a) pt+ in [ p0,p1,p2,p3, p4,p5,p6 ] ++ wedge3 a = + let (p0,p1,p2,p3) = bezierMinorArc a radius theta pt+ (_ ,p4,p5,p6) = bezierMinorArc a radius (theta+a) pt+ (_ ,p7,p8,p9) = bezierMinorArc a radius (theta+a+a) pt+ in [ p0,p1,p2,p3, p4,p5,p6, p7, p8, p9 ] + + wedge4 a = + let (p0,p1,p2,p3) = bezierMinorArc a radius theta pt+ (_ ,p4,p5,p6) = bezierMinorArc a radius (theta+a) pt+ (_ ,p7,p8,p9) = bezierMinorArc a radius (theta+a+a) pt+ (_ ,p10,p11,p12) = bezierMinorArc a radius (theta+a+a+a) pt+ in [ p0,p1,p2,p3, p4,p5,p6, p7,p8,p9, p10,p11, p12 ] +++-- | 'bezierMinorArc' : @ apex_angle * radius * rotation * center -> BezierCurve @+--+-- > ang should be in the range 0 < ang <= 90deg.+--+bezierMinorArc :: Floating u + => Radian -> u -> Radian -> Point2 u + -> (Point2 u, Point2 u, Point2 u, Point2 u)+bezierMinorArc ang radius theta pt = (p0,p1,p2,p3)+ where+ kfactor = fromRadian $ ang / (0.5*pi)+ rl = kfactor * radius * kappa+ totang = circularModulo $ ang + theta++ p0 = pt .+^ orthoVec radius 0 theta+ p1 = p0 .+^ orthoVec 0 rl theta+ p2 = p3 .+^ orthoVec 0 (-rl) totang+ p3 = pt .+^ orthoVec radius 0 totang+++--------------------------------------------------------------------------------+++-- | Applicative /both/ - run both computations return the pair+-- of the the answers.+--+both :: Applicative f => f a -> f b -> f (a,b)+both fa fb = (,) <$> fa <*> fb+++-- | Monodial scheme - prefix, repeat body n times, suffix.+--+monPreRepeatPost :: Monoid a => a -> (Int, a) -> a -> a+monPreRepeatPost pre (n,body1) post = step pre n+ where+ step ac i | i < 1 = ac `mappend` post+ | otherwise = step (ac `mappend` body1) (i - 1) +
− src/Wumpus/Basic/Kernel/Base/ContextFun.hs
@@ -1,550 +0,0 @@-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Basic.Kernel.Base.ContextFun--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : stephen.tetley@gmail.com--- Stability : highly unstable--- Portability : GHC ------ Function types operating over the DrawingContext as a /static/ --- argument. --------------------------------------------------------------------------------------module Wumpus.Basic.Kernel.Base.ContextFun- (-- -- * /Context functional/ types- CF - , CF1- , CF2-- , LocCF- , LocThetaCF- , ConnectorCF- , DLocCF- , DLocThetaCF- , DConnectorCF---- -- * Run functions- , runCF- , runCF1 - , runCF2-- -- * Lift functions- , lift0R1- , lift0R2- , lift1R2- - , promoteR1- , promoteR2--- , apply1R1- , apply2R2- , apply1R2-- -- * Extractors- , drawingCtx- , queryCtx- , locCtx- , locPoint- , locThetaCtx- , locThetaPoint- , locThetaAng- , connCtx- , connStart- , connEnd-- -- * Combinators- , at- , rot- , atRot- , connect- , chain1--- ) where--import Wumpus.Basic.Kernel.Base.BaseDefs-import Wumpus.Basic.Kernel.Base.DrawingContext--import Wumpus.Core -- package: wumpus-core---import Control.Applicative-import Data.Monoid------------------------------------------------------------------------------------------ | Most drawing operations in Wumpus-Basic have an implicit --- /graphics state/ the 'DrawingContext', so the most primitive --- building block is a function from the DrawingContext to some --- polymorphic answer.--- --- This functional type is represented concretely as the initials --- @CF@ for /contextual function/.--- --- > CF :: DrawingContext -> a ----newtype CF a = CF { unCF :: DrawingContext -> a }----- | Variation of 'CF' with one parametric /static argument/.------ The static argument is commonly a point representing the start --- point / origin of a drawing.--- --- > CF1 :: DrawingContext -> r1 -> a ----newtype CF1 r1 a = CF1 { unCF1 :: DrawingContext -> r1 -> a }----- | Variation of 'CF' with two parametric /static arguments/.------ The first argument is commonly a point representing the start --- point / origin of a drawing. The second argument might --- typically be the angle of displacement (for drawing arrowheads) --- or an end point (for drawing connectors between two points).--- --- > CF2 :: DrawingContext -> r1 -> r2 -> a ----newtype CF2 r1 r2 a = CF2 { unCF2 :: DrawingContext -> r1 -> r2 -> a }-- ---- | Type specialized verison of 'CF1' where the /static argument/--- is the /start point/.--- --- > LocCF :: DrawingContext -> Point2 u -> a ----type LocCF u a = CF1 (Point2 u) a----- | Type specialized verison of 'CF2' where the /static arguments/--- are the /start point/ and the /angle of displacement/.--- --- > LocThetaCF :: DrawingContext -> Point2 u -> Radian -> a ----type LocThetaCF u a = CF2 (Point2 u) Radian a----- | Type specialized verison of 'CF2' where the /static arguments/--- are the /start point/ and the /end point/.--- --- > ConnectorCF :: DrawingContext -> Point2 u -> Point2 u -> a ----type ConnectorCF u a = CF2 (Point2 u) (Point2 u) a----- | Alias of 'LocCF' where the unit type is specialized to --- @Double@.----type DLocCF a = LocCF Double a----- | Alias of 'LocThetaCF' where the unit type is specialized to --- @Double@.----type DLocThetaCF a = LocThetaCF Double a---- | Alias of 'ConnectorCF' where the unit type is specialized to --- @Double@.----type DConnectorCF a = ConnectorCF Double a--------------------------------------------------------------------------------------- CF instances---- OPlus--instance OPlus a => OPlus (CF a) where- fa `oplus` fb = CF $ \ctx -> unCF fa ctx `oplus` unCF fb ctx--instance OPlus a => OPlus (CF1 r1 a) where- fa `oplus` fb = CF1 $ \ctx r1 -> unCF1 fa ctx r1 `oplus` unCF1 fb ctx r1--instance OPlus a => OPlus (CF2 r1 r2 a) where- fa `oplus` fb = CF2 $ \ctx r1 r2 -> - unCF2 fa ctx r1 r2 `oplus` unCF2 fb ctx r1 r2----- Monoid- --- Nothing is stopping monoid instances, though in practice there--- might be few useful types (more in Semgigroup / OPlus)...--instance Monoid a => Monoid (CF a) where - mempty = CF $ \_ -> mempty- fa `mappend` fb = CF $ \ctx -> unCF fa ctx `mappend` unCF fb ctx--instance Monoid a => Monoid (CF1 r1 a) where - mempty = CF1 $ \_ _ -> mempty- fa `mappend` fb = CF1 $ \ctx r1 -> unCF1 fa ctx r1 `mappend` unCF1 fb ctx r1--instance Monoid a => Monoid (CF2 r1 r2 a) where - mempty = CF2 $ \_ _ _ -> mempty- fa `mappend` fb = CF2 $ \ctx r1 r2 -> - unCF2 fa ctx r1 r2 `mappend` unCF2 fb ctx r1 r2------ Functor--instance Functor CF where- fmap f ma = CF $ \ctx -> f $ unCF ma ctx ---instance Functor (CF1 r1) where- fmap f ma = CF1 $ \ctx r1 -> f $ unCF1 ma ctx r1 --instance Functor (CF2 r1 r2) where- fmap f ma = CF2 $ \ctx r1 r2 -> f $ unCF2 ma ctx r1 r2------ Applicative--instance Applicative CF where- pure a = CF $ \_ -> a- mf <*> ma = CF $ \ctx -> let f = unCF mf ctx- a = unCF ma ctx- in f a---instance Applicative (CF1 r1) where- pure a = CF1 $ \_ _ -> a- mf <*> ma = CF1 $ \ctx r1 -> let f = unCF1 mf ctx r1 - a = unCF1 ma ctx r1- in f a---instance Applicative (CF2 r1 r2) where- pure a = CF2 $ \_ _ _ -> a- mf <*> ma = CF2 $ \ctx r1 r2 -> let f = unCF2 mf ctx r1 r2- a = unCF2 ma ctx r1 r2- in f a------- Monad --instance Monad CF where- return a = CF $ \_ -> a- ma >>= k = CF $ \ctx -> let a = unCF ma ctx in (unCF . k) a ctx --instance Monad (CF1 r1) where- return a = CF1 $ \_ _ -> a- ma >>= k = CF1 $ \ctx r1 -> let a = unCF1 ma ctx r1 in (unCF1 . k) a ctx r1 --instance Monad (CF2 r1 r2) where- return a = CF2 $ \_ _ _ -> a- ma >>= k = CF2 $ \ctx r1 r2 -> - let a = unCF2 ma ctx r1 r2 in (unCF2 . k) a ctx r1 r2------ DrawingCtxM --instance DrawingCtxM CF where- askDC = CF $ \ctx -> ctx- localize upd df = CF $ \ctx -> unCF df (upd ctx)- --instance DrawingCtxM (CF1 r1) where- askDC = CF1 $ \ctx _ -> ctx- localize upd df = CF1 $ \ctx r1 -> unCF1 df (upd ctx) r1---instance DrawingCtxM (CF2 r1 r2) where- askDC = CF2 $ \ctx _ _ -> ctx- localize upd df = CF2 $ \ctx r1 r2 -> unCF2 df (upd ctx) r1 r2----- Note - there is nothing determining a DUnit for the CF types, --- so it seems appropriate not to define affine instances.------ However affine instances can be made for the Image type in --- Objects.BaseObjects.----------------------------------------------------------------------------------------- Run functions---- | Run a /CF/ (context function) with the supplied --- /DrawingContext/.----runCF :: DrawingContext -> CF a -> a-runCF ctx df = unCF df ctx----- | Run a /CF1/ (context function) with the supplied --- /DrawingContext/ and static argument.----runCF1 :: DrawingContext -> r1 -> CF1 r1 a -> a-runCF1 ctx r1 df = unCF1 df ctx r1----- | Run a /CF1/ (context function) with the supplied --- /DrawingContext/ and two static arguments.----runCF2 :: DrawingContext -> r1 -> r2 -> CF2 r1 r2 a -> a-runCF2 ctx r1 r2 df = unCF2 df ctx r1 r2--------------------------------------------------------------------------------------- lift functions----- | Lift a zero-arity context function 'CF' to an arity one --- context function 'CF1'.--- -lift0R1 :: CF a -> CF1 r1 a-lift0R1 mf = CF1 $ \ctx _ -> unCF mf ctx---- | Lift a zero-arity context function 'CF' to an arity two --- context function 'CF2'.--- -lift0R2 :: CF a -> CF2 r1 r2 a-lift0R2 mf = CF2 $ \ctx _ _ -> unCF mf ctx---- | Lift an arity one context function 'CF1' to an arity two--- context function 'CF2'.--- -lift1R2 :: CF1 r1 a -> CF2 r1 r2 a-lift1R2 mf = CF2 $ \ctx r1 _ -> unCF1 mf ctx r1------ | Promote a function @from one argument to a Context Function@ --- to an arity one @Context Function@.------ The type signature is as explanatory as a description:------ > promoteR1 :: (r1 -> CF a) -> CF1 r1 a--- -promoteR1 :: (r1 -> CF a) -> CF1 r1 a-promoteR1 mf = CF1 $ \ctx r1 -> unCF (mf r1) ctx---- | Promote a function @from two arguments to a Context Function@ --- to an arity two @Context Function@.------ The type signature is as explanatory as a description:------ > promoteR2 :: (r1 -> r2 -> CF a) -> CF2 r1 r2 a--- -promoteR2 :: (r1 -> r2 -> CF a) -> CF2 r1 r2 a-promoteR2 mf = CF2 $ \ctx r1 r2 -> unCF (mf r1 r2) ctx------ | Apply an arity-one Context Function to a single argument, --- downcasting it by one level, making an arity-zero Context --- function. --- --- The type signature is as explanatory as a description:------ > apply1R1 :: CF1 r1 a -> r1 -> CF a----apply1R1 :: CF1 r1 a -> r1 -> CF a-apply1R1 mf r1 = CF $ \ctx -> unCF1 mf ctx r1----- | Apply an arity-two Context Function to two arguments, --- downcasting it by two levels, making an arity-zero Context --- function. --- --- The type signature is as explanatory as a description:------ > apply2R2 :: CF2 r1 r2 a -> r1 -> r2 -> CF a--- -apply2R2 :: CF2 r1 r2 a -> r1 -> r2 -> CF a-apply2R2 mf r1 r2 = CF $ \ctx -> unCF2 mf ctx r1 r2---- | Apply an arity-two Context Function to one argument, --- downcasting it by one level, making an arity-one Context --- function. --- --- The type signature is as explanatory as a description:------ > apply1R2 :: CF2 r1 r2 a -> r2 -> CF1 r1 a----apply1R2 :: CF2 r1 r2 a -> r2 -> CF1 r1 a-apply1R2 mf r2 = CF1 $ \ctx r1 -> unCF2 mf ctx r1 r2-------------------------------------------------------------------------------------- extractors ---- | Extract the drawing context from a CtxFun.------ > (ctx -> ctx)--- -drawingCtx :: CF DrawingContext-drawingCtx = CF $ \ctx -> ctx---- | Apply the projection function to the drawing context.------ > (ctx -> a) -> (ctx -> a)----queryCtx :: (DrawingContext -> a) -> CF a-queryCtx f = CF $ \ctx -> f ctx----- | Extract the drawing context from a LocCF.------ > (ctx -> pt -> ctx)----locCtx :: LocCF u DrawingContext-locCtx = CF1 $ \ctx _ -> ctx---- | Extract the /start/ point from a LocCF.------ > (ctx -> pt -> pt)----locPoint :: LocCF u (Point2 u)-locPoint = CF1 $ \_ pt -> pt----- | Extract the drawing context from a LocThetaCF.------ > (ctx -> pt -> ang -> ctx)----locThetaCtx :: LocThetaCF u DrawingContext-locThetaCtx = CF2 $ \ctx _ _ -> ctx----- | Extract the /start/ point from a LocThetaCF.------ > (ctx -> pt -> ang -> pt)----locThetaPoint :: LocThetaCF u (Point2 u)-locThetaPoint = CF2 $ \_ pt _ -> pt---- | Extract the angle from a LocThetaCF.------ > (ctx -> pt -> ang -> ang)----locThetaAng :: LocThetaCF u Radian-locThetaAng = CF2 $ \_ _ ang -> ang---- | Extract the drawing context from a ConnectorCF.------ > (ctx -> pt1 -> pt2 -> ctx)----connCtx :: ConnectorCF u DrawingContext-connCtx = CF2 $ \ctx _ _ -> ctx---- | Extract the start point from a ConnectorCF.------ > (ctx -> pt1 -> pt2 -> pt1)----connStart :: ConnectorCF u (Point2 u) -connStart = CF2 $ \_ pt _ -> pt---- | Extract the end point from a ConnectorCF.------ > (ctx -> pt1 -> pt2 -> pt2)----connEnd :: ConnectorCF u (Point2 u) -connEnd = CF2 $ \_ _ pt -> pt----------------------------------------------------------------------------------------- Combinators----infixr 1 `at`----- | Downcast a 'LocCF' function by applying it to the supplied --- point, making an arity-zero Context Function. --- --- Remember a 'LocCF' function is a 'CF1' context function where--- the /static argument/ is specialized to a start point.----at :: LocCF u a -> Point2 u -> CF a-at = apply1R1---infixr 1 `rot`----- | Downcast a 'LocThetaCF' function by applying it to the --- supplied angle, making an arity-one Context Function (a --- 'LocCF'). --- --rot :: LocThetaCF u a -> Radian -> LocCF u a-rot = apply1R2----- | Downcast a 'LocThetaCF' function by applying it to the --- supplied point and angle, making an arity-zero Context --- Function (a 'CF'). ----atRot :: LocThetaCF u a -> Point2 u -> Radian -> CF a-atRot = apply2R2----- | Downcast a 'ConnectorCF' function by applying it to the --- start and end point, making an arity-zero Context Function --- (a 'CF'). --- -connect :: ConnectorCF u a -> Point2 u -> Point2 u -> CF a-connect = apply2R2----infixr 6 `chain1`---- | /Chaining/ combinator - the /answer/ of the --- first Context Function is feed to the second Context Function. ------ This contrasts with the usual idiom in @Wumpus-Basic@ where --- composite graphics are built by applying both functions to the --- same initial /static argument/.------ Desciption:------ Evaluate the first Context Function with the drawing context --- and the /initial state/ @st0@. The result of the evaluation is --- a new /state/ @st1@ and and answer @a1@. ------ Evaluate the second Context Function with the drawing context --- and the new state @st1@, producing a new state @s2@ and an --- answer @a2@.------ Return the result of combining the answers with --- @op :: (ans -> ans -> ans)@ and the second state @s2@.------ @ (ctx -> s1 -> (w,s1)) -> (ctx -> s1 -> (w,s1)) -> (ctx -> s1 -> (w,s1)) @------ This models chaining start points together, which is the model--- PostScript uses for text output when successively calling the --- @show@ operator.--- -chain1 :: OPlus w - => CF1 s1 (s1,w) -> CF1 s1 (s1,w) -> CF1 s1 (s1,w)-chain1 f g = CF1 $ \ctx s -> let (s1,a1) = unCF1 f ctx s- (s2,a2) = unCF1 g ctx s1- in (s2, a1 `oplus` a2)--
src/Wumpus/Basic/Kernel/Base/DrawingContext.hs view
@@ -4,7 +4,7 @@ -------------------------------------------------------------------------------- -- | -- Module : Wumpus.Basic.Kernel.Base.DrawingContext--- Copyright : (c) Stephen Tetley 2010+-- Copyright : (c) Stephen Tetley 2010-2011 -- License : BSD3 -- -- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>@@ -24,22 +24,23 @@ module Wumpus.Basic.Kernel.Base.DrawingContext ( - -- * Drawing context+ -- * Drawing context types DrawingContext(..)- , DrawingContextF + , DrawingContextF , TextMargin(..) + -- * Construction , standardContext , metricsContext+ , addFontTables - , default_drawing_context+ , reset_drawing_properties+ , reset_drawing_metrics -- * DrawingCtxM (reader) monad , DrawingCtxM(..)- , asksDC - -- * Glyph metrics , withFontMetrics @@ -47,13 +48,15 @@ ) where -import Wumpus.Basic.Kernel.Base.GlyphMetrics+import Wumpus.Basic.Kernel.Base.FontSupport+import Wumpus.Basic.Kernel.Base.Units import Wumpus.Core -- package: wumpus-core import Wumpus.Core.Text.StandardEncoding import Control.Applicative import Data.Maybe+import Data.Monoid -- | 'DrawingContext' - the \"graphics state\" of Wumpus-Basic. -- DrawingContext is operated on within a Reader monad rather than @@ -61,29 +64,30 @@ -- operation (called @localize@ in Wumpus), rather than permanent -- until overridden as per @set@ of a State monad. -- --- Note - @round_corner_factor@ is only accounted for by some --- graphic objects (certain Path objects and Shapes in --- Wumpus-Drawing for instance). There many be many objects that --- ignore it and are drawn only with angular corners.--- --- Also note - in contrast to most other drawing objects in --- Wumpus, none of the measurement values are parameteric - --- usually notated with the type variable @u@ in Wumpus. This is --- so Wumpus can (potentially) support different units e.g. --- centimeters rather than just Doubles (represening printers --- points), though adding support for other units has a very low --- priority.--- +-- Note - in contrast to most other drawing objects in Wumpus, +-- none of the types of measurement values are parameteric +-- (usually notated with the type variable @u@ in Wumpus). Types +-- are either 'Double' representing PostScript points or Em - a +-- contextual size that is interpreted according to the current +-- font size.+--+-- It is easier to specialize all the measurement types and +-- within the 'DrawingContext' and add parametricity to the +-- /getters/ and /setters/ instead.+-- data DrawingContext = DrawingContext- { glyph_tables :: GlyphMetrics- , fallback_metrics :: MetricsOps- , stroke_props :: StrokeAttr- , font_props :: FontAttr- , stroke_colour :: RGBi -- also text colour...- , fill_colour :: RGBi - , line_spacing_factor :: Double- , round_corner_factor :: Double - , text_margin :: TextMargin+ { dc_font_metrics_table :: FontTable+ , dc_font_load_log :: FontLoadLog+ , dc_fallback_metrics :: FontMetrics+ , dc_font_face :: FontFace+ , dc_font_size :: !FontSize+ , dc_snap_grid_factors :: (Double,Double)+ , dc_stroke_props :: StrokeAttr+ , dc_stroke_colour :: RGBi+ , dc_fill_colour :: RGBi + , dc_text_colour :: RGBi+ , dc_line_spacing_factor :: Double+ , dc_text_margin :: TextMargin } -- TODO - what parts of the Drawing Context should be strict? @@ -93,43 +97,163 @@ -- type DrawingContextF = DrawingContext -> DrawingContext --- | The unit of Margin is always Double representing Points, e.g.--- 1.0 is 1 Point. Margins are not scaled relative to the current--- font size.+-- | The text margin is measured in 'Em' so it is relative to the+-- current font size. -- --- The default value is 2 point.+-- The default value is 0.5. -- data TextMargin = TextMargin- { text_margin_x :: !Double- , text_margin_y :: !Double+ { text_margin_x :: !Em+ , text_margin_y :: !Em } +-- | 'standardContext' : @ font_size -> DrawingContext @ +--+-- Create a 'DrawingContext'.+-- +-- Note - @font_size@ is used for sizing more than just text +-- labels. Arrowheads, plot marks and other elements have their+-- metrics derived from the font size.+-- +-- No real font metrics are present in the 'DrawingContext' +-- created by 'standardContext'. Static, hard-coded fallback +-- metrics derived from the @Courier@ font are available but+-- these metrics might not accurately correspond to the +-- @Courier@ available to the the final renderer (GhostScript,+-- an SVG viewer, etc.).+-- +-- Use this constructor for drawings that make primitive use of+-- text.+-- +-- > font_metrics_table: empty+-- > font_load_log: empty+-- > fallback_metrics: monospace_metrics+-- > font_face: Courier+-- > font_size: @supplied_font_size@+-- > stroke_props: line_width 1, no dash_pattern, cap-butt, join-miter. +-- > stroke_colour: black+-- > fill_colour: light_gray+-- > text_colour: black+-- > line_spacing_factor: 0.2+-- > round_corner_factor: 0+-- > text_margin: (0.5 em, 0.5 em) +--+-- standardContext :: FontSize -> DrawingContext standardContext sz = - DrawingContext { glyph_tables = emptyGlyphMetrics- , fallback_metrics = monospace_metrics- , stroke_props = default_stroke_attr- , font_props = FontAttr sz wumpus_courier- , stroke_colour = wumpus_black- , fill_colour = wumpus_light_gray- , line_spacing_factor = 1.2 - , round_corner_factor = 0- , text_margin = standardTextMargin+ DrawingContext { dc_font_metrics_table = emptyFontTable+ , dc_font_load_log = mempty+ , dc_fallback_metrics = monospace_metrics+ , dc_font_face = wumpus_courier+ , dc_font_size = sz+ , dc_stroke_props = default_stroke_attr+ , dc_snap_grid_factors = (50.0, 50.0)+ , dc_stroke_colour = wumpus_black+ , dc_fill_colour = wumpus_light_gray+ , dc_text_colour = wumpus_black+ , dc_line_spacing_factor = default_line_spacing + , dc_text_margin = default_text_margin } -standardTextMargin :: TextMargin-standardTextMargin = TextMargin { text_margin_x = 2.0, text_margin_y = 2.0 } --- out-of-date - should be adding loaded fonts, not replacing the --- GlyphMetrics Map wholesale.+-- | 'metricsContext' : @ font_size * font_metrics -> DrawingContext @ ---metricsContext :: FontSize -> GlyphMetrics -> DrawingContext-metricsContext sz bgm = - let env = standardContext sz in env { glyph_tables = bgm }+-- Create a 'DrawingContext' with font metrics loaded from the +-- file system.+-- +-- Note - @font_size@ is used for sizing more than just text +-- labels. Arrowheads, plot marks and other elements have their+-- metrics derived from the font size.+-- +-- Use this constructor for drawings that make use of the text +-- objects provided by @Wumpus-Drawing@ (DocText and RotText).+-- +metricsContext :: FontSize -> FontLoadResult -> DrawingContext+metricsContext sz res = + let env = standardContext sz + in env { dc_font_metrics_table = loaded_font_table res+ , dc_font_load_log = loader_errors res+ } ++-- | 'addFontTables' : @ font_load_result -> DrawinContextUpdate @+--+-- Add the font metrics from the FontLoadResult, if a font with +-- the same name alreay exists in the 'DrawingContext' it will be +-- replaced. Error and warning messages in the @font_load_result@ +-- will be appended to the 'font_load_log'.+--+addFontTables :: FontLoadResult -> DrawingContextF+addFontTables (FontLoadResult table msgs) = + (\s i j -> s { dc_font_metrics_table = i `mappend` table+ , dc_font_load_log = j `mappend` msgs }) + <*> dc_font_metrics_table <*> dc_font_load_log++++-- | 'reset_drawing_properties' : @ DrawingContextF @ +--+-- Reset the drawing properties in the 'DrawingContext' to their +-- default values. This changes the following fields:+--+-- > stroke_props: line_width 1, no dash_pattern, cap-butt, join-miter. +-- > stroke_colour: black+-- > fill_colour: light_gray+-- > text_colour: black+-- > line_spacing_factor: 0.2+-- > round_corner_factor: 0+-- > text_margin: (0.5 em, 0.5 em) +--+reset_drawing_properties :: DrawingContextF +reset_drawing_properties dcxt = + dcxt { dc_stroke_props = default_stroke_attr+ , dc_stroke_colour = wumpus_black+ , dc_fill_colour = wumpus_light_gray+ , dc_text_colour = wumpus_black+ , dc_line_spacing_factor = default_line_spacing+ , dc_text_margin = default_text_margin+ }++-- Ideally @reset_drawing_properties@ would be in the UpdateDC +-- module, but that would mean exporting @default_line_spacing@ +-- etc.+--++++-- | 'reset_drawing_metrics' : @ DrawingContextF @ +--+-- Reset the drawing metrics in the 'DrawingContext' to their +-- default values. This is a more limited version of+-- 'reset_drawing_properties' and changes the following fields:+--+-- > stroke_props: line_width 1, no dash_pattern, cap-butt, join-miter. +-- > line_spacing_factor: 0.2+-- > round_corner_factor: 0+-- > text_margin: (0.5 em, 0.5 em) +--+reset_drawing_metrics :: DrawingContextF +reset_drawing_metrics dcxt = + dcxt { dc_stroke_props = default_stroke_attr+ , dc_line_spacing_factor = default_line_spacing+ , dc_text_margin = default_text_margin+ }++++-- Helpers - not exported++default_text_margin :: TextMargin+default_text_margin = TextMargin { text_margin_x = 0.5, text_margin_y = 0.5 }++++default_line_spacing :: Double+default_line_spacing = 0.2+ wumpus_black :: RGBi wumpus_black = RGBi 0 0 0 @@ -144,26 +268,27 @@ FontFace "Courier" "Courier New" SVG_REGULAR standard_encoding --default_drawing_context :: DrawingContext-default_drawing_context = - standardContext (font_size wumpus_default_font)--- -------------------------------------------------------------------------------- --class (Applicative m, Monad m) => DrawingCtxM (m :: * -> *) where- askDC :: m DrawingContext- localize :: (DrawingContext -> DrawingContext) -> m a -> m a----- | Project a value out of a context.+-- | 'DrawingCtxM' is equivalent to the to the @MonadReader@ +-- class, but the environment type is fixed to 'DrawingContext'. ---asksDC :: DrawingCtxM m => (DrawingContext -> a) -> m a-asksDC f = askDC >>= (return . f)+-- To avoid name clashes with @mtl@ this scheme is used:+--+-- > askDC = ask+-- > asksDC = asks+-- > localize = local+--+-- Note, because the derived operation 'query' (aka @asks@) is+-- expected to be used more often than queryCtx (aka @ask@) it +-- gets the more convenient name.+--+class (Applicative m, Monad m) => DrawingCtxM (m :: * -> *) where+ askDC :: m DrawingContext+ asksDC :: (DrawingContext -> a) -> m a+ localize :: (DrawingContext -> DrawingContext) -> m a -> m a + asksDC f = f <$> askDC --------------------------------------------------------------------------------@@ -173,13 +298,15 @@ -withFontMetrics :: (MetricsOps -> PtSize -> u) -> DrawingContext -> u-withFontMetrics fn ctx@(DrawingContext { font_props = font_stats }) = - fn metric_set point_sz+withFontMetrics :: (FontMetrics -> FontSize -> u) -> DrawingContext -> u+withFontMetrics fn ctx@(DrawingContext { dc_font_face = fface+ , dc_font_size = ftsize }) = + fn metric_set ftsize where - ps_name = ps_font_name $ font_face font_stats- point_sz = fromIntegral $ font_size font_stats - metric_set = fromMaybe (fallback_metrics ctx) $ - lookupFont ps_name (glyph_tables ctx) + ps_name = ps_font_name fface+ metric_set = fromMaybe (dc_fallback_metrics ctx) $ + lookupFont ps_name (dc_font_metrics_table ctx)++
+ src/Wumpus/Basic/Kernel/Base/FontSupport.hs view
@@ -0,0 +1,262 @@+{-# LANGUAGE RankNTypes #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.Kernel.Base.FontSupport+-- Copyright : (c) Stephen Tetley 2010-2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Data types representing font metrics.+-- +--------------------------------------------------------------------------------++module Wumpus.Basic.Kernel.Base.FontSupport+ ( ++ FontName+ , CodePoint+ , FontDef(..)+ , FontFamily(..)+ , regularWeight+ , boldWeight+ , italicWeight+ , boldItalicWeight++ , CharWidthLookup++ , FontMetrics(..)++ , FontTable+ , emptyFontTable+ , lookupFont+ , insertFont++ , FontLoadMsg+ , FontLoadLog+ , fontLoadMsg++ , FontLoadResult(..)+ , printLoadErrors++ , monospace_metrics++ + ) where++import Wumpus.Basic.Utils.HList++import Wumpus.Core -- package: wumpus-core++import qualified Data.Map as Map+import Data.Monoid++++type FontName = String++-- | A Unicode code-point.+--+type CodePoint = Int++-- | FontDef wraps @FontFace@ from Wumpus-Core with file name+-- information for the font loaders.+--+data FontDef = FontDef+ { font_def_face :: FontFace+ , gs_file_name :: String+ , afm_file_name :: String+ }+ deriving (Eq,Ord,Show)++++-- | A family group of FontDefs (regular, bold, italic and +-- bold-italic).+--+-- It is convenient for some higher-level text objects in Wumpus +-- (particularly @Doc@ in Wumpus-Drawing) to treat a font and its+-- standard weights as the same entity. This allows @Doc@ API to +-- provide a @bold@ operation to simply change to the the bold+-- weight of the current family, rather than use the primitive +-- @set_font@ operation to change to an explicitly named font.+--+--+data FontFamily = FontFamily+ { ff_regular :: FontDef+ , ff_bold :: Maybe FontDef+ , ff_italic :: Maybe FontDef+ , ff_bold_italic :: Maybe FontDef + }++-- | Extract the regular weight 'FontDef' from a 'FontFamily'.+--+regularWeight :: FontFamily -> FontDef+regularWeight = ff_regular+++-- | Extract the bold weight 'FontDef' from a 'FontFamily'.+--+-- Note - this falls back to the regular weight if the font family +-- has no bold weight. To get the bold weight or @Nothing@ if it+-- is not present use the record selector @ff_bold@.+--+boldWeight :: FontFamily -> FontDef+boldWeight s = maybe (ff_regular s) id $ ff_bold s+++-- | Extract the @italic@ weight 'FontDef' from a 'FontFamily'.+--+-- Note - this falls back to the regular weight if the font family +-- has no italic weight. To get the italic weight or @Nothing@ if +-- it is not present use the record selector @ff_italic@.+--+italicWeight :: FontFamily -> FontDef+italicWeight s = maybe (ff_regular s) id $ ff_italic s+++-- | Extract the @bold-italic@ weight 'FontDef' from a +-- 'FontFamily'.+--+-- Note - this falls back to the regular weight if the font family +-- has no bold-italic weight. To get the bold-italic weight or +-- @Nothing@ if it is not present use the record selector +-- @ff_bold_italic@.+--+boldItalicWeight :: FontFamily -> FontDef+boldItalicWeight s = maybe (ff_regular s) id $ ff_bold_italic s++++-- | A lookup function from code point to /width vector/.+--+-- The unit is always stored as a Double representing PostScript+-- points.+--+-- Note - in PostScript terminology a width vector is not obliged+-- to be left-to-right (writing direction 0). It could be +-- top-to-bottom (writing direction 1).+--+type CharWidthLookup = CodePoint -> Vec2 Double++++-- | 'FontMetrics' store a subset of the properties available in +-- a font file - enough to calculate accurate bounding boxes and+-- positions for text.+--+-- > Bounding box representing the maximum glyph area.+-- > Width vectors for each character.+-- > Cap height+-- > Descender depth.+--+-- Because Wumpus always needs font metrics respective to the +-- current point size, the actual fields are all functions.+--+data FontMetrics = FontMetrics+ { get_bounding_box :: FontSize -> BoundingBox Double+ , get_cw_table :: FontSize -> CharWidthLookup+ , get_cap_height :: FontSize -> Double+ , get_descender :: FontSize -> Double+ , get_underline_position :: FontSize -> Double+ , get_underline_thickness :: FontSize -> Double+ }+++-- | A map between a font name and the respective FontMetrics.+--+newtype FontTable = FontTable { + getFontTable :: Map.Map FontName FontMetrics }++++instance Monoid FontTable where+ mempty = emptyFontTable+ a `mappend` b = FontTable $ getFontTable a `mappend` getFontTable b+++emptyFontTable :: FontTable+emptyFontTable = FontTable $ Map.empty+++-- | 'FontLoadMsg' - type synonym for String.+--+type FontLoadMsg = String++-- | 'FontLoadLog' is a Hughes list of Strings, so it supports +-- efficient append.+--+newtype FontLoadLog = FontLoadLog { getFontLoadLog :: H FontLoadMsg }+++instance Monoid FontLoadLog where+ mempty = FontLoadLog $ emptyH+ a `mappend` b = FontLoadLog $ getFontLoadLog a `appendH` getFontLoadLog b+++fontLoadMsg :: String -> FontLoadLog +fontLoadMsg = FontLoadLog . wrapH+++-- Need a synonym for @FontLoading@...+data FontLoadResult = FontLoadResult+ { loaded_font_table :: FontTable+ , loader_errors :: FontLoadLog+ }+++-- | Print the loader errors from the 'FontLoadResult' to std-out.+--+printLoadErrors :: FontLoadResult -> IO ()+printLoadErrors = mapM_ putStrLn . toListH . getFontLoadLog . loader_errors++--------------------------------------------------------------------------------+++-- | 'lookupFont' : @ name * font_table -> Maybe FontMetrics @ +-- +-- Lookup a font in the font_table.+-- +lookupFont :: FontName -> FontTable -> Maybe FontMetrics+lookupFont name = Map.lookup name . getFontTable++-- | 'insertFont' : @ name * font_metrics * font_table -> FontTable @ +-- +-- Insert a named font into the font_table.+-- +insertFont :: FontName -> FontMetrics -> FontTable -> FontTable+insertFont name ops = + FontTable . Map.insert name ops . getFontTable++-- | This ignores the Char code lookup and just returns the +-- default advance vector.+--+monospace_metrics :: FontMetrics+monospace_metrics = FontMetrics+ { get_bounding_box = \sz -> BBox (lowerLeft sz) (upperRight sz)+ , get_cw_table = \sz _ -> hvec (upscale sz width_vec) + , get_cap_height = \sz -> upscale sz cap_height+ , get_descender = \sz -> upscale sz descender+ , get_underline_position = \sz -> upscale sz underline_pos+ , get_underline_thickness = \sz -> upscale sz underline_width+ }+ where+ llx = (-23) / 1000+ lly = (-250) / 1000+ urx = 715 / 1000+ ury = 805 / 1000+ width_vec = 600 / 1000+ cap_height = 562 / 1000+ descender = (-157) / 1000+ underline_pos = (-100) / 1000+ underline_width = 50 / 1000++ upscale sz d = d * fromIntegral sz+ lowerLeft sz = P2 (upscale sz llx) (upscale sz lly) + upperRight sz = P2 (upscale sz urx) (upscale sz ury) ++
− src/Wumpus/Basic/Kernel/Base/GlyphMetrics.hs
@@ -1,117 +0,0 @@-{-# LANGUAGE RankNTypes #-}-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Basic.Kernel.Base.GlyphMetrics--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : Stephen Tetley <stephen.tetley@gmail.com>--- Stability : highly unstable--- Portability : GHC------ Data types representing glyph metrics loaded from font files.--- -----------------------------------------------------------------------------------module Wumpus.Basic.Kernel.Base.GlyphMetrics- ( -- FontName- , CodePoint- , CharWidthTable- , MetricsOps(..)- , FontMetricsOps(..)-- , GlyphMetrics- , emptyGlyphMetrics- , lookupFont- , insertFont-- , monospace_metrics-- - ) where--import Wumpus.Core -- package: wumpus-core--import qualified Data.Map as Map-import Data.Monoid----type FontName = String---- | A Unicode code-point.----type CodePoint = Int---- | A lookup from code point to /width vector/.------ Note - in PostScript terminology a width vector is not obliged--- to be left-to-right (writing direction 0). It could be --- top-to-bottom (writing direction 1).----type CharWidthTable u = CodePoint -> Vec2 u------ | Operations on the metrics set of a font.------ The is the internal representation used by Wumpus-Basic after--- parsing the font file.----data MetricsOps = MetricsOps- { get_bounding_box :: forall u. FromPtSize u => PtSize -> BoundingBox u - , get_cw_table :: forall u. FromPtSize u => PtSize -> CharWidthTable u- , get_cap_height :: forall u. FromPtSize u => PtSize -> u- }---- | 'MetricsOps' tfor a particular named font.--- -data FontMetricsOps = FontMetricsOps FontName MetricsOps----- | A map between a font name and MetricsOps.----newtype GlyphMetrics = GlyphMetrics { - getGlyphMetrics :: Map.Map FontName MetricsOps }--instance Monoid GlyphMetrics where- mempty = emptyGlyphMetrics- a `mappend` b = GlyphMetrics $ getGlyphMetrics a `mappend` getGlyphMetrics b---emptyGlyphMetrics :: GlyphMetrics-emptyGlyphMetrics = GlyphMetrics $ Map.empty--lookupFont :: FontName -> GlyphMetrics -> Maybe MetricsOps-lookupFont name = Map.lookup name . getGlyphMetrics--insertFont :: FontMetricsOps -> GlyphMetrics -> GlyphMetrics-insertFont (FontMetricsOps name ops) = - GlyphMetrics . Map.insert name ops . getGlyphMetrics---- | This ignores the Char code lookup and just returns the --- default advance vector.----monospace_metrics :: MetricsOps-monospace_metrics = MetricsOps- { get_bounding_box = \sz -> BBox (lowerLeft sz) (upperRight sz)- , get_cw_table = \sz _ -> hvec (upscale sz width_vec) - , get_cap_height = \sz -> upscale sz cap_height- }- where- llx = (-23) / 1000- lly = (-250) / 1000- urx = 715 / 1000- ury = 805 / 1000- width_vec = 600 / 1000- cap_height = 562 / 1000-- upscale sz d = fromPtSize $ sz * d- lowerLeft sz = P2 (upscale sz llx) (upscale sz lly) - upperRight sz = P2 (upscale sz urx) (upscale sz ury) --
src/Wumpus/Basic/Kernel/Base/QueryDC.hs view
@@ -3,7 +3,7 @@ -------------------------------------------------------------------------------- -- | -- Module : Wumpus.Basic.Kernel.Base.QueryDC--- Copyright : (c) Stephen Tetley 2010+-- Copyright : (c) Stephen Tetley 2010-2011 -- License : BSD3 -- -- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>@@ -12,274 +12,322 @@ -- -- Querying the Drawing Context. --+-- \*\* WARNING \*\* - parts of this module especially the +-- mono-space glyph metrics need a re-think and will change or be +-- dropped.+-- -------------------------------------------------------------------------------- module Wumpus.Basic.Kernel.Base.QueryDC ( - - textAttr- , withTextAttr+ normalizeCtx+ , normalizeCtxF+ , dinterpCtx+ , dinterpCtxF - , strokeAttr- , withStrokeAttr+ , uconvertCtx1+ , uconvertCtxF - , fillAttr- , withFillAttr+ , pointSize + , strokeAttr+ , fillAttr , borderedAttr- , withBorderedAttr+ , textAttr - , getRoundCornerSize- , getTextMargin+ , position+ , snapmove + , textMargin+ , getLineWidth , getFontAttr , getFontSize , getFontFace- , markHeight- , markHalfHeight- , baselineSpacing+ , getTextColour + , textlineSpace+ -- * Glyph metrics , glyphBoundingBox- , glyphHeightRange- , glyphHeight- , glyphCapHeight+ , capHeight+ , descender+ , underlinePosition+ , underlineThickness+ , verticalSpan+ , heightSpan - , cwLookupTable+ -- * Text metrics+ , escTextVector+ , escCharVector+ , hkernVector - -- * Default monospace metrics+ , cwLookupTable - , monoFontPointSize- , monoCharWidth- , monoTextWidth- , monoTextLength- , monoCapHeight- , monoLowerxHeight- , monoDescenderDepth- , monoAscenderHeight- , monoTextDimensions- , monoMultiLineHeight- , monoDefaultPadding- , monoVecToCenter ) where +import Wumpus.Basic.Kernel.Base.BaseDefs import Wumpus.Basic.Kernel.Base.DrawingContext-import Wumpus.Basic.Kernel.Base.GlyphMetrics+import Wumpus.Basic.Kernel.Base.FontSupport import Wumpus.Core -- package: wumpus-core-import qualified Wumpus.Core.FontSize as FS+import Wumpus.Core.Text.GlyphIndices++import Data.VectorSpace -- package: vector-space import Control.Applicative+import Data.Char+import qualified Data.Map as Map+import Data.Maybe -textAttr :: DrawingCtxM m => m (RGBi,FontAttr)-textAttr = (,) <$> asksDC stroke_colour <*> asksDC font_props---- | Because @textAttr@ is so commonly used here is a functional--- version that avoids tupling. ---withTextAttr :: DrawingCtxM m => (RGBi -> FontAttr -> a) -> m a-withTextAttr fn = fn <$> asksDC stroke_colour <*> asksDC font_props+-- NOTE 20.5.11 - The normalize functions are not satisfactory,+-- point size is too promiscuous for evaluation contextual units.+-- +normalizeCtx :: (DrawingCtxM m, InterpretUnit u) => u -> m Double+normalizeCtx u = (\sz -> normalize sz u) <$> pointSize -strokeAttr :: DrawingCtxM m => m (RGBi, StrokeAttr)-strokeAttr = (,) <$> asksDC stroke_colour <*> asksDC stroke_props+normalizeCtxF :: (DrawingCtxM m, Functor t, InterpretUnit u) + => t u -> m (t Double)+normalizeCtxF t = (\sz -> fmap (normalize sz) t) <$> pointSize -withStrokeAttr :: DrawingCtxM m => (RGBi -> StrokeAttr -> a) -> m a-withStrokeAttr fn = fn <$> asksDC stroke_colour <*> asksDC stroke_props+dinterpCtx :: (DrawingCtxM m, InterpretUnit u) => Double -> m u+dinterpCtx u = (\sz -> dinterp sz u) <$> pointSize +dinterpCtxF :: (DrawingCtxM m, Functor t, InterpretUnit u) => t Double -> m (t u)+dinterpCtxF u = (\sz -> fmap (dinterp sz) u) <$> pointSize +uconvertCtx1 :: (DrawingCtxM m, InterpretUnit u, InterpretUnit u1) + => u -> m u1+uconvertCtx1 t = (\sz -> uconvert1 sz t) <$> pointSize++uconvertCtxF :: (DrawingCtxM m, Functor t, InterpretUnit u, InterpretUnit u1) + => t u -> m (t u1)+uconvertCtxF t = (\sz -> uconvertF sz t) <$> pointSize+++pointSize :: DrawingCtxM m => m FontSize+pointSize = asksDC dc_font_size++strokeAttr :: DrawingCtxM m => m (RGBi, StrokeAttr)+strokeAttr = (,) <$> asksDC dc_stroke_colour <*> asksDC dc_stroke_props+ fillAttr :: DrawingCtxM m => m RGBi-fillAttr = asksDC fill_colour+fillAttr = asksDC dc_fill_colour -withFillAttr :: DrawingCtxM m => (RGBi -> a) -> m a-withFillAttr fn = fn <$> asksDC fill_colour borderedAttr :: DrawingCtxM m => m (RGBi, StrokeAttr, RGBi)-borderedAttr = (,,) <$> asksDC fill_colour <*> asksDC stroke_props - <*> asksDC stroke_colour+borderedAttr = (,,) <$> asksDC dc_fill_colour + <*> asksDC dc_stroke_props + <*> asksDC dc_stroke_colour -withBorderedAttr :: DrawingCtxM m => (RGBi -> StrokeAttr -> RGBi -> a) -> m a-withBorderedAttr fn = - fn <$> asksDC fill_colour <*> asksDC stroke_props - <*> asksDC stroke_colour +textAttr :: DrawingCtxM m => m (RGBi,FontAttr)+textAttr = + (\a b c -> (a, FontAttr b c)) + <$> asksDC dc_text_colour <*> asksDC dc_font_size <*> asksDC dc_font_face --- | Vertical distance between baselines of consecutive text --- lines.++++-- | Get the Point corresponding the grid coordinates scaled by+-- the snap-grid scaling factors. ---getRoundCornerSize :: (DrawingCtxM m, Fractional u, FromPtSize u) => m u-getRoundCornerSize = (\factor -> (realToFrac factor) * fromPtSize 1)- <$> asksDC round_corner_factor+position :: (DrawingCtxM m, Fractional u) => (Int, Int) -> m (Point2 u)+position (x,y) = post <$> asksDC dc_snap_grid_factors+ where+ post (sx,sy) = P2 (realToFrac $ sx * fromIntegral x) + (realToFrac $ sy * fromIntegral y) --- | Vertical distance between baselines of consecutive text --- lines.++-- | Scale a vector coordinate by the snap-grid scaling factors. ---getTextMargin :: (DrawingCtxM m, Fractional u, FromPtSize u) => m (u,u)-getTextMargin = (\(TextMargin xsep ysep) -> (fn xsep, fn ysep))- <$> asksDC text_margin+-- Absolute units.+--+snapmove :: (DrawingCtxM m, Fractional u) => (Int,Int) -> m (Vec2 u)+snapmove (x,y) = post <$> asksDC dc_snap_grid_factors where- fn d = (realToFrac d) * fromPtSize 1+ post (sx,sy) = V2 (realToFrac $ sx * fromIntegral x) + (realToFrac $ sy * fromIntegral y) -getLineWidth :: DrawingCtxM m => m Double-getLineWidth = line_width <$> asksDC stroke_props+-- | Get the (x,y) margin around text.+--+-- Note - not all text operations in Wumpus are drawn with text +-- margin. +-- +textMargin :: (DrawingCtxM m, InterpretUnit u) => m (u,u)+textMargin = post <$> asksDC dc_font_size <*> asksDC dc_text_margin+ where+ post sz (TextMargin xem yem) = (uconvert1 sz xem, uconvert1 sz yem) -getFontAttr :: DrawingCtxM m => m FontAttr-getFontAttr = asksDC font_props -getFontSize :: DrawingCtxM m => m Int-getFontSize = font_size <$> asksDC font_props -getFontFace :: DrawingCtxM m => m FontFace-getFontFace = font_face <$> asksDC font_props +getLineWidth :: DrawingCtxM m => m Double+getLineWidth = line_width <$> asksDC dc_stroke_props +getFontAttr :: DrawingCtxM m => m FontAttr+getFontAttr = FontAttr <$> asksDC dc_font_size <*> asksDC dc_font_face --- | Vertical distance between baselines of consecutive text --- lines.----baselineSpacing :: (DrawingCtxM m, Fractional u) => m u-baselineSpacing = - (\sz factor -> realToFrac $ factor * fromIntegral sz)- <$> asksDC (font_size . font_props) <*> asksDC line_spacing_factor --- | The /mark/ height is the height of a lowercase letter in the --- current font.------ Arrowheads, dots etc. should generally be drawn at the mark --- height.--- -markHeight :: (DrawingCtxM m, FromPtSize u) => m u-markHeight = (fromPtSize . FS.xcharHeight . font_size) <$> asksDC font_props+getFontSize :: DrawingCtxM m => m Int+getFontSize = asksDC dc_font_size +getFontFace :: DrawingCtxM m => m FontFace+getFontFace = asksDC dc_font_face -markHalfHeight :: (DrawingCtxM m, Fractional u, FromPtSize u) => m u-markHalfHeight = (0.5*) <$> markHeight+getTextColour :: DrawingCtxM m => m RGBi+getTextColour = asksDC dc_text_colour --- Note - there are probably enough functions that use just --- markHeight to merit a withMarkHeight function. +-- | Vertical distance between descender of a line and the +-- cap-height of the line below. +-- +textlineSpace :: (DrawingCtxM m, Fractional u, InterpretUnit u) => m u+textlineSpace = + post <$> asksDC dc_font_size <*> asksDC dc_line_spacing_factor+ where+ post sz factor = dinterp sz ((fromIntegral sz) * (realToFrac factor)) -------------------------------------------------------------------------------- -glyphQuery :: DrawingCtxM m => (MetricsOps -> PtSize -> u) -> m u++glyphQuery :: DrawingCtxM m => (FontMetrics -> FontSize -> a) -> m a glyphQuery fn = (\ctx -> withFontMetrics fn ctx) <$> askDC -glyphBoundingBox :: (FromPtSize u, DrawingCtxM m) => m (BoundingBox u)-glyphBoundingBox = glyphQuery get_bounding_box -glyphHeightRange :: (FromPtSize u, DrawingCtxM m) => m (u,u)-glyphHeightRange = fn <$> glyphBoundingBox- where- fn (BBox (P2 _ ymin) (P2 _ ymax)) = (ymin,ymax)+-- | Get the font bounding box - this is the maximum boundary of +-- the glyphs in the font. The span of the height is expected to +-- be bigger than the cap_height plus descender depth.+--+glyphBoundingBox :: (DrawingCtxM m, InterpretUnit u) => m (BoundingBox u)+glyphBoundingBox = + uconvertF <$> asksDC dc_font_size <*> glyphQuery get_bounding_box -glyphHeight :: (FromPtSize u, DrawingCtxM m) => m u-glyphHeight = (\(ymax,ymin) -> ymax - ymin) <$> glyphHeightRange +-- | Height of a capital letter.+--+capHeight :: (DrawingCtxM m, InterpretUnit u) => m u+capHeight = dinterp <$> asksDC dc_font_size <*> glyphQuery get_cap_height -glyphCapHeight :: (FromPtSize u, DrawingCtxM m) => m u-glyphCapHeight = glyphQuery get_cap_height -cwLookupTable :: (FromPtSize u, DrawingCtxM m) => m (CharWidthTable u)-cwLookupTable = glyphQuery get_cw_table+-- | Note - descender is expected to be negative.+--+descender :: (DrawingCtxM m, InterpretUnit u) => m u+descender = dinterp <$> asksDC dc_font_size <*> glyphQuery get_descender -----------------------------------------------------------------------------------withFontSize :: DrawingCtxM m => (FontSize -> u) -> m u-withFontSize fn = (fn . font_size) <$> asksDC font_props+-- | Note - underline_position is expected to be negative.+--+underlinePosition :: (DrawingCtxM m, InterpretUnit u) => m u+underlinePosition = + dinterp <$> asksDC dc_font_size <*> glyphQuery get_underline_position --- NOTE - textHeight in Wumpus-Core should be renamed as it is--- (probably) more indiactive of Cap height than /font point size/+-- | Line width of underline line. --+underlineThickness :: (DrawingCtxM m, InterpretUnit u) => m u+underlineThickness = + dinterp <$> asksDC dc_font_size <*> glyphQuery get_underline_thickness -monoFontPointSize :: (DrawingCtxM m, FromPtSize u) => m u-monoFontPointSize = withFontSize (fromPtSize . fromIntegral) -monoCharWidth :: (DrawingCtxM m, FromPtSize u) => m u-monoCharWidth = withFontSize (fromPtSize . FS.charWidth)+-- | This is the distance from cap_height to descender.+--+verticalSpan :: (DrawingCtxM m, InterpretUnit u) => m u+verticalSpan = + (\ch dd -> ch - dd) <$> capHeight <*> descender -monoTextWidth :: (DrawingCtxM m, FromPtSize u) => Int -> m u-monoTextWidth n = withFontSize $ \sz -> fromPtSize $ FS.textWidth sz n +-- | Variant of 'verticalSpan' that accounts for the specified+-- 'TextHeight'.+--+-- This returns a pair of @(yminor, ymajor)@.+-- +heightSpan :: (DrawingCtxM m, InterpretUnit u )+ => TextHeight -> m (u,u)+heightSpan JUST_CAP_HEIGHT = (\ymaj -> (0, ymaj)) <$> capHeight+heightSpan CAP_HEIGHT_PLUS_DESCENDER = + (\ymin ymaj -> (abs ymin, ymaj)) <$> descender <*> capHeight -monoTextLength :: (DrawingCtxM m, FromPtSize u) => String -> m u-monoTextLength ss = monoTextWidth $ charCount ss -monoCapHeight :: (DrawingCtxM m, FromPtSize u) => m u-monoCapHeight = withFontSize (fromPtSize . FS.capHeight)--monoTotalCharHeight :: (DrawingCtxM m, FromPtSize u) => m u-monoTotalCharHeight = withFontSize (fromPtSize . FS.totalCharHeight)+-------------------------------------------------------------------------------- --- | Height of a lower case \'x\' in Courier.--- --- \'x\' has no ascenders or descenders. +-- | Find the advance vector for the supplied 'EscapedText'.+--+-- Note - the text assumed to be a single line. -- -monoLowerxHeight :: (DrawingCtxM m, FromPtSize u) => m u-monoLowerxHeight = withFontSize (fromPtSize . FS.xcharHeight)+escTextVector :: (DrawingCtxM m, InterpretUnit u) + => EscapedText -> m (Vec2 u)+escTextVector esc = + cwLookupTable >>= \table -> + pointSize >>= \sz -> + let cs = destrEscapedText id esc + in return $ foldr (step sz table) (vec 0 0) cs+ where+ step sz table ch v = let cv = escCharWidth sz table ch in v ^+^ cv -monoDescenderDepth :: (DrawingCtxM m, FromPtSize u) => m u-monoDescenderDepth = withFontSize (fromPtSize . FS.descenderDepth) -monoAscenderHeight :: (DrawingCtxM m, FromPtSize u) => m u-monoAscenderHeight = withFontSize (fromPtSize . FS.ascenderHeight) +-- | Find the advance vector for the supplied 'EscapedChar'.+--+escCharVector :: (DrawingCtxM m, InterpretUnit u) + => EscapedChar -> m (Vec2 u)+escCharVector ch = + (\table sz -> escCharWidth sz table ch) <$> cwLookupTable <*> pointSize --- | Query the dimensions of the text using the current font size--- but using metrics derived from Courier.++-- | This is outside the Drawing context as we don\'t want to get+-- the @cwLookupTable@ for every char. ----- Note - the width will generally be a over-estimate for --- non-monospaced fonts.--- -monoTextDimensions :: (DrawingCtxM m, Num u, Ord u, FromPtSize u)- => String -> m (u,u)-monoTextDimensions ss = - (\sz -> post $ textBounds sz zeroPt ss) - <$> asksDC (font_size . font_props)+escCharWidth :: InterpretUnit u + => FontSize -> CharWidthLookup -> EscapedChar -> Vec2 u+escCharWidth sz fn (CharLiteral c) = fmap (dinterp sz) $ fn $ ord c+escCharWidth sz fn (CharEscInt i) = fmap (dinterp sz) $ fn i+escCharWidth sz fn (CharEscName s) = fmap (dinterp sz) $ fn ix where- post bb = (boundaryWidth bb, boundaryHeight bb)+ ix = fromMaybe (-1) $ Map.lookup s ps_glyph_indices --- | The heigth of @n@ lines of text, which is --- @n lines + n-1 line spacers@----monoMultiLineHeight :: (DrawingCtxM m, Fractional u, FromPtSize u) - => Int -> m u-monoMultiLineHeight n | n < 0 = pure 0-monoMultiLineHeight n = - (\h lsf -> h + (fromIntegral $ n-1) * (h * realToFrac lsf))- <$> monoTotalCharHeight <*> asksDC line_spacing_factor- - -- Note as the height calculation has changed in Wumpus-Core this- -- no longer quite works... - --- | The default padding is half of the /char width/.+-- | 'hkernVector' : @ [kerning_char] -> AdvanceVec @+-- +-- 'hkernvector' takes whatever length is paired with the +-- EscapedChar for the init of the the list, for the last element +-- it takes the charVector. ---monoDefaultPadding :: (DrawingCtxM m, Fractional u, FromPtSize u) => m u-monoDefaultPadding = (0.5*) <$> monoCharWidth+hkernVector :: (DrawingCtxM m, InterpretUnit u) + => [KernChar u] -> m (Vec2 u)+hkernVector = go 0+ where+ go w [] = return $ V2 w 0+ go w [(dx,ch)] = fmap (addWidth $ w + dx) (escCharVector ch)+ go w ((dx,_ ):xs) = go (w + dx) xs+ + addWidth w (V2 x y) = V2 (w+x) y --- | Vector from baseline left to center+++-- | Note the CharWidthLookup is not parameteric on units. ---monoVecToCenter :: (DrawingCtxM m, Fractional u, Ord u, FromPtSize u) - => String -> m (Vec2 u)-monoVecToCenter ss = (\(w,h) dy -> vec (0.5*w) (0.5*h - dy)) - <$> monoTextDimensions ss <*> monoDescenderDepth+-- /CharWidth/ is always Double representing PostScript points.+-- Client code must convert this value accordingly.+--+cwLookupTable :: DrawingCtxM m => m CharWidthLookup+cwLookupTable = glyphQuery get_cw_table
− src/Wumpus/Basic/Kernel/Base/ScalingContext.hs
@@ -1,104 +0,0 @@-{-# LANGUAGE TypeFamilies #-}-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Basic.Kernel.Base.ScalingContext--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : stephen.tetley@gmail.com--- Stability : unstable--- Portability : GHC------ Scaling in X and Y------ \*\* WARNING \*\* - half baked.--- -----------------------------------------------------------------------------------module Wumpus.Basic.Kernel.Base.ScalingContext- (-- ScalingContext(..)-- , scaleX- , scaleY- , scalePt- , scaleVec-- , unitX- , unitY-- , uniformScaling- , coordinateScaling-- ) where---import Wumpus.Core -- package: wumpus-core------ | ScalingContext is a dictionary of two functions for scaling --- in X and Y.----data ScalingContext ux uy u = ScalingContext- { scale_in_x :: ux -> u- , scale_in_y :: uy -> u- }---scaleX :: ScalingContext ux uy u -> ux -> u-scaleX ctx ux = (scale_in_x ctx) ux--scaleY :: ScalingContext ux uy u -> uy -> u-scaleY ctx uy = (scale_in_y ctx) uy---scalePt :: ScalingContext ux uy u -> ux -> uy -> Point2 u-scalePt ctx ux uy = P2 (scale_in_x ctx ux) (scale_in_y ctx uy)--scaleVec :: ScalingContext ux uy u -> ux -> uy -> Vec2 u-scaleVec ctx ux uy = V2 (scale_in_x ctx ux) (scale_in_y ctx uy)---unitX :: Num ux => ScalingContext ux uy u -> u-unitX ctx = scaleX ctx 1- -unitY :: Num uy => ScalingContext ux uy u -> u-unitY ctx = scaleY ctx 1---------------------------------------------------------------------------------------- constructors for scaling context----- | Build a ScalingContext where both X and Y are scaled by the --- same uniform step.------ The dimensions (types) of the ScalingContext are unified - the --- output type and the input types are all the same.----uniformScaling :: Num u => u -> ScalingContext u u u-uniformScaling u = ScalingContext- { scale_in_x = (\x -> u*x)- , scale_in_y = (\y -> u*y)- }------ | Build a ScalingContext for scaling Int coordinates.------ The scaling factors in X and Y can be different sizes.-----coordinateScaling :: Num u => u -> u -> ScalingContext Int Int u-coordinateScaling sx sy = ScalingContext- { scale_in_x = (\x -> sx * fromIntegral x)- , scale_in_y = (\y -> sy * fromIntegral y)- }---
+ src/Wumpus/Basic/Kernel/Base/Units.hs view
@@ -0,0 +1,166 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.Kernel.Base.Units+-- Copyright : (c) Stephen Tetley 2011-2012+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Units @cm@, @pica@ and \"contextual\" units - @em@, @en@.+-- +--------------------------------------------------------------------------------++module Wumpus.Basic.Kernel.Base.Units+ (+++ -- * Centimeter+ Centimeter+ , cm+ , dcm++ -- * Pica+ , Pica+ , pica+ , dpica++ -- * Em+ , Em++ -- * En+ , En++ ) where+++import Wumpus.Basic.Kernel.Base.BaseDefs++import Wumpus.Core -- package: wumpus-core+++--------------------------------------------------------------------------------++-- | Wrapped Double representing Centimeter.+-- +newtype Centimeter = Centimeter { getCentimeter :: Double } + deriving (Eq,Ord,Num,Floating,Fractional,Real,RealFrac,RealFloat)+++instance Show Centimeter where+ showsPrec p d = showsPrec p (getCentimeter d)++-- | Cast a value in Centimeters to some Fractional type.+-- +-- Note - this casting should only be used for non-contextual+-- units such as Double.+--+cm :: Fractional u => Centimeter -> u +cm = realToFrac . (28.45275619 *) . getCentimeter++-- | Convert Double to Centimer.+-- +dcm :: Double -> Centimeter+dcm = Centimeter . (0.03514598 *)++instance ScalarUnit Centimeter where+ fromPsPoint = dcm+ toPsPoint = cm ++instance InterpretUnit Centimeter where+ normalize _ = cm + dinterp _ = dcm++instance Tolerance Centimeter where + eq_tolerance = 0.001+ length_tolerance = 0.01++++-- | Wrapped Double /Pica/ unit type.+-- +-- Pica is 12 Points.+--+newtype Pica = Pica { getPica :: Double } + deriving (Eq,Ord,Num,Floating,Fractional,Real,RealFrac,RealFloat)+++instance Show Pica where+ showsPrec p d = showsPrec p (getPica d)++ +-- | Cast a value in Pica to some Fractional type.+-- +-- Note - this casting should only be used for non-contextual+-- units such as Double.+--+pica :: Fractional u => Pica -> u +pica = realToFrac . (* 12.0) . getPica++-- | Convert a Double to a Pica.+--+dpica :: Double -> Pica+dpica = Pica . (\x -> x / 12.0)++++instance ScalarUnit Pica where+ fromPsPoint = dpica+ toPsPoint = pica++instance InterpretUnit Pica where+ normalize _ = pica+ dinterp _ = dpica+++instance Tolerance Pica where + eq_tolerance = 0.001+ length_tolerance = 0.01++++--------------------------------------------------------------------------------+-- Contextual units++-- | Wrapped Double representing an Em. +-- +newtype Em = Em { getEm :: Double } + deriving (Eq,Ord,Num,Floating,Fractional,Real,RealFrac,RealFloat)++instance Show Em where+ showsPrec p d = showsPrec p (getEm d)++++instance InterpretUnit Em where+ normalize sz a = fromIntegral sz * realToFrac a+ dinterp sz d = realToFrac d / fromIntegral sz+++instance Tolerance Em where + eq_tolerance = 0.001+ length_tolerance = 0.01+++-- | Wrapped Double representing an En.+-- +newtype En = En { getEn :: Double } + deriving (Eq,Ord,Num,Floating,Fractional,Real,RealFrac,RealFloat)++instance Show En where+ showsPrec p d = showsPrec p (getEn d)+++instance InterpretUnit En where+ normalize sz a = (realToFrac a) * 0.5 * fromIntegral sz+ dinterp sz d = 2 * (realToFrac d) / (fromIntegral sz)+++instance Tolerance En where+ eq_tolerance = 0.001 + length_tolerance = 0.01+
src/Wumpus/Basic/Kernel/Base/UpdateDC.hs view
@@ -3,77 +3,105 @@ -------------------------------------------------------------------------------- -- | -- Module : Wumpus.Basic.Kernel.Base.UpdateDC--- Copyright : (c) Stephen Tetley 2010+-- Copyright : (c) Stephen Tetley 2010-2011 -- License : BSD3 -- -- Maintainer : Stephen Tetley <stephen.tetley@gmail.com> -- Stability : highly unstable -- Portability : GHC ----- Customize drawing attributes------ \*\* WARNING \*\* - this module needs systematic naming --- schemes both for update functions (primaryColour, ...) and --- for synthesized selectors (e.g. lowerxHeight). The current --- names will change.+-- Customize drawing attributes. The functions here are +-- @DrawingContext@ modifiers to be run within a the scope of a +-- @localize@ block (cf. @local@ of the Reader monad). --+-- By convention, underscore-separated names are used for +-- DrawingContext modifiers in this module. This is because the +-- modifiers defined here are expected to be used mostly as static +-- \"properties\" resembling constants in drawings. -- -------------------------------------------------------------------------------- module Wumpus.Basic.Kernel.Base.UpdateDC ( - -- * Modifiers - - roundCornerFactor- , textMargin+ -- * Globals+ snap_grid_factors - -- ** Line widths- , lineWidth- , thick- , ultrathick- , thin+ -- * Line widths+ , set_line_width - -- ** Line caps- , capButt- , capRound- , capSquare+ , line_default+ , line_thin+ , line_thick+ , line_ultra_thick+ + , contextual_line_width+ , relative_line_width - -- ** Line joins- , joinMiter- , joinRound- , joinBevel+ -- * Line cap+ , cap_default+ , cap_butt+ , cap_round+ , cap_square - -- ** Dash Pattern- , dashPattern- , unit_dash_pattern- , phase- , dphase- , doublegaps- , doubledashes+ -- * Line join+ , join_default+ , join_miter+ , join_round+ , join_bevel + -- * Dash pattern+ , set_dash_pattern++ , solid_line+ , dotted_line+ , packed_dotted+ , loose_dotted+ + , dashed_line+ , packed_dashed+ , loose_dashed+ -- * Font properties- , fontAttr- , fontSize- , fontFace+ , font_attr+ , set_font+ , set_font_size+ , scale_point_size - -- * Font / mark drawing size- , doublesize- , halfsize+ , double_point_size+ , half_point_size ++ -- * Text margins+ , text_margin+ , text_margin_none+ , text_margin_tight+ , text_margin_default+ , text_margin_loose++ -- * Colour- , swapColours- , bothStrokeColour- , bothFillColour- , strokeColour- , fillColour + , stroke_colour+ , fill_colour + , text_colour+ , single_colour + , swap_colours+ , fill_use_stroke_colour+ , stroke_use_fill_colour - + , fill_use_text_colour+ , stroke_use_text_colour++ , text_use_stroke_colour+ , text_use_fill_colour+ ) where import Wumpus.Basic.Kernel.Base.DrawingContext+import Wumpus.Basic.Kernel.Base.FontSupport+import Wumpus.Basic.Kernel.Base.Units import Wumpus.Core -- package: wumpus-core @@ -81,180 +109,443 @@ --------------------------------------------------------------------------------+-- helpers updateStrokeProps :: (StrokeAttr -> StrokeAttr) -> DrawingContextF-updateStrokeProps fn = (\s i -> s { stroke_props = fn i }) <*> stroke_props--updateFontProps :: (FontAttr -> FontAttr) -> DrawingContextF-updateFontProps fn = (\s i -> s { font_props = fn i }) <*> font_props+updateStrokeProps fn = + (\s i -> s { dc_stroke_props = fn i }) <*> dc_stroke_props +withFontSize :: (FontSize -> DrawingContextF) -> DrawingContextF+withFontSize fn = (\s i -> fn i s) <*> dc_font_size -------------------------------------------------------------------------------- -roundCornerFactor :: Double -> DrawingContextF-roundCornerFactor d = (\s -> s { round_corner_factor = d }) --- | 'textMargin' : @ xsep * ysep -> DrawingContextF @++-- | 'snap_grid_factors' : @ x_unit * y_unit -> DrawingContextF @+-- +-- Set the @snap grid factors@ - a snap grid is an alternative +-- coordinate space, it can be convenient for drawing +-- \"box and arrow\" diagrams. ---textMargin :: Double -> Double -> DrawingContextF-textMargin xsep ysep = (\s -> s { text_margin = TextMargin xsep ysep })+snap_grid_factors :: Double -> Double -> DrawingContextF+snap_grid_factors xu yu = \s -> s { dc_snap_grid_factors = (xu, yu) } -------------------------------------------------------------------------------- -- line widths --- Note - some care might be needed if we ever define other unit --- types... -lineWidth :: Double -> DrawingContextF-lineWidth d = updateStrokeProps (\s -> s { line_width = d })+-- | set_line_width : @ width_in_points -> DrawingContextF @+--+-- Set the line_width to the supplied point size.+--+-- Initially the line width is 1.0.+--+-- /Constant/ variations of the function maybe be more +-- convenient:+--+-- > line_default, line_thin, line_thick, line_ultra_thick+--+set_line_width :: Double -> DrawingContextF+set_line_width d = updateStrokeProps (\s -> s { line_width = d }) --- std_line_width :: Double--- std_line_width = 1.0+-- | Set the line_width to @default@ - 1.0.+--+line_default :: DrawingContextF+line_default = set_line_width 1.0 -thick_line :: Double-thick_line = 2.0 -ultra_thick_line :: Double-ultra_thick_line = 4.0+-- | Set the line_width to @thin@ - 0.5.+--+line_thin :: DrawingContextF+line_thin = set_line_width 0.5 -thin_line :: Double-thin_line = 0.5 +-- | Set the line_width to @thick@ - 2.0.+--+line_thick :: DrawingContextF+line_thick = set_line_width 2.0 --- | Set the line width to a /thick/.+-- | Set the line_width to @ultra_thick@ - 4.0. ----- Note this context update is /oblivious/ - operationally the --- line width is set to exactly @2.0@.+line_ultra_thick :: DrawingContextF+line_ultra_thick = set_line_width 4.0++++-- | Scale the line width respective to its current value. +-- +-- The size is calculated with the supplied function. ---thick :: DrawingContextF-thick = lineWidth thick_line+relative_line_width :: (Double -> Double) -> DrawingContextF+relative_line_width fn = + updateStrokeProps (\s -> let lw = line_width s in s { line_width = fn lw }) -ultrathick :: DrawingContextF-ultrathick = lineWidth ultra_thick_line -thin :: DrawingContextF-thin = lineWidth thin_line+-- | Set the line width to a size relative to the current font +-- size. The size is calculated with the supplied function.+--+contextual_line_width :: (FontSize -> Double) -> DrawingContextF+contextual_line_width fn = + withFontSize $ \sz s -> set_line_width (fn sz) s + --------------------------------------------------------------------------------+-- Line props setLineCap :: LineCap -> DrawingContextF setLineCap d = updateStrokeProps (\s -> s { line_cap = d }) +setLineJoin :: LineJoin -> DrawingContextF+setLineJoin d = updateStrokeProps (\s -> s { line_join = d }) -capButt :: DrawingContextF-capButt = setLineCap CapButt -capRound :: DrawingContextF-capRound = setLineCap CapRound+-- | Set the line_cap to the default which is @butt@.+--+-- This is a synonym for 'cap_butt'.+--+cap_default :: DrawingContextF+cap_default = cap_butt -capSquare :: DrawingContextF-capSquare = setLineCap CapSquare+-- | Set the line_cap to @butt@.+--+-- Butt chamfers off the stroke, flush to the end point.+--+-- This is the default.+--+-- > .-------.+-- > |=======|+-- > '-------'+--+cap_butt :: DrawingContextF+cap_butt = setLineCap CapButt +-- | Set the line_cap to @round@.+--+-- This rounds the end of the stroke and the visually the +-- rounding slightly extends the length of the line.+--+-- > .-------.+-- > ( ======= )+-- > '-------'+--+cap_round :: DrawingContextF+cap_round = setLineCap CapRound -setLineJoin :: LineJoin -> DrawingContextF-setLineJoin d = updateStrokeProps (\s -> s { line_join = d }) +-- | Set the line_cap to @square@.+--+-- This squares off the end of the stroke, but visual extends the +-- stroke by half the line width.+--+-- > .---------.+-- > | ======= |+-- > '---------'+--+cap_square :: DrawingContextF+cap_square = setLineCap CapSquare -joinMiter :: DrawingContextF-joinMiter = setLineJoin JoinMiter -joinRound :: DrawingContextF-joinRound = setLineJoin JoinRound -joinBevel :: DrawingContextF-joinBevel = setLineJoin JoinBevel+-- | Set the line_join to the default which is @miter@.+--+-- This is a synonym for 'join_miter'.+--+join_default :: DrawingContextF+join_default = join_miter +-- | Set the line_join to @miter@.+--+-- This extends the joining line segments to form a sharp miter.+--+-- This is the default.+--+-- > /\+-- > /..\ +-- > /./\.\+-- > /./ \.\+-- > /./ \.\+--+join_miter :: DrawingContextF+join_miter = setLineJoin JoinMiter+++-- | Set the line_join to @round@.+--+-- This rounds off the corner of the joined line segments.+--+-- > \.\ +-- > \.\ +-- > ,.)+-- > /./+-- > /./+--+join_round :: DrawingContextF+join_round = setLineJoin JoinRound+++-- | Set the line_join to @round@.+--+-- This bevels off the corner of the joined line segments with a +-- notch.+--+-- > __+-- > /..\ +-- > /./\.\+-- > /./ \.\+-- > /./ \.\+--+join_bevel :: DrawingContextF+join_bevel = setLineJoin JoinBevel++ -------------------------------------------------------------------------------- -dashPattern :: DashPattern -> DrawingContextF-dashPattern d = updateStrokeProps (\s -> s { dash_pattern = d }) + +-- | Set the dash pattern.+--+-- Initially the dash pattern is 'Solid'.+--+set_dash_pattern :: DashPattern -> DrawingContextF+set_dash_pattern d = updateStrokeProps (\s -> s { dash_pattern = d }) -unit_dash_pattern :: DashPattern-unit_dash_pattern = Dash 0 [(1,1)] --- oblivious-phase :: Int -> DashPattern -> DashPattern-phase _ Solid = Solid-phase i (Dash _ xs) = Dash i xs+-- | Set the dash_pattern to @solid@ - i.e. no dash pattern.+--+-- This is the default.+--+solid_line :: DrawingContextF +solid_line = set_dash_pattern Solid --- non-oblivious-dphase :: Int -> DashPattern -> DashPattern-dphase _ Solid = Solid-dphase d (Dash i xs) = Dash (i+d) xs+-- | Set the dash pattern to draw a dotted line.+-- +-- A dot is actually a square - side length is equal to the line +-- width.+-- +-- The spacing between dots is 2 times the dot width.+--+dotted_line :: DrawingContextF +dotted_line = set_dash_pattern $ Dash 0 [(1,2)] -doublegaps :: DashPattern -> DashPattern-doublegaps Solid = Solid-doublegaps (Dash i xs) = Dash i (map fn xs)- where- fn (a,b) = (a,2*b)+-- | Set the dash pattern to draw a tightly packed dotted line.+-- +-- A dot is actually a square - side length is equal to the line +-- width.+-- +-- The spacing between dots is equal to the dot width.+--+packed_dotted :: DrawingContextF +packed_dotted = set_dash_pattern $ Dash 0 [(1,1)] -doubledashes :: DashPattern -> DashPattern-doubledashes Solid = Solid-doubledashes (Dash i xs) = Dash i (map fn xs)- where- fn (a,b) = (a*2,b) +-- | Set the dash pattern to draw a loosely dotted line.+-- +-- A dot is actually a square - side length is equal to the line +-- width.+-- +-- The spacing between dots is 4 times the dot width.+--+loose_dotted :: DrawingContextF +loose_dotted = set_dash_pattern $ Dash 0 [(1,4)] --------------------------------------------------------------------------------- -fontAttr :: FontFace -> Int -> DrawingContextF-fontAttr ff sz = (\s -> s { font_props = FontAttr sz ff }) -fontFace :: FontFace -> DrawingContextF-fontFace ff = updateFontProps (\(FontAttr sz _) -> FontAttr sz ff)+-- | Set the dash pattern to draw a dashed line.+-- +-- The dash length is 3 times the line width, the spacing is 2+-- times the line width.+--+dashed_line :: DrawingContextF+dashed_line = set_dash_pattern $ Dash 0 [(3,2)] -fontSize :: Int -> DrawingContextF-fontSize sz = updateFontProps (\(FontAttr _ ff) -> FontAttr sz ff) +-- | Set the dash pattern to draw a tightly packed, dashed line.+-- +-- The dash length is 3 times the line width, the spacing is +-- equal to the line width.+--+packed_dashed :: DrawingContextF+packed_dashed = set_dash_pattern $ Dash 0 [(3,1)] ++-- | Set the dash pattern to draw a loosely dashed line.+-- +-- The dash length is 3 times the line width, the spacing is 4+-- times the line width.+--+loose_dashed :: DrawingContextF+loose_dashed = set_dash_pattern $ Dash 0 [(3,4)]+++ -------------------------------------------------------------------------------- --- | Set the font size to double the current size, note the font--- size also controls the size of dots, arrowsheads etc.+-- | Set the font attributes, point size and font face.+--+font_attr :: FontDef -> Int -> DrawingContextF+font_attr ft sz = \s -> s { dc_font_size = sz+ , dc_font_face = font_def_face ft }++-- | Set the font face.+--+set_font :: FontDef -> DrawingContextF+set_font ft = \s -> s { dc_font_face = font_def_face ft }+++-- | Set the point size.+--+-- This controls the drawing size of both +-- text labels and marks (e.g. dots and arrowheads).+--+set_font_size :: Int -> DrawingContextF+set_font_size sz = \s -> s { dc_font_size = sz }+++-- | Scale the current point size by the supplied ratio. -- -doublesize :: DrawingContextF-doublesize = (\s sz -> fontSize (sz*2) s) - <*> (font_size . font_props)+-- Note - as fonts can only be drawn at integral sizes this +-- operation is not exact - for instance scaling 15pt by (1%2) +-- results in 7pt.+-- +scale_point_size :: Double -> DrawingContextF+scale_point_size a = (\s sz -> set_font_size (floor $ a * fromIntegral sz) s) + <*> dc_font_size +-- | Set the point size (font and mark size) to double the current +-- size.+--+double_point_size :: DrawingContextF+double_point_size = scale_point_size 2 --- | Set the font size to half the current size, note the font++-- | Set the point size to half the current size, note the point -- size also controls the size of dots, arrowsheads etc. -- --- As fontsize is an integer this is not exact - half size of--- 15pt type is 7pt.+-- Note - as fonts can only be drawn at integral sizes this +-- operation is not exact - half size of 15pt type is 7pt. -- -halfsize :: DrawingContextF-halfsize = (\s sz -> fontSize (sz `div` 2) s) - <*> (font_size . font_props)+half_point_size :: DrawingContextF+half_point_size = scale_point_size 0.5 +-- | 'text_margin' : @ x_sep * y_sep -> DrawingContextF @+--+-- Note - this is in @Em@ units.+--+text_margin :: Em -> Em -> DrawingContextF+text_margin xsep ysep = \s -> + s { dc_text_margin = TextMargin xsep ysep }+++-- | Set the text margin to (0,0).+-- +-- This produces a tight box around the text vertically measured +-- to the cap-height and descender. Therefore some characters may +-- extend outside the margin (e.g. accented capitals like +-- A-grave).+--+text_margin_none :: DrawingContextF+text_margin_none = text_margin 0 0 ++++-- | Set the text margin to (0.25 em, 0.25 em).+-- +text_margin_tight :: DrawingContextF+text_margin_tight = text_margin 0.25 0.25++-- | Set the text margin to (0.5 em, 0.5 em).+-- +text_margin_default :: DrawingContextF+text_margin_default = text_margin 0.5 0.5++-- | Set the text margin to (1.0 em, 1.0 em).+-- +text_margin_loose :: DrawingContextF+text_margin_loose = text_margin 1.0 1.0++ -------------------------------------------------------------------------------- -swapColours :: DrawingContextF-swapColours = - (\s a b -> s { stroke_colour = b, fill_colour = a })- <*> stroke_colour <*> fill_colour+-- | Set the stroke colour.+--+stroke_colour :: RGBi -> DrawingContextF+stroke_colour rgb = \s -> s { dc_stroke_colour = rgb } -bothStrokeColour :: DrawingContextF-bothStrokeColour = (\s a -> s { fill_colour = a }) <*> stroke_colour -bothFillColour :: DrawingContextF-bothFillColour = (\s a -> s { stroke_colour = a }) <*> fill_colour+-- | Set the fill colour.+--+fill_colour :: RGBi -> DrawingContextF+fill_colour rgb = \s -> s { dc_fill_colour = rgb } +-- | Set the text colour.+--+text_colour :: RGBi -> DrawingContextF+text_colour rgb = (\s -> s { dc_text_colour = rgb}) -strokeColour :: RGBi -> DrawingContextF-strokeColour rgb = \s -> s { stroke_colour = rgb } -fillColour :: RGBi -> DrawingContextF-fillColour rgb = \s -> s { fill_colour = rgb } +-- | Set the stroke, fill and text colours to a single colour.+--+single_colour :: RGBi -> DrawingContextF+single_colour rgb = stroke_colour rgb . fill_colour rgb . text_colour rgb ++-- | Swap the stroke colour and fill colours.+--+swap_colours :: DrawingContextF+swap_colours = + (\s a b -> s { dc_stroke_colour = b, dc_fill_colour = a })+ <*> dc_stroke_colour <*> dc_fill_colour+++-- | Set the fill colour to use the current stroke colour.+--+fill_use_stroke_colour :: DrawingContextF+fill_use_stroke_colour = + (\s a -> s { dc_fill_colour = a }) <*> dc_stroke_colour+++-- | Set the stroke colour to use the current fill colour.+--+stroke_use_fill_colour :: DrawingContextF+stroke_use_fill_colour = + (\s a -> s { dc_stroke_colour = a }) <*> dc_fill_colour+++-- | Set the fill colour to use the current text colour.+--+fill_use_text_colour :: DrawingContextF+fill_use_text_colour = + (\s a -> s { dc_fill_colour = a }) <*> dc_text_colour+++-- | Set the stroke colour to use the current fill colour.+--+stroke_use_text_colour :: DrawingContextF+stroke_use_text_colour = + (\s a -> s { dc_stroke_colour = a }) <*> dc_text_colour++++-- | Set the text colour to use the current stroke colour.+--+text_use_stroke_colour :: DrawingContextF+text_use_stroke_colour = + (\s a -> s { dc_text_colour = a }) <*> dc_stroke_colour+++-- | Set the text colour to use the current fill colour.+--+text_use_fill_colour :: DrawingContextF+text_use_fill_colour = + (\s a -> s { dc_text_colour = a }) <*> dc_fill_colour
src/Wumpus/Basic/Kernel/Base/WrappedPrimitive.hs view
@@ -1,43 +1,38 @@ {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeSynonymInstances #-}-{-# LANGUAGE FlexibleInstances #-} {-# OPTIONS -Wall #-} -------------------------------------------------------------------------------- -- | -- Module : Wumpus.Basic.Kernel.Base.WrappedPrimitive--- Copyright : (c) Stephen Tetley 2010+-- Copyright : (c) Stephen Tetley 2010-2011 -- License : BSD3 -- -- Maintainer : stephen.tetley@gmail.com -- Stability : highly unstable -- Portability : GHC ----- Two /warpped/ versions of the Primitive type from Wumpus-Core.+-- Wrapped versions of the @Primitive@ type from Wumpus-Core. --+-- This file is essentially /internal/ to Wumpus-Basic.+-- -------------------------------------------------------------------------------- module Wumpus.Basic.Kernel.Base.WrappedPrimitive ( + -- * Primitives- HPrim+ CatPrim+ , prim1+ , cpmap+ , cpmove++ , HPrim , hprimToList , singleH - -- * Collect primitives (writer monad) - , TraceM(..)-- , PrimGraphic-- , getPrimGraphic- , primGraphic- , metamorphPrim- , collectH- ) where -import Wumpus.Basic.Kernel.Base.BaseDefs import Wumpus.Basic.Utils.HList import Wumpus.Core -- package: wumpus-core@@ -47,103 +42,117 @@ --- As of version 0.36.0, Wumpus-Core supports grouping primitives--- together (a common operation in vector drawing editors). +-- | A wrapped version of 'Primitive' from Wumpus-Core that +-- supports Monoid.+-- +-- Note that CatPrim provides a /single-object/ that can be+-- hyperlinked or whatever. ----- For Wumpus-Basic this means e.g. a line with arrowheads can --- still be a primitive.+-- It is different to 'HPrim' which is intended as a list type +-- with efficient concatenation to support building of multiple+-- Primitives in a frame. ----- Still, we wrap Primitive as a newtype...+-- This type is essentially internal to Wumpus-Basic. --+data CatPrim = CZero+ | Cat1 Primitive -newtype PrimGraphic u = PrimGraphic { getPrimGraphic :: Primitive u }- deriving (Eq,Show)+type instance DUnit CatPrim = Double -type instance DUnit (PrimGraphic u) = u +instance Monoid CatPrim where+ mempty = CZero+ CZero `mappend` b = b+ a `mappend` CZero = a+ Cat1 a `mappend` Cat1 b = Cat1 $ a `primCat` b + mconcat [] = mempty+ mconcat (a:as) = step a as+ where+ step ac [] = ac+ step ac (x:xs) = step (ac `mappend` x) xs ------------------------------------------------------------------------------------ Lists of primitives... --- | Graphics objects, even simple ones (line, arrow, dot) might --- need more than one primitive (path or text label) for their--- construction. Hence, the primary representation that all the --- others are built upon must support /concatenation/ of --- primitives. ------ Wumpus-Core has a type Picture - made from one or more --- Primitives - but Pictures include support for affine frames. --- For drawing many simple graphics (dots, connector lines...) --- that do not need individual affine transformations this is a --- penalty. A list of Primitives is therefore more suitable --- representation, and a Hughes list which supports--- efficient concatenation is wise.----newtype HPrim u = HPrim { getHPrim :: H (Primitive u) } --- Note - only a Monoid instance for HPrim - they cannot be --- shown, fmapped etc. -instance Monoid (HPrim u) where- mempty = HPrim emptyH- ha `mappend` hb = HPrim $ getHPrim ha `appendH` getHPrim hb+-------------------------------------------------------------------------------- +instance Rotate CatPrim where+ rotate _ CZero = CZero+ rotate ang (Cat1 a) = Cat1 $ rotate ang a -hprimToList :: HPrim u -> [Primitive u]-hprimToList = toListH . getHPrim+instance RotateAbout CatPrim where+ rotateAbout _ _ CZero = CZero+ rotateAbout ang pt (Cat1 a) = Cat1 $ rotateAbout ang pt a +instance Scale CatPrim where+ scale _ _ CZero = CZero+ scale sx sy (Cat1 a) = Cat1 $ scale sx sy a -singleH :: Primitive u -> HPrim u-singleH = HPrim . wrapH +instance Translate CatPrim where+ translate _ _ CZero = CZero+ translate dx dy (Cat1 a) = Cat1 $ translate dx dy a -------------------------------------------------------------------------------- --- | Collect elementary graphics as part of a larger drawing.------ TraceM works much like a writer monad.----class TraceM (m :: * -> *) where- trace :: MonUnit m ~ u => HPrim u -> m ()+prim1 :: Primitive -> CatPrim +prim1 = Cat1 +-- | Map +cpmap :: (Primitive -> Primitive) -> CatPrim -> CatPrim+cpmap _ CZero = CZero+cpmap f (Cat1 a) = Cat1 $ f a +cpmove :: Vec2 Double -> CatPrim -> CatPrim+cpmove (V2 x y) = translate x y + ----------------------------------------------------------------------------------- instances+-- Lists of primitives... -instance OPlus (PrimGraphic u) where- oplus a b = PrimGraphic $ getPrimGraphic a `oplus` getPrimGraphic b +-- | Collected primitives - this type is effectively an analogue+-- to a @Frame@ in Wumpus-Core.+--+-- This type is essentially internal to Wumpus-Basic.+-- +newtype HPrim u = HPrim { getHPrim :: H Primitive } --- Affine transformations+-- Note - only a Monoid instance for HPrim - they cannot be +-- shown, fmapped etc. -instance (Real u, Floating u) => Rotate (PrimGraphic u) where- rotate ang = PrimGraphic . rotate ang . getPrimGraphic+instance Monoid (HPrim u) where+ mempty = HPrim emptyH+ ha `mappend` hb = HPrim $ getHPrim ha `appendH` getHPrim hb + mconcat [] = mempty+ mconcat (a:as) = step a as+ where+ step ac [] = ac+ step ac (x:xs) = step (ac `mappend` x) xs -instance (Real u, Floating u) => RotateAbout (PrimGraphic u) where- rotateAbout ang pt = PrimGraphic . rotateAbout ang pt . getPrimGraphic +-- | Extract the internal list of 'Primitive' from a 'HPrim'.+-- +-- The expectation is that this Primitive list will be rendered+-- by Wumpus-Core as a @frame@.+--+hprimToList :: HPrim u -> [Primitive]+hprimToList = toListH . getHPrim -instance Num u => Scale (PrimGraphic u) where- scale sx sy = PrimGraphic . scale sx sy . getPrimGraphic +-- | Form a 'HPrim' from a 'CatPrim'.+--+singleH :: CatPrim -> HPrim u+singleH CZero = HPrim emptyH+singleH (Cat1 a) = HPrim $ wrapH a -instance Num u => Translate (PrimGraphic u) where- translate dx dy = PrimGraphic . translate dx dy . getPrimGraphic --------------------------------------------------------------------------------- -primGraphic :: Primitive u -> PrimGraphic u -primGraphic = PrimGraphic -metamorphPrim :: (Primitive u -> Primitive u) -> PrimGraphic u -> PrimGraphic u-metamorphPrim f = primGraphic . f . getPrimGraphic -collectH :: PrimGraphic u -> HPrim u-collectH = singleH . getPrimGraphic
+ src/Wumpus/Basic/Kernel/Drawing/Basis.hs view
@@ -0,0 +1,101 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.Kernel.Drawing.Basis+-- Copyright : (c) Stephen Tetley 2011-2012+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : highly unstable+-- Portability : GHC +--+-- User state class for Drawing monads.+--+--------------------------------------------------------------------------------++module Wumpus.Basic.Kernel.Drawing.Basis+ (++ UState+ , UserStateM(..)++ , InsertlM(..)+ , LocationM(..)+ , CursorM(..)+ , BranchCursorM(..)+ , hmoveby+ , vmoveby++ ) where++import Wumpus.Basic.Kernel.Base.BaseDefs+import Wumpus.Basic.Kernel.Objects.Basis+import Wumpus.Basic.Kernel.Objects.LocImage++import Wumpus.Core -- package: wumpus-core++import Control.Applicative++type family UState m :: *+++class (Applicative m, Monad m) => UserStateM (m :: * -> *) where+ getState :: st ~ UState (m a) => m st+ setState :: st ~ UState (m a) => st -> m ()+ updateState :: st ~ UState (m a) => (st -> st) -> m ()+ +++++-- | Monad that collects a graphic trace, 'insertl' is analogue +-- to the Writer monad\'s @tell@.+--+class InsertlM (m :: * -> *) where+ insertl :: u ~ DUnit (m ()) => LocImage u a -> m a+ insertl_ :: u ~ DUnit (m ()) => LocImage u a -> m (UNil u)++ insertl_ = insertl . ignoreAns +++-- | Monad with notion of location - i.e. the current point.+--+class Monad m => LocationM (m :: * -> *) where+ location :: u ~ DUnit (m ()) => m (Point2 u)+++-- | Monad with turtle-like cursor movememnt.+--+class LocationM m => CursorM (m :: * -> *) where + moveby :: u ~ DUnit (m ()) => Vec2 u -> m ()++++-- | Add operations for branching at the current point.+-- +-- Not all drawings that support tracing support branching. For+-- instance Paths can be built by tracing but they always need +-- a cumulative progression of /next point/ they cannot resrt to +-- the start point and go in a differnt direction.+-- +class CursorM m => BranchCursorM (m :: * -> *) where+ -- | Branch is like @local@ in the Reader monad.+ branchCursor :: m a -> m a++++--------------------------------------------------------------------------------+-- Derived operations+++-- | Move the /cursor/ horizontally.+--+hmoveby :: (CursorM m, Num u, u ~ DUnit (m ())) => u -> m ()+hmoveby dx = moveby (hvec dx)++-- | Move the /cursor/ vertically.+--+vmoveby :: (CursorM m, Num u, u ~ DUnit (m ())) => u -> m ()+vmoveby dx = moveby (vvec dx)
+ src/Wumpus/Basic/Kernel/Drawing/Chain.hs view
@@ -0,0 +1,463 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.Kernel.Drawing.Chain+-- Copyright : (c) Stephen Tetley 2011-2012+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : highly unstable+-- Portability : GHC +--+-- Chaining LocGraphics.+--+--------------------------------------------------------------------------------++module Wumpus.Basic.Kernel.Drawing.Chain+ (+ + GenChain+ , Chain+ , DChain+ , ChainScheme(..)++ , runGenChain+ , evalGenChain+ , execGenChain+ , stripGenChain++ , runChain+ , runChain_++ , chain1+ , chainSkip_+ , chainMany+ , chainReplicate+ , chainCount++ , iterationScheme+ , sequenceScheme+ , catTrailScheme+ , countingScheme+++ , horizontalScheme+ , verticalScheme+++ , rowwiseTableScheme+ , columnwiseTableScheme+ ++ , distribRowwiseTable+ , duplicateRowwiseTable+ , distribColumnwiseTable+ , duplicateColumnwiseTable++ , radialChainScheme++ ) where+++import Wumpus.Basic.Kernel.Base.BaseDefs+import Wumpus.Basic.Kernel.Base.DrawingContext+import Wumpus.Basic.Kernel.Base.WrappedPrimitive+import Wumpus.Basic.Kernel.Drawing.Basis+import Wumpus.Basic.Kernel.Objects.Basis+import Wumpus.Basic.Kernel.Objects.Displacement+import Wumpus.Basic.Kernel.Objects.Image+import Wumpus.Basic.Kernel.Objects.LocImage+import Wumpus.Basic.Kernel.Objects.Trail++import Wumpus.Core -- package: wumpus-core++import Control.Applicative+import Data.Monoid++++newtype GenChain st u a = GenChain+ { getGenChain :: DrawingContext -> DPoint2 -> ChainSt st u + -> (a, DPoint2, ChainSt st u, CatPrim) }+++type instance DUnit (GenChain st u a) = u+type instance UState (GenChain st u a) = st++type Chain u a = GenChain () u a++type DChain a = Chain Double a++-- | scheme_start is a function from the origin to state.+-- +-- For instance, we might want to cache the origin - this would+-- not be possible if start was just a pure @cst@ value. +--+data ChainScheme u = forall cst. ChainScheme + { chain_init :: Point2 u -> cst+ , chain_step :: Point2 u -> cst -> (Point2 u,cst)+ }++type instance DUnit (ChainScheme u) = u+++data ChainSt st u = forall cst. ChainSt + { chain_count :: Int+ , chain_st :: cst+ , chain_next :: Point2 u -> cst -> (Point2 u,cst) + , chain_user_state :: st+ }+++type instance DUnit (ChainSt st u) = u+++-- Functor ++instance Functor (GenChain st u) where+ fmap f ma = GenChain $ \ctx pt s -> + let (a,p1,s1,w) = getGenChain ma ctx pt s in (f a, p1, s1, w)++++-- Applicative++instance Applicative (GenChain st u) where+ pure a = GenChain $ \_ pt s -> (a, pt, s, mempty)+ mf <*> ma = GenChain $ \ctx pt s -> + let (f,p1,s1,w1) = getGenChain mf ctx pt s+ (a,p2,s2,w2) = getGenChain ma ctx p1 s1+ in (f a, p2, s2, w1 `mappend` w2)++++-- Monad++instance Monad (GenChain st u) where+ return a = GenChain $ \_ pt s -> (a, pt, s, mempty)+ ma >>= k = GenChain $ \ctx pt s -> + let (a,p1,s1,w1) = getGenChain ma ctx pt s+ (b,p2,s2,w2) = (getGenChain . k) a ctx p1 s1+ in (b, p2, s2, w1 `mappend` w2)+++-- DrawingCtxM++instance DrawingCtxM (GenChain st u) where+ askDC = GenChain $ \ctx pt s -> (ctx, pt, s, mempty)+ asksDC fn = GenChain $ \ctx pt s -> (fn ctx, pt, s, mempty)+ localize upd ma = GenChain $ \ctx pt s -> getGenChain ma (upd ctx) pt s++++-- UserStateM ++instance UserStateM (GenChain st u) where+ getState = GenChain $ \_ pt s@(ChainSt _ _ _ ust) -> + (ust, pt, s, mempty)+ setState ust = GenChain $ \_ pt (ChainSt i a b _) -> + ((), pt, ChainSt i a b ust, mempty)+ updateState upd = GenChain $ \_ pt (ChainSt i a b ust) -> + ((), pt, ChainSt i a b (upd ust), mempty)+++-- LocationM++instance InterpretUnit u => LocationM (GenChain st u) where+ location = GenChain $ \ctx pt s ->+ let upt = dinterpF (dc_font_size ctx) pt in (upt, pt, s, mempty) ++++-- Monoid++instance Monoid a => Monoid (GenChain st u a) where+ mempty = GenChain $ \_ pt s -> (mempty, pt, s, mempty)+ ma `mappend` mb = GenChain $ \ctx pt s -> + let (a,p1,s1,w1) = getGenChain ma ctx pt s+ (b,p2,s2,w2) = getGenChain mb ctx p1 s1+ in (a `mappend` b, p2, s2, w1 `mappend` w2)++--------------------------------------------------------------------------------+-- Run functions++runGenChain :: InterpretUnit u + => ChainScheme u -> st -> GenChain st u a -> LocImage u (a,st)+runGenChain (ChainScheme start step) ust ma = promoteLoc $ \pt -> + askDC >>= \ctx ->+ let st_zero = ChainSt { chain_count = 0+ , chain_st = start pt+ , chain_next = step+ , chain_user_state = ust }+ dpt = normalizeF (dc_font_size ctx) pt+ (a,_,s1,w1) = getGenChain ma ctx dpt st_zero+ in replaceAns (a, chain_user_state s1) $ primGraphic w1++++-- | Forget the user state LocImage, just return the /answer/.+--+evalGenChain :: InterpretUnit u + => ChainScheme u -> st -> GenChain st u a -> LocImage u a+evalGenChain cscm st ma = fmap fst $ runGenChain cscm st ma+++-- | Forget the /answer/, just return the user state.+--+execGenChain :: InterpretUnit u + => ChainScheme u -> st -> GenChain st u a -> LocImage u st +execGenChain cscm st ma = fmap snd $ runGenChain cscm st ma+++stripGenChain :: InterpretUnit u + => ChainScheme u -> st -> GenChain st u a -> LocQuery u (a,st)+stripGenChain cscm st ma = stripLocImage $ runGenChain cscm st ma++++runChain :: InterpretUnit u + => ChainScheme u -> Chain u a -> LocImage u a+runChain cscm ma = evalGenChain cscm () ma++runChain_ :: InterpretUnit u + => ChainScheme u -> Chain u a -> LocGraphic u+runChain_ cscm ma = ignoreAns $ runChain cscm ma+++++--------------------------------------------------------------------------------+-- Operations+++-- | Demand a point on the Chain and draw the LocImage+-- at it.+--+chain1 :: InterpretUnit u + => LocImage u a -> GenChain st u a+chain1 gf = GenChain $ \ctx pt (ChainSt i0 s0 sf ust) -> + let upt = dinterpF (dc_font_size ctx) pt+ (a,w1) = runImage ctx $ applyLoc gf upt+ (pt1,st1) = sf upt s0+ dpt1 = normalizeF (dc_font_size ctx) pt1+ new_st = ChainSt { chain_count = i0 + 1+ , chain_st = st1+ , chain_next = sf+ , chain_user_state = ust }+ in (a, dpt1, new_st, w1)+++-- | Demand the next position, but draw nothing.+--+chainSkip_ :: InterpretUnit u => GenChain st u ()+chainSkip_ = GenChain $ \ctx pt (ChainSt i0 s0 sf ust) -> + let upt = dinterpF (dc_font_size ctx) pt+ (pt1,st1) = sf upt s0+ dpt1 = normalizeF (dc_font_size ctx) pt1+ new_st = ChainSt { chain_count = i0 + 1+ , chain_st = st1+ , chain_next = sf+ , chain_user_state = ust }+ in ((), dpt1, new_st, mempty)++++-- | Chain a list of images, each demanding a succesive start +-- point.+--+chainMany :: InterpretUnit u + => [LocImage u a] -> GenChain st u (UNil u)+chainMany = ignoreAns . mapM_ chain1+++-- | Replicate a LocImage @n@ times along a Chain.+--+chainReplicate :: InterpretUnit u + => Int -> LocImage u a -> GenChain st u (UNil u)+chainReplicate n = chainMany . replicate n +++-- | Return the count of chain steps.+--+chainCount :: GenChain st u Int+chainCount = GenChain $ \_ dpt st@(ChainSt i _ _ _) -> (i, dpt, st, mempty)+ +++++--------------------------------------------------------------------------------+-- Schemes+++-- | General scheme - iterate the next point with the supplied+-- function.+--+iterationScheme :: (Point2 u -> Point2 u) -> ChainScheme u+iterationScheme fn = ChainScheme { chain_init = const ()+ , chain_step = \pt _ -> (fn pt, ())+ }++-- | General scheme - displace successively by the elements of the+-- list of vectors. +-- +-- Note - the list is cycled to make the chain infinite.+--+sequenceScheme :: Num u => [Vec2 u] -> ChainScheme u+sequenceScheme [] = error "sequenceScheme - empty list."+sequenceScheme vs = ChainScheme { chain_init = const $ cycle vs+ , chain_step = step+ }+ where+ step _ [] = error "sequenceScheme - unreachable, cycled."+ step pt (w:ws) = (displace w pt, ws) +++-- | Derive a ChainScheme from a CatTrail.+--+-- Note - this iterates the control points of curves, it does not+-- iterate points on the curve.+--+catTrailScheme :: Num u => CatTrail u -> ChainScheme u+catTrailScheme = sequenceScheme . linear . destrCatTrail+ where+ linear (TLine v0 :xs) = v0 : linear xs+ linear (TCurve v0 v1 v2 :xs) = v0 : v1 : v2 : linear xs+ linear [] = []+++-- | Build an (infinite) ChainScheme for a prefix list of counted +-- schemes and a final scheme that runs out to infinity.+--+countingScheme :: [(Int, ChainScheme u)] -> ChainScheme u -> ChainScheme u+countingScheme [] rest = rest+countingScheme (x:xs) rest = chainPrefix x (countingScheme xs rest)+++-- | Helper - complicated...+--+chainPrefix :: (Int, ChainScheme u) -> ChainScheme u -> ChainScheme u+chainPrefix (ntimes, ChainScheme astart astep) rest@(ChainScheme bstart bstep)+ | ntimes < 1 = rest+ | otherwise = ChainScheme { chain_init = start, chain_step = next }+ where+ start pt = (astart pt,ntimes, bstart pt)++ next pt (ast,n,bst) + | n > 0 = let (p2,ast1) = astep pt ast in (p2, (ast1,n-1,bst))+ | n == 0 = let bst1 = bstart pt + (p2,bst2) = bstep pt bst1 + in (p2, (ast,(-1),bst2))+ | otherwise = let (p2,bst1) = bstep pt bst in (p2,(ast, (-1), bst1))+ ++++horizontalScheme :: Num u => u -> ChainScheme u+horizontalScheme dx = iterationScheme (displace (hvec dx))+ + +verticalScheme :: Num u => u -> ChainScheme u+verticalScheme dy = iterationScheme (displace (vvec dy))+ +++++-- | Outer and inner steppers.+--+scStepper :: PointDisplace u -> Int -> PointDisplace u + -> ChainScheme u+scStepper outF n innF = + ChainScheme { chain_init = start, chain_step = step }+ where+ start pt = (pt,1)+ step pt (ogin,i) | i < n = (innF pt, (ogin, i+1))+ | otherwise = let o1 = outF ogin + in (o1, (o1,1)) ++++-- | Generate a tabular scheme going rowwise (left-to-right) and+-- downwards.+--+-- TODO - should probably account for the initial position... +--+rowwiseTableScheme :: Num u => Int -> (u,u) -> ChainScheme u+rowwiseTableScheme num_cols (col_width,row_height) = + scStepper downF num_cols rightF+ where+ downF = displace $ vvec $ negate row_height+ rightF = displace $ hvec col_width++-- | Generate a tabular scheme going columwise (top-to-bottom) +-- and rightwards.+--+-- TODO - should probably account for the initial position... +--+columnwiseTableScheme :: Num u => Int -> (u,u) -> ChainScheme u+columnwiseTableScheme num_rows (col_width,row_height) = + scStepper rightF num_rows downF+ where+ downF = displace $ vvec $ negate row_height+ rightF = displace $ hvec col_width+++++distribRowwiseTable :: (Monoid a, InterpretUnit u)+ => Int -> (u,u) -> [LocImage u a] -> LocImage u a+distribRowwiseTable num_cols dims gs = fmap mconcat $ + runChain (rowwiseTableScheme num_cols dims) $ mapM chain1 gs+ ++duplicateRowwiseTable :: (Monoid a, InterpretUnit u)+ => Int -> Int -> (u,u) -> LocImage u a -> LocImage u a+duplicateRowwiseTable i num_cols dims gf =+ distribRowwiseTable num_cols dims (replicate i gf) ++++distribColumnwiseTable :: (Monoid a, InterpretUnit u)+ => Int -> (u,u) -> [LocImage u a] -> LocImage u a+distribColumnwiseTable num_rows dims gs = fmap mconcat $ + runChain (columnwiseTableScheme num_rows dims) $ mapM chain1 gs+ ++duplicateColumnwiseTable :: (Monoid a, InterpretUnit u)+ => Int -> Int -> (u,u) -> LocImage u a -> LocImage u a+duplicateColumnwiseTable i num_rows dims gf = + distribColumnwiseTable num_rows dims (replicate i gf) ++++-- | TODO - account for CW CCW or just rely on +ve -ve angles?...+--+radialChainScheme :: Floating u + => u -> Radian -> Radian -> ChainScheme u+radialChainScheme radius angstart angi = + ChainScheme { chain_init = start, chain_step = step }+ where+ start pt = let ogin = displace (avec angstart (-radius)) pt+ in (ogin, angstart)+ step _ (ogin,ang) = let ang_next = ang + angi + pt = displace (avec ang_next radius) ogin+ in (pt, (ogin, ang_next))++ ++-- radialChain is convoluted because first point is not the +-- circle center but a point on the circumference. Also the next+-- step iterates the (constant) origin rather than the previous +-- point.+++++ + +
+ src/Wumpus/Basic/Kernel/Drawing/CtxPicture.hs view
@@ -0,0 +1,408 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE RankNTypes #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.Kernel.Drawing.CtxPicture+-- Copyright : (c) Stephen Tetley 2010-2011+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : unstable+-- Portability : GHC +--+-- A Picture-with-implicit-context object. +-- +-- This is the corresponding type to Picture in the Wumpus-Core.+-- +-- Note - many of the composition functions are in +-- /destructor form/. As Wumpus cannot make a Picture from an +-- empty list of Pictures, /destructor form/ decomposes the +-- list into the @head@ and @rest@ as arguments in the function +-- signature, rather than take a possibly empty list and have to +-- throw an error.+-- +-- TODO - PosImage no longer supports composition operators, so +-- better names are up for grabs...+-- +--------------------------------------------------------------------------------++module Wumpus.Basic.Kernel.Drawing.CtxPicture+ (++ CtxPicture+ , runCtxPicture+ , runCtxPictureU+ , drawTracing+ , udrawTracing++ , mapCtxPicture++ -- * Composition++ , uniteCenter+ + , centeredAt++ ) where++import Wumpus.Basic.Kernel.Base.BaseDefs+import Wumpus.Basic.Kernel.Base.DrawingContext+import Wumpus.Basic.Kernel.Drawing.TraceDrawing+import Wumpus.Basic.Kernel.Objects.Anchors+import Wumpus.Basic.Kernel.Objects.Concat++import Wumpus.Core -- package: wumpus-core++import Data.AdditiveGroup -- package: vector-space+import Data.AffineSpace++import Data.Monoid++++-- | A /Contextual/ Picture.+-- +-- > CtxPicture = DrawingContext -> Maybe Picture+-- +-- This type corresponds to the 'Picture' type in Wumpus-Core, but+-- it is embedded with a 'DrawingContext' (for font properties, +-- fill colour etc.). The DrawingContext is embedded so that font+-- metrics - loaded in @IO@ can be passed into the pure world of+-- 'TraceDrawing'.+--+-- Internally a /context picture/ is a function from +-- 'DrawingContext' to @(Maybe Picture)@. The @Maybe@ represents+-- that it is possible to construct empty Pictures, even though+-- @Wumpus-Core@ cannot render them. Just as the DrawingContext+-- pushes font-metrics from the IO to the pure world, the Maybe+-- lifts the problem of unrenderable Pictures into the API where+-- client code must deal with it explicitly. +--+-- (In practice, it is very unlikely a program will create empty +-- pictures and @runCtxPictureU@ can be used without worry).+-- +-- +-- Note - pictures are fixed to the unit @Double@ (representing +-- PostScript points). Pictures are intentionally unsophisticated,+-- any fine grained control of units should be delegated to the +-- elements that build the picture (Graphics, LocGraphics, etc.). +--+newtype CtxPicture = CtxPicture { + getCtxPicture :: DrawingContext -> Maybe Picture }++type instance DUnit CtxPicture = Double+++++-- | 'runCtxPicture' : @ drawing_ctx * ctx_picture -> Maybe Picture @+--+-- Run a 'CtxPicture' with the supplied 'DrawingContext' +-- producing a 'Picture'.+--+-- The resulting Picture may be empty. Wumpus-Core cannot +-- generate empty pictures as they have no bounding box, so the +-- result is wrapped within a Maybe. This delegates reponsibility +-- for handling empty pictures to client code.+--+runCtxPicture :: DrawingContext -> CtxPicture -> Maybe Picture+runCtxPicture ctx drw = getCtxPicture drw ctx+++-- | 'runCtxPictureU' : @ drawing_ctx * ctx_picture -> Picture @+--+-- /Unsafe/ version of 'runCtxPicture'.+--+-- This function throws a runtime error when supplied with an+-- empty CtxPicture.+--+runCtxPictureU :: DrawingContext -> CtxPicture -> Picture+runCtxPictureU ctx df = maybe fk id $ runCtxPicture ctx df+ where+ fk = error "runCtxPictureU - empty CtxPicture." +++-- | 'drawTracing' : @ trace_drawing -> CtxPicture @+--+-- Transform a 'TraceDrawing' into a 'CtxPicture'.+--+drawTracing :: TraceDrawing u a -> CtxPicture+drawTracing ma = + CtxPicture $ \ctx -> liftToPictureMb $ execTraceDrawing ctx ma++-- | 'udrawTracing' : @ scalar_unit_value * trace_drawing -> CtxPicture @+--+-- Variant of 'drawTracing' with a phantom first argument - the +-- phantom identifies the unit type of the 'TraceDrawing'. It is +-- not scurtinized at the value level.+--+--+udrawTracing :: u -> TraceDrawing u a -> CtxPicture+udrawTracing _ ma = + CtxPicture $ \ctx -> liftToPictureMb $ execTraceDrawing ctx ma+++-- Note need Gen versions with user state...+++-- | 'mapCtxPicture' : @ trafo * ctx_picture -> CtxPicture @+--+-- Apply a picture transformation function to the 'Picture'+-- warpped in a 'CtxPicture'.+--+mapCtxPicture :: (Picture -> Picture) -> CtxPicture -> CtxPicture+mapCtxPicture pf pic1 = CtxPicture $ \ctx -> fmap pf $ getCtxPicture pic1 ctx+++--------------------------------------------------------------------------------+-- Affine instances+++instance Rotate CtxPicture where + rotate ang = mapCtxPicture (rotate ang)++instance RotateAbout CtxPicture where+ rotateAbout ang pt = mapCtxPicture (rotateAbout ang pt)++instance Scale CtxPicture where+ scale sx sy = mapCtxPicture (scale sx sy)++instance Translate CtxPicture where+ translate dx dy = mapCtxPicture (translate dx dy)++++--------------------------------------------------------------------------------+-- Monoid++-- | Avoid initial mempty for mconcat.+--+instance Monoid CtxPicture where+ mempty = CtxPicture $ \_ -> Nothing+ mappend = moveSnd $ \_ _ -> V2 0 0++ mconcat [] = mempty+ mconcat (a:as) = step a as+ where+ step ac [] = ac+ step ac (x:xs) = step (ac `mappend` x) xs+++--------------------------------------------------------------------------------+-- Extract /planes/.+++leftEdge :: BoundingBox Double -> Double+leftEdge = point_x . ll_corner++rightEdge :: BoundingBox Double -> Double+rightEdge = point_x . ur_corner++bottomEdge :: BoundingBox Double -> Double+bottomEdge = point_y . ll_corner+++topEdge :: BoundingBox Double -> Double+topEdge = point_y . ur_corner++++++--------------------------------------------------------------------------------+-- Composition operators++-- Naming convention - Wumpus-Core already prefixes operations+-- on Pictures with pic. As the picture operators here work on a+-- different type, they merit a different naming scheme.+--+-- Unfortunately the @cxp_@ prefix is rather ugly...+--+-- Directional names seem better than positional ones (less +-- ambiguous as when used as binary operators).+--++++combineP2 :: (Picture -> Picture -> Picture) + -> CtxPicture -> CtxPicture -> CtxPicture+combineP2 op mf mg = + CtxPicture $ \ctx -> fn (getCtxPicture mf ctx) (getCtxPicture mg ctx)+ where+ fn (Just a) (Just b) = Just $ a `op` b+ fn a Nothing = a+ fn Nothing b = b+++-- Note - the megaCombR operator is in some way an+-- /anti-combinator/. It seems easier to think about composing +-- drawings if we do work on the result Pictures directly rather +-- than build combinators to manipulate CtxPictures.+--+-- The idea of combining pre- and post- operating combinators+-- makes me worry about circular programs even though I know +-- lazy evaluation allows me to write them (in some cicumstances).+--+++moveSnd :: (DBoundingBox -> DBoundingBox -> DVec2) + -> CtxPicture -> CtxPicture+ -> CtxPicture+moveSnd mkV = combineP2 fn+ where+ fn pl pr = let v1 = mkV (boundary pl) (boundary pr)+ in pl `picOver` (picMoveBy pr v1)+++instance ZConcat CtxPicture where+ superior = mappend+ anterior = flip mappend++--------------------------------------------------------------------------------+-- Composition+++infixr 6 `uniteCenter`+++++-- | Draw @a@, move @b@ so its center is at the same center as +-- @a@, @b@ is drawn over underneath in the zorder.+--+-- > a `cxpUniteCenter` b +--++uniteCenter :: CtxPicture -> CtxPicture -> CtxPicture+uniteCenter = moveSnd $ \a b -> center a .-. center b+--+-- Are combinator names less ambiguous if they name direction+-- rather than position?+--++instance Concat CtxPicture where+ hconcat = cxpRight+ vconcat = cxpBelow+++-- | > a `cxpRight` b+-- +-- Horizontal composition - position picture @b@ to the right of +-- picture @a@.+-- +cxpRight :: CtxPicture -> CtxPicture -> CtxPicture+cxpRight = moveSnd $ \a b -> hvec $ rightEdge a - leftEdge b+++-- | > a `cxpBelow` b+--+-- Vertical composition - position picture @b@ /down/ from picture+-- @a@.+--+cxpBelow :: CtxPicture -> CtxPicture -> CtxPicture+cxpBelow = moveSnd $ \a b -> vvec $ bottomEdge a - topEdge b+++-- | Center the picture at the supplied point.+--+centeredAt :: CtxPicture -> DPoint2 -> CtxPicture+centeredAt pic (P2 x y) = mapCtxPicture fn pic+ where+ fn p = let bb = boundary p+ dx = x - (boundaryWidth bb * 0.5)+ dy = y - (boundaryHeight bb * 0.5)+ in p `picMoveBy` vec dx dy+++++--------------------------------------------------------------------------------++instance CatSpace CtxPicture where+ hspace = cxpRightSep+ vspace = cxpDownSep+++-- | > cxpRightSep n a b+--+-- Horizontal composition - move @b@, placing it to the right +-- of @a@ with a horizontal gap of @n@ separating the pictures.+--+cxpRightSep :: Double -> CtxPicture -> CtxPicture -> CtxPicture+cxpRightSep n = moveSnd $ \a b -> hvec $ n + (rightEdge a - leftEdge b)++++-- | > cxpDownSep n a b+--+-- Vertical composition - move @b@, placing it below @a@ with a+-- vertical gap of @n@ separating the pictures.+--+cxpDownSep :: Double -> CtxPicture -> CtxPicture -> CtxPicture+cxpDownSep n = moveSnd $ \a b -> vvec $ bottomEdge a - (topEdge b + n)+++--------------------------------------------------------------------------------+-- Aligning pictures+++instance Align CtxPicture where+ halign = cxpAlignH+ valign = cxpAlignV++-- | > cxpAlignH align a b+-- +-- Horizontal composition - move @b@, placing it to the right +-- of @a@ and align it with the top, center or bottom of @a@.+-- +cxpAlignH :: HAlign -> CtxPicture -> CtxPicture -> CtxPicture+cxpAlignH HALIGN_TOP = moveSnd $ \a b -> northeast a .-. northwest b+cxpAlignH HALIGN_CENTER = moveSnd $ \a b -> east a .-. west b+cxpAlignH HALIGN_BASE = moveSnd $ \a b -> southeast a .-. southwest b+++-- | > cxpAlignV align a b+-- +-- Vertical composition - move @b@, placing it below @a@ +-- and align it with the left, center or right of @a@.+-- +cxpAlignV :: VAlign -> CtxPicture -> CtxPicture -> CtxPicture+cxpAlignV VALIGN_LEFT = moveSnd $ \a b -> southwest a .-. northwest b+cxpAlignV VALIGN_CENTER = moveSnd $ \a b -> south a .-. north b+cxpAlignV VALIGN_RIGHT = moveSnd $ \a b -> southeast a .-. northeast b++++instance AlignSpace CtxPicture where+ halignSpace = cxpAlignSpaceH+ valignSpace = cxpAlignSpaceV++-- | > cxpAlignSpaceH align sep a b+-- +-- Spacing version of 'cxpAlignH' - move @b@ to the right of @a@ +-- separated by @sep@ units, align @b@ according to @align@.+-- +cxpAlignSpaceH :: HAlign -> Double -> CtxPicture -> CtxPicture -> CtxPicture+cxpAlignSpaceH align dx = go align+ where+ mv f g = moveSnd $ \a b -> hvec dx ^+^ (f a .-. g b)+ go HALIGN_TOP = mv northeast northwest+ go HALIGN_CENTER = mv east west + go HALIGN_BASE = mv southeast southwest+++-- | > cxpAlignSpaceV align sep a b+-- +-- Spacing version of alignV - move @b@ below @a@ +-- separated by @sep@ units, align @b@ according to @align@.+-- +cxpAlignSpaceV :: VAlign -> Double -> CtxPicture -> CtxPicture -> CtxPicture+cxpAlignSpaceV align dy = go align+ where+ mv f g = moveSnd $ \a b -> vvec (-dy) ^+^ (f a .-. g b)+ go VALIGN_LEFT = mv southwest northwest + go VALIGN_CENTER = mv south north + go VALIGN_RIGHT = mv southeast northeast +
+ src/Wumpus/Basic/Kernel/Drawing/LocDrawing.hs view
@@ -0,0 +1,237 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}+++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.Kernel.Drawing.LocDrawing+-- Copyright : (c) Stephen Tetley 2011-2012+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : highly unstable+-- Portability : GHC +--+-- Drawing monad with immutable start point.+--+--------------------------------------------------------------------------------++module Wumpus.Basic.Kernel.Drawing.LocDrawing+ (++ -- * GenLocDrawing monad+ GenLocDrawing+ , LocDrawing++ , LocDrawM(..)++ , runGenLocDrawing+ , evalGenLocDrawing+ , execGenLocDrawing+ , stripGenLocDrawing++ , runLocDrawing+ , runLocDrawing_ ++ )++ where++import Wumpus.Basic.Kernel.Base.BaseDefs+import Wumpus.Basic.Kernel.Base.DrawingContext+import Wumpus.Basic.Kernel.Base.WrappedPrimitive+import Wumpus.Basic.Kernel.Drawing.Basis+import Wumpus.Basic.Kernel.Objects.Anchors+import Wumpus.Basic.Kernel.Objects.Basis+import Wumpus.Basic.Kernel.Objects.Connector+import Wumpus.Basic.Kernel.Objects.Image+import Wumpus.Basic.Kernel.Objects.LocImage+++import Wumpus.Core -- package: wumpus-core+++import Control.Applicative+import Data.Monoid+++++-- | 'GenLocDrawing' is a reader-writer-state monad, unlike +-- 'GenLocTrace' there is no updateable current point, instead +-- the start point is supplied when the drawing is run and it +-- is translated by the components of the start point.+--+-- The writer accumulates a graphical trace.+--+-- Essentially, 'GenLocDrawing' is an 'Image' object extended +-- with user state.+--+newtype GenLocDrawing st u a = GenLocDrawing { + getGenLocDrawing :: DrawingContext -> st -> (a, st, CatPrim)}++type instance DUnit (GenLocDrawing st u a) = u+type instance UState (GenLocDrawing st u a) = st++type LocDrawing u a = GenLocDrawing () u a+++-- Functor++instance Functor (GenLocDrawing st u) where+ fmap f ma = GenLocDrawing $ \ctx s -> + let (a,s1,o) = getGenLocDrawing ma ctx s in (f a, s1, o)+++-- Applicative++instance Applicative (GenLocDrawing st u) where+ pure a = GenLocDrawing $ \_ s -> (a, s, mempty)+ mf <*> ma = GenLocDrawing $ \ctx s -> + let (f,s1,o1) = getGenLocDrawing mf ctx s+ (a,s2,o2) = getGenLocDrawing ma ctx s1+ in (f a, s2, o1 `mappend` o2)++++-- Monad++instance Monad (GenLocDrawing st u) where+ return a = GenLocDrawing $ \_ s -> (a, s, mempty)+ ma >>= k = GenLocDrawing $ \ctx s -> + let (a,s1,o1) = getGenLocDrawing ma ctx s+ (b,s2,o2) = (getGenLocDrawing . k) a ctx s1+ in (b, s2, o1 `mappend` o2)++++-- DrawingCtxM++instance DrawingCtxM (GenLocDrawing st u) where+ askDC = GenLocDrawing $ \ctx s -> (ctx, s, mempty)+ asksDC fn = GenLocDrawing $ \ctx s -> (fn ctx, s, mempty)+ localize upd ma = GenLocDrawing $ \ctx s -> + getGenLocDrawing ma (upd ctx) s++++-- UserStateM ++instance UserStateM (GenLocDrawing st u) where+ getState = GenLocDrawing $ \_ s -> (s, s, mempty)+ setState s = GenLocDrawing $ \_ _ -> ((), s, mempty)+ updateState upd = GenLocDrawing $ \_ s -> ((), upd s, mempty)+++-- Monoid++instance Monoid a => Monoid (GenLocDrawing st u a) where+ mempty = GenLocDrawing $ \_ s -> (mempty, s, mempty)+ ma `mappend` mb = GenLocDrawing $ \ctx s -> + let (a,s1,w1) = getGenLocDrawing ma ctx s+ (b,s2,w2) = getGenLocDrawing mb ctx s1+ in (a `mappend` b, s2, w1 `mappend` w2)++++--------------------------------------------------------------------------------++class Monad m => LocDrawM (m :: * -> *) where+ inserti :: u ~ DUnit (m ()) => Image u a -> m a+ inserti_ :: u ~ DUnit (m ()) => Image u a -> m ()+ insertli :: u ~ DUnit (m ()) => Anchor u -> LocImage u a -> m a+ insertli_ :: u ~ DUnit (m ()) => Anchor u -> LocImage u a -> m ()+ insertci :: u ~ DUnit (m ()) => + Anchor u -> Anchor u -> ConnectorImage u a -> m a+ insertci_ :: u ~ DUnit (m ()) => + Anchor u -> Anchor u -> ConnectorImage u a -> m ()++ inserti_ gf = inserti gf >> return ()+ insertli_ pt gf = insertli pt gf >> return ()+ insertci_ p1 p2 gf = insertci p1 p2 gf >> return ()+++instance InterpretUnit u => LocDrawM (GenLocDrawing st u) where+ inserti = insertiImpl+ insertli = insertliImpl+ insertci = insertciImpl++ +--------------------------------------------------------------------------------+-- Run functions+++runGenLocDrawing :: (Translate a, InterpretUnit u, u ~ DUnit a) + => st -> GenLocDrawing st u a -> LocImage u (a,st)+runGenLocDrawing st ma = promoteLoc $ \(P2 x y) -> + askDC >>= \ctx ->+ let (a,s1,w1) = getGenLocDrawing ma ctx st+ ans = translate x y a + dv1 = normalizeF (dc_font_size ctx) (V2 x y)+ in replaceAns (ans,s1) $ primGraphic $ cpmove dv1 w1+++++-- | Forget the user state LocImage, just return the /answer/.+--+evalGenLocDrawing :: (Translate a, InterpretUnit u, u ~ DUnit a) + => st -> GenLocDrawing st u a -> LocImage u a+evalGenLocDrawing st ma = fmap fst $ runGenLocDrawing st ma+++-- | Forget the /answer/, just return the user state.+--+execGenLocDrawing :: (Translate a, InterpretUnit u, u ~ DUnit a) + => st -> GenLocDrawing st u a -> LocImage u st +execGenLocDrawing st ma = fmap snd $ runGenLocDrawing st ma+++stripGenLocDrawing :: (Translate a, InterpretUnit u, u ~ DUnit a) + => st -> GenLocDrawing st u a -> LocQuery u (a,st)+stripGenLocDrawing st ma = stripLocImage $ runGenLocDrawing st ma+++-- | Simple version of 'runGenLocDrawing' - run a 'LocDrawing' without+-- user state.+--+runLocDrawing :: (Translate a, InterpretUnit u, u ~ DUnit a) + => LocDrawing u a -> LocImage u a+runLocDrawing ma = evalGenLocDrawing () ma+++runLocDrawing_ :: (Translate a, InterpretUnit u, u ~ DUnit a) + => LocDrawing u a -> LocGraphic u +runLocDrawing_ ma = ignoreAns $ runLocDrawing ma++++--------------------------------------------------------------------------------++insertiImpl :: InterpretUnit u + => Image u a -> GenLocDrawing st u a+insertiImpl gf = GenLocDrawing $ \ctx s -> + let (a,w1) = runImage ctx gf in (a,s,w1) ++++++insertliImpl :: InterpretUnit u+ => Anchor u -> LocImage u a -> GenLocDrawing st u a+insertliImpl p1 gf = GenLocDrawing $ \ctx s -> + let (a,w1) = runLocImage ctx p1 gf in (a,s,w1) ++++-- This is not right - if I\'ve taken an anchor from an object+-- within the relative coord system, the anchor points are already+-- translated respective to the origin. This implementation of +-- @insertci@ adds the translation a second time.+++insertciImpl :: InterpretUnit u + => Anchor u -> Anchor u -> ConnectorImage u a + -> GenLocDrawing st u a+insertciImpl p1 p2 gf = GenLocDrawing $ \ctx s -> + let (a,w1) = runConnectorImage ctx p1 p2 gf in (a,s,w1)
+ src/Wumpus/Basic/Kernel/Drawing/LocTrace.hs view
@@ -0,0 +1,213 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}+++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.Kernel.Drawing.LocTrace+-- Copyright : (c) Stephen Tetley 2011-2012+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : highly unstable+-- Portability : GHC +--+-- Writer monad with imperative /turtle/ style movement to build +-- LocGraphics.+--+--------------------------------------------------------------------------------++module Wumpus.Basic.Kernel.Drawing.LocTrace+ (++ -- * GenLocTrace monad+ GenLocTrace+ , LocTrace++ , runGenLocTrace+ , evalGenLocTrace+ , execGenLocTrace+ , stripGenLocTrace++ , runLocTrace+ , runLocTrace_ ++ )++ where++import Wumpus.Basic.Kernel.Base.BaseDefs+import Wumpus.Basic.Kernel.Base.DrawingContext+import Wumpus.Basic.Kernel.Base.WrappedPrimitive+import Wumpus.Basic.Kernel.Drawing.Basis+import Wumpus.Basic.Kernel.Objects.Basis+import Wumpus.Basic.Kernel.Objects.Image+import Wumpus.Basic.Kernel.Objects.LocImage+++import Wumpus.Core -- package: wumpus-core++import Data.AffineSpace -- package: vector-space++import Control.Applicative+import Data.Monoid+++++-- | GenLocTrace is a reader-writer-state monad.+--+-- The writer accumulates a graphical trace and the state is +-- the current point.+--+newtype GenLocTrace st u a = GenLocTrace { + getGenLocTrace :: DrawingContext -> DPoint2 -> st + -> (a, DPoint2, st, CatPrim)}++type instance DUnit (GenLocTrace st u a) = u+type instance UState (GenLocTrace st u a) = st++type LocTrace u a = GenLocTrace () u a+++-- Functor++instance Functor (GenLocTrace st u) where+ fmap f ma = GenLocTrace $ \ctx pt s -> + let (a,p1,s1,o) = getGenLocTrace ma ctx pt s in (f a, p1, s1, o)+++-- Applicative++instance Applicative (GenLocTrace st u) where+ pure a = GenLocTrace $ \_ pt s -> (a, pt, s, mempty)+ mf <*> ma = GenLocTrace $ \ctx pt s -> + let (f,p1,s1,o1) = getGenLocTrace mf ctx pt s+ (a,p2,s2,o2) = getGenLocTrace ma ctx p1 s1+ in (f a, p2, s2, o1 `mappend` o2)++++-- Monad++instance Monad (GenLocTrace st u) where+ return a = GenLocTrace $ \_ pt s -> (a, pt, s, mempty)+ ma >>= k = GenLocTrace $ \ctx pt s -> + let (a,p1,s1,o1) = getGenLocTrace ma ctx pt s+ (b,p2,s2,o2) = (getGenLocTrace . k) a ctx p1 s1+ in (b, p2, s2, o1 `mappend` o2)++++-- DrawingCtxM++instance DrawingCtxM (GenLocTrace st u) where+ askDC = GenLocTrace $ \ctx pt s -> (ctx, pt, s, mempty)+ asksDC fn = GenLocTrace $ \ctx pt s -> (fn ctx, pt, s, mempty)+ localize upd ma = GenLocTrace $ \ctx pt s -> getGenLocTrace ma (upd ctx) pt s++++-- UserStateM ++instance UserStateM (GenLocTrace st u) where+ getState = GenLocTrace $ \_ pt s -> (s, pt, s, mempty)+ setState s = GenLocTrace $ \_ pt _ -> ((), pt, s, mempty)+ updateState upd = GenLocTrace $ \_ pt s -> ((), pt, upd s, mempty)+++-- Monoid++instance Monoid a => Monoid (GenLocTrace st u a) where+ mempty = GenLocTrace $ \_ pt s -> (mempty, pt, s, mempty)+ ma `mappend` mb = GenLocTrace $ \ctx pt s -> + let (a,p1,s1,w1) = getGenLocTrace ma ctx pt s+ (b,p2,s2,w2) = getGenLocTrace mb ctx p1 s1+ in (a `mappend` b, p2, s2, w1 `mappend` w2)+++-- LocationM++instance InterpretUnit u => LocationM (GenLocTrace st u) where+ location = GenLocTrace $ \ctx pt s ->+ let upt = dinterpF (dc_font_size ctx) pt in (upt, pt, s, mempty) +++-- CursorM ++instance InterpretUnit u => InsertlM (GenLocTrace st u) where+ insertl = insertlImpl++instance InterpretUnit u => CursorM (GenLocTrace st u) where+ moveby = movebyImpl+++insertlImpl :: InterpretUnit u => LocImage u a -> GenLocTrace st u a+insertlImpl gf = GenLocTrace $ \ctx pt s ->+ let upt = dinterpF (dc_font_size ctx) pt + (a,w1) = runLocImage ctx upt gf+ in (a,pt,s,w1) +++movebyImpl :: InterpretUnit u => Vec2 u -> GenLocTrace st u ()+movebyImpl v1 = GenLocTrace $ \ctx pt s ->+ let dv1 = normalizeF (dc_font_size ctx) v1 + in ((), pt .+^ dv1, s, mempty) ++++-- BranchCursorM ++instance InterpretUnit u => BranchCursorM (GenLocTrace st u) where+ branchCursor ma = GenLocTrace $ \ctx pt s ->+ let (a,_,s1,w1) = getGenLocTrace ma ctx pt s+ in (a, pt, s1, w1) -- re-instate pt ++++--------------------------------------------------------------------------------+-- Run functions+++runGenLocTrace :: InterpretUnit u + => st -> GenLocTrace st u a -> LocImage u (a,st)+runGenLocTrace st ma = promoteLoc $ \pt -> + askDC >>= \ctx ->+ let dpt = normalizeF (dc_font_size ctx) pt+ (a,_,s1,w1) = getGenLocTrace ma ctx dpt st+ in replaceAns (a,s1) $ primGraphic w1+++++-- | Forget the user state LocImage, just return the /answer/.+--+evalGenLocTrace :: InterpretUnit u + => st -> GenLocTrace st u a -> LocImage u a+evalGenLocTrace st ma = fmap fst $ runGenLocTrace st ma+++-- | Forget the /answer/, just return the user state.+--+execGenLocTrace :: InterpretUnit u + => st -> GenLocTrace st u a -> LocImage u st +execGenLocTrace st ma = fmap snd $ runGenLocTrace st ma+++stripGenLocTrace :: InterpretUnit u + => st -> GenLocTrace st u a -> LocQuery u (a,st)+stripGenLocTrace st ma = stripLocImage $ runGenLocTrace st ma+++-- | Simple version of 'runGenLocTrace' - run a 'LocTrace' without+-- user state.+--+runLocTrace :: InterpretUnit u + => LocTrace u a -> LocImage u a+runLocTrace ma = evalGenLocTrace () ma+++runLocTrace_ :: InterpretUnit u + => LocTrace u a -> LocGraphic u +runLocTrace_ ma = ignoreAns $ runLocTrace ma+
+ src/Wumpus/Basic/Kernel/Drawing/PosObject.hs view
@@ -0,0 +1,668 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.Kernel.Drawing.PosObject+-- Copyright : (c) Stephen Tetley 2011-2012+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : highly unstable+-- Portability : GHC +--+-- Extended Graphic object - a rectangular /positionable/ Image.+-- +-- This graphic object has a more flexible API for positioning +-- than other graphic objects. Rather than a LocGraphic which +-- supports a single method of positioning at some start-point,+-- a @PosGraphic@ can be drawn at its center or locations on its +-- outer rectangle.+--+--------------------------------------------------------------------------------++module Wumpus.Basic.Kernel.Drawing.PosObject+ (++ -- * Positionable image+ GenPosObject+ , GenPosGraphic++ , PosObject+ , DPosObject++ , PosGraphic+ , DPosGraphic++ -- * Operations+ , runGenPosObject+ , evalGenPosObject+ , execGenPosObject+ + , runPosObject++ , runPosObjectBBox++ , makePosObject+ , emptyPosObject++ , elaboratePosObject+ , decoratePosObject++ , extendPosObject+ , mapOrientation++ , illustratePosObject++ -- * Primitive text PosObjects+ , posChar+ , posEscChar + , posCharUpright+ , posEscCharUpright+ + , posCharPrim++ , posText+ , posEscText + , posTextUpright+ , posEscTextUpright++ , posTextPrim++ , multilinePosText+ , multilinePosEscText++ , rposText+ , rposEscText+ , rposChar+ , rposEscChar+ ++ , posHKernText++ , monospaceText+ , monospaceEscText++ ) where+++import Wumpus.Basic.Kernel.Base.BaseDefs+import Wumpus.Basic.Kernel.Base.DrawingContext+import Wumpus.Basic.Kernel.Base.QueryDC+import Wumpus.Basic.Kernel.Base.UpdateDC+import Wumpus.Basic.Kernel.Base.WrappedPrimitive+import Wumpus.Basic.Kernel.Drawing.Basis+import Wumpus.Basic.Kernel.Objects.Basis+import Wumpus.Basic.Kernel.Objects.Concat+import Wumpus.Basic.Kernel.Objects.DrawingPrimitives+import Wumpus.Basic.Kernel.Objects.Image+import Wumpus.Basic.Kernel.Objects.LocImage+import Wumpus.Basic.Kernel.Objects.LocThetaImage+import Wumpus.Basic.Kernel.Objects.Orientation++import Wumpus.Core -- package: wumpus-core+import Wumpus.Core.Colour ( red, blue )++import Data.AffineSpace -- package: vector-space+import Data.VectorSpace++import Control.Applicative+import Data.Monoid++--+-- Note - PosObject could be in the @Object@ rather than @Drawing@+-- namespace.+--++type DOrt = Orientation Double++-- | A positionable \"Object\".+--+newtype GenPosObject st u a = GenPosObject { + getGenPosObject :: DrawingContext -> DPoint2 -> st -> (a, st, DOrt, CatPrim) }++type instance DUnit (GenPosObject st u a) = u+type instance UState (GenPosObject st u a) = st++type GenPosGraphic st u = GenPosObject st u (UNil u)+++-- | Type synonym for @GenPosObject () u a@, a PosObject without+-- user state.+--+type PosObject u a = GenPosObject () u a+ +-- | Version of PosObject specialized to Double for the unit type.+--+type DPosObject a = PosObject Double a+++-- | Version of PosObject with answer specialized to UNil.+--+type PosGraphic u = PosObject u (UNil u)++-- | Version of PosGraphic specialized to Double for the unit type.+--+type DPosGraphic = PosGraphic Double+++++instance Functor (GenPosObject st u) where+ fmap f mf = GenPosObject $ \ctx pt s -> + let (a,s1,o1,w1) = getGenPosObject mf ctx pt s in (f a,s1,o1,w1)+++instance Applicative (GenPosObject st u) where+ pure a = GenPosObject $ \_ _ s -> (a,s,mempty,mempty)+ mf <*> ma = GenPosObject $ \ctx pt s -> + let (f,s1,o1,w1) = getGenPosObject mf ctx pt s+ (a,s2,o2,w2) = getGenPosObject ma ctx pt s1+ in (f a, s2, o1 `mappend` o2, w1 `mappend` w2)++++instance Monad (GenPosObject st u) where+ return a = GenPosObject $ \_ _ s -> (a, s, mempty, mempty)+ mf >>= k = GenPosObject $ \ctx pt s -> + let (a,s1,o1,w1) = getGenPosObject mf ctx pt s+ (b,s2,o2,w2) = getGenPosObject (k a) ctx pt s1+ in (b, s2, o1 `mappend` o2, w1 `mappend` w2)+++instance (Monoid a, InterpretUnit u) => Monoid (GenPosObject st u a) where+ mempty = GenPosObject $ \_ _ s -> (mempty, s, mempty, mempty)+ ma `mappend` mb = GenPosObject $ \ctx pt s -> + let (a,s1,o1,w1) = getGenPosObject ma ctx pt s+ (b,s2,o2,w2) = getGenPosObject mb ctx pt s1+ in (a `mappend` b, s2, o1 `mappend` o2, w1 `mappend` w2)++++-- DrawingCtxM++instance DrawingCtxM (GenPosObject st u) where+ askDC = GenPosObject $ \ctx _ s -> (ctx, s, mempty, mempty)+ asksDC fn = GenPosObject $ \ctx _ s -> (fn ctx, s, mempty, mempty)+ localize upd ma = GenPosObject $ \ctx pt s -> + getGenPosObject ma (upd ctx) pt s++instance UserStateM (GenPosObject st u) where+ getState = GenPosObject $ \_ _ s -> (s, s, mempty, mempty)+ setState s = GenPosObject $ \_ _ _ -> ((), s, mempty, mempty)+ updateState upd = GenPosObject $ \_ _ s -> ((), upd s, mempty, mempty)++++-- | Running an PosObject produces a LocImage.+--+runGenPosObject :: InterpretUnit u + => RectAddress -> st -> GenPosObject st u a -> LocImage u (a,st)+runGenPosObject addr st ma = promoteLoc $ \ot -> + askDC >>= \ctx -> + let dot = normalizeF (dc_font_size ctx) ot+ (a,s1,o1,ca) = getGenPosObject ma ctx dot st+ v1 = vtoOrigin addr o1+ in replaceAns (a,s1) $ primGraphic $ cpmove v1 ca+++evalGenPosObject :: InterpretUnit u + => RectAddress -> st -> GenPosObject st u a -> LocImage u a+evalGenPosObject addr st ma = fmap fst $ runGenPosObject addr st ma++execGenPosObject :: InterpretUnit u + => RectAddress -> st -> GenPosObject st u a -> LocImage u st+execGenPosObject addr st ma = fmap snd $ runGenPosObject addr st ma++runPosObject :: InterpretUnit u + => RectAddress -> PosObject u a -> LocImage u a+runPosObject addr ma = fmap fst $ runGenPosObject addr () ma+++++-- | Run a PosObject producing a LocImage (BoundingBox u).+--+runPosObjectBBox :: InterpretUnit u + => RectAddress -> PosObject u a -> LocImage u (BoundingBox u)+runPosObjectBBox addr ma = promoteLoc $ \pt -> + askDC >>= \ctx -> + let sz = dc_font_size ctx + dpt = normalizeF sz pt+ (_,_,o1,w1) = getGenPosObject ma ctx dpt ()+ v1 = vtoOrigin addr o1+ bb = dinterpF sz $ orientationBounds o1 (dpt .+^ v1)+ in replaceAns bb $ primGraphic $ cpmove v1 w1+++++--------------------------------------------------------------------------------+++-- | 'makePosObject' : @ object_pos * loc_image -> PosObject @ +--+-- Create a 'PosObject' from an 'Orientation' describing how it+-- is orientated within a border rectangle and a 'LocImage' that +-- draws it.+--+-- This is the /primary/ constructor for PosObjects. Because the+-- PosObject type is considered as a specialized object it does+-- not have the range of functions of LocImage or LocThetaImage.+-- +makePosObject :: InterpretUnit u+ => Query u (Orientation u) -> LocImage u a -> GenPosObject st u a+makePosObject ma gf = GenPosObject $ \ctx pt s -> + let ort1 = runQuery ctx ma+ dort1 = normalizeF (dc_font_size ctx) ort1+ upt = dinterpF (dc_font_size ctx) pt+ (a,w) = runLocImage ctx upt gf+ in (a,s,dort1,w)+++-- | 'emptyPosObject' : @ PosObject @+--+-- Build an empty 'PosGraphicObject'.+--+emptyPosObject :: (Monoid a, InterpretUnit u) => GenPosObject st u a+emptyPosObject = mempty++ ++--+-- decorate - oblivious to /answer/.+-- elaborate - derives annotation from the /answer/ and makes a +-- cumulative graphic.+--+++elaboratePosObject :: (Fractional u, Ord u, InterpretUnit u)+ => ZOrder -> RectAddress -> LocGraphic u + -> GenPosObject st u a+ -> GenPosObject st u a+elaboratePosObject zo raddr gf ma = decoratePosObject zo fn ma+ where+ fn ortt = moveStart (vtoRectAddress ortt raddr) gf++++decoratePosObject :: InterpretUnit u + => ZOrder -> (Orientation u -> LocGraphic u) + -> GenPosObject st u a+ -> GenPosObject st u a+decoratePosObject zo fn ma = GenPosObject $ \ctx pt s -> + let (a,s1,o1,w1) = getGenPosObject ma ctx pt s+ uortt = dinterpF (dc_font_size ctx) o1+ upt = dinterpF (dc_font_size ctx) pt+ (_,w2) = runLocImage ctx upt $ fn uortt+ wout = case zo of+ ZABOVE -> w1 `mappend` w2+ ZBELOW -> w2 `mappend` w1+ in (a,s1,o1,wout)+++++-- | Extend the orientation.+--+extendPosObject :: InterpretUnit u + => u -> u -> u -> u -> GenPosObject st u a + -> GenPosObject st u a+extendPosObject x0 x1 y0 y1 ma = GenPosObject $ \ctx pt s ->+ let (a,s1,o1,w1) = getGenPosObject ma ctx pt s+ sz = dc_font_size ctx + ux0 = normalize sz x0+ ux1 = normalize sz x1+ uy0 = normalize sz y0+ uy1 = normalize sz y1+ o2 = extendOrientation ux0 ux1 uy0 uy1 o1+ in (a,s1,o2,w1)+++-- | Note - this is a bad API, it would be better to have padders+-- and fillers and not expose the orientation directly.+-- +mapOrientation :: InterpretUnit u+ => (Orientation u -> Orientation u) + -> GenPosObject st u a -> GenPosObject st u a+mapOrientation fn mf = GenPosObject $ \ctx pt s -> + let (a,s1,o1,w1) = getGenPosObject mf ctx pt s+ uort = fn $ dinterpF (dc_font_size ctx) o1+ o2 = normalizeF (dc_font_size ctx) uort+ in (a,s1,o2,w1)+++--------------------------------------------------------------------------------+++-- | Illustrate a 'PosObject' by super-imposing its 'Orientation'.+--+-- This turns the 'PosObject' into a 'LocImage' drawn at the locus+-- of the PosObject.+--+illustratePosObject :: InterpretUnit u + => PosObject u a -> LocGraphic u+illustratePosObject mf = promoteLoc $ \pt -> + askDC >>= \ctx ->+ let dpt = normalizeF (dc_font_size ctx) pt + (_,_,o1,w1) = getGenPosObject mf ctx dpt ()+ uort = dinterpF (dc_font_size ctx) o1+ in decorateBelow (primGraphic w1) (illustrateOrientation uort `at` pt)+++illustrateOrientation :: InterpretUnit u + => Orientation u -> LocGraphic u+illustrateOrientation (Orientation xmin xmaj ymin ymaj) = promoteLoc $ \pt -> + dinterpCtx 3 >>= \radius -> + let upd = localize (fill_colour blue . dotted_line)+ bl = pt .-^ V2 xmin ymin+ dot = localize (fill_colour red) $ dcDisk DRAW_FILL radius `at` pt+ hln = upd $ locStraightLine (hvec $ xmin+xmaj) `at` pt .-^ hvec xmin+ vln = upd $ locStraightLine (vvec $ ymin+ymaj) `at` pt .-^ vvec ymin+ bdr = upd $ dcRectangle DRAW_STROKE (xmin+xmaj) (ymin+ymaj) `at` bl+ in mconcat [ bdr, hln, vln, dot ]++++--------------------------------------------------------------------------------+-- Char PosObjects+++-- Note - because the TextHeight constructors are so long winded,+-- using them directly makes for a bad API. Instead we have two +-- versions for each function.++-- | Note - no margins are added to the containing rectangle.+-- +-- To get a Char with margins, use 'posText' instead:+--+-- > posText ['1']+-- +posChar :: InterpretUnit u + => Char -> GenPosGraphic st u+posChar = makeCharPO CAP_HEIGHT_PLUS_DESCENDER . CharLiteral++posEscChar :: InterpretUnit u + => EscapedChar -> GenPosGraphic st u+posEscChar = makeCharPO CAP_HEIGHT_PLUS_DESCENDER++posCharUpright :: InterpretUnit u + => Char -> GenPosGraphic st u+posCharUpright = makeCharPO JUST_CAP_HEIGHT . CharLiteral++posEscCharUpright :: InterpretUnit u + => EscapedChar -> GenPosGraphic st u+posEscCharUpright = makeCharPO JUST_CAP_HEIGHT+++-- | Primtive builder that does not add margins.+--+posCharPrim :: InterpretUnit u + => Either Char EscapedChar -> GenPosGraphic st u+posCharPrim = makeCharPO CAP_HEIGHT_PLUS_DESCENDER . either CharLiteral id+++makeCharPO :: InterpretUnit u + => TextHeight -> EscapedChar -> GenPosGraphic st u+makeCharPO hspec esc = + makePosObject (charOrientation hspec esc) + (dcEscapedlabel $ wrapEscChar esc)+++++-- | Build the Orientation of an EscapedChar.+-- +-- The locus of the Orientation is baseline left - margins are +-- added.+--+charOrientation :: (DrawingCtxM m, InterpretUnit u)+ => TextHeight -> EscapedChar -> m (Orientation u)+charOrientation hspec esc = + (\(V2 x _ ) (ymin,ymaj) -> Orientation 0 x ymin ymaj) + <$> escCharVector esc <*> heightSpan hspec+++--------------------------------------------------------------------------------+-- Text PosObjects++++posText :: InterpretUnit u + => String -> GenPosGraphic st u+posText = addMargins . makeTextPO CAP_HEIGHT_PLUS_DESCENDER . escapeString++posEscText :: InterpretUnit u + => EscapedText -> GenPosGraphic st u+posEscText = addMargins . makeTextPO CAP_HEIGHT_PLUS_DESCENDER+++posTextUpright :: InterpretUnit u + => String -> GenPosGraphic st u+posTextUpright = addMargins . makeTextPO JUST_CAP_HEIGHT . escapeString++posEscTextUpright :: InterpretUnit u + => EscapedText -> GenPosGraphic st u+posEscTextUpright = addMargins . makeTextPO JUST_CAP_HEIGHT++-- | Primtive builder that does not add margins.+--+posTextPrim :: InterpretUnit u + => Either String EscapedText -> GenPosGraphic st u+posTextPrim = makeTextPO CAP_HEIGHT_PLUS_DESCENDER . either escapeString id+++multilinePosText :: (Fractional u, InterpretUnit u)+ => VAlign -> String -> PosGraphic u+multilinePosText vspec xs = + multilinePosEscText vspec $ map escapeString $ lines xs++multilinePosEscText :: (Fractional u, InterpretUnit u)+ => VAlign -> [EscapedText] -> GenPosGraphic st u+multilinePosEscText vspec xs = addMargins $ GenPosObject $ \ctx pt s -> + let sep = runQuery ctx textlineSpace+ in getGenPosObject (body sep) ctx pt s+ where+ body sp = alignColumnSep vspec sp $ + map (makeTextPO CAP_HEIGHT_PLUS_DESCENDER) xs++++-- | Note - this does not add margins.+--+makeTextPO :: InterpretUnit u + => TextHeight -> EscapedText -> GenPosGraphic st u+makeTextPO hspec esc = + makePosObject (textOrientationZero hspec esc) (dcEscapedlabel esc)+++addMargins :: InterpretUnit u => GenPosObject st u a -> GenPosObject st u a+addMargins ma = + textMargin >>= \(xsep,ysep) -> extendPosObject xsep xsep ysep ysep ma++-- | Build the Orientation of a single line of EscapedText - +-- writing direction zero (left-to-right).+-- +-- The locus of the Orientation is baseline left - margins are +-- added.+--+textOrientationZero :: (DrawingCtxM m, InterpretUnit u )+ => TextHeight -> EscapedText -> m (Orientation u)+textOrientationZero hspec esc = + (\(V2 x _ ) (ymin,ymaj) -> Orientation 0 x ymin ymaj) + <$> escTextVector esc <*> heightSpan hspec+++--------------------------------------------------------------------------------+-- Rotated text++-- | Note - for single line text.+--+rposText :: (Real u, Floating u, InterpretUnit u) + => Radian -> String -> GenPosGraphic st u+rposText ang = addMargins . makeRotatedPO ang . escapeString++-- | Note - for single line text.+--+rposEscText :: (Real u, Floating u, InterpretUnit u) + => Radian -> EscapedText -> GenPosGraphic st u+rposEscText ang = addMargins . makeRotatedPO ang+++rposChar :: (Real u, Floating u, InterpretUnit u) + => Radian -> Char -> GenPosGraphic st u+rposChar ang ch = rposEscText ang $ wrapEscChar $ CharLiteral ch++rposEscChar :: (Real u, Floating u, InterpretUnit u) + => Radian -> EscapedChar -> GenPosGraphic st u+rposEscChar ang ch = rposEscText ang $ wrapEscChar ch+++++makeRotatedPO :: (Real u, Floating u, InterpretUnit u) + => Radian -> EscapedText -> GenPosGraphic st u+makeRotatedPO ang esc = makePosObject qry body+ where+ qry = rotateOrientation ang <$> + textOrientationZero CAP_HEIGHT_PLUS_DESCENDER esc++ body = incline (dcREscapedlabel esc) ang+++--------------------------------------------------------------------------------+-- Kerned text+++posHKernText :: InterpretUnit u+ => [KernChar u] -> GenPosGraphic st u+posHKernText xs = makePosObject (hkernOrientationZero xs) (hkernLine xs)++-- | The query should retrieve the width of one char.+--+monospaceText :: InterpretUnit u + => Query u u -> String -> GenPosGraphic st u+monospaceText qry = monospaceEscText qry . escapeString+++-- | The query should retrieve the width of one char.+--+monospaceEscText :: InterpretUnit u + => Query u u -> EscapedText -> GenPosGraphic st u+monospaceEscText qry esc = GenPosObject $ \ctx pt s ->+ let upt = dinterpF (dc_font_size ctx) pt+ uw = runQuery ctx qry+ ks = monos uw $ destrEscapedText id esc+ ortt = runQuery ctx $ hkernOrientationZero ks+ dort = normalizeF (dc_font_size ctx) ortt+ (_,w1) = runLocImage ctx upt $ hkernLine ks+ in (UNil, s, dort, w1)++++++monos :: Num u => u -> [EscapedChar] -> [KernChar u]+monos w1 (c:cs) = (0,c) : map (\ch -> (w1,ch)) cs+monos _ [] = []++++-- | Note - always CAP_HEIGHT_PLUS_DESCENDER for this one.+--+hkernOrientationZero :: (DrawingCtxM m, InterpretUnit u )+ => [KernChar u] -> m (Orientation u)+hkernOrientationZero xs = + (\(V2 x _ ) (ymin,ymaj) -> Orientation 0 x ymin ymaj) + <$> hkernVector xs <*> heightSpan CAP_HEIGHT_PLUS_DESCENDER++ ++--------------------------------------------------------------------------------+-- Combining PosObject+++instance (Monoid a, InterpretUnit u) => ZConcat (GenPosObject st u a) where+ superior = mappend+ anterior = flip mappend+++instance Monoid a => Concat (GenPosObject st u a) where+ hconcat = genMoveAlign spinemoveH spineRight+ vconcat = genMoveAlign spinemoveV spineBelow++instance (Monoid a, InterpretUnit u) => CatSpace (GenPosObject st u a) where+ hspace = genMoveSepH spinemoveH spineRight+ vspace = genMoveSepV spinemoveV spineBelow++++instance Monoid a => Align (GenPosObject st u a) where+ halign HALIGN_TOP = genMoveAlign binmoveHTop halignTopO+ halign HALIGN_CENTER = genMoveAlign binmoveHCenter halignCenterO+ halign HALIGN_BASE = genMoveAlign binmoveHBottom halignBottomO++ valign VALIGN_LEFT = genMoveAlign binmoveVLeft valignLeftO+ valign VALIGN_CENTER = genMoveAlign binmoveVCenter valignCenterO+ valign VALIGN_RIGHT = genMoveAlign binmoveVRight valignRightO++++genMoveAlign :: Monoid a+ => (Orientation Double -> Orientation Double -> Vec2 Double) + -> (Orientation Double -> Orientation Double -> Orientation Double) + -> GenPosObject st u a -> GenPosObject st u a -> GenPosObject st u a+genMoveAlign mkV mkO ma mb = GenPosObject $ \ctx pt s -> + let (a,s1,o1,w1) = getGenPosObject ma ctx pt s+ (b,s2,o2,w2) = getGenPosObject mb ctx pt s1+ v1 = mkV o1 o2+ ortt = mkO o1 o2+ w2' = cpmove v1 w2 + in (a `mappend` b, s2, ortt, w1 `mappend` w2')+++--------------------------------------------------------------------------------+-- Sep++instance (Monoid a, InterpretUnit u) => AlignSpace (GenPosObject st u a) where+ halignSpace HALIGN_TOP = genMoveSepH binmoveHTop halignTopO+ halignSpace HALIGN_CENTER = genMoveSepH binmoveHCenter halignCenterO+ halignSpace HALIGN_BASE = genMoveSepH binmoveHBottom halignBottomO++ valignSpace VALIGN_LEFT = genMoveSepV binmoveVLeft valignLeftO+ valignSpace VALIGN_CENTER = genMoveSepV binmoveVCenter valignCenterO+ valignSpace VALIGN_RIGHT = genMoveSepV binmoveVRight valignRightO+++genMoveSepH :: (Monoid a, InterpretUnit u) + => (Orientation Double -> Orientation Double -> Vec2 Double) + -> (Orientation Double -> Orientation Double -> Orientation Double) + -> u+ -> GenPosObject st u a -> GenPosObject st u a + -> GenPosObject st u a+genMoveSepH mkV mkO sep ma mb = GenPosObject $ \ctx pt s -> + let (a,s1,o1,w1) = getGenPosObject ma ctx pt s+ (b,s2,o2,w2) = getGenPosObject mb ctx pt s1+ dsep = normalize (dc_font_size ctx) sep+ v1 = hvec dsep ^+^ mkV o1 o2+ ortt = extendORight dsep $ mkO o1 o2+ w2' = cpmove v1 w2+ in (a `mappend` b, s2, ortt, w1 `mappend` w2')++++genMoveSepV :: (Monoid a, InterpretUnit u)+ => (Orientation Double -> Orientation Double -> Vec2 Double) + -> (Orientation Double -> Orientation Double -> Orientation Double) + -> u+ -> GenPosObject st u a -> GenPosObject st u a + -> GenPosObject st u a+genMoveSepV mkV mkO sep ma mb = GenPosObject $ \ctx pt s -> + let (a,s1,o1,w1) = getGenPosObject ma ctx pt s + (b,s2,o2,w2) = getGenPosObject mb ctx pt s1+ dsep = normalize (dc_font_size ctx) sep+ v1 = vvec (-dsep) ^+^ mkV o1 o2+ ortt = extendODown dsep $ mkO o1 o2+ w2' = cpmove v1 w2+ in (a `mappend` b, s2, ortt, w1 `mappend` w2')+
+ src/Wumpus/Basic/Kernel/Drawing/TraceDrawing.hs view
@@ -0,0 +1,410 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.Kernel.Drawing.TraceDrawing+-- Copyright : (c) Stephen Tetley 2010-2012+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : unstable+-- Portability : GHC +--+-- Drawing with /trace/ - a Writer like monad collecting +-- intermediate graphics - and /drawing context/ - a reader monad+-- of attributes - font_face, fill_colour etc.+--+--------------------------------------------------------------------------------++module Wumpus.Basic.Kernel.Drawing.TraceDrawing+ (++ -- * Collect primitives (writer-like monad) + GenTraceDrawing+ , TraceDrawing+ , DTraceDrawing++ , runTraceDrawing+ , execTraceDrawing+ , evalTraceDrawing++ , runGenTraceDrawing+++ , liftToPictureU+ , liftToPictureMb+ , mbPictureU+ + , trace+ , fontDelta+ , evalQuery++ , draw+ , drawi+ , drawl+ , drawli++ , drawc+ , drawci++ , node+ , nodei+ + , drawrc+ , drawrci++ ) where+++import Wumpus.Basic.Kernel.Base.BaseDefs+import Wumpus.Basic.Kernel.Base.DrawingContext+import Wumpus.Basic.Kernel.Base.QueryDC+import Wumpus.Basic.Kernel.Base.WrappedPrimitive+import Wumpus.Basic.Kernel.Drawing.Basis+import Wumpus.Basic.Kernel.Objects.Anchors+import Wumpus.Basic.Kernel.Objects.Connector+import Wumpus.Basic.Kernel.Objects.Image+import Wumpus.Basic.Kernel.Objects.LocImage++import Wumpus.Core -- package: wumpus-core++import Control.Applicative+import Data.Monoid+++--------------------------------------------------------------------------------+++++-- Note - TraceDrawing run \once\ - it is supplied with the starting+-- environment (DrawingContext) and returns a Picture.+--+-- Other Wumpus monads (e.g. Turtle) will typically be run inside+-- the TraceDrawing monad as a local effect, rather than built into a +-- transformer stack.+--+++newtype GenTraceDrawing st u a = GenTraceDrawing { + getGenTraceDrawing :: DrawingContext -> st -> (a, st, HPrim u) }+++type instance DUnit (GenTraceDrawing st u a) = u+type instance UState (GenTraceDrawing st u a) = st++type TraceDrawing u a = GenTraceDrawing () u a++type DTraceDrawing a = TraceDrawing Double a+++-- Functor++instance Functor (GenTraceDrawing st u) where+ fmap f ma = GenTraceDrawing $ \ctx s -> + let (a,s1,w1) = getGenTraceDrawing ma ctx s in (f a,s1,w1)+++-- Applicative++instance Applicative (GenTraceDrawing st u) where+ pure a = GenTraceDrawing $ \_ s -> (a, s, mempty)+ mf <*> ma = GenTraceDrawing $ \ctx s -> + let (f,s1,w1) = getGenTraceDrawing mf ctx s+ (a,s2,w2) = getGenTraceDrawing ma ctx s1+ in (f a, s2, w1 `mappend` w2)+++-- Monad++instance Monad (GenTraceDrawing st u) where+ return a = GenTraceDrawing $ \_ s -> (a, s, mempty)+ ma >>= k = GenTraceDrawing $ \ctx s -> + let (a,s1,w1) = getGenTraceDrawing ma ctx s+ (b,s2,w2) = (getGenTraceDrawing . k) a ctx s1+ in (b,s2,w1 `mappend` w2)+ ++-- DrawingCtxM++instance DrawingCtxM (GenTraceDrawing st u) where+ askDC = GenTraceDrawing $ \ctx s -> (ctx, s, mempty)+ asksDC f = GenTraceDrawing $ \ctx s -> (f ctx, s, mempty)+ localize upd ma = GenTraceDrawing $ \ctx s -> + getGenTraceDrawing ma (upd ctx) s+++-- UserStateM ++instance UserStateM (GenTraceDrawing st u) where+ getState = GenTraceDrawing $ \_ s -> (s, s, mempty)+ setState s = GenTraceDrawing $ \_ _ -> ((), s, mempty)+ updateState upd = GenTraceDrawing $ \_ s -> ((), upd s, mempty)+ ++++runTraceDrawing :: DrawingContext -> TraceDrawing u a -> (a, HPrim u)+runTraceDrawing ctx ma = post $ getGenTraceDrawing ma ctx ()+ where+ post (a,_,w1) = (a,w1)++++-- | Run the drawing returning only the output it produces, drop+-- any answer from the monadic computation.+--+execTraceDrawing :: DrawingContext -> TraceDrawing u a -> HPrim u+execTraceDrawing ctx ma = snd $ runTraceDrawing ctx ma++-- | Run the drawing ignoring the output it produces, return the +-- answer from the monadic computation.+--+-- Note - this useful for testing, generally one would want the +-- opposite behaviour (return the drawing, ignore than the +-- answer).+-- +evalTraceDrawing :: DrawingContext -> TraceDrawing u a -> a+evalTraceDrawing ctx ma = fst $ runTraceDrawing ctx ma+++runGenTraceDrawing :: DrawingContext -> st -> GenTraceDrawing st u a + -> (a,st,HPrim u)+runGenTraceDrawing ctx st ma = getGenTraceDrawing ma ctx st++++++-- | /Unsafe/ promotion of @HPrim@ to @Picture@.+--+-- If the HPrim is empty, a run-time error is thrown.+-- +liftToPictureU :: HPrim u -> Picture+liftToPictureU hf = + let prims = hprimToList hf in if null prims then errK else frame prims+ where+ errK = error "toPictureU - empty prims list."++-- | /Safe/ promotion of @HPrim@ to @(Maybe Picture)@.+--+-- If the HPrim is empty, then @Nothing@ is returned.+-- +liftToPictureMb :: HPrim u -> Maybe Picture+liftToPictureMb hf = let prims = hprimToList hf in + if null prims then Nothing else Just (frame prims)++++-- | /Unsafe/ promotion of @(Maybe Picture)@ to @Picture@.+--+-- This is equivalent to:+--+-- > fromMaybe (error "empty") $ pic+--+-- This function is solely a convenience, using it saves one +-- import and a few characters.+--+-- If the supplied value is @Nothing@ a run-time error is thrown.+-- +mbPictureU :: Maybe Picture -> Picture+mbPictureU Nothing = error "mbPictureU - empty picture."+mbPictureU (Just a) = a++-- Note - need an equivalent to Parsec\`s parseTest that provides+-- a very simple way to run graphics without concern for return +-- type or initial drawing context.++++--------------------------------------------------------------------------------++++-- TraceM +--+-- Note - @ state `mappend` a @ means the first expression in a +-- monadic drawing is the first element in the output file. It is+-- also \*\* at the back \*\* in the the Z-Order.+--+-- Some control over the Z-Order, possibly by adding /layers/ to +-- the drawing model would be valuable. +-- ++-- | Primitive operation - cf. tell in Reader monad.+--+trace :: HPrim u -> GenTraceDrawing st u ()+trace a = GenTraceDrawing $ \_ s -> ((), s, a)++++fontDelta :: GenTraceDrawing st u a -> GenTraceDrawing st u a+fontDelta mf = GenTraceDrawing $ \ctx s -> + let (_,font_attrs) = runQuery ctx textAttr+ (a,s1,w1) = getGenTraceDrawing mf ctx s+ prim = fontDeltaContext font_attrs $ primGroup $ hprimToList w1+ in (a, s1, singleH $ prim1 $ prim)++-- Note - this function is in the wrong module....+--+evalQuery :: DrawingCtxM m => Query u a -> m a+evalQuery df = askDC >>= \ctx -> return $ runQuery ctx df++++-- | Draw a Graphic taking the drawing style from the +-- /drawing context/. +--+-- This function is the /forgetful/ version of 'drawi'. +-- Commonly, it is used to draw 'Graphic' objects which +-- have no /answer/.+-- +draw :: Image u a -> GenTraceDrawing st u ()+draw gf = askDC >>= \ctx -> + let (_,w) = runImage ctx gf+ in trace (singleH w) >> return ()+++++-- | Draw an Image taking the drawing style from the +-- /drawing context/. +--+-- The graphic representation of the Image is drawn in the Trace +-- monad, and the result is returned.+-- +drawi :: Image u a -> GenTraceDrawing st u a+drawi gf = askDC >>= \ctx -> + let (a,w) = runImage ctx gf+ in trace (singleH w) >> return a+ +++-- | Draw a LocImage at the supplied Anchor taking the drawing +-- style from the /drawing context/. +--+-- This function is the /forgetful/ version of 'drawli'. +-- Commonly, it is used to draw 'LocGraphic' objects which +-- have no /answer/.+-- +drawl :: InterpretUnit u+ => Anchor u -> LocImage u a -> GenTraceDrawing st u ()+drawl ancr img = drawli ancr img >> return ()++++-- | Draw a LocImage at the supplied Point taking the drawing +-- style from the /drawing context/. +--+-- The graphic representation of the Image is drawn in the Trace +-- monad, and the result is returned.+-- +drawli :: InterpretUnit u+ => Anchor u -> LocImage u a -> GenTraceDrawing st u a+drawli pt gf = askDC >>= \ctx -> + let (a,w) = runLocImage ctx pt gf+ in trace (singleH w) >> return a+++-- Design note - having @drawlti@ for LocThetaImage does not seem +-- compelling (at the moment). The thinking is that LocTheta+-- objects should be downcast to Loc objects before drawing. +--+-- Connectors however are be different. +-- +-- PosImages would seem to be the same as LocThetaImages.+--++++-- | Draw a ConnectorGraphic with the supplied Anchors taking the +-- drawing style from the /drawing context/. +--+-- This function is the /forgetful/ version of 'drawci'. +-- Commonly, it is used to draw 'ConnectorGraphic' objects which +-- have no /answer/.+-- +drawc :: InterpretUnit u+ => Anchor u -> Anchor u -> ConnectorImage u a -> GenTraceDrawing st u ()+drawc an0 an1 gf = drawci an0 an1 gf >> return () +++-- | Draw a ConnectorImage with the supplied Points taking the +-- drawing style from the /drawing context/. +--+-- The graphic representation of the Image is drawn in the Trace +-- monad, and the result is returned.+-- +drawci :: InterpretUnit u + => Anchor u -> Anchor u -> ConnectorImage u a -> GenTraceDrawing st u a+drawci p0 p1 gf = drawi (connect gf p0 p1)+++++++++-- | Draw the object with the supplied grid coordinate. The +-- actual position is scaled according to the +-- @snap_grid_factors@ in the /drawing context/.+-- +-- This function is the /forgetful/ version of 'nodei'. +-- Commonly, it is used to draw 'LocGraphic' objects which +-- have no /answer/.+-- +node :: ( Fractional u, InterpretUnit u)+ => (Int,Int) -> LocImage u a -> GenTraceDrawing st u ()+node coord gf = nodei coord gf >> return ()+++-- | Draw the object with the supplied grid coordinate. The +-- actual position is scaled according to the +-- @snap_grid_factors@ in the /drawing context/.+-- +nodei :: (Fractional u, InterpretUnit u) + => (Int,Int) -> LocImage u a -> GenTraceDrawing st u a+nodei coord gf = askDC >>= \ctx -> + position coord >>= \pt ->+ let (a,w) = runLocImage ctx pt gf+ in trace (singleH w) >> return a+ +++++-- | Draw a connector between two objects. The projection of the+-- connector line is drawn on the line from center to center of +-- the objects, the actual start and end points of the drawn line+-- are the radial points on the objects borders that cross the +-- projected line.+-- +-- This function is the /forgetful/ version of 'drawrci'. +-- Commonly, it is used to draw 'LocGraphic' objects which +-- have no /answer/.+-- +drawrc :: ( Real u, Floating u, InterpretUnit u+ , CenterAnchor a1, RadialAnchor a1+ , CenterAnchor a2, RadialAnchor a2+ , u ~ DUnit a1, u ~ DUnit a2+ ) + => a1 -> a2 -> ConnectorImage u a -> GenTraceDrawing st u ()+drawrc a b gf = drawrci a b gf >> return ()+++-- | Draw a connector between two objects. The projection of the+-- connector line is drawn on the line from center to center of +-- the objects, the actual start and end points of the drawn line+-- are the radial points on the objects borders that cross the +-- projected line.+-- +drawrci :: ( Real u, Floating u, InterpretUnit u+ , CenterAnchor a1, RadialAnchor a1+ , CenterAnchor a2, RadialAnchor a2+ , u ~ DUnit a1, u ~ DUnit a2+ ) + => a1 -> a2 -> ConnectorImage u a -> GenTraceDrawing st u a+drawrci a b gf = + let (p0,p1) = radialConnectorPoints a b in drawi (connect gf p0 p1)
+ src/Wumpus/Basic/Kernel/Objects/AdvObject.hs view
@@ -0,0 +1,315 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.Kernel.Objects.AdvObject+-- Copyright : (c) Stephen Tetley 2010-2011+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : highly unstable+-- Portability : GHC +--+-- Extended Graphic object - an AdvanceGraphic is a Graphic +-- twinned with and advance vector.+--+--------------------------------------------------------------------------------++module Wumpus.Basic.Kernel.Objects.AdvObject+ (+++ -- * Advance vector+ AdvanceVec+ , advanceH+ , advanceV+++ -- * Advance-vector object and graphic+ , AdvObject+ , DAdvObject++ , AdvGraphic+ , DAdvGraphic+ + , runAdvObject++ , makeAdvObject+ , emptyAdvObject+ , blankAdvObject+ ++ -- * Composition+ , advance+ , advances+ , advspace+ , evenspace++ , advrepeat+ , punctuate+ , advfill++ ) where++import Wumpus.Basic.Kernel.Base.BaseDefs+import Wumpus.Basic.Kernel.Base.DrawingContext+import Wumpus.Basic.Kernel.Base.WrappedPrimitive+import Wumpus.Basic.Kernel.Objects.Basis+import Wumpus.Basic.Kernel.Objects.Image+import Wumpus.Basic.Kernel.Objects.LocImage++import Wumpus.Core -- package: wumpus-core++import Data.VectorSpace -- package: vector-space++import Control.Applicative+import Data.Monoid+++++--------------------------------------------------------------------------------++-- | Advance vectors provide an idiom for drawing consecutive+-- graphics. PostScript uses them to draw left-to-right text - +-- each character has an advance vector for the width and +-- as characters are drawn they successively displace the start+-- point for the next character with their advance vector.+--+-- Type alias for Vec2.+--+type AdvanceVec u = Vec2 u+++-- | Extract the horizontal component of an advance vector.+--+-- For left-to-right latin text, the vertical component of an+-- advance vector is expected to be 0. Ingoring it seems +-- permissible when drawing text.+--+advanceH :: AdvanceVec u -> u+advanceH (V2 w _) = w++-- | Extract the verticall component of an advance vector.+--+advanceV :: AdvanceVec u -> u+advanceV (V2 _ h) = h+++--------------------------------------------------------------------------------+-- AdvObject++-- | Internal newtype wrapper so we can have a monoid instance +-- with vector plus (^+^) for mappend.+--+newtype DAV = DAV { getDAV :: AdvanceVec Double }+++instance Monoid DAV where+ mempty = DAV $ V2 0 0+ DAV v1 `mappend` DAV v2 = DAV $ v1 ^+^ v2++-- | /Advance vector/ graphic - this partially models the +-- PostScript @show@ command which moves the /current point/ by the+-- advance (width) vector as each character is drawn.+--+newtype AdvObject u a = AdvObject + { getAdvObject :: DrawingContext -> DPoint2 -> (a, DAV, CatPrim) }++type instance DUnit (AdvObject u a) = u++type DAdvObject a = AdvObject Double a+++type AdvGraphic u = AdvObject u (UNil u)+type DAdvGraphic = AdvGraphic Double+++instance Functor (AdvObject u) where+ fmap f mf = AdvObject $ \ctx pt -> + let (a,v1,w1) = getAdvObject mf ctx pt in (f a,v1,w1)+++instance Applicative (AdvObject u) where+ pure a = AdvObject $ \_ _ -> (a,mempty,mempty)+ mf <*> ma = AdvObject $ \ctx pt -> + let (f,v1,w1) = getAdvObject mf ctx pt+ (a,v2,w2) = getAdvObject ma ctx pt+ in (f a, v1 `mappend` v2, w1 `mappend` w2)++++instance Monad (AdvObject u) where+ return a = AdvObject $ \_ _ -> (a,mempty,mempty)+ mf >>= k = AdvObject $ \ctx pt -> + let (a,v1,w1) = getAdvObject mf ctx pt+ (b,v2,w2) = getAdvObject (k a) ctx pt+ in (b, v1 `mappend` v2, w1 `mappend` w2)++++instance DrawingCtxM (AdvObject u) where+ askDC = AdvObject $ \ctx _ -> (ctx, mempty, mempty)+ asksDC fn = AdvObject $ \ctx _ -> (fn ctx, mempty, mempty)+ localize upd ma = AdvObject $ \ctx pt -> getAdvObject ma (upd ctx) pt+++++instance (Monoid a, InterpretUnit u) => Monoid (AdvObject u a) where+ mempty = AdvObject $ \_ _ -> (mempty, mempty, mempty)+ ma `mappend` mb = AdvObject $ \ctx pt -> + let (a,v1,w1) = getAdvObject ma ctx pt+ (b,v2,w2) = getAdvObject mb ctx pt+ w2r = cpmove (getDAV v1) w2+ in (a `mappend` b, v1 `mappend` v2, w1 `mappend` w2r)++++-- | Running an AdvObject produces a LocImage.+--+runAdvObject :: InterpretUnit u + => AdvObject u a -> LocImage u a+runAdvObject ma = promoteLoc $ \ot -> + askDC >>= \ctx -> + let dot = normalizeF (dc_font_size ctx) ot+ (a,_,ca) = getAdvObject ma ctx dot+ in replaceAns a $ primGraphic ca++++--------------------------------------------------------------------------------+++-- | 'makeAdvObject' : @ loc_context_function * image -> AdvObject @+--+-- Build an 'AdvObject' from a context function ('CF') that +-- generates the answer displacement vector and a 'LocGraphic' +-- that draws the 'AdvObject'.+--+makeAdvObject :: InterpretUnit u + => Query u (Vec2 u) -> LocImage u a -> AdvObject u a+makeAdvObject ma gf = AdvObject $ \ctx pt -> + let v1 = runQuery ctx ma+ dav1 = DAV $ normalizeF (dc_font_size ctx) v1+ upt = dinterpF (dc_font_size ctx) pt+ (a,w) = runLocImage ctx upt gf+ in (a,dav1,w)++++-- | 'emptyAdvObjectAU' : @ AdvObject @+--+-- Build an empty 'AdvObject'.+-- +-- The 'emptyAdvObject' is treated as a /null primitive/ by +-- @Wumpus-Core@ and is not drawn, the answer vector generated is+-- the zero vector @(V2 0 0)@.+-- +emptyAdvObject :: (Monoid a, InterpretUnit u) => AdvObject u a+emptyAdvObject = mempty++++blankAdvObject :: (Monoid a, InterpretUnit u) + => Vec2 u -> AdvObject u a+blankAdvObject v1 = AdvObject $ \ctx _ ->+ let dav1 = DAV $ normalizeF (dc_font_size ctx) v1+ in (mempty, dav1, mempty)++++++--------------------------------------------------------------------------------+-- Combining AdvObjects++++-- Helper for list concatenation.+-- +listcat :: (Monoid a, InterpretUnit u)+ => (AdvObject u a -> AdvObject u a -> AdvObject u a)+ -> [AdvObject u a] -> AdvObject u a+listcat _ [] = mempty+listcat op (x:xs) = go x xs+ where+ go acc [] = acc+ go acc (b:bs) = go (acc `op` b) bs++++-- AdvObject does not have the same ability to be concatenated+-- as PosObject - all the advance vector says is \"where to go +-- next\". Nothing in the AdvObject tracks the boundary so we+-- cannot implement the Concat classes.++infixr 6 `advance`+++-- | Draw the first AdvObject and use the advance vector to +-- displace the second AdvObject.+--+-- The final answer is the sum of both advance vectors.+--+advance :: (Monoid a, InterpretUnit u) + => AdvObject u a -> AdvObject u a -> AdvObject u a+advance = mappend+ ++-- | Concatenate the list of AdvObjects with 'advance'.+--+advances :: (Monoid a, InterpretUnit u) + => [AdvObject u a] -> AdvObject u a+advances = mconcat+++-- | Combine the AdvObjects using the answer vector of the +-- first object plus the separator to move the start of the second+-- object. +--+advspace :: (Monoid a, InterpretUnit u) + => Vec2 u -> AdvObject u a -> AdvObject u a -> AdvObject u a+advspace sep a b = a `mappend` blank `mappend` b+ where+ blank = blankAdvObject sep+++-- | List version of 'nextSpace'.+--+evenspace :: (Monoid a, InterpretUnit u) + => Vec2 u -> [AdvObject u a] -> AdvObject u a+evenspace v = listcat (advspace v)++++-- | Repeat the AdvObject @n@ times, moving each time with +-- 'advance'.+--+advrepeat :: (Monoid a, InterpretUnit u)+ => Int -> AdvObject u a -> AdvObject u a+advrepeat n = advances . replicate n+++-- | Concatenate the list of AdvObjects, going next and adding+-- the separator at each step.+--+punctuate :: (Monoid a, InterpretUnit u)+ => AdvObject u a -> [AdvObject u a] -> AdvObject u a+punctuate sep = listcat (\a b -> a `advance` sep `advance` b)++++-- | Render the supplied AdvObject, but swap the result advance+-- for the supplied vector. This function has behaviour analogue +-- to @fill@ in the @wl-pprint@ library.+-- +advfill :: InterpretUnit u + => Vec2 u -> AdvObject u a -> AdvObject u a+advfill sv mf = AdvObject $ \ctx pt -> + let (a,_,ca) = getAdvObject mf ctx pt+ dav1 = DAV $ normalizeF (dc_font_size ctx) sv + in (a,dav1,ca)++
− src/Wumpus/Basic/Kernel/Objects/AdvanceGraphic.hs
@@ -1,106 +0,0 @@-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeSynonymInstances #-}-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Basic.Kernel.Objects.AdvanceGraphic--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : stephen.tetley@gmail.com--- Stability : highly unstable--- Portability : GHC ------ Extended Graphic object - an AdvanceGraphic is a Graphic --- twinned with and AdvanceV vector.--------------------------------------------------------------------------------------module Wumpus.Basic.Kernel.Objects.AdvanceGraphic- (-- -- * Advance-vector graphic- AdvGraphic- , DAdvGraphic--- , makeAdvGraphic- , extractLocGraphic- , runAdvGraphic-- -- * Composition- , advplus- , advconcat-- ) where--import Wumpus.Basic.Kernel.Base.BaseDefs-import Wumpus.Basic.Kernel.Base.ContextFun-import Wumpus.Basic.Kernel.Base.DrawingContext-import Wumpus.Basic.Kernel.Base.WrappedPrimitive-import Wumpus.Basic.Kernel.Objects.BaseObjects-import Wumpus.Basic.Kernel.Objects.Graphic--import Wumpus.Core -- package: wumpus-core---import Control.Applicative---- | /Advance vector/ graphic - this partially models the --- PostScript @show@ command which moves the /current point/ by the--- width (advance) vector as each character is drawn.----type AdvGraphic u = LocImage u (Point2 u)--type DAdvGraphic = AdvGraphic Double------------------------------------------------------------------------------------------ | Construction is different to intoZZ functions hence the --- different name.----makeAdvGraphic :: DrawingInfo (PointDisplace u)- -> LocGraphic u - -> AdvGraphic u-makeAdvGraphic dispf gf = - promoteR1 $ \pt -> dispf >>= \fn -> fmap (replaceL $ fn pt) (gf `at` pt) -------- This should probably go - the name is not exact enough...--extractLocGraphic :: AdvGraphic u -> LocGraphic u-extractLocGraphic = fmap (replaceL uNil)--runAdvGraphic :: DrawingContext -> Point2 u -> AdvGraphic u - -> (Point2 u, PrimGraphic u)-runAdvGraphic ctx pt df = runCF1 ctx pt df--------------------------------------------------------------------------------------- composition---- Note there are opportunities for extra composition operators--- like the /picture language/...--infixr 6 `advplus`----- | \*\* WARNING \*\* - pending removal.----advplus :: AdvGraphic u -> AdvGraphic u -> AdvGraphic u-advplus = chain1---advconcat :: Num u => [AdvGraphic u] -> AdvGraphic u-advconcat [] = makeAdvGraphic (pure id) emptyLocGraphic-advconcat [x] = x-advconcat (x:xs) = x `chain1` advconcat xs
+ src/Wumpus/Basic/Kernel/Objects/Anchors.hs view
@@ -0,0 +1,230 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE KindSignatures #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.Kernel.Objects.Anchors+-- Copyright : (c) Stephen Tetley 2010-2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Anchor points on shapes, bounding boxes, etc.+--+-- Anchors are addressable positions, an examplary use is taking+-- anchors on node shapes to get the in-bound and out-bound points +-- for connectors in a network (graph) diagram.+-- +-- \*\* WARNING \*\* - the API here probably needs some more +-- thought.+--+--------------------------------------------------------------------------------++module Wumpus.Basic.Kernel.Objects.Anchors+ ( ++ -- * Anchors+ Anchor++ -- * Anchor classes+ , CenterAnchor(..)+ , ApexAnchor(..)+ , CardinalAnchor(..)+ , CardinalAnchor2(..)+ , RadialAnchor(..)+ , TopCornerAnchor(..)+ , BottomCornerAnchor(..)+ , SideMidpointAnchor(..)+++ -- * Extended anchor points+ , projectAnchor++ , radialConnectorPoints++ ) where+++import Wumpus.Core -- package: wumpus-core++import Data.AffineSpace -- package: vector-space+++-- | Note an Anchor is just a Point2.+--+type Anchor u = Point2 u+++-- | Center of an object.+--+class CenterAnchor a where+ center :: u ~ DUnit a => a -> Anchor u+++-- | Apex of an object.+--+class ApexAnchor a where+ apex :: u ~ DUnit a => a -> Anchor u+++-- | Cardinal (compass) positions on an object. +-- +-- Cardinal anchors should be at their equivalent radial position.+-- However, some shapes may not be able to easily define radial +-- positions or may be able to provide more efficient definitions +-- for the cardinal anchors. Hence the redundancy seems justified. +--+class CardinalAnchor a where+ north :: u ~ DUnit a => a -> Anchor u+ south :: u ~ DUnit a => a -> Anchor u+ east :: u ~ DUnit a => a -> Anchor u+ west :: u ~ DUnit a => a -> Anchor u++--+-- Note - a design change is probably in order where the cardinals +-- should /always/ represent their true cardinal position.+--+-- If this change is made, it is worthwhile having cardinals as+-- classes (rather than making them derived operations on +-- RadialAnchor) as classes allow for more efficient +-- implementations usually by trigonometry.+-- +++-- | Secondary group of cardinal (compass) positions on an object+-- for the diagonal positions. +-- +-- It seems possible that for some objects defining the primary+-- compass points (north, south,...) will be straight-forward +-- whereas defining the secondary compass points may be +-- problematic, hence the compass points are split into two +-- classes.+--+class CardinalAnchor2 a where+ northeast :: u ~ DUnit a => a -> Anchor u+ southeast :: u ~ DUnit a => a -> Anchor u+ southwest :: u ~ DUnit a => a -> Anchor u+ northwest :: u ~ DUnit a => a -> Anchor u+++-- | Anchor on a border that can be addressed by an angle.+--+-- The angle is counter-clockwise from the right-horizontal, i.e.+-- 0 is /east/.+--+class RadialAnchor a where+ radialAnchor :: Radian -> u ~ DUnit a => a -> Anchor u+++-- | Anchors at the top left and right corners of a shape.+--+-- For some shapes (Rectangle) the TikZ convention appears to be+-- have cardinals as the corner anchors, but this doesn\'t seem+-- to be uniform. Wumpus will need to reconsider anchors at some +-- point...+--+class TopCornerAnchor a where+ topLeftCorner :: u ~ DUnit a => a -> Anchor u+ topRightCorner :: u ~ DUnit a => a -> Anchor u+++-- | Anchors at the bottom left and right corners of a shape.+--+class BottomCornerAnchor a where+ bottomLeftCorner :: u ~ DUnit a => a -> Anchor u+ bottomRightCorner :: u ~ DUnit a => a -> Anchor u+++-- | Anchors in the center of a side.+-- +-- Sides are addressable by index. Following TikZ, side 1 is +-- expected to be the top of the shape. If the shape has an apex +-- instead of a side then side 1 is expected to be the first side +-- left of the apex.+-- +-- Implementations are also expected to modulo the side number, +-- rather than throw an out-of-bounds error.+--+class SideMidpointAnchor a where+ sideMidpoint :: Int -> u ~ DUnit a => a -> Anchor u++++--------------------------------------------------------------------------------++-- | 'projectAnchor' : @ extract_func * dist * object -> Point @+-- +-- Derive a anchor by projecting a line from the center of an +-- object through the intermediate anchor (produced by the +-- extraction function). The final answer point is located along+-- the projected line at the supplied distance @dist@.+-- +-- E.g. take the north of a rectangle and project it 10 units +-- further on:+-- +-- > projectAnchor north 10 my_rect+--+-- If the distance is zero the answer with be whatever point the +-- the extraction function produces.+--+-- If the distance is negative the answer will be along the +-- projection line, between the center and the intermediate anchor.+--+-- If the distance is positive the anchor will be extend outwards +-- from the intermediate anchor.+--+projectAnchor :: (Real u, Floating u, CenterAnchor a, u ~ DUnit a) + => (a -> Anchor u) -> u -> a -> Anchor u+projectAnchor fn d a = p1 .+^ (avec (vdirection v) d)+ where+ p1 = fn a + v = pvec (center a) p1 + +++--------------------------------------------------------------------------------++-- | 'radialConnectorPoints' : @ object_a * object_b -> (Point_a, Point_b) @+--+-- Find the radial connectors points for objects @a@ and @b@ along+-- the line joining their centers.+--+radialConnectorPoints :: ( Real u, Floating u+ , CenterAnchor a, RadialAnchor a+ , CenterAnchor b, RadialAnchor b+ , u ~ DUnit a, u ~ DUnit b) + => a -> b -> (Point2 u, Point2 u) +radialConnectorPoints a b = (radialAnchor ang a, radialAnchor (ang+pi) b)+ where+ ang = vdirection $ pvec (center a) (center b)+ + ++--------------------------------------------------------------------------------+-- Instances ++instance Fractional u => CenterAnchor (BoundingBox u) where+ center (BBox (P2 xl ylo) (P2 xr yhi)) = P2 x y + where+ x = xl+0.5*(xr-xl)+ y = ylo+0.5*(yhi-ylo)+ ++instance Fractional u => CardinalAnchor (BoundingBox u) where+ north (BBox (P2 xl _ ) (P2 xr yhi)) = P2 (xl+0.5*(xr-xl)) yhi+ south (BBox (P2 xl ylo) (P2 xr _ )) = P2 (xl+0.5*(xr-xl)) ylo+ east (BBox (P2 _ ylo) (P2 xr yhi)) = P2 xr (ylo+0.5*(yhi-ylo))+ west (BBox (P2 xl ylo) (P2 _ yhi)) = P2 xl (ylo+0.5*(yhi-ylo))+++instance Fractional u => CardinalAnchor2 (BoundingBox u) where+ northeast (BBox _ ur) = ur+ southeast (BBox (P2 _ ylo) (P2 xr _)) = P2 xr ylo+ southwest (BBox ll _) = ll+ northwest (BBox (P2 xl _) (P2 _ yhi)) = P2 xl yhi +
− src/Wumpus/Basic/Kernel/Objects/BaseObjects.hs
@@ -1,191 +0,0 @@-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeSynonymInstances #-}-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Basic.Kernel.Objects.BaseObjects--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : stephen.tetley@gmail.com--- Stability : highly unstable--- Portability : GHC ------ Aliases for ContextFun types.--- -----------------------------------------------------------------------------------module Wumpus.Basic.Kernel.Objects.BaseObjects- (-- -- * Drawing info- DrawingInfo- , LocDrawingInfo- , LocThetaDrawingInfo--- - -- * Drawing objects- , ImageAns- , GraphicAns-- , Image- , LocImage- , LocThetaImage-- , DImage- , DLocImage- , DLocThetaImage-- , hyperlink-- ) where--import Wumpus.Basic.Kernel.Base.ContextFun-import Wumpus.Basic.Kernel.Base.WrappedPrimitive--import Wumpus.Core -- package: wumpus-core------------------------------------------------------------------------------------- DrawingInfo---- | A query on the DrawingContext.------ Alias for 'CF'.--- -type DrawingInfo a = CF a----- | A query on the DrawingContext respective to the supplied--- point.------ Alias for 'LocCF'.--- -type LocDrawingInfo u a = LocCF u a----- | A query on the DrawingContext respective to the supplied--- point and angle.------ Alias for 'LocCF'.--- -type LocThetaDrawingInfo u a = LocThetaCF u a---------------------------------------------------------------------------------------- Image----- | An Image always returns a pair of some polymorphic answer @a@--- and a PrimGraphic.------ Note a PrimGraphic cannot be empty.--- -type ImageAns u a = (a, PrimGraphic u)---type GraphicAns u = ImageAns u (UNil u)----- | Draw a PrimGraphic repsective to the 'DrawingContext' and --- return some answer @a@.--- -type Image u a = CF (ImageAns u a)----- | Draw a PrimGraphic respective to the 'DrawingContext' and --- the supplied point, return some answer @a@.--- -type LocImage u a = LocCF u (ImageAns u a)----- | Draw a PrimGraphic respective to the 'DrawingContext' and--- the supplied point and angle.--- -type LocThetaImage u a = LocThetaCF u (ImageAns u a)----type DImage a = Image Double a-type DLocImage a = LocImage Double a-type DLocThetaImage a = LocThetaImage Double a---type instance DUnit (Image u a) = u -- GuardEq (DUnit a) (DUnit (PrimGraphic u))--type instance DUnit (LocImage u a) = u -- GuardEq (DUnit a) (DUnit (PrimGraphic u))--type instance DUnit (LocThetaImage u a) = u--------------------------------------------------------------------------------------- Affine instances---- Note - it seems better to have these instances for Image (even --- though Image is a type synonym) rather than more general --- instances on a CF.------ There is nothing determining a DUnit for the CF types.------ The downside is these instances are effectively orphan --- instances.-----instance (Real u, Floating u, Rotate a, DUnit a ~ u) => - Rotate (Image u a) where- rotate ang = fmap (rotate ang)---instance (Real u, Floating u, RotateAbout a, DUnit a ~ u) => - RotateAbout (Image u a) where- rotateAbout ang pt = fmap (rotateAbout ang pt)---instance (Num u, Scale a, DUnit a ~ u) => Scale (Image u a) where- scale sx sy = fmap (scale sx sy)---instance (Num u, Translate a, DUnit a ~ u) => Translate (Image u a) where- translate dx dy = fmap (translate dx dy)----- \*\* WARNING \*\* - I am not sure having affine instances for --- LocImage makes sense...------ Particularly, what is a rotateAbout on a function from Point to --- Graphic? Is it just a post-transformation, or should the start --- point be transformed as well.-----instance (Real u, Floating u, Rotate a, DUnit a ~ u) => - Rotate (LocImage u a) where- rotate ang = fmap (rotate ang)--instance (Real u, Floating u, RotateAbout a, DUnit a ~ u) => - RotateAbout (LocImage u a) where- rotateAbout ang pt = fmap (rotateAbout ang pt)---instance (Num u, Scale a, DUnit a ~ u) => Scale (LocImage u a) where- scale sx sy = fmap (scale sx sy)---instance (Num u, Translate a, DUnit a ~ u) => Translate (LocImage u a) where- translate dx dy = fmap (translate dx dy)-------------------------------------------------------------------------------------hyperlink :: XLink -> Image u a -> Image u a-hyperlink hypl = - fmap (\(a,prim) -> (a, metamorphPrim (xlink hypl) prim))------
+ src/Wumpus/Basic/Kernel/Objects/Basis.hs view
@@ -0,0 +1,116 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.Kernel.Objects.Basis+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : highly unstable+-- Portability : GHC +--+-- Common types and operations.+-- +--+--------------------------------------------------------------------------------++module Wumpus.Basic.Kernel.Objects.Basis+ (++ PrimResult++ , UConvert(..)++ , ignoreAns+ , replaceAns++ , Decorate(..)+ , decorateAbove+ , decorateBelow++ , elaborateAbove+ , elaborateBelow++ ) where++import Wumpus.Basic.Kernel.Base.BaseDefs+import Wumpus.Basic.Kernel.Base.WrappedPrimitive++import Wumpus.Core -- package: wumpus-core+++type PrimResult u a = (a, CatPrim)++++--------------------------------------------------------------------------------+++class UConvert (f :: * -> * -> *) where+ uconvF :: (Functor t, InterpretUnit u, InterpretUnit u1) + => f u (t u) -> f u1 (t u1)++ uconvZ :: (InterpretUnit u, InterpretUnit u1) + => f u a -> f u1 a+++--------------------------------------------------------------------------------++++-- | Note - the kind of f allows fo unit annotation.+--+ignoreAns :: Functor (f u) => f u a -> f u (UNil u)+ignoreAns = fmap (const UNil)++-- | Replace the answer produced by a graphic object.+--+replaceAns :: Functor (f u) => a -> f u z -> f u a+replaceAns a = fmap (const a)+++++-- | Decorate an object+--+-- oliterate - drops the graphic from the first object replacing +-- it with the graphic from the second.+--+class Decorate (f :: * -> * -> *) where+ -- | Should be read as @ decorate (above|below) A with B @+ decorate :: ZOrder -> f u a -> f u z -> f u a+ elaborate :: ZOrder -> f u a -> (a -> f u z) -> f u a+ obliterate :: f u a -> f u a+ hyperlink :: XLink -> f u a -> f u a+ svgId :: String -> f u a -> f u a+ svgAnnotate :: [SvgAttr] -> f u a -> f u a++++-- | Decorate (ABOVE) a with b.+--+decorateAbove :: Decorate f => f u a -> f u z -> f u a+decorateAbove = decorate ZABOVE++-- | Decorate (BELOW) a with b.+--+decorateBelow :: Decorate f => f u a -> f u z -> f u a+decorateBelow = decorate ZBELOW++-- | Elaborate (ABOVE) a with b.+--+elaborateAbove :: Decorate f => f u a -> (a -> f u z) -> f u a+elaborateAbove = elaborate ZABOVE++-- | Elaborate (BELOW) a with b.+--+elaborateBelow :: Decorate f => f u a -> (a -> f u z) -> f u a+elaborateBelow = elaborate ZBELOW++++
src/Wumpus/Basic/Kernel/Objects/Bounded.hs view
@@ -3,17 +3,15 @@ -------------------------------------------------------------------------------- -- | -- Module : Wumpus.Basic.Kernel.Objects.Bounded--- Copyright : (c) Stephen Tetley 2010+-- Copyright : (c) Stephen Tetley 2010-2012 -- License : BSD3 -- -- Maintainer : stephen.tetley@gmail.com -- Stability : highly unstable -- Portability : GHC ----- Bounded versions of Graphic and LocGraphic.------ Bounded meaning they are actually Images that return the --- bounding box of the Graphic.+-- Helpers for working with Images and LocImages that produce+-- bounding boxes. -- -------------------------------------------------------------------------------- @@ -21,67 +19,53 @@ ( - -- * Bounded graphic / loc graphic+ -- * Type synonyms BoundedGraphic- , DBoundedGraphic , BoundedLocGraphic- , DBoundedLocGraphic , BoundedLocThetaGraphic- , DBoundedLocThetaGraphic + -- * Bounding box graphic helpers+ , centerOrthoBBox++ , emptyBoundedLocGraphic+ , emptyBoundedLocThetaGraphic - , centerOrthoBBox+ , illustrateBoundedGraphic , illustrateBoundedLocGraphic , illustrateBoundedLocThetaGraphic + , bbrectangle++ , boundedRect+ ) where import Wumpus.Basic.Kernel.Base.BaseDefs-import Wumpus.Basic.Kernel.Base.ContextFun import Wumpus.Basic.Kernel.Base.DrawingContext import Wumpus.Basic.Kernel.Base.UpdateDC-import Wumpus.Basic.Kernel.Objects.BaseObjects-import Wumpus.Basic.Kernel.Objects.Graphic+import Wumpus.Basic.Kernel.Objects.Basis+import Wumpus.Basic.Kernel.Objects.DrawingPrimitives+import Wumpus.Basic.Kernel.Objects.Image+import Wumpus.Basic.Kernel.Objects.LocImage+import Wumpus.Basic.Kernel.Objects.LocThetaImage import Wumpus.Core -- package: wumpus-core-import Wumpus.Core.Colour ( blue ) -import Control.Applicative+import Data.Monoid -------------------------------------------------------------------------------- --- | Graphic with a bounding box.--- -type BoundedGraphic u = Image u (BoundingBox u) -type DBoundedGraphic = BoundedGraphic Double------- | LocGraphic with a bounding box.----type BoundedLocGraphic u = LocImage u (BoundingBox u)--type DBoundedLocGraphic = BoundedLocGraphic Double----- | LocThetaGraphic with a bounding box.------ Note the size of bounding box for the \"same\" shape will vary --- according to the rotation. A bounding box is always --- orthonormal (?) to the x- and y-axes.---+type BoundedGraphic u = Image u (BoundingBox u)+type BoundedLocGraphic u = LocImage u (BoundingBox u) type BoundedLocThetaGraphic u = LocThetaImage u (BoundingBox u) -type DBoundedLocThetaGraphic = BoundedLocThetaGraphic Double ---- | 'openStroke' : @ theta * bbox -> BBox @+-- | 'centerOrthoBBox' : @ theta * bbox -> BBox @ -- -- Rotate a bounding box by @theta@ about its center. Take the -- new bounding box.@@ -90,51 +74,120 @@ -- so the dimensions as well as the positions may change under -- rotation. ---centerOrthoBBox :: (Real u, Floating u) ++centerOrthoBBox :: (Real u, Floating u, Ord u) => Radian -> BoundingBox u -> BoundingBox u-centerOrthoBBox theta bb = - traceBoundary $ map (rotateAbout theta ctr) ps+centerOrthoBBox theta bb = traceBoundary $ map (rotateAbout theta ctr) ps where- ps = boundaryCornerList bb ctr = boundaryCenter bb+ ps = boundaryCornerList bb -emptyBoundedLocGraphic :: Num u => BoundedLocGraphic u-emptyBoundedLocGraphic = intoLocImage fn emptyLocGraphic- where- fn = promoteR1 $ \pt -> pure (BBox pt pt) +-- | Build an empty 'LocGraphic' returning a bounding box.+-- +-- The 'emptyBoundedLocGraphic' is treated as a /null primitive/ +-- by @Wumpus-Core@ and is not drawn, although it does generate+-- the minimum bounding box with both the bottom-left and +-- upper-right corners at the implicit start point.+--+emptyBoundedLocGraphic :: InterpretUnit u => BoundedLocGraphic u +emptyBoundedLocGraphic = promoteLoc $ \pt -> + replaceAns (BBox pt pt) $ primGraphic mempty+++++-- | Build an empty 'LocThetaGraphic' returning a bounding box.+-- +-- The 'emptyBoundedLocThetaGraphic' is treated as a +-- /null primitive/ by @Wumpus-Core@ and is not drawn, although +-- it does generate the minimum bounding box with both the +-- bottom-left and upper-right corners at the implicit start point +--+emptyBoundedLocThetaGraphic :: InterpretUnit u + => BoundedLocThetaGraphic u+emptyBoundedLocThetaGraphic = promoteLocTheta $ \pt _ -> + replaceAns (BBox pt pt) $ primGraphic mempty+++--+-- NOTE - CONCATENATION+--+-- Because there is no tangible relation between the start point +-- and answer BoundingBox, LocBoundedGraphic supports exactly the+-- same concatenation as LocImage.+--+-- PosImage and AdvGraphic are the objects that support more +-- sophisticated concatenation.+--+ ++ -------------------------------------------------------------------------------- -- --- This is a common pattern so needs a name... -illustrateBoundedGraphic :: Fractional u => BoundedGraphic u -> BoundedGraphic u-illustrateBoundedGraphic mf = - mf >>= \(bb,g1) -> bbrectangle bb >>= \(_,g0) -> return (bb, g0 `oplus` g1)+-- | Draw a BoundedGraphic, illustrating the bounding box.+--+illustrateBoundedGraphic :: InterpretUnit u+ => Image u (BoundingBox u) -> Image u (BoundingBox u)+illustrateBoundedGraphic gf = elaborateBelow gf bbrectangle -illustrateBoundedLocGraphic :: Fractional u - => BoundedLocGraphic u -> BoundedLocGraphic u-illustrateBoundedLocGraphic mf = - promoteR1 $ \pt -> illustrateBoundedGraphic $ apply1R1 mf pt +-- | Draw a BoundedLocGraphic, illustrating the bounding box.+--+illustrateBoundedLocGraphic :: InterpretUnit u+ => LocImage u (BoundingBox u) + -> LocImage u (BoundingBox u)+illustrateBoundedLocGraphic gf = elaborateBelow gf fn+ where+ fn bb = promoteLoc $ \_ -> bbrectangle bb -illustrateBoundedLocThetaGraphic :: Fractional u - => BoundedLocThetaGraphic u -> BoundedLocThetaGraphic u-illustrateBoundedLocThetaGraphic mf = - promoteR2 $ \pt theta-> illustrateBoundedGraphic $ apply2R2 mf pt theta -bbrectangle :: Fractional u => BoundingBox u -> Graphic u+-- | Draw a BoundedLocThetaGraphic, illustrating the bounding box.+--+illustrateBoundedLocThetaGraphic :: InterpretUnit u+ => LocThetaImage u (BoundingBox u)+ -> LocThetaImage u (BoundingBox u)+illustrateBoundedLocThetaGraphic gf = elaborateBelow gf fn+ where+ fn bb = promoteLocTheta $ \_ _ -> bbrectangle bb++++-- | Draw a bounding box as a stroked rectangle with +-- dotted lines.+--+bbrectangle :: InterpretUnit u => BoundingBox u -> Graphic u bbrectangle (BBox p1@(P2 llx lly) p2@(P2 urx ury))- | llx == urx && lly == ury = emptyLocGraphic `at` p1+ | llx == urx && lly == ury = mempty `at` p1 | otherwise = - localize drawing_props $ rect1 `oplus` cross+ localize drawing_props $ rect1 `mappend` cross where- drawing_props = strokeColour blue . capRound . dashPattern (Dash 0 [(1,2)])- rect1 = strokedRectangle (urx-llx) (ury-lly) `at` p1- cross = straightLineBetween p1 p2 - `oplus` straightLineBetween (P2 llx ury) (P2 urx lly)+ drawing_props = cap_round . dotted_line+ rect1 = dcRectangle DRAW_STROKE (urx-llx) (ury-lly) `at` p1+ cross = straightLine p1 p2 + `mappend` straightLine (P2 llx ury) (P2 urx lly) +++-- | 'boundedRect' : @ style * width * height -> LocGraphic @+--+-- Create a stroked rectangle - the implicit start point is +-- /bottom-left/, return the bounding box of the rectangle as the +-- answer.+-- +-- The line properties (colour, pen thickness, etc.) are taken +-- from the implicit 'DrawingContext'.+-- +boundedRect :: InterpretUnit u + => DrawMode -> u -> u -> BoundedLocGraphic u +boundedRect style w h = promoteLoc $ \pt@(P2 x y) -> + let bb = BBox pt (P2 (x + w) (y + h))+ in replaceAns bb $ applyLoc (dcRectangle style w h) pt++
+ src/Wumpus/Basic/Kernel/Objects/Concat.hs view
@@ -0,0 +1,188 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.Kernel.Objects.Concat+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : highly unstable+-- Portability : GHC +--+-- Classes for concatenation.+--+--------------------------------------------------------------------------------++module Wumpus.Basic.Kernel.Objects.Concat+ (++ ZConcat(..)+ , cat+ , Concat(..)+ , hcat+ , vcat++ , CatSpace(..)+ , hsep+ , vsep++ , Align(..)+ , alignRow+ , alignColumn++ , AlignSpace(..)+ , alignRowSep+ , alignColumnSep+ + + ) where++import Wumpus.Basic.Kernel.Base.BaseDefs++import Wumpus.Core -- package: wumpus-core++import Data.Monoid++infixr 6 `superior`, `anterior`++-- | Minimal defintion is 'superior', 'anterior' is usually +-- @flip superior@.+--+-- > `superior` (infixr 6)+--+-- > `anterior` (infixr 6)+-- +-- +class ZConcat o where+ anterior :: o -> o -> o + superior :: o -> o -> o++ anterior = flip superior+++cat :: (Monoid o, ZConcat o) => [o] -> o+cat [] = mempty+cat (x:xs) = go x xs+ where+ go acc [] = acc+ go acc (a:as) = go (acc `superior` a) as+ +++infixr 6 `hconcat`+infixr 5 `vconcat`+++-- | Concatenation with /movement/ - the second object is moved+-- next to the first.+--+-- > hconcat is equivalent to @(<>)@ in WL-PPrint.+-- > (infixr 6)+-- +-- > vconcat is equivalent to @(<$>)@ in WL_PPrint.+-- > (infixr 5)+--+class Concat o where+ hconcat :: o -> o -> o+ vconcat :: o -> o -> o++-- | Horizontally concatenate a list of objects.+-- +-- Note - the first argument is an /alternative/ - this is drawn +-- if the list is empty, otherwise it is not drawn.+--+hcat :: (Monoid o, Concat o) => [o] -> o+hcat [] = mempty+hcat (x:xs) = go x xs+ where+ go acc [] = acc+ go acc (a:as) = go (acc `hconcat` a) as+ ++-- | Vertically concatenate a list of objects.+-- +-- Note - the first argument is an /alternative/ - this is drawn +-- if the list is empty, otherwise it is not drawn.+--+vcat :: (Monoid o, Concat o) => [o] -> o+vcat [] = mempty+vcat (x:xs) = go x xs+ where+ go acc [] = acc+ go acc (a:as) = go (acc `vconcat` a) as+ ++class CatSpace o where+ hspace :: u ~ DUnit o => u -> o -> o -> o+ vspace :: u ~ DUnit o => u -> o -> o -> o++hsep :: (Monoid o, CatSpace o, u ~ DUnit o) => u -> [o] -> o+hsep _ [] = mempty+hsep dx (x:xs) = go x xs+ where+ op = hspace dx+ go acc [] = acc+ go acc (a:as) = go (acc `op` a) as+ ++vsep :: (Monoid o, CatSpace o, u ~ DUnit o) => u -> [o] -> o+vsep _ [] = mempty+vsep dx (x:xs) = go x xs+ where+ op = vspace dx+ go acc [] = acc+ go acc (a:as) = go (acc `op` a) as+++class Align o where+ halign :: HAlign -> o -> o -> o + valign :: VAlign -> o -> o -> o+++alignRow :: (Monoid o, Align o) => HAlign -> [o] -> o+alignRow _ [] = mempty+alignRow ha (x:xs) = go x xs+ where+ op = halign ha + go acc [] = acc+ go acc (a:as) = go (acc `op` a) as+++alignColumn :: (Monoid o, Align o) => VAlign -> [o] -> o+alignColumn _ [] = mempty+alignColumn va (x:xs) = go x xs+ where+ op = valign va + go acc [] = acc+ go acc (a:as) = go (acc `op` a) as++++class AlignSpace o where+ halignSpace :: u ~ DUnit o => HAlign -> u -> o -> o -> o + valignSpace :: u ~ DUnit o => VAlign -> u -> o -> o -> o++++alignRowSep :: (Monoid o, AlignSpace o, u ~ DUnit o) + => HAlign -> u -> [o] -> o+alignRowSep _ _ [] = mempty+alignRowSep ha dx (x:xs) = go x xs+ where+ op = halignSpace ha dx+ go acc [] = acc+ go acc (a:as) = go (acc `op` a) as+++alignColumnSep :: (Monoid o, AlignSpace o, u ~ DUnit o) + => VAlign -> u -> [o] -> o+alignColumnSep _ _ [] = mempty+alignColumnSep va dx (x:xs) = go x xs+ where+ op = valignSpace va dx+ go acc [] = acc+ go acc (a:as) = go (acc `op` a) as++
src/Wumpus/Basic/Kernel/Objects/Connector.hs view
@@ -1,74 +1,295 @@ {-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeSynonymInstances #-} {-# OPTIONS -Wall #-} -------------------------------------------------------------------------------- -- | -- Module : Wumpus.Basic.Kernel.Objects.Connector--- Copyright : (c) Stephen Tetley 2010+-- Copyright : (c) Stephen Tetley 2011 -- License : BSD3 -- -- Maintainer : stephen.tetley@gmail.com -- Stability : highly unstable -- Portability : GHC ----- Graphic and Image types representing connectors - connectors--- have two implicit points - start and end.+-- ConnImage and ConnGraphic types - these are functional types+-- from the DrawingContext plus start point and end point to a +-- graphic /primitive/. -- -------------------------------------------------------------------------------- module Wumpus.Basic.Kernel.Objects.Connector- (+ (+ ConnectorImage+ , ConnectorGraphic - -- * Connector Graphic- ConnectorGraphic- , DConnectorGraphic+ , DConnectorImage+ , DConnectorGraphic + , ConnectorQuery - -- * Connector Image- , ConnectorImage- , DConnectorImage+ , runConnectorImage+ , runConnectorQuery+ , connect + , stripConnectorImage+ , liftConnectorQuery + , promoteConn+ , applyConn - ) where+ , qpromoteConn+ , qapplyConn -import Wumpus.Basic.Kernel.Base.ContextFun-import Wumpus.Basic.Kernel.Objects.BaseObjects+ , emptyConnectorImage --- import Wumpus.Core -- package: wumpus-core+ ) + where ------------------------------------------------------------------------------------ Connector Graphic+import Wumpus.Basic.Kernel.Base.BaseDefs+import Wumpus.Basic.Kernel.Base.DrawingContext+import Wumpus.Basic.Kernel.Base.QueryDC+import Wumpus.Basic.Kernel.Objects.Basis+import Wumpus.Basic.Kernel.Objects.Image +import Wumpus.Core -- package: wumpus-core --- | ConnectorGraphic is a connector drawn between two points --- contructing a Graphic.+import Control.Applicative+import Data.Monoid+++-- | ConnectorImage - function from DrawingContext and start and +-- end points to a polymorphic /answer/ and a graphic /primitive/. ---type ConnectorGraphic u = ConnectorCF u (GraphicAns u)+newtype ConnectorImage u a = ConnectorImage { + getConnectorImage :: DPoint2 -> DPoint2 -> Image u a } -type DConnectorGraphic = ConnectorGraphic Double+type instance DUnit (ConnectorImage u a) = u +type ConnectorGraphic u = ConnectorImage u (UNil u) +-- | Type specialized version of 'ConnectorImage'.+--+type DConnectorImage a = ConnectorImage Double a++-- | Type specialized version of 'ConnectorGraphic'.+--+type DConnectorGraphic = ConnectorGraphic Double +++newtype ConnectorQuery u a = ConnectorQuery { + getConnectorQuery :: DPoint2 -> DPoint2 -> Query u a }+++-- Functor ++instance Functor (ConnectorImage u) where+ fmap f ma = ConnectorImage $ \p0 p1 -> fmap f $ getConnectorImage ma p0 p1++instance Functor (ConnectorQuery u) where+ fmap f ma = ConnectorQuery $ \p0 p1 -> fmap f $ getConnectorQuery ma p0 p1+++-- Applicative++instance Applicative (ConnectorImage u) where+ pure a = ConnectorImage $ \_ _ -> pure a+ mf <*> ma = ConnectorImage $ \p0 p1 -> + getConnectorImage mf p0 p1 <*> getConnectorImage ma p0 p1++instance Applicative (ConnectorQuery u) where+ pure a = ConnectorQuery $ \_ _ -> pure a+ mf <*> ma = ConnectorQuery $ \p0 p1 -> + getConnectorQuery mf p0 p1 <*> getConnectorQuery ma p0 p1+++-- Monad ++instance Monad (ConnectorImage u) where+ return a = ConnectorImage $ \_ _ -> return a+ ma >>= k = ConnectorImage $ \p0 p1 -> + getConnectorImage ma p0 p1 >>= \ans -> + getConnectorImage (k ans) p0 p1+++instance Monad (ConnectorQuery u) where+ return a = ConnectorQuery $ \_ _ -> return a+ ma >>= k = ConnectorQuery $ \p0 p1 -> + getConnectorQuery ma p0 p1 >>= \ans -> + getConnectorQuery (k ans) p0 p1+++-- Monoid++instance Monoid a => Monoid (ConnectorImage u a) where+ mempty = pure mempty+ ma `mappend` mb = ConnectorImage $ \p0 p1 -> + getConnectorImage ma p0 p1 + `mappend` getConnectorImage mb p0 p1 +++instance Monoid a => Monoid (ConnectorQuery u a) where+ mempty = pure mempty+ ma `mappend` mb = ConnectorQuery $ \p0 p1 -> + getConnectorQuery ma p0 p1 + `mappend` getConnectorQuery mb p0 p1 ++++-- DrawingCtxM++instance DrawingCtxM (ConnectorImage u) where+ askDC = ConnectorImage $ \_ _ -> askDC+ asksDC fn = ConnectorImage $ \_ _ -> asksDC fn+ localize upd ma = ConnectorImage $ \p0 p1 -> + localize upd (getConnectorImage ma p0 p1)++instance DrawingCtxM (ConnectorQuery u) where+ askDC = ConnectorQuery $ \_ _ -> askDC+ asksDC fn = ConnectorQuery $ \_ _ -> asksDC fn+ localize upd ma = ConnectorQuery $ \p0 p1 -> + localize upd (getConnectorQuery ma p0 p1)+++ +++instance Decorate ConnectorImage where+ decorate zo ma mz = ConnectorImage $ \p0 p1 -> + decorate zo (getConnectorImage ma p0 p1) (getConnectorImage mz p0 p1)++ elaborate zo ma f = ConnectorImage $ \p0 p1 -> + elaborate zo (getConnectorImage ma p0 p1) + (\a -> getConnectorImage (f a) p0 p1)++ obliterate ma = ConnectorImage $ \p0 p1 -> + obliterate $ getConnectorImage ma p0 p1++ hyperlink xl ma = ConnectorImage $ \p0 p1 -> + hyperlink xl $ getConnectorImage ma p0 p1++ svgId ss ma = ConnectorImage $ \p0 p1 -> + svgId ss $ getConnectorImage ma p0 p1++ svgAnnotate attrs ma = ConnectorImage $ \p0 p1 -> + svgAnnotate attrs $ getConnectorImage ma p0 p1+++++runConnectorImage :: InterpretUnit u + => DrawingContext -> Point2 u -> Point2 u+ -> ConnectorImage u a+ -> PrimResult u a+runConnectorImage ctx p0 p1 ma = + let dp0 = normalizeF (dc_font_size ctx) p0+ dp1 = normalizeF (dc_font_size ctx) p1 + in runImage ctx $ getConnectorImage ma dp0 dp1+++runConnectorQuery :: InterpretUnit u + => DrawingContext -> Point2 u -> Point2 u + -> ConnectorQuery u a+ -> a+runConnectorQuery ctx p0 p1 ma = + let dp0 = normalizeF (dc_font_size ctx) p0+ dp1 = normalizeF (dc_font_size ctx) p1 + in runQuery ctx $ getConnectorQuery ma dp0 dp1+++connect :: InterpretUnit u + => ConnectorImage u a -> Point2 u -> Point2 u -> Image u a+connect ma p0 p1 = normalizeCtxF p0 >>= \dp0 -> + normalizeCtxF p1 >>= \dp1 -> + getConnectorImage ma dp0 dp1+++stripConnectorImage :: ConnectorImage u a -> ConnectorQuery u a+stripConnectorImage ma = ConnectorQuery $ \p1 p2 -> + stripImage $ getConnectorImage ma p1 p2+++liftConnectorQuery :: ConnectorQuery u a -> ConnectorImage u a+liftConnectorQuery ma = ConnectorImage $ \p1 p2 -> + liftQuery $ getConnectorQuery ma p1 p2+++promoteConn :: InterpretUnit u + => (Point2 u -> Point2 u -> Image u a) -> ConnectorImage u a+promoteConn k = ConnectorImage $ \p0 p1 ->+ dinterpCtxF p0 >>= \up0 -> + dinterpCtxF p1 >>= \up1 -> + k up0 up1++applyConn :: InterpretUnit u + => ConnectorImage u a -> Point2 u -> Point2 u -> Image u a+applyConn ma p0 p1 = normalizeCtxF p0 >>= \dp0 -> + normalizeCtxF p1 >>= \dp1 -> + getConnectorImage ma dp0 dp1+++++qpromoteConn :: InterpretUnit u + => (Point2 u -> Point2 u -> Query u a) -> ConnectorQuery u a+qpromoteConn k = ConnectorQuery $ \p0 p1 ->+ dinterpCtxF p0 >>= \up0 -> + dinterpCtxF p1 >>= \up1 -> + k up0 up1++qapplyConn :: InterpretUnit u+ => ConnectorQuery u a -> Point2 u -> Point2 u -> Query u a+qapplyConn ma p0 p1 = normalizeCtxF p0 >>= \dp0 -> + normalizeCtxF p1 >>= \dp1 -> + getConnectorQuery ma dp0 dp1++ ----------------------------------------------------------------------------------- Connector Image+-- UConvert instance +instance UConvert ConnectorImage where+ uconvF = uconvConnectorImageF+ uconvZ = uconvConnectorImageZ --- | ConnectorImage is a connector drawn between two points --- constructing an Image.++-- | Use this to convert 'ConnectorGraphic' or 'ConnectorImage' +-- with Functor answer. ----- Usually the answer type of a ConnectorImage will be a Path so--- the Points ar @midway@, @atstart@ etc. can be taken on it.+uconvConnectorImageF :: (InterpretUnit u, InterpretUnit u1, Functor t) + => ConnectorImage u (t u) -> ConnectorImage u1 (t u1)+uconvConnectorImageF ma = ConnectorImage $ \p0 p1 -> + uconvF $ getConnectorImage ma p0 p1+++++-- | Use this to convert 'ConnectorImage' with unit-less answer. ---type ConnectorImage u a = ConnectorCF u (ImageAns u a)+uconvConnectorImageZ :: (InterpretUnit u, InterpretUnit u1) + => ConnectorImage u a -> ConnectorImage u1 a+uconvConnectorImageZ ma = ConnectorImage $ \p0 p1 -> + uconvZ $ getConnectorImage ma p0 p1 +-- | Having /empty/ at the specific 'ConnectorImage' type is useful.+-- +emptyConnectorImage :: Monoid a => ConnectorImage u a+emptyConnectorImage = mempty -type DConnectorImage a = ConnectorImage Double a +-------------------------------------------------------------------------------- ++--+-- Design note - potentially there are no useful combining +-- operators on Connectors (!).+--+-- Division - i.e. splitting a path at points between the start +-- and end - seems a more obvious operation on connector paths +-- than combination. See the ConnectorPath operations in +-- Wumpus-Drawing for some examples.+--
− src/Wumpus/Basic/Kernel/Objects/CtxPicture.hs
@@ -1,563 +0,0 @@-{-# LANGUAGE TypeFamilies #-}-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Basic.Kernel.Objects.CtxPicture--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : stephen.tetley@gmail.com--- Stability : unstable--- Portability : GHC ------ A Picture-with-implicit-context object. --- --- This is the corresponding type to Picture in the Wumpus-Core.--- --- CtxPicture is a function from the DrawingContext to a Picture.--- Internally the result is actually a (Maybe Picture) and not a --- Picture, this is a trick to promote the extraction from --- possibly empty drawings (created by TraceDrawing) to the --- top-level of the type hierarchy where client code can deal --- with empty drawings explicitly (empty Pictures cannot be --- rendered by Wumpus-Core).--- -----------------------------------------------------------------------------------module Wumpus.Basic.Kernel.Objects.CtxPicture- (-- CtxPicture- , DCtxPicture- , runCtxPicture- , runCtxPictureU- , drawTracing-- , clipCtxPicture- , mapCtxPicture-- -- * Composition- , over - , under-- , centric- , nextToH- , nextToV- - , atPoint - , centeredAt-- , zconcat-- , hcat - , vcat--- , hspace- , vspace- , hsep- , vsep- - -- * Compose with alignment- , alignH- , alignV- , alignHSep- , alignVSep- , hcatA- , vcatA- , hsepA- , vsepA--- ) where--import Wumpus.Basic.Kernel.Base.Anchors-import Wumpus.Basic.Kernel.Base.BaseDefs-import Wumpus.Basic.Kernel.Base.ContextFun-import Wumpus.Basic.Kernel.Base.DrawingContext-import Wumpus.Basic.Kernel.Objects.TraceDrawing--import Wumpus.Core -- package: wumpus-core--import Data.AdditiveGroup -- package: vector-space-import Data.AffineSpace--import Control.Applicative-import Data.List ( foldl' )----newtype CtxPicture u = CtxPicture { getCtxPicture :: CF (Maybe (Picture u)) }--type DCtxPicture = CtxPicture Double---type instance DUnit (CtxPicture u) = u-----runCtxPicture :: DrawingContext -> CtxPicture u -> Maybe (Picture u)-runCtxPicture ctx drw = runCF ctx (getCtxPicture drw) ---runCtxPictureU :: DrawingContext -> CtxPicture u -> Picture u-runCtxPictureU ctx df = maybe fk id $ runCtxPicture ctx df- where- fk = error "runCtxPictureU - empty CtxPicture." ----drawTracing :: (Real u, Floating u, FromPtSize u) - => TraceDrawing u a -> CtxPicture u-drawTracing mf = CtxPicture $ - drawingCtx >>= \ctx -> return (liftToPictureMb (execTraceDrawing ctx mf) )----- Note - cannot get an answer from a TraceDrawing with this --- CtxPicture type. There is nowhere to put the answer in the type.------ If the type was extended:------ > newtype CtxPicture u a = CtxPicture { getCtxPicture :: CF (a, Maybe (Picture u))) }------ It would make things difficult for the drawing composition --- operators. @a@ could be monoidial but are there any types of --- a where this would be useful (rather than just making things --- more complicated)? --------------------------------------------------------------------------------------clipCtxPicture :: (Num u, Ord u) => (PrimPath u) -> CtxPicture u -> CtxPicture u-clipCtxPicture cpath = mapCtxPicture (clip cpath)---mapCtxPicture :: (Picture u -> Picture u) -> CtxPicture u -> CtxPicture u-mapCtxPicture pf = CtxPicture . fmap (fmap pf) . getCtxPicture--instance (Real u, Floating u) => Rotate (CtxPicture u) where - rotate ang = mapCtxPicture (rotate ang)--instance (Real u, Floating u) => RotateAbout (CtxPicture u) where- rotateAbout r pt = mapCtxPicture (rotateAbout r pt)--instance (Num u, Ord u) => Scale (CtxPicture u) where- scale sx sy = mapCtxPicture (scale sx sy)--instance (Num u, Ord u) => Translate (CtxPicture u) where- translate dx dy = mapCtxPicture (translate dx dy)-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Extract anchors--boundaryExtr :: (BoundingBox u -> a) -> Picture u -> a-boundaryExtr f = f . boundary---- Operations on bounds---- | The center of a picture.----boundaryCtr :: Fractional u => Picture u -> Point2 u-boundaryCtr = boundaryExtr center------ | Extract the mid point of the top edge.----boundaryN :: Fractional u => Picture u -> Point2 u-boundaryN = boundaryExtr north---- | Extract the mid point of the bottom edge.----boundaryS :: Fractional u => Picture u -> Point2 u-boundaryS = boundaryExtr south---- | Extract the mid point of the left edge.----boundaryE :: Fractional u => Picture u -> Point2 u-boundaryE = boundaryExtr east---- | Extract the mid point of the right edge.----boundaryW :: Fractional u => Picture u -> Point2 u-boundaryW = boundaryExtr west----- | Extract the top-left corner.----boundaryNW :: Fractional u => Picture u -> Point2 u-boundaryNW = boundaryExtr northwest---- | Extract the top-right corner.----boundaryNE :: Picture u -> Point2 u-boundaryNE = boundaryExtr ur_corner---- | Extract the bottom-left corner.----boundarySW :: Picture u -> Point2 u-boundarySW = boundaryExtr ll_corner---- | Extract the bottom-right corner.----boundarySE :: Fractional u => Picture u -> Point2 u-boundarySE = boundaryExtr southeast---boundaryLeftEdge :: Picture u -> u-boundaryLeftEdge = boundaryExtr (point_x . ll_corner)--boundaryRightEdge :: Picture u -> u-boundaryRightEdge = boundaryExtr (point_x . ur_corner)--boundaryBottomEdge :: Picture u -> u-boundaryBottomEdge = boundaryExtr (point_y . ll_corner)---boundaryTopEdge :: Picture u -> u-boundaryTopEdge = boundaryExtr (point_y . ur_corner)----- --- Note - do not export the empty drawing. It is easier to --- pretend it doesn't exist.--- -empty_drawing :: (Real u, Floating u, FromPtSize u) => CtxPicture u-empty_drawing = drawTracing $ return ()---------------------------------------------------------------------------------------- Composition operators---drawingConcat :: (Picture u -> Picture u -> Picture u) - -> CtxPicture u -> CtxPicture u -> CtxPicture u-drawingConcat op a b = CtxPicture $ mbpostcomb op (getCtxPicture a) (getCtxPicture b)----mbpostcomb :: (a -> a -> a) -> CF (Maybe a) -> CF (Maybe a) -> CF (Maybe a)-mbpostcomb op = liftA2 fn - where- fn (Just a) (Just b) = Just $ a `op` b- fn a Nothing = a- fn Nothing b = b----- Note - the megaCombR operator is in some way an--- /anti-combinator/. It seems easier to think about composing --- drawings if we do work on the result Pictures directly rather --- than build combinators to manipulate CtxPictures.------ The idea of combining pre- and post- operating combinators--- makes me worry about circular programs even though I know --- lazy evaluation allows me to write them (in some cicumstances).-------- Picture /mega-combiner/ - moves only the second argument aka the --- right picture.----megaCombR :: (Num u, Ord u)- => (Picture u -> a) -> (Picture u -> a) - -> (a -> a -> Picture u -> Picture u) - -> CtxPicture u -> CtxPicture u- -> CtxPicture u-megaCombR qL qR trafoR = drawingConcat fn- where- fn pic1 pic2 = let a = qL pic1- b = qR pic2- p2 = trafoR a b pic2- in pic1 `picOver` p2------- | > a `over` b--- --- Place \'drawing\' a over b. The idea of @over@ here is in --- terms z-ordering, nither picture a or b are actually moved.----over :: (Num u, Ord u) => CtxPicture u -> CtxPicture u -> CtxPicture u-over = drawingConcat picOver------ | > a `under` b------ Similarly @under@ draws the first drawing behind --- the second but move neither.----under :: (Num u, Ord u) => CtxPicture u -> CtxPicture u -> CtxPicture u-under = flip over------ | Move in both the horizontal and vertical.----move :: (Num u, Ord u) => Vec2 u -> CtxPicture u -> CtxPicture u-move v = mapCtxPicture (\p -> p `picMoveBy` v)---------------------------------------------------------------------------------------- Composition--infixr 5 `nextToV`-infixr 6 `nextToH`, `centric`------- | Draw @a@, move @b@ so its center is at the same center as --- @a@, @b@ is drawn over underneath in the zorder.------ > a `centeric` b -------centric :: (Fractional u, Ord u) => CtxPicture u -> CtxPicture u -> CtxPicture u-centric = megaCombR boundaryCtr boundaryCtr moveFun- where- moveFun p1 p2 pic = let v = p1 .-. p2 in pic `picMoveBy` v------ | > a `nextToH` b--- --- Horizontal composition - move @b@, placing it to the right --- of @a@.--- -nextToH :: (Num u, Ord u) => CtxPicture u -> CtxPicture u -> CtxPicture u-nextToH = megaCombR boundaryRightEdge boundaryLeftEdge moveFun- where - moveFun a b pic = pic `picMoveBy` hvec (a - b)------ | > a `nextToV` b------ Vertical composition - move @b@, placing it below @a@.----nextToV :: (Num u, Ord u) => CtxPicture u -> CtxPicture u -> CtxPicture u-nextToV = megaCombR boundaryBottomEdge boundaryTopEdge moveFun- where - moveFun a b drw = drw `picMoveBy` vvec (a - b)----- | Place the picture at the supplied point.------ `atPoint` was previous the `at` operator.--- -atPoint :: (Num u, Ord u) => CtxPicture u -> Point2 u -> CtxPicture u-p `atPoint` (P2 x y) = move (V2 x y) p------ | Center the picture at the supplied point.----centeredAt :: (Fractional u, Ord u) => CtxPicture u -> Point2 u -> CtxPicture u-centeredAt d (P2 x y) = mapCtxPicture fn d- where- fn p = let bb = boundary p- dx = x - (boundaryWidth bb * 0.5)- dy = y - (boundaryHeight bb * 0.5)- in p `picMoveBy` vec dx dy----- | Concatenate the list of drawings. ------ No pictures are moved. ----zconcat :: (Real u, Floating u, FromPtSize u) => [CtxPicture u] -> CtxPicture u-zconcat [] = empty_drawing-zconcat (d:ds) = foldl' over d ds------- | Concatenate the list pictures @xs@ horizontally.--- -hcat :: (Real u, Floating u, FromPtSize u) => [CtxPicture u] -> CtxPicture u-hcat [] = empty_drawing-hcat (d:ds) = foldl' nextToH d ds----- | Concatenate the list of pictures @xs@ vertically.----vcat :: (Real u, Floating u, FromPtSize u) => [CtxPicture u] -> CtxPicture u-vcat [] = empty_drawing-vcat (d:ds) = foldl' nextToV d ds-------------------------------------------------------------------------------------------- | > hspace n a b------ Horizontal composition - move @b@, placing it to the right --- of @a@ with a horizontal gap of @n@ separating the pictures.----hspace :: (Num u, Ord u) => u -> CtxPicture u -> CtxPicture u -> CtxPicture u-hspace n = megaCombR boundaryRightEdge boundaryLeftEdge moveFun- where- moveFun a b pic = pic `picMoveBy` hvec (n + a - b)-- ------ | > vspace n a b------ Vertical composition - move @b@, placing it below @a@ with a--- vertical gap of @n@ separating the pictures.----vspace :: (Num u, Ord u) => u -> CtxPicture u -> CtxPicture u -> CtxPicture u-vspace n = megaCombR boundaryBottomEdge boundaryTopEdge moveFun- where - moveFun a b pic = pic `picMoveBy` vvec (a - b - n)------ | > hsep n xs------ Concatenate the list of pictures @xs@ horizontally with --- @hspace@ starting at @x@. The pictures are interspersed with --- spaces of @n@ units.----hsep :: (Real u, Floating u, FromPtSize u) => u -> [CtxPicture u] -> CtxPicture u-hsep _ [] = empty_drawing-hsep n (d:ds) = foldl' (hspace n) d ds------ | > vsep n xs------ Concatenate the list of pictures @xs@ vertically with --- @vspace@ starting at @x@. The pictures are interspersed with --- spaces of @n@ units.----vsep :: (Real u, Floating u, FromPtSize u) => u -> [CtxPicture u] -> CtxPicture u-vsep _ [] = empty_drawing-vsep n (d:ds) = foldl' (vspace n) d ds-------------------------------------------------------------------------------------- Aligning pictures--alignMove :: (Num u, Ord u) => Point2 u -> Point2 u -> Picture u -> Picture u-alignMove p1 p2 pic = pic `picMoveBy` (p1 .-. p2)------ | > alignH align a b--- --- Horizontal composition - move @b@, placing it to the right --- of @a@ and align it with the top, center or bottom of @a@.--- -alignH :: (Fractional u, Ord u) - => HAlign -> CtxPicture u -> CtxPicture u -> CtxPicture u-alignH HTop = megaCombR boundaryNE boundaryNW alignMove-alignH HCenter = megaCombR boundaryE boundaryW alignMove-alignH HBottom = megaCombR boundarySE boundarySW alignMove----- | > alignV align a b--- --- Vertical composition - move @b@, placing it below @a@ --- and align it with the left, center or right of @a@.--- -alignV :: (Fractional u, Ord u) - => VAlign -> CtxPicture u -> CtxPicture u -> CtxPicture u-alignV VLeft = megaCombR boundarySW boundaryNW alignMove-alignV VCenter = megaCombR boundaryS boundaryN alignMove-alignV VRight = megaCombR boundarySE boundaryNE alignMove----alignMove2 :: (Num u, Ord u) - => Vec2 u -> Point2 u -> Point2 u -> Picture u -> Picture u-alignMove2 v p1 p2 pic = pic `picMoveBy` (v ^+^ (p1 .-. p2))------ | > alignHSep align sep a b--- --- Spacing version of alignH - move @b@ to the right of @a@ --- separated by @sep@ units, align @b@ according to @align@.--- -alignHSep :: (Fractional u, Ord u) - => HAlign -> u -> CtxPicture u -> CtxPicture u -> CtxPicture u-alignHSep HTop dx = megaCombR boundaryNE boundaryNW (alignMove2 (hvec dx))-alignHSep HCenter dx = megaCombR boundaryE boundaryW (alignMove2 (hvec dx))-alignHSep HBottom dx = megaCombR boundarySE boundarySW (alignMove2 (hvec dx))----- | > alignVSep align sep a b--- --- Spacing version of alignV - move @b@ below @a@ --- separated by @sep@ units, align @b@ according to @align@.--- -alignVSep :: (Fractional u, Ord u) - => VAlign -> u -> CtxPicture u -> CtxPicture u -> CtxPicture u-alignVSep VLeft dy = megaCombR boundarySW boundaryNW (alignMove2 $ vvec (-dy)) -alignVSep VCenter dy = megaCombR boundaryS boundaryN (alignMove2 $ vvec (-dy)) -alignVSep VRight dy = megaCombR boundarySE boundaryNE (alignMove2 $ vvec (-dy))----- | Variant of 'hcat' that aligns the pictures as well as--- concatenating them.----hcatA :: (Real u, Floating u, FromPtSize u) - => HAlign -> [CtxPicture u] -> CtxPicture u-hcatA _ [] = empty_drawing-hcatA ha (d:ds) = foldl' (alignH ha) d ds------ | Variant of 'vcat' that aligns the pictures as well as--- concatenating them.----vcatA :: (Real u, Floating u, FromPtSize u) - => VAlign -> [CtxPicture u] -> CtxPicture u-vcatA _ [] = empty_drawing-vcatA va (d:ds) = foldl' (alignV va) d ds----- | Variant of @hsep@ that aligns the pictures as well as--- concatenating and spacing them.----hsepA :: (Real u, Floating u, FromPtSize u) - => HAlign -> u -> [CtxPicture u] -> CtxPicture u-hsepA _ _ [] = empty_drawing-hsepA ha n (d:ds) = foldl' op d ds- where - a `op` b = alignHSep ha n a b ----- | Variant of @vsep@ that aligns the pictures as well as--- concatenating and spacing them.----vsepA :: (Real u, Floating u, FromPtSize u) - => VAlign -> u -> [CtxPicture u] -> CtxPicture u-vsepA _ _ [] = empty_drawing-vsepA va n (d:ds) = foldl' op d ds- where - a `op` b = alignVSep va n a b ---
+ src/Wumpus/Basic/Kernel/Objects/Displacement.hs view
@@ -0,0 +1,358 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.Kernel.Objects.Displacement+-- Copyright : (c) Stephen Tetley 2010-2011+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : highly unstable+-- Portability : GHC +--+-- Displacing points - often start points. +--+--------------------------------------------------------------------------------++module Wumpus.Basic.Kernel.Objects.Displacement+ (+++ -- * Displacement+ PointDisplace+ , ThetaPointDisplace+ , displace++ , dispParallel+ , dispPerpendicular+ , dispOrtho++ , dispDirectionTheta+ , dispCardinalTheta++ -- * Named vector constructors++ , go_up+ , go_down+ , go_left+ , go_right++ , go_north+ , go_south+ , go_east+ , go_west+ , go_north_east+ , go_north_west+ , go_south_east+ , go_south_west+ + , go_up_left+ , go_up_right+ , go_down_left+ , go_down_right++ , theta_up+ , theta_down+ , theta_left+ , theta_right++ , theta_north+ , theta_south+ , theta_east+ , theta_west+ , theta_north_east+ , theta_north_west+ , theta_south_east+ , theta_south_west++ , theta_up_left+ , theta_up_right+ , theta_down_left+ , theta_down_right++ , theta_adj_grazing+ , theta_bkwd_adj_grazing+++ ) where+++import Wumpus.Basic.Kernel.Base.BaseDefs++import Wumpus.Core -- package: wumpus-core++import Data.AffineSpace -- package: vector-space+++++-- | 'PointDisplace' is a type representing functions +-- @from Point to Point@.+--+-- It is especially useful for building composite graphics where +-- one part of the graphic is drawn from a different start point +-- to the other part.+--+type PointDisplace u = Point2 u -> Point2 u+++++-- | 'ThetaPointDisplace' is a type representing functions +-- @from Radian * Point to Point@.+--+-- It is useful for building arrowheads which are constructed +-- with an implicit angle representing the direction of the line +-- at the arrow tip.+--+type ThetaPointDisplace u = Radian -> Point2 u -> Point2 u++++-- | 'displace' : @ Vec2 -> PointDisplace @+--+-- Alias for @.+^@ from @Data.AffineSpace@.+--+displace :: Num u => Vec2 u -> PointDisplace u+displace (V2 dx dy) (P2 x y) = P2 (x+dx) (y+dy)+++++++++--------------------------------------------------------------------------------+-- ThetaPointDisplace functions+++-- | 'dispParallel' : @ dist -> ThetaPointDisplace @+-- +-- Build a combinator to move @Points@ in parallel to the +-- direction of the implicit angle by the supplied distance +-- @dist@. +--+dispParallel :: Floating u => u -> ThetaPointDisplace u+dispParallel d = \theta pt -> pt .+^ avec (circularModulo theta) d+++-- | 'dispParallel' : @ dist -> ThetaPointDisplace @+-- +-- Build a combinator to move @Points@ perpendicular to the +-- inclnation of the implicit angle by the supplied distance +-- @dist@. +--+dispPerpendicular :: Floating u => u -> ThetaPointDisplace u+dispPerpendicular d = + \theta pt -> pt .+^ avec (circularModulo $ theta + (0.5*pi)) d++++-- | 'dispOrtho' : @ vec -> ThetaPointDisplace @+-- +-- This is a combination of @displaceParallel@ and +-- @displacePerpendicular@, with the x component of the vector+-- displaced in parallel and the y component displaced+-- perpendicular. +-- +dispOrtho :: Floating u => u -> u -> ThetaPointDisplace u+dispOrtho x y = \theta -> dispParallel x theta . dispPerpendicular y theta+++++-- | /Angular/ version of 'dispDirection'. +--+-- The displacement direction is with respect to implicit angle+-- of inclination, so:+--+-- > up == perpendicular+-- > down == perdendicular . negate+-- > left == parallel . negate+-- > right == parallel+-- +dispDirectionTheta :: Floating u => Direction -> u -> ThetaPointDisplace u+dispDirectionTheta UP = dispPerpendicular+dispDirectionTheta DOWN = dispPerpendicular . negate+dispDirectionTheta LEFT = dispParallel . negate+dispDirectionTheta RIGHT = dispParallel+++-- | /Angular/ version of 'dispCardinal'.+--+-- The displacement direction is with respect to implicit angle+-- of inclination, so:+--+-- > north == perpendicular+-- > east == parallel+-- > south == perdendicular . negate+-- > etc.+-- +dispCardinalTheta :: Floating u => Cardinal -> u -> ThetaPointDisplace u+dispCardinalTheta NORTH = dispPerpendicular+dispCardinalTheta NORTH_EAST = \d ang -> displace (avec (ang + (0.25*pi)) d)+dispCardinalTheta EAST = dispParallel+dispCardinalTheta SOUTH_EAST = \d ang -> displace (avec (ang + (1.75*pi)) d)+dispCardinalTheta SOUTH = dispPerpendicular . negate+dispCardinalTheta SOUTH_WEST = \d ang -> displace (avec (ang + (1.25*pi)) d)+dispCardinalTheta WEST = dispParallel . negate+dispCardinalTheta NORTH_WEST = \d ang -> displace (avec (ang + (0.75*pi)) d)+++--------------------------------------------------------------------------------+-- Named vectors+++go_up :: Num u => u -> Vec2 u+go_up d = V2 0 d++go_down :: Num u => u -> Vec2 u+go_down d = V2 0 (-d)++go_left :: Num u => u -> Vec2 u+go_left d = V2 (-d) 0++go_right :: Num u => u -> Vec2 u+go_right d = V2 d 0+++go_north :: Num u => u -> Vec2 u+go_north = go_up++go_south :: Num u => u -> Vec2 u+go_south = go_down++go_east :: Num u => u -> Vec2 u+go_east = go_right++go_west :: Num u => u -> Vec2 u+go_west = go_left+++go_north_east :: Floating u => u -> Vec2 u+go_north_east = avec (0.25*pi)++go_north_west :: Floating u => u -> Vec2 u+go_north_west = avec (0.75*pi)++go_south_east :: Floating u => u -> Vec2 u+go_south_east = avec (1.75*pi)++go_south_west :: Floating u => u -> Vec2 u+go_south_west = avec (1.25*pi)+++go_up_left :: Num u => u -> Vec2 u+go_up_left d = V2 (-d) d++go_up_right :: Num u => u -> Vec2 u+go_up_right d = V2 d d++go_down_left :: Num u => u -> Vec2 u+go_down_left d = V2 (-d) (-d)++go_down_right :: Num u => u -> Vec2 u+go_down_right d = V2 d (-d)+++--------------------------------------------------------------------------------++++theta_up :: Floating u => u -> Radian -> Vec2 u+theta_up d ang = avec (ang + half_pi) d++theta_down :: Floating u => u -> Radian -> Vec2 u+theta_down d ang = avec (ang - half_pi) d+++-- | Parallel (reverse)+--+theta_left :: Floating u => u -> Radian -> Vec2 u+theta_left d ang = avec (ang + pi) d++-- | Parallel (forward)+--+theta_right :: Floating u => u -> Radian -> Vec2 u+theta_right d ang = avec ang d++++theta_north :: Floating u => u -> Radian -> Vec2 u+theta_north = theta_up++theta_south :: Floating u => u -> Radian -> Vec2 u+theta_south = theta_down++theta_east :: Floating u => u -> Radian -> Vec2 u+theta_east = theta_right++theta_west :: Floating u => u -> Radian -> Vec2 u+theta_west = theta_left+++theta_north_east :: Floating u => u -> Radian -> Vec2 u+theta_north_east d ang = avec (ang + quarter_pi) d++theta_north_west :: Floating u => u -> Radian -> Vec2 u+theta_north_west d ang = avec (ang + 0.75*pi) d++theta_south_east :: Floating u => u -> Radian -> Vec2 u+theta_south_east d ang = avec (ang - quarter_pi) d++theta_south_west :: Floating u => u -> Radian -> Vec2 u+theta_south_west d ang = avec (ang + 1.25*pi) d++++theta_up_left :: Floating u => u -> Radian -> Vec2 u+theta_up_left d = orthoVec (-d) d++theta_up_right :: Floating u => u -> Radian -> Vec2 u+theta_up_right d = orthoVec d d++theta_down_left :: Floating u => u -> Radian -> Vec2 u+theta_down_left d = orthoVec (-d) (-d)++theta_down_right :: Floating u => u -> Radian -> Vec2 u+theta_down_right d = orthoVec d (-d)+++++-- | Return @a-o@ when supplied length of @b-o@ and the grazing +-- angle @boa@:+--+-- > a+-- > .\+-- > . \+-- > ..b..o+--+-- This is useful for building arrowhead vectors.+--+theta_adj_grazing :: Floating u => u -> Radian -> Radian -> Vec2 u +theta_adj_grazing adj_len ang theta = orthoVec adj_len (-opp) theta+ where+ opp = adj_len * (fromRadian $ tan ang)+++-- | Return @o-c@ when supplied length of @b-o@ and the grazing +-- angle @boc@:+--+--+-- > ..b..o+-- > . /+-- > ./+-- > c+--+-- This is useful for building arrowhead vectors.+--+theta_bkwd_adj_grazing :: Floating u => u -> Radian -> Radian -> Vec2 u +theta_bkwd_adj_grazing adj_len ang theta = orthoVec (-adj_len) (-opp) theta+ where+ opp = adj_len * (fromRadian $ tan ang)++
+ src/Wumpus/Basic/Kernel/Objects/DrawingPrimitives.hs view
@@ -0,0 +1,614 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.Kernel.Objects.DrawingPrimitives+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : highly unstable+-- Portability : GHC +--+-- Primitive drawings - text, paths, lines, rectangles, disks, +-- ellipses...+--+-- All the primitives take their drawing properties (colour, line +-- width, etc.) from the DrawingContext.+--+--------------------------------------------------------------------------------++module Wumpus.Basic.Kernel.Objects.DrawingPrimitives+ (+++ -- * Prim Paths+ locPP++ , emptyLocPP+ , vertexPP+ , curvePP+++ , dcPath+ , dcOpenPath+ , dcClosedPath++ -- * Text+ , dcTextlabel+ , dcRTextlabel+ , dcEscapedlabel+ , dcREscapedlabel++ , hkernLine+ , vkernLine++ -- * Lines+ , straightLine+ , locStraightLine+ , curvedLine+ , straightConnector++ -- * Circles+ , dcCircle++ -- * Ellipses+ , dcEllipse+ , dcREllipse+++ -- * Rectangles+ , dcRectangle++ -- * Disks + , dcDisk+ , dcEllipseDisk++ -- * Arc+ , dcArc+++ ) where++import Wumpus.Basic.Kernel.Base.BaseDefs+import Wumpus.Basic.Kernel.Base.QueryDC+import Wumpus.Basic.Kernel.Base.WrappedPrimitive+import Wumpus.Basic.Kernel.Objects.Connector+import Wumpus.Basic.Kernel.Objects.Image+import Wumpus.Basic.Kernel.Objects.LocImage+import Wumpus.Basic.Kernel.Objects.LocThetaImage++import Wumpus.Core -- package: wumpus-core++import Control.Applicative+++-- Helpers+++textPrim :: (RGBi -> FontAttr -> Primitive) -> Graphic u+textPrim fn = + textAttr >>= \(rgb,attr) -> primGraphic (prim1 $ fn rgb attr)+ +++strokePrim :: (RGBi -> StrokeAttr -> Primitive) -> Graphic u+strokePrim fn = + strokeAttr >>= \(rgb,attr) -> primGraphic (prim1 $ fn rgb attr)+ ++fillPrim :: (RGBi -> Primitive) -> Graphic u+fillPrim fn = + fillAttr >>= \rgb -> primGraphic (prim1 $ fn rgb)++fillStrokePrim :: (RGBi -> StrokeAttr -> RGBi -> Primitive) + -> Graphic u+fillStrokePrim fn = + borderedAttr >>= \(frgb,attr,srgb) -> + primGraphic (prim1 $ fn frgb attr srgb)+++textLoc :: InterpretUnit u + => (RGBi -> FontAttr -> DPoint2 -> Primitive) -> LocGraphic u+textLoc fn = promoteLoc $ \pt -> + normalizeCtxF pt >>= \dpt -> textPrim (\rgb attr -> fn rgb attr dpt)+++strokeLoc :: InterpretUnit u+ => (RGBi -> StrokeAttr -> DPoint2 -> Primitive) -> LocGraphic u+strokeLoc fn = promoteLoc $ \pt -> + normalizeCtxF pt >>= \dpt -> strokePrim (\rgb attr -> fn rgb attr dpt)++fillLoc :: InterpretUnit u + => (RGBi -> DPoint2 -> Primitive) -> LocGraphic u+fillLoc fn = promoteLoc $ \pt ->+ normalizeCtxF pt >>= \dpt -> fillPrim (\rgb -> fn rgb dpt)++fillStrokeLoc :: InterpretUnit u+ => (RGBi -> StrokeAttr -> RGBi -> DPoint2 -> Primitive) + -> LocGraphic u+fillStrokeLoc fn = promoteLoc $ \pt ->+ normalizeCtxF pt >>= \dpt -> + fillStrokePrim (\frgb attr srgb -> fn frgb attr srgb dpt) + +++textLocTheta :: InterpretUnit u + => (RGBi -> FontAttr -> DPoint2 -> Radian -> Primitive) + -> LocThetaGraphic u+textLocTheta fn = promoteLocTheta $ \pt ang -> + normalizeCtxF pt >>= \dpt -> textPrim (\rgb attr -> fn rgb attr dpt ang)+++++--------------------------------------------------------------------------------+-- Paths++-- Note - naming convention, the PP suffix is to avoid confusion +-- with the Path data type in Wumpus-Drawing. These paths are+-- considered more /internal/.+--++-- | 'locPP' : @ [next_vector] -> LocImage PrimPath @+--+-- Create a path /query/ - i.e. a functional type +-- /from Point to PrimPath/.+-- +-- This is the analogue to 'vectorPath' in @Wumpus-Core@, but the +-- result is produced /within/ the 'DrawingContext'.+--+locPP :: InterpretUnit u => [Vec2 u] -> LocQuery u PrimPath+locPP vs = qpromoteLoc $ \pt -> + vectorPrimPath <$> normalizeCtxF pt <*> mapM normalizeCtxF vs+++++-- | 'emptyLocPP' : @ (Point ~> PrimPath) @+--+-- Create an empty path /query/ - i.e. a functional type +-- /from Point to PrimPath/.+--+-- This is the analogue to 'emptyPath' in @Wumpus-Core@, but the+-- result is produced /within/ the 'DrawingContext'.+--+emptyLocPP :: InterpretUnit u => LocQuery u PrimPath+emptyLocPP = locPP []+++++-- | 'vertexPP' : @ (Point ~> PrimPath) @+--+-- Create a PrimPath made of straight line segments joining the +-- supplied points.+--+-- This is the analogue to 'vertexPrimPath' in @Wumpus-Core@, but +-- it is polymorphic on unit.+--+vertexPP :: InterpretUnit u => [Point2 u] -> Query u PrimPath+vertexPP xs = vertexPrimPath <$> mapM normalizeCtxF xs++++-- | 'curvePP' : @ (Point ~> PrimPath) @+--+-- Create a path made of curve segments joining the +-- supplied points.+--+-- This is the analogue to 'curvedPrimPath' in @Wumpus-Core@, but +-- it is polymorphic on unit.+--+curvePP :: InterpretUnit u => [Point2 u] -> Query u PrimPath+curvePP xs = curvedPrimPath <$> mapM normalizeCtxF xs+++--------------------------------------------------------------------------------++--+-- Drawing paths (stroke, fill, fillStroke)...+--++dcPath :: PathMode -> PrimPath -> Graphic u+dcPath OSTROKE = dcOpenPath+dcPath CSTROKE = dcClosedPath DRAW_STROKE+dcPath CFILL = dcClosedPath DRAW_FILL+dcPath CFILL_STROKE = dcClosedPath DRAW_FILL_STROKE++++-- | 'dcOpenPath' : @ path -> Graphic @+--+-- This is the analogue to the 'ostroke' function in +-- @Wumpus-Core@, but the drawing properties (colour, line width, +-- etc.) are taken from the implicit 'DrawingContext'.+--+dcOpenPath :: PrimPath -> Graphic u+dcOpenPath pp = strokePrim (\rgb attr -> ostroke rgb attr pp)++++-- | 'dcClosedPath' : @ DrawStyle * path -> Graphic @+--+-- Draw a closed path according to the supplied DrawStyle+-- ( fill | stroke | fill_stroke). +---+-- Drawing properties (colour, line width, etc.) for the +-- respective style are taken from the implicit 'DrawingContext'.+--+dcClosedPath :: DrawMode -> PrimPath -> Graphic u+dcClosedPath DRAW_FILL pp = fillPrim (\rgb -> fill rgb pp)++dcClosedPath DRAW_STROKE pp = strokePrim (\rgb attr -> cstroke rgb attr pp)++dcClosedPath DRAW_FILL_STROKE pp = + fillStrokePrim (\frgb attr srgb -> fillStroke frgb attr srgb pp)+++--------------------------------------------------------------------------------+-- Text++-- | 'dcTextlabel' : @ string -> LocGraphic @+-- +-- Create a text 'LocGraphic' - i.e. a functional type +-- /from Point to Graphic/.+--+-- The implicit point of the LocGraphic is the baseline left.+--+-- This is the analogue to 'textlabel' in @Wumpus-core@, but the+-- text properties (font family, font size, colour) are taken from+-- the implicit 'DrawingContext'.+--+dcTextlabel :: InterpretUnit u => String -> LocGraphic u+dcTextlabel ss = textLoc (\rgb attr pt -> textlabel rgb attr ss pt)++++++-- | 'dcRTextlabel' : @ string -> LocThetaGraphic @+-- +-- Create a text 'LocThetaGraphic' - i.e. a functional type +-- /from Point and Angle to Graphic/.+--+-- The implicit point of the LocGraphic is the baseline left, the+-- implicit angle is rotation factor of the text.+--+-- Note - rotated text often does not render well in PostScript or+-- SVG. Rotated text should be used sparingly.+-- +-- This is the analogue to 'rtextlabel' in @Wumpus-core@.+--+dcRTextlabel :: InterpretUnit u => String -> LocThetaGraphic u+dcRTextlabel ss =+ textLocTheta (\rgb attr pt ang -> rtextlabel rgb attr ss ang pt)+++-- | 'dcEscapedlabel' : @ escaped_text -> LocGraphic @+-- +-- Create a text 'LocGraphic' - i.e. a functional type +-- /from Point to Graphic/.+--+-- The implicit point of the LocGraphic is the baseline left.+--+-- This is the analogue to 'escapedlabel' in @Wumpus-core@, but +-- the text properties (font family, font size, colour) are taken +-- from the implicit 'DrawingContext'.+--+dcEscapedlabel :: InterpretUnit u => EscapedText -> LocGraphic u+dcEscapedlabel esc = + textLoc (\rgb attr pt -> escapedlabel rgb attr esc pt)++++-- | 'dcREscapedlabel' : @ escaped_text -> LocThetaGraphic @+-- +-- Create a text 'LocThetaGraphic' - i.e. a functional type +-- /from Point and Angle to Graphic/.+--+-- The implicit point of the LocGraphic is the baseline left, the+-- implicit angle is rotation factor of the text.+--+-- Note - rotated text often does not render well in PostScript or+-- SVG. Rotated text should be used sparingly.+-- +-- This is the analogue to 'rescapedlabel' in @Wumpus-core@, but+-- the text properties (font family, font size, colour) are taken +-- from the implicit 'DrawingContext'.+--+dcREscapedlabel :: InterpretUnit u => EscapedText -> LocThetaGraphic u+dcREscapedlabel esc = + textLocTheta (\rgb attr pt ang -> rescapedlabel rgb attr esc ang pt)+++++uconvKernChar :: InterpretUnit u => [KernChar u] -> Query u [KerningChar]+uconvKernChar = mapM mf+ where+ mf (u,ch) = (\u1 -> (u1,ch)) <$> normalizeCtx u++++-- | 'hkernLine' : @ [kern_char] -> LocGraphic @+-- +-- Create a horizontally kerned text 'LocGraphic' - i.e. a +-- functional type /from Point to Graphic/.+--+-- The implicit point of the LocGraphic is the baseline left.+--+-- This is the analogue to 'hkernlabel' in @Wumpus-core@, but +-- the text properties (font family, font size, colour) are taken +-- from the implicit 'DrawingContext'.+--+hkernLine :: InterpretUnit u => [KernChar u] -> LocGraphic u+hkernLine ks = promoteLoc $ \pt -> + normalizeCtxF pt >>= \dpt -> liftQuery (uconvKernChar ks) >>= body dpt+ where+ body pt ans = textPrim (\rgb attr -> hkernlabel rgb attr ans pt)++++-- | 'vkernLine' : @ [kern_char] -> LocGraphic @+-- +-- Create a vertically kerned text 'LocGraphic' - i.e. a +-- functional type /from Point to Graphic/.+--+-- The implicit point of the LocGraphic is the baseline left.+--+-- This is the analogue to 'vkernlabel' in @Wumpus-core@, but +-- the text properties (font family, font size, colour) are taken +-- from the implicit 'DrawingContext'.+--+vkernLine :: InterpretUnit u => [KernChar u] -> LocGraphic u+vkernLine ks = promoteLoc $ \pt -> + normalizeCtxF pt >>= \dpt -> liftQuery (uconvKernChar ks) >>= body dpt+ where+ body pt ans = textPrim (\rgb attr -> vkernlabel rgb attr ans pt)++--------------------------------------------------------------------------------+-- Lines++-- | 'straightLine' : @ start_point * end_point -> LocGraphic @ +-- +-- Create a straight line 'Graphic', the start and end point +-- are supplied explicitly.+-- +-- The line properties (colour, pen thickness, etc.) are taken +-- from the implicit 'DrawingContext'.+-- +straightLine :: InterpretUnit u => Point2 u -> Point2 u -> Graphic u+straightLine p1 p2 = liftQuery (vertexPP [p1,p2]) >>= dcOpenPath+++-- | 'locStraightLine' : @ vec_to -> LocGraphic @ +--+-- Create a stright line 'LocGraphic' - i.e. a functional type +-- /from Point to Graphic/.+--+-- The implicit point of the LocGraphic is the start point, the +-- end point is calculated by displacing the start point with the +-- supplied vector.+--+-- The line properties (colour, pen thickness, etc.) are taken +-- from the implicit 'DrawingContext'.+-- +locStraightLine :: InterpretUnit u => Vec2 u -> LocGraphic u+locStraightLine v = promoteLoc $ \pt -> + liftQuery (qapplyLoc (locPP [v]) pt) >>= dcOpenPath++++-- | 'curveLine' : @ start_point * control_point1 * +-- control_point2 * end_point -> Graphic @ +-- +-- Create a Bezier curve 'Graphic', all control points are +-- supplied explicitly.+-- +-- The line properties (colour, pen thickness, etc.) are taken +-- from the implicit 'DrawingContext'.+-- +curvedLine :: InterpretUnit u+ => Point2 u -> Point2 u -> Point2 u -> Point2 u -> Graphic u+curvedLine p0 p1 p2 p3 = liftQuery (curvePP [p0,p1,p2,p3]) >>= dcOpenPath+++++-- | 'straightConnector' : @ start_point * end_point -> Connector @ +-- +-- Create a straight line 'Graphic', the start and end point +-- are supplied implicitly.+-- +-- The line properties (colour, pen thickness, etc.) are taken +-- from the implicit 'DrawingContext'.+-- +straightConnector :: InterpretUnit u => ConnectorGraphic u+straightConnector = promoteConn $ \p0 p1 -> + liftQuery (vertexPP [p0,p1]) >>= dcOpenPath++++--------------------------------------------------------------------------------+-- Circles++-- | Helper for circle drawing.+--+circlePath :: InterpretUnit u + => u -> LocQuery u PrimPath+circlePath r = qpromoteLoc $ \pt -> + (\dr dpt -> curvedPrimPath $ bezierCircle dr dpt) + <$> normalizeCtx r <*> normalizeCtxF pt+++-- | 'dcCircle' : @ DrawStyle * radius -> LocGraphic @+--+-- Create a circle 'LocGraphic' - the implicit point is +-- center. The circle is drawn with four Bezier curves. +-- +-- The respective line or fill properties for the 'DrawStyle' are +-- taken from the implicit 'DrawingContext'.+-- +dcCircle :: InterpretUnit u => DrawMode -> u -> LocGraphic u+dcCircle style r = promoteLoc $ \pt -> + liftQuery (qapplyLoc (circlePath r) pt) >>= dcClosedPath style++++--------------------------------------------------------------------------------+-- Ellipses+++-- | Helper for ellipse drawing.+--+ellipsePath :: InterpretUnit u + => u -> u -> LocQuery u PrimPath+ellipsePath rx ry = qpromoteLoc $ \pt -> + (\drx dry dpt -> curvedPrimPath $ bezierEllipse drx dry dpt) + <$> normalizeCtx rx <*> normalizeCtx ry <*> normalizeCtxF pt+++-- | Helper for ellipse drawing.+--+rellipsePath :: InterpretUnit u + => u -> u -> LocThetaQuery u PrimPath+rellipsePath rx ry = qpromoteLocTheta $ \pt ang ->+ (\drx dry dpt -> curvedPrimPath $ rbezierEllipse drx dry ang dpt) + <$> normalizeCtx rx <*> normalizeCtx ry <*> normalizeCtxF pt+++-- | 'strokedEllipse' : @ x_radius * y_radius -> LocGraphic @+--+-- Create a stroked ellipse 'LocGraphic' - the implicit point is +-- center. The ellipse is drawn with four Bezier curves. +-- +-- The line properties (colour, pen thickness, etc.) are taken +-- from the implicit 'DrawingContext'.+-- +dcEllipse :: InterpretUnit u => DrawMode -> u -> u -> LocGraphic u+dcEllipse style rx ry = promoteLoc $ \pt ->+ liftQuery (qapplyLoc (ellipsePath rx ry) pt) >>= dcClosedPath style +++-- | 'dcREllipse' : @ x_radius * y_radius -> LocGraphic @+--+-- Create a bordered ellipse 'LocThetaGraphic' - the implicit point+-- is center and the angle is rotation about the center. The +-- ellipse is drawn with four Bezier curves. +-- +-- The background fill colour and the outline stroke properties +-- are taken from the implicit 'DrawingContext'.+-- +dcREllipse :: InterpretUnit u+ => DrawMode -> u -> u -> LocThetaGraphic u+dcREllipse style rx ry = promoteLocTheta $ \pt ang -> + liftQuery (qapplyLocTheta (rellipsePath rx ry) pt ang) >>= + dcClosedPath style+++-- Note - clipping needs some higher level path object than is defined here.++--------------------------------------------------------------------------------+-- Rectangles++-- | Supplied point is /bottom-left/.+--+rectanglePath :: InterpretUnit u + => u -> u -> LocQuery u PrimPath+rectanglePath w h = locPP [hvec w, vvec h, hvec (-w)]++++-- | 'strokedRectangle' : @ style * width * height -> LocGraphic @+--+-- Create a stroked rectangle 'LocGraphic' - the implicit point is +-- bottom-left. +-- +-- The line properties (colour, pen thickness, etc.) are taken +-- from the implicit 'DrawingContext'.+-- +dcRectangle :: InterpretUnit u => DrawMode -> u -> u -> LocGraphic u+dcRectangle style w h = promoteLoc $ \pt -> + liftQuery (qapplyLoc (rectanglePath w h) pt) >>= dcClosedPath style+++---------------------------------------------------------------------------++-- | 'dcDisk' : @ radius -> LocGraphic @+--+-- Create a circle 'LocGraphic' - the implicit point is the +-- center. +-- +-- This is a efficient representation of circles using +-- PostScript\'s @arc@ or SVG\'s @circle@ in the generated +-- output. However, stroked-circles do not draw well after +-- non-uniform scaling - the pen width is scaled as well as +-- the shape.+--+-- For stroked circles that can be adequately scaled, use +-- 'dcCircle' instead.+--+-- The fill or stroke properties for the respective DrawStyle are+-- taken from the implicit 'DrawingContext'.+-- +dcDisk :: InterpretUnit u => DrawMode -> u -> LocGraphic u+dcDisk DRAW_FILL radius = + normalizeCtx radius >>= \r -> + fillLoc (\rgb pt -> fillEllipse rgb r r pt)++dcDisk DRAW_STROKE radius = + normalizeCtx radius >>= \r -> + strokeLoc (\rgb attr pt -> strokeEllipse rgb attr r r pt)++dcDisk DRAW_FILL_STROKE radius = + normalizeCtx radius >>= \r -> + fillStrokeLoc (\frgb attr srgb pt -> fillStrokeEllipse frgb attr srgb r r pt)+++++-- | 'strokeEllipseDisk' : @ x_radius * y_radius -> LocGraphic @+--+-- Create a stroked ellipse 'LocGraphic' - the implicit point is +-- the center. +-- +-- This is a efficient representation of circles using +-- PostScript\'s @arc@ or SVG\'s @ellipse@ in the generated +-- output. However, stroked ellipses do not draw well after +-- non-uniform scaling - the pen width is scaled as well as +-- the shape.+--+-- For stroked ellipses that can be adequately scaled, use +-- 'strokedEllipse' instead.+--+-- The line properties (colour, pen thickness, etc.) are taken +-- from the implicit 'DrawingContext'.+-- +dcEllipseDisk :: InterpretUnit u + => DrawMode -> u -> u -> LocGraphic u+dcEllipseDisk style rx ry = + normalizeCtx rx >>= \drx -> + normalizeCtx ry >>= \dry -> + case style of+ DRAW_FILL -> fillLoc (\rgb pt -> fillEllipse rgb drx dry pt)+ DRAW_STROKE -> strokeLoc + (\rgb attr pt -> strokeEllipse rgb attr drx dry pt)+ DRAW_FILL_STROKE -> fillStrokeLoc $ + (\frgb attr srgb pt -> + fillStrokeEllipse frgb attr srgb drx dry pt)++++--------------------------------------------------------------------------------++++-- | dcArc : radius * apex_angle+-- +-- Always open-stroked.+--+dcArc :: (Floating u, InterpretUnit u) => u -> Radian -> LocThetaGraphic u+dcArc radius ang = promoteLocTheta $ \pt inclin -> + let ps = bezierArcPoints ang radius inclin pt+ in liftQuery (curvePP ps) >>= dcOpenPath
− src/Wumpus/Basic/Kernel/Objects/Graphic.hs
@@ -1,478 +0,0 @@-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeSynonymInstances #-}-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Basic.Kernel.Objects.Graphic--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : stephen.tetley@gmail.com--- Stability : highly unstable--- Portability : GHC ------ Graphic type - this is largely equivalent to Primitive in--- Wumpus-Core, but drawing attributes are implicitly supplied --- by the DrawingContext.------ API in @Wumpus.Core@, but here they exploit the implicit --- @DrawingContext@.--------------------------------------------------------------------------------------module Wumpus.Basic.Kernel.Objects.Graphic- (-- Graphic- , DGraphic--- -- * LocGraphic - , LocGraphic- , DLocGraphic--- , LocThetaGraphic- , DLocThetaGraphic-- -- * Functions- , intoImage- , intoLocImage- , intoLocThetaImage-- , moveStartPoint- , moveStartPointTheta-- , locPath- , emptyLocPath- , emptyLocGraphic--- , openStroke- , closedStroke- , filledPath- , borderedPath-- , textline- , rtextline- , escapedline- , rescapedline-- , hkernline- , vkernline-- , strokedEllipse- , rstrokedEllipse- , filledEllipse- , rfilledEllipse-- , borderedEllipse- , rborderedEllipse-- , straightLine- , straightLineBetween- , curveBetween-- , strokedRectangle- , filledRectangle- , borderedRectangle-- , strokedCircle- , filledCircle- , borderedCircle- - , strokedDisk- , filledDisk- , borderedDisk-- ) where--import Wumpus.Basic.Kernel.Base.ContextFun-import Wumpus.Basic.Kernel.Base.BaseDefs-import Wumpus.Basic.Kernel.Base.QueryDC-import Wumpus.Basic.Kernel.Base.WrappedPrimitive-import Wumpus.Basic.Kernel.Objects.BaseObjects--import Wumpus.Core -- package: wumpus-core--import Data.AffineSpace -- package: vector-space--import Control.Applicative------------------------------------------------------------------------------------- Graphic---- | Simple drawing - produce a primitive, access the DrawingContext--- as required, e.g for fill colour, stroke colur, line width, etc.----type Graphic u = Image u (UNil u)---- | Alias of 'Graphic' where the unit type is specialized to --- Double. ----type DGraphic = Graphic Double----- | /Originated/ drawing - produce a primitive respective to the --- supplied start-point, access the DrawingContext as required.----type LocGraphic u = LocImage u (UNil u)---- | Alias of 'LocGraphic' where the unit type is specialized to --- Double. ----type DLocGraphic = LocGraphic Double------- | /Originated/ drawing - produce a primitive respective to the --- supplied start-point, access the DrawingContext as required.----type LocThetaGraphic u = LocThetaImage u (UNil u)----- | Alias of 'LocThetaGraphic' where the unit type is specialized --- to Double. ----type DLocThetaGraphic = LocThetaGraphic Double-------------------------------------------------------------------------------------- Functions----- | Build an Image...----intoImage :: CF a -> Graphic u -> Image u a-intoImage = liftA2 (\a (_,b) -> (a,b))----- | Build a LocImage...----intoLocImage :: LocCF u a -> LocGraphic u -> LocImage u a-intoLocImage = liftA2 (\a (_,b) -> (a,b))---- | Build a LocThetaImage...----intoLocThetaImage :: LocThetaCF u a -> LocThetaGraphic u -> LocThetaImage u a-intoLocThetaImage = liftA2 (\a (_,b) -> (a,b))----- | Move the start-point of a LocImage with the supplied --- displacement function.----moveStartPoint :: PointDisplace u -> LocCF u a -> LocCF u a-moveStartPoint f ma = promoteR1 $ \pt -> apply1R1 ma (f pt)----- | Move the start-point of a LocImage with the supplied --- displacement function.----moveStartPointTheta :: PointDisplace u -> LocThetaCF u a -> LocThetaCF u a-moveStartPointTheta f ma = promoteR2 $ \pt theta -> apply2R2 ma (f pt) theta--------------------------------------------------------------------------------------graphicBody :: Primitive u -> (UNil u, PrimGraphic u)-graphicBody p = (uNil, primGraphic p)----- | This is the analogue to 'vectorPath' in @Wumpus-core@.----locPath :: Num u => [Vec2 u] -> LocCF u (PrimPath u)-locPath vs = promoteR1 $ \pt -> pure $ vectorPath pt vs----- | This is the analogue to 'emptyPath' in @Wumpus-core@.----emptyLocPath :: Num u => LocCF u (PrimPath u)-emptyLocPath = locPath []---- | Build an empty LocGraphic - this is a path with a start--- point but no path segments. --- --- The 'emptyLocGraphic' It is treated as a /null primitive/ by --- @Wumpus-Core@ and is not drawn, although it does generate a --- minimum bounding box at the implicit start point.--- -emptyLocGraphic :: Num u => LocGraphic u-emptyLocGraphic = emptyLocPath >>= (lift0R1 . openStroke)----- | 'openStroke' : @ path -> Graphic @------ This is the analogue to 'ostroke' in @Wumpus-core@, but the --- drawing properties (colour, line width, etc.) are taken from --- the implicit 'DrawingContext'.----openStroke :: Num u => PrimPath u -> Graphic u-openStroke pp = - withStrokeAttr $ \rgb attr -> graphicBody $ ostroke rgb attr pp----- | 'closedStroke' : @ path -> Graphic @------ This is the analogue to 'cstroke' in @Wumpus-core@, but the --- drawing properties (colour, line width, etc.) are taken from --- the implicit 'DrawingContext'.----closedStroke :: Num u => PrimPath u -> Graphic u-closedStroke pp = - withStrokeAttr $ \rgb attr -> graphicBody $ cstroke rgb attr pp----- | 'filledPath' : @ path -> Graphic @--- --- This is the analogue to 'fill' in @Wumpus-core@, but the --- fill colour is taken from the implicit 'DrawingContext'.-------filledPath :: Num u => PrimPath u -> Graphic u-filledPath pp = withFillAttr $ \rgb -> graphicBody $ fill rgb pp- ---- | 'borderedPath' : @ path -> Graphic @------ This is the analogue to 'fillStroke' in @Wumpus-core@, but the --- drawing properties (fill colour, border colour, line width, --- etc.) are taken from the implicit 'DrawingContext'.-------borderedPath :: Num u => PrimPath u -> Graphic u-borderedPath pp =- withBorderedAttr $ \frgb attr srgb -> - graphicBody $ fillStroke frgb attr srgb pp------- | This is the analogue to 'textlabel' in @Wumpus-core@.----textline :: Num u => String -> LocGraphic u-textline ss = - promoteR1 $ \pt -> - withTextAttr $ \rgb attr -> graphicBody (textlabel rgb attr ss pt)------- | This is the analogue to 'rtextlabel' in @Wumpus-core@.----rtextline :: Num u => String -> LocThetaGraphic u-rtextline ss = - promoteR2 $ \pt theta -> - withTextAttr $ \rgb attr -> graphicBody (rtextlabel rgb attr ss theta pt)------ | This is the analogue to 'escapedlabel' in @Wumpus-core@.----escapedline :: Num u => EscapedText -> LocGraphic u-escapedline ss = - promoteR1 $ \pt -> - withTextAttr $ \rgb attr -> graphicBody (escapedlabel rgb attr ss pt)------ | This is the analogue to 'rescapedlabel' in @Wumpus-core@.----rescapedline :: Num u => EscapedText -> LocThetaGraphic u-rescapedline ss = - promoteR2 $ \pt theta -> - withTextAttr $ \rgb attr -> graphicBody (rescapedlabel rgb attr ss theta pt)------- | This is the analogue to 'hkernlabel' in @Wumpus-core@.----hkernline :: Num u => [KerningChar u] -> LocGraphic u-hkernline xs = - promoteR1 $ \pt -> - withTextAttr $ \rgb attr -> graphicBody (hkernlabel rgb attr xs pt)----- | This is the analogue to 'vkernlabel' in @Wumpus-core@.----vkernline :: Num u => [KerningChar u] -> LocGraphic u-vkernline xs = - promoteR1 $ \pt -> - withTextAttr $ \rgb attr -> graphicBody (vkernlabel rgb attr xs pt)--------------------------------------------------------------------------------------------- | This is the analogue to 'strokeEllipse' in @Wumpus-core@.----strokedEllipse :: Num u => u -> u -> LocGraphic u-strokedEllipse hw hh =- promoteR1 $ \pt -> - withStrokeAttr $ \rgb attr -> graphicBody (strokeEllipse rgb attr hw hh pt)------ | This is the analogue to 'rstrokeEllispe' in @Wumpus-core@.----rstrokedEllipse :: Num u => u -> u -> LocThetaGraphic u-rstrokedEllipse hw hh = - promoteR2 $ \ pt theta -> - withStrokeAttr $ \rgb attr -> - graphicBody (rstrokeEllipse rgb attr hw hh theta pt)----- | This is the analogue to 'fillEllispe' in @Wumpus-core@.----filledEllipse :: Num u => u -> u -> LocGraphic u-filledEllipse hw hh = - promoteR1 $ \pt -> - withFillAttr $ \rgb -> graphicBody (fillEllipse rgb hw hh pt)----- | This is the analogue to 'rfillEllispe' in @Wumpus-core@.----rfilledEllipse :: Num u => u -> u -> LocThetaGraphic u-rfilledEllipse hw hh = - promoteR2 $ \pt theta ->- withFillAttr $ \rgb -> graphicBody (rfillEllipse rgb hw hh theta pt)------ | This is the analogue to 'fillStrokeEllispe' in @Wumpus-core@.----borderedEllipse :: Num u => u -> u -> LocGraphic u-borderedEllipse hw hh =- promoteR1 $ \pt -> - withBorderedAttr $ \frgb attr srgb -> - graphicBody (fillStrokeEllipse frgb attr srgb hw hh pt)---- | This is the analogue to 'rfillStrokeEllispe' in @Wumpus-core@.----rborderedEllipse :: Num u => u -> u -> LocThetaGraphic u-rborderedEllipse hw hh = - promoteR2 $ \pt theta -> - withBorderedAttr $ \frgb attr srgb -> - graphicBody (rfillStrokeEllipse frgb attr srgb hw hh theta pt)------ Note - clipping needs a picture as well as a path, so there is--- no analogous @clippedPath@ function.--------------------------------------------------------------------------------------- | Draw a straight line formed from displacing the implicit --- start point with the supplied vector.--- -straightLine :: Fractional u => Vec2 u -> LocGraphic u-straightLine v = mf >>= (lift0R1 . openStroke)- where- mf = promoteR1 $ \pt -> pure $ primPath pt [lineTo $ pt .+^ v]-- --- | Draw a straight line - start and end point are supplied --- explicitly.--- -straightLineBetween :: Fractional u => Point2 u -> Point2 u -> Graphic u-straightLineBetween p1 p2 = openStroke $ primPath p1 [lineTo p2]------ | Draw a Bezier curve - all points are supplied explicitly.--- -curveBetween :: Fractional u - => Point2 u -> Point2 u -> Point2 u -> Point2 u -> Graphic u-curveBetween sp cp1 cp2 ep = openStroke $ primPath sp [curveTo cp1 cp2 ep]----- This is a permuted version of the cardinal-prime combinator...--- --- > (r2 -> a) -> (a -> r1 -> ans) -> (r1 -> r2 -> ans)-----drawWith :: (Point2 u -> PrimPath u) -> (PrimPath u -> Graphic u) -> LocGraphic u -drawWith g mf = promoteR1 $ \pt -> mf (g pt)----- | Supplied point is /bottom-left/.----rectanglePath :: Num u => u -> u -> Point2 u -> PrimPath u-rectanglePath w h bl = primPath bl [ lineTo br, lineTo tr, lineTo tl ]- where- br = bl .+^ hvec w- tr = br .+^ vvec h- tl = bl .+^ vvec h---- | Supplied point is /bottom left/.----strokedRectangle :: Fractional u => u -> u -> LocGraphic u-strokedRectangle w h = rectanglePath w h `drawWith` closedStroke----- | Supplied point is /bottom left/.----filledRectangle :: Fractional u => u -> u -> LocGraphic u-filledRectangle w h = rectanglePath w h `drawWith` filledPath---- | Supplied point is /bottom left/.----borderedRectangle :: Fractional u => u -> u -> LocGraphic u-borderedRectangle w h = rectanglePath w h `drawWith` borderedPath----- | Supplied point is center. Circle is drawn with Bezier --- curves. ----strokedCircle :: Floating u => Int -> u -> LocGraphic u-strokedCircle n r = (curvedPath . bezierCircle n r) `drawWith` closedStroke ------ | Supplied point is center. Circle is drawn with Bezier --- curves. ----filledCircle :: Floating u => Int -> u -> LocGraphic u-filledCircle n r = (curvedPath . bezierCircle n r) `drawWith` filledPath------ | Supplied point is center. Circle is drawn with Bezier --- curves. ----borderedCircle :: Floating u => Int -> u -> LocGraphic u-borderedCircle n r = (curvedPath . bezierCircle n r) `drawWith` borderedPath ----- | 'disk' is drawn with Wumpus-Core\'s @ellipse@ primitive.------ This is a efficient representation of circles using --- PostScript\'s @arc@ or SVG\'s @circle@ in the generated --- output. However, stroked-circles do not draw well after --- non-uniform scaling - the line width is scaled as well as --- the shape.------ For stroked circles that can be adequately scaled, use --- 'strokedCircle' instead.----strokedDisk :: Num u => u -> LocGraphic u-strokedDisk radius = strokedEllipse radius radius---- | Filled disk...----filledDisk :: Num u => u -> LocGraphic u-filledDisk radius = filledEllipse radius radius---- | bordered disk...----borderedDisk :: Num u => u -> LocGraphic u-borderedDisk radius = borderedEllipse radius radius
+ src/Wumpus/Basic/Kernel/Objects/Image.hs view
@@ -0,0 +1,293 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.Kernel.Objects.Image+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : highly unstable+-- Portability : GHC +--+-- Common types and operations.+-- +--+--------------------------------------------------------------------------------++module Wumpus.Basic.Kernel.Objects.Image+ (+++ Image+ , Graphic ++ , Query++ , DImage+ , DGraphic++ , runImage+ , runQuery++ , stripImage+ , liftQuery++ , emptyImage+ , primGraphic+ , clipImage+++ ) where++import Wumpus.Basic.Kernel.Base.BaseDefs+import Wumpus.Basic.Kernel.Base.DrawingContext+import Wumpus.Basic.Kernel.Base.WrappedPrimitive+import Wumpus.Basic.Kernel.Objects.Basis++import Wumpus.Core -- package: wumpus-core+++import Control.Applicative+import Data.Monoid++--------------------------------------------------------------------------------+++newtype Image u a = Image { + getImage :: DrawingContext -> (a, CatPrim) }++type instance DUnit (Image u a) = u++type Graphic u = Image u (UNil u)+++-- | Type specialized version of 'Image'.+--+type DImage a = Image Double a++-- | Type specialized version of 'Graphic'.+--+type DGraphic = Graphic Double +++newtype Query u a = Query { + getQuery :: DrawingContext -> a }++type instance DUnit (Query u a) = u++-- Functor++instance Functor (Image u) where+ fmap f ma = Image $ \ctx -> let (a,w1) = getImage ma ctx in (f a, w1)++instance Functor (Query u) where+ fmap f ma = Query $ \ctx -> f $ getQuery ma ctx++-- Applicative++instance Applicative (Image u) where+ pure a = Image $ \_ -> (a,mempty)+ mf <*> ma = Image $ \ctx -> let (f,w1) = getImage mf ctx + (a,w2) = getImage ma ctx+ in (f a, w1 `mappend` w2)++instance Applicative (Query u) where+ pure a = Query $ \_ -> a+ mf <*> ma = Query $ \ctx -> let f = getQuery mf ctx + a = getQuery ma ctx+ in f a+++-- Monad++instance Monad (Image u) where+ return a = Image $ \_ -> (a,mempty)+ ma >>= k = Image $ \ctx -> let (a,w1) = getImage ma ctx + (b,w2) = getImage (k a) ctx+ in (b,w1 `mappend` w2)+++instance Monad (Query u) where+ return a = Query $ \_ -> a+ ma >>= k = Query $ \ctx -> let a = getQuery ma ctx in getQuery (k a) ctx++-- Monoid++instance Monoid a => Monoid (Image u a) where+ mempty = pure mempty+ ma `mappend` mb = Image $ \ctx -> + getImage ma ctx `mappend` getImage mb ctx++instance Monoid a => Monoid (Query u a) where+ mempty = pure mempty+ ma `mappend` mb = Query $ \ctx -> + getQuery ma ctx `mappend` getQuery mb ctx+++-- DrawingCtxM+++instance DrawingCtxM (Image u) where+ askDC = Image $ \ctx -> (ctx, mempty)+ asksDC fn = Image $ \ctx -> (fn ctx, mempty)+ localize upd ma = Image $ \ctx -> getImage ma (upd ctx)++instance DrawingCtxM (Query u) where+ askDC = Query $ \ctx -> ctx+ asksDC fn = Query $ \ctx -> (fn ctx)+ localize upd ma = Query $ \ctx -> getQuery ma (upd ctx)+++runImage :: DrawingContext -> Image u a -> PrimResult u a+runImage ctx ma = getImage ma ctx++runQuery :: DrawingContext -> Query u a -> a+runQuery ctx ma = getQuery ma ctx++++-- | Strip the graphic content from an 'Image' making a 'Query'.+-- +stripImage :: Image u a -> Query u a+stripImage ma = Query $ \ctx -> fst $ getImage ma ctx+++-- | Turn a 'Query' into an 'Image' without graphic content.+--+liftQuery :: Query u a -> Image u a+liftQuery ma = askDC >>= \ctx -> let a = runQuery ctx ma in return a++++-- | Constructor for Primtive graphics.+--+primGraphic :: CatPrim -> Graphic u+primGraphic w = Image $ \_ -> (UNil, w)+++-- | Clip an Image.+-- +clipImage :: PrimPath -> Image u a -> Image u a+clipImage pp ma = Image $ \ctx -> + let (a,w) = getImage ma ctx in (a, cpmap (clipPrim pp) w)++++instance UConvert Image where+ uconvZ = uconvImageZ+ uconvF = uconvImageF++uconvImageF :: (Functor t, InterpretUnit u, InterpretUnit u1) + => Image u (t u) -> Image u1 (t u1) +uconvImageF ma = Image $ \ctx -> + let (a,w) = getImage ma ctx+ a' = uconvertF (dc_font_size ctx) a+ in (a',w)+++uconvImageZ :: (InterpretUnit u, InterpretUnit u1) + => Image u a -> Image u1 a+uconvImageZ ma = Image $ \ctx -> getImage ma ctx+++-- | Having /empty/ at the specific 'Image' type is useful.+-- +emptyImage :: Monoid a => Image u a+emptyImage = mempty++--------------------------------------------------------------------------------+++instance Decorate Image where+ decorate = decorateImage+ elaborate = elaborateImage + obliterate = obliterateImage+ hyperlink = hyperlinkImage+ svgId = svgIdImage + svgAnnotate = svgAnnotateImage ++-- | Decorate Image.+--+decorateImage :: ZOrder -> Image u a -> Image u z -> Image u a+decorateImage zo ma mb = Image $ \ctx -> + step zo (getImage ma ctx) (getImage mb ctx)+ where+ step ZABOVE (a,w1) (_,w2) = (a, w1 `mappend` w2)+ step ZBELOW (a,w1) (_,w2) = (a, w2 `mappend` w1)+++-- | Elaborate Image.+--+elaborateImage :: ZOrder -> Image u a -> (a -> Image u z) -> Image u a+elaborateImage zo ma k = Image $ \ ctx ->+ let (a,w1) = getImage ma ctx+ (_,w2) = getImage (k a) ctx + in case zo of+ ZABOVE -> (a, w1 `mappend` w2)+ ZBELOW -> (a, w2 `mappend` w1)+++obliterateImage :: Image u a -> Image u a+obliterateImage ma = Image $ \ctx -> + let (a,_) = getImage ma ctx in (a,mempty)+ +hyperlinkImage :: XLink -> Image u a -> Image u a+hyperlinkImage xl ma = Image $ \ctx -> step (getImage ma ctx)+ where+ step (a,w) = (a, cpmap (xlinkPrim xl) w)++svgIdImage :: String -> Image u a -> Image u a+svgIdImage ss ma = Image $ \ctx -> step (getImage ma ctx)+ where+ step (a,w) = (a, cpmap (xidPrim ss) w)++svgAnnotateImage :: [SvgAttr] -> Image u a -> Image u a+svgAnnotateImage attrs ma = Image $ \ctx -> step (getImage ma ctx)+ where+ step (a,w) = (a, cpmap (annotateGroup attrs) w)+++--------------------------------------------------------------------------------+-- Affine instances ++-- +-- Design Note+--+-- Are PrimW instances needed as Image cannot use them?+-- ++instance Rotate a => Rotate (Image u a) where+ rotate ang ma = Image $ \ctx -> + let (a,w) = getImage ma ctx+ in (rotate ang a, rotate ang w)++instance (RotateAbout a, InterpretUnit u, u ~ DUnit a) => + RotateAbout (Image u a) where+ rotateAbout ang pt ma = Image $ \ctx -> + let ptu = uconvertF (dc_font_size ctx) pt+ (a,w) = getImage ma ctx+ in (rotateAbout ang pt a, rotateAbout ang ptu w)+++instance Scale a => Scale (Image u a) where+ scale sx sy ma = Image $ \ctx -> + let (a,w) = getImage ma ctx+ in (scale sx sy a, scale sx sy w)+++instance (Translate a, InterpretUnit u, u ~ DUnit a) => + Translate (Image u a) where+ translate dx dy ma = Image $ \ctx -> + let sz = dc_font_size ctx+ ddx = uconvert1 sz dx+ ddy = uconvert1 sz dy+ (a,w) = getImage ma ctx+ in (translate dx dy a, translate ddx ddy w)++++
+ src/Wumpus/Basic/Kernel/Objects/LocImage.hs view
@@ -0,0 +1,357 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.Kernel.Objects.LocImage+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : highly unstable+-- Portability : GHC +--+-- LocImage and LocGraphic types - these are functional types from the +-- DrawingContext and start point to a graphic /primitive/.+--+--------------------------------------------------------------------------------++module Wumpus.Basic.Kernel.Objects.LocImage+ (++ LocImage+ , LocGraphic++ , DLocImage+ , DLocGraphic++ , LocQuery++ , runLocImage+ , runLocQuery++ , stripLocImage+ , liftLocQuery++ , promoteLoc+ , applyLoc+ , supplyLoc+ , qpromoteLoc+ , qapplyLoc++ , emptyLocImage++ , moveStart+ , at++ -- * Composing LocImages+ , distrib+ , distribH + , distribV+ + , duplicate+ , duplicateH+ , duplicateV+++ )++ where++import Wumpus.Basic.Kernel.Base.BaseDefs+import Wumpus.Basic.Kernel.Base.DrawingContext+import Wumpus.Basic.Kernel.Base.QueryDC+import Wumpus.Basic.Kernel.Objects.Basis+import Wumpus.Basic.Kernel.Objects.Image+++import Wumpus.Core -- package: wumpus-core++import Data.AffineSpace -- package: vector-space+import Data.VectorSpace++import Control.Applicative+import Data.Monoid++++-- | 'LocThetaImage' - function from start point and +-- DrawingContext to a polymorphic /answer/ and a graphic +-- /primitive/ (PrimW).+--+newtype LocImage u a = LocImage { + getLocImage :: DPoint2 -> Image u a }++type instance DUnit (LocImage u a) = u++type LocGraphic u = LocImage u (UNil u)+++-- | Type specialized version of 'LocImage'.+--+type DLocImage a = LocImage Double a++-- | Type specialized version of 'LocGraphic'.+--+type DLocGraphic = LocGraphic Double ++newtype LocQuery u a = LocQuery { + getLocQuery :: DPoint2 -> Query u a }++-- Functor++instance Functor (LocImage u) where+ fmap f ma = LocImage $ \pt -> fmap f $ getLocImage ma pt+++instance Functor (LocQuery u) where+ fmap f ma = LocQuery $ \pt -> fmap f $ getLocQuery ma pt++-- Applicative++instance Applicative (LocImage u) where+ pure a = LocImage $ \_ -> pure a+ mf <*> ma = LocImage $ \pt -> getLocImage mf pt <*> getLocImage ma pt++instance Applicative (LocQuery u) where+ pure a = LocQuery $ \_ -> pure a+ mf <*> ma = LocQuery $ \pt -> getLocQuery mf pt <*> getLocQuery ma pt+ +++-- Monad++instance Monad (LocImage u) where+ return a = LocImage $ \_ -> return a+ ma >>= k = LocImage $ \pt -> getLocImage ma pt >>= \ans -> + getLocImage (k ans) pt+++instance Monad (LocQuery u) where+ return a = LocQuery $ \_ -> return a+ ma >>= k = LocQuery $ \pt -> getLocQuery ma pt >>= \ans -> + getLocQuery (k ans) pt+++-- Monoid++instance Monoid a => Monoid (LocImage u a) where+ mempty = pure mempty+ ma `mappend` mb = LocImage $ \pt -> + getLocImage ma pt `mappend` getLocImage mb pt ++instance Monoid a => Monoid (LocQuery u a) where+ mempty = pure mempty+ ma `mappend` mb = LocQuery $ \pt -> + getLocQuery ma pt `mappend` getLocQuery mb pt ++-- DrawingCtxM++instance DrawingCtxM (LocImage u) where+ askDC = LocImage $ \_ -> askDC+ asksDC fn = LocImage $ \_ -> asksDC fn+ localize upd ma = LocImage $ \pt -> localize upd (getLocImage ma pt)++instance DrawingCtxM (LocQuery u) where+ askDC = LocQuery $ \_ -> askDC+ asksDC fn = LocQuery $ \_ -> asksDC fn+ localize upd ma = LocQuery $ \pt -> localize upd (getLocQuery ma pt)++++ +instance Decorate LocImage where+ decorate zo ma mz = LocImage $ \pt -> + decorate zo (getLocImage ma pt) (getLocImage mz pt)++ elaborate zo ma f = LocImage $ \pt -> + elaborate zo (getLocImage ma pt) (\a -> getLocImage (f a) pt)++ obliterate ma = LocImage $ \pt -> obliterate $ getLocImage ma pt ++ hyperlink xl ma = LocImage $ \pt -> hyperlink xl $ getLocImage ma pt ++ svgId ss ma = LocImage $ \pt -> svgId ss $ getLocImage ma pt ++ svgAnnotate attrs ma = LocImage $ \pt -> svgAnnotate attrs $ getLocImage ma pt+++runLocImage :: InterpretUnit u + => DrawingContext -> Point2 u -> LocImage u a -> PrimResult u a+runLocImage ctx pt ma = + let dpt = normalizeF (dc_font_size ctx) pt + in runImage ctx $ getLocImage ma dpt++runLocQuery :: InterpretUnit u + => DrawingContext -> Point2 u -> LocQuery u a -> a+runLocQuery ctx pt ma = + let dpt = normalizeF (dc_font_size ctx) pt + in runQuery ctx $ getLocQuery ma dpt+++stripLocImage :: LocImage u a -> LocQuery u a+stripLocImage ma = LocQuery $ \pt -> + stripImage $ getLocImage ma pt+++liftLocQuery :: LocQuery u a -> LocImage u a+liftLocQuery ma = LocImage $ \pt -> + liftQuery $ getLocQuery ma pt+++promoteLoc :: InterpretUnit u => (Point2 u -> Image u a) -> LocImage u a+promoteLoc k = LocImage $ \pt -> dinterpCtxF pt >>= k ++applyLoc :: InterpretUnit u => LocImage u a -> Point2 u -> Image u a+applyLoc ma pt = normalizeCtxF pt >>= getLocImage ma+++-- | Flipped version of 'applyLoc'. +-- +supplyLoc :: InterpretUnit u => Point2 u -> LocImage u a -> Image u a+supplyLoc = flip at+++qpromoteLoc :: InterpretUnit u + => (Point2 u -> Query u a) -> LocQuery u a+qpromoteLoc k = LocQuery $ \pt -> dinterpCtxF pt >>= k++qapplyLoc :: InterpretUnit u+ => LocQuery u a -> Point2 u -> Query u a+qapplyLoc ma pt = normalizeCtxF pt >>= getLocQuery ma++++--------------------------------------------------------------------------------+-- Affine instances++instance (Real u, Floating u, InterpretUnit u, Rotate a) => + Rotate (LocImage u a) where+ rotate ang ma = promoteLoc $ \pt -> + normalizeCtxF pt >>= \dpt -> + fmap (rotate ang) $ getLocImage ma (rotate ang dpt)+++instance (Real u, Floating u, InterpretUnit u, RotateAbout a, u ~ DUnit a) => + RotateAbout (LocImage u a) where+ rotateAbout ang pt ma = promoteLoc $ \p0 ->+ normalizeCtxF p0 >>= \dp0 -> + normalizeCtxF pt >>= \dpt -> + fmap (rotateAbout ang pt) $ + getLocImage ma (rotateAbout ang dpt dp0)+++instance (Fractional u, InterpretUnit u, Scale a) => Scale (LocImage u a) where+ scale sx sy ma = promoteLoc $ \pt -> + normalizeCtxF pt >>= \dpt -> + fmap (scale sx sy) $ getLocImage ma (scale sx sy dpt)++instance (InterpretUnit u, Translate a, ScalarUnit u, u ~ DUnit a) => + Translate (LocImage u a) where+ translate dx dy ma = promoteLoc $ \pt -> + normalizeCtxF pt >>= \dpt -> + normalizeCtx dx >>= \ddx ->+ normalizeCtx dy >>= \ddy ->+ translate dx dy $ getLocImage ma (translate ddx ddy dpt)++--------------------------------------------------------------------------------+++instance UConvert LocImage where+ uconvF = uconvLocImageF+ uconvZ = uconvLocImageZ++-- | Use this to convert 'LocGraphic' or 'LocImage' with Functor +-- answer.+--+uconvLocImageF :: (InterpretUnit u, InterpretUnit u1, Functor t) + => LocImage u (t u) -> LocImage u1 (t u1)+uconvLocImageF ma = LocImage $ \pt -> uconvF $ getLocImage ma pt++++-- | Use this to convert 'LocImage' with unit-less answer.+--+uconvLocImageZ :: (InterpretUnit u, InterpretUnit u1) + => LocImage u a -> LocImage u1 a+uconvLocImageZ ma = LocImage $ \pt -> uconvZ $ getLocImage ma pt+++-- | Having /empty/ at the specific 'LocImage' type is useful.+-- +emptyLocImage :: Monoid a => LocImage u a+emptyLocImage = mempty+++++-- Note - maybe this should just be an operator on LocImage...+--++moveStart :: InterpretUnit u => Vec2 u -> LocImage u a -> LocImage u a+moveStart v1 ma = LocImage $ \pt -> + normalizeCtxF v1 >>= \dv -> getLocImage ma (pt .+^ dv) ++++infixr 1 `at`++-- | Downcast a 'LocImage' function by applying it to the supplied +-- point, making an 'Image'. +-- +-- > infixr 1 `at`+-- +at :: InterpretUnit u => LocImage u a -> Point2 u -> Image u a+at = applyLoc++++--------------------------------------------------------------------------------+-- Combining LocImages ++-- LocImages have no concept of /border/ or /next/, so they can +-- only be combined by manipulating the start point of successive+-- drawings.++-- 'oplus' gives super-imposition - Locimages are drawn at the same+-- start point.++++distrib :: (Monoid a, InterpretUnit u) + => Vec2 u -> [LocImage u a] -> LocImage u a+distrib _ [] = mempty+distrib v1 (x:xs) = promoteLoc $ \pt -> + go (applyLoc x pt) (pt .+^ v1) xs+ where+ go acc _ [] = acc+ go acc pt (a:as) = go (acc `mappend` applyLoc a pt) (pt .+^ v1) as++distribH :: (Monoid a, InterpretUnit u) + => u -> [LocImage u a] -> LocImage u a+distribH dx = distrib (hvec dx)++distribV :: (Monoid a, InterpretUnit u) + => u -> [LocImage u a] -> LocImage u a+distribV dy = distrib (hvec dy)+++-- | This is analogue to @replicate@ in the Prelude.+--+duplicate :: (Monoid a, InterpretUnit u) + => Int -> Vec2 u -> LocImage u a -> LocImage u a+duplicate n _ _ | n < 1 = mempty+duplicate n v img = go img v (n-1)+ where+ go acc _ i | i < 1 = acc+ go acc v1 i = let img1 = moveStart v1 img+ in go (acc `mappend` img1) (v1 ^+^ v) (i-1)++duplicateH :: (Monoid a, InterpretUnit u) + => Int -> u -> LocImage u a -> LocImage u a+duplicateH n dx = duplicate n (hvec dx)++duplicateV :: (Monoid a, InterpretUnit u) + => Int -> u -> LocImage u a -> LocImage u a+duplicateV n dy = duplicate n (vvec dy)+
+ src/Wumpus/Basic/Kernel/Objects/LocThetaImage.hs view
@@ -0,0 +1,294 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.Kernel.Objects.LocThetaImage+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : highly unstable+-- Portability : GHC +--+-- LocThetaImage and LocThetaGraphic types - these are functional +-- types from the DrawingContext, start point and angle of +-- inclination to a graphic /primitive/.+--+--------------------------------------------------------------------------------++module Wumpus.Basic.Kernel.Objects.LocThetaImage+ (+ LocThetaGraphic+ , LocThetaImage++ , DLocThetaGraphic+ , DLocThetaImage++ , LocThetaQuery++ , runLocThetaImage+ , runLocThetaQuery++ , stripLocThetaImage+ , liftLocThetaQuery+ + , promoteLocTheta+ , applyLocTheta+ , supplyLocTheta+ , qpromoteLocTheta+ , qapplyLocTheta++ , emptyLocThetaImage++ , incline+ , atIncline+ , supplyIncline+ + )++ where++import Wumpus.Basic.Kernel.Base.BaseDefs+import Wumpus.Basic.Kernel.Base.DrawingContext+import Wumpus.Basic.Kernel.Base.QueryDC+import Wumpus.Basic.Kernel.Objects.Basis+import Wumpus.Basic.Kernel.Objects.Image+import Wumpus.Basic.Kernel.Objects.LocImage++import Wumpus.Core -- package: wumpus-core++import Control.Applicative+import Data.Monoid++-- | 'LocThetaImage' - function from start point, inclination and+-- DrawingContext to a polymorphic /answer/ and a graphic +-- /primitive/ (PrimW).+--+newtype LocThetaImage u a = LocThetaImage { + getLocThetaImage :: DPoint2 -> Radian -> Image u a }++type instance DUnit (LocThetaImage u a) = u++type LocThetaGraphic u = LocThetaImage u (UNil u)+++-- | Type specialized version of 'LocThetaImage'.+--+type DLocThetaImage a = LocThetaImage Double a++-- | Type specialized version of 'LocThetaGraphic'.+--+type DLocThetaGraphic = LocThetaGraphic Double +++newtype LocThetaQuery u a = LocThetaQuery { + getLocThetaQuery :: DPoint2 -> Radian -> Query u a }++-- Functor++instance Functor (LocThetaImage u) where+ fmap f ma = LocThetaImage $ \pt ang -> + fmap f $ getLocThetaImage ma pt ang++instance Functor (LocThetaQuery u) where+ fmap f ma = LocThetaQuery $ \pt ang -> + fmap f $ getLocThetaQuery ma pt ang+++-- Applicative++instance Applicative (LocThetaImage u) where+ pure a = LocThetaImage $ \_ _ -> pure a+ mf <*> ma = LocThetaImage $ \pt ang -> + getLocThetaImage mf pt ang <*> getLocThetaImage ma pt ang++instance Applicative (LocThetaQuery u) where+ pure a = LocThetaQuery $ \_ _ -> pure a+ mf <*> ma = LocThetaQuery $ \pt ang -> + getLocThetaQuery mf pt ang <*> getLocThetaQuery ma pt ang+++-- Monad ++instance Monad (LocThetaImage u) where+ return a = LocThetaImage $ \_ _ -> return a+ ma >>= k = LocThetaImage $ \pt ang -> + getLocThetaImage ma pt ang >>= \ans -> + getLocThetaImage (k ans) pt ang+++instance Monad (LocThetaQuery u) where+ return a = LocThetaQuery $ \_ _ -> return a+ ma >>= k = LocThetaQuery $ \pt ang -> + getLocThetaQuery ma pt ang >>= \ans -> + getLocThetaQuery (k ans) pt ang++-- Monoid++instance Monoid a => Monoid (LocThetaImage u a) where+ mempty = pure mempty+ ma `mappend` mb = LocThetaImage $ \pt ang -> + getLocThetaImage ma pt ang + `mappend` getLocThetaImage mb pt ang++++instance Monoid a => Monoid (LocThetaQuery u a) where+ mempty = pure mempty+ ma `mappend` mb = LocThetaQuery $ \pt ang -> + getLocThetaQuery ma pt ang + `mappend` getLocThetaQuery mb pt ang++-- DrawingCtxM++instance DrawingCtxM (LocThetaImage u) where+ askDC = LocThetaImage $ \_ _ -> askDC+ asksDC fn = LocThetaImage $ \_ _ -> asksDC fn+ localize upd ma = LocThetaImage $ \pt ang -> + localize upd (getLocThetaImage ma pt ang)++instance DrawingCtxM (LocThetaQuery u) where+ askDC = LocThetaQuery $ \_ _ -> askDC+ asksDC fn = LocThetaQuery $ \_ _ -> asksDC fn+ localize upd ma = LocThetaQuery $ \pt ang -> + localize upd (getLocThetaQuery ma pt ang)++--++instance Decorate LocThetaImage where+ decorate zo ma mz = LocThetaImage $ \pt ang -> + decorate zo (getLocThetaImage ma pt ang) (getLocThetaImage mz pt ang)++ elaborate zo ma f = LocThetaImage $ \pt ang -> + elaborate zo (getLocThetaImage ma pt ang) + (\a -> getLocThetaImage (f a) pt ang)++ obliterate ma = LocThetaImage $ \pt ang -> + obliterate $ getLocThetaImage ma pt ang++ hyperlink xl ma = LocThetaImage $ \pt ang -> + hyperlink xl $ getLocThetaImage ma pt ang++ svgId ss ma = LocThetaImage $ \pt ang -> + svgId ss $ getLocThetaImage ma pt ang++ svgAnnotate attrs ma = LocThetaImage $ \pt ang -> + svgAnnotate attrs $ getLocThetaImage ma pt ang+ ++++runLocThetaImage :: InterpretUnit u + => DrawingContext -> Point2 u -> Radian+ -> LocThetaImage u a + -> PrimResult u a+runLocThetaImage ctx pt incl ma = + let dpt = normalizeF (dc_font_size ctx) pt + in runImage ctx $ getLocThetaImage ma dpt incl+++runLocThetaQuery :: InterpretUnit u + => DrawingContext -> Point2 u -> Radian + -> LocThetaQuery u a + -> a+runLocThetaQuery ctx pt incl ma = + let dpt = normalizeF (dc_font_size ctx) pt + in runQuery ctx $ getLocThetaQuery ma dpt incl++++stripLocThetaImage :: LocThetaImage u a -> LocThetaQuery u a+stripLocThetaImage ma = LocThetaQuery $ \pt ang -> + stripImage $ getLocThetaImage ma pt ang+++liftLocThetaQuery :: LocThetaQuery u a -> LocThetaImage u a+liftLocThetaQuery ma = LocThetaImage $ \pt ang -> + liftQuery $ getLocThetaQuery ma pt ang+++++promoteLocTheta :: InterpretUnit u + => (Point2 u -> Radian -> Image u a) -> LocThetaImage u a+promoteLocTheta k = LocThetaImage $ \pt ang -> + dinterpCtxF pt >>= \upt -> k upt ang++applyLocTheta :: InterpretUnit u + => LocThetaImage u a -> Point2 u -> Radian -> Image u a+applyLocTheta ma pt ang = + normalizeCtxF pt >>= \dpt -> getLocThetaImage ma dpt ang++supplyLocTheta :: InterpretUnit u + => Point2 u -> Radian -> LocThetaImage u a -> Image u a+supplyLocTheta pt ang ma = applyLocTheta ma pt ang+++qpromoteLocTheta :: InterpretUnit u + => (Point2 u -> Radian -> Query u a) -> LocThetaQuery u a+qpromoteLocTheta k = LocThetaQuery $ \pt ang ->+ dinterpCtxF pt >>= \upt -> k upt ang+++qapplyLocTheta :: InterpretUnit u+ => LocThetaQuery u a -> Point2 u -> Radian -> Query u a+qapplyLocTheta ma pt ang = + normalizeCtxF pt >>= \dpt -> getLocThetaQuery ma dpt ang+++++instance UConvert LocThetaImage where+ uconvF = uconvLocThetaImageF+ uconvZ = uconvLocThetaImageZ+++-- | Use this to convert 'LocThetaThetaGraphic' or 'LocThetaThetaImage' +-- with Functor answer.+--+uconvLocThetaImageF :: (InterpretUnit u, InterpretUnit u1, Functor t) + => LocThetaImage u (t u) -> LocThetaImage u1 (t u1)+uconvLocThetaImageF ma = LocThetaImage $ \pt ang -> + uconvF $ getLocThetaImage ma pt ang+++++-- | Use this to convert 'LocThetaImage' with unit-less answer.+--+uconvLocThetaImageZ :: (InterpretUnit u, InterpretUnit u1) + => LocThetaImage u a -> LocThetaImage u1 a+uconvLocThetaImageZ ma = LocThetaImage $ \pt ang -> + uconvZ $ getLocThetaImage ma pt ang++++-- | Having /empty/ at the specific 'LocThetaImage' type is useful.+-- +emptyLocThetaImage :: Monoid a => LocThetaImage u a+emptyLocThetaImage = mempty++++++infixr 1 `incline`+++-- | Downcast a 'LocThetaImage' function by applying it to the +-- supplied angle, making a 'LocImage'. +-- +incline :: InterpretUnit u => LocThetaImage u a -> Radian -> LocImage u a+incline ma incl = promoteLoc $ \pt -> + normalizeCtxF pt >>= \dpt -> getLocThetaImage ma dpt incl++atIncline :: InterpretUnit u + => LocThetaImage u a -> Point2 u -> Radian -> Image u a+atIncline = applyLocTheta+++-- | Flipped version of 'incline'+--+supplyIncline :: InterpretUnit u => Radian -> LocThetaImage u a -> LocImage u a+supplyIncline = flip incline
+ src/Wumpus/Basic/Kernel/Objects/Orientation.hs view
@@ -0,0 +1,564 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.Kernel.Objects.Orientation+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : highly unstable+-- Portability : GHC +--+-- Graphic objects RectAddress and Orientation to model +-- rectangular positioning.+--+--------------------------------------------------------------------------------++module Wumpus.Basic.Kernel.Objects.Orientation+ (+++ -- * Components+ RectAddress(..)+ , Orientation(..)++ , vtoRectAddress+ , vtoOrigin+ , orientationBounds+ , orientationWidth+ , orientationHeight++ , rotateOrientation+ + , extendOrientation+ , extendOLeft+ , extendORight+ , extendODown+ , extendOUp++ , fillHEven+ , fillXMinor+ , fillXMajor+ , fillVEven+ , fillYMajor+ , fillYMinor++ , spineRight+ , spineBelow++ , halignBottomO+ , halignCenterO+ , halignTopO+ , valignLeftO+ , valignCenterO+ , valignRightO++ , spinemoveH+ , spinemoveV+ , binmoveHBottom+ , binmoveHCenter+ , binmoveHTop+ , binmoveVLeft+ , binmoveVCenter+ , binmoveVRight++ ) where++++import Wumpus.Core -- package: wumpus-core++import Data.VectorSpace -- package: vector-space++import Data.Monoid++-- | Datatype enumerating the addressable positions of a rectangle +-- that can be derived for a 'PosObject'. +--+-- The positions are the compass points, plus the geometric +-- center, origin and the baseline positions: +-- +-- > BLL - baseline-left+--+-- > BLC - baseline-center +-- +-- > BLR - baseline-right+--+data RectAddress = CENTER | ORIGIN+ | NN | SS | EE | WW | NE | NW | SE | SW + | BLL | BLC | BLR+ deriving (Enum,Eq,Ord,Show)+++++-- | Utility datatype representing orientation within a +-- rectangular /frame/. RectPos is useful for graphics such as +-- text where the start point is not necessarily at the center +-- (or bottom left).+--+-- > x_minor is the horizontal distance from the left to the start point+-- >+-- > x_major is the horizontal distance from the start point to the right+-- >+-- > y_minor is the vertical distance from the bottom to the start point+-- >+-- > y_major is the vertical distance from the start point to the top+--+-- Values should be not be negative!+--+-- +data Orientation u = Orientation+ { or_x_minor :: !u+ , or_x_major :: !u+ , or_y_minor :: !u+ , or_y_major :: !u+ }+ deriving (Eq,Ord,Show)+++++--------------------------------------------------------------------------------++instance Functor Orientation where+ fmap f (Orientation xmin xmaj ymin ymaj) = + Orientation (f xmin) (f xmaj) (f ymin) (f ymaj)+++-- | Concatenation coalesces the origins.+--+instance (Fractional u, Ord u) => Monoid (Orientation u) where+ mempty = Orientation 0 0 0 0+ a `mappend` b = + Orientation { or_x_minor = max (or_x_minor a) (or_x_minor b)+ , or_x_major = max (or_x_major a) (or_x_major b)+ , or_y_minor = max (or_y_minor a) (or_y_minor b)+ , or_y_major = max (or_y_major a) (or_y_major b)+ }+++-- Helper types for calculating vector from Origin +-- (not exported).++data HDist = HCENTER | HLEFT | HRIGHT+ deriving (Eq,Ord,Show)++data VDist = VCENTER | VBASE | VTOP+ deriving (Eq,Ord,Show)++++-- | The vector from a origin ro a 'RectAddress'.+--+vtoRectAddress :: (Fractional u, Ord u) + => Orientation u -> RectAddress -> Vec2 u+vtoRectAddress (Orientation xmin xmaj ymin ymaj) = go+ where+ hw = 0.5 * (xmin + xmaj)+ hh = 0.5 * (ymin + ymaj)+ + -- CENTER, NN, SS, EE, WW all go to bottomleft then add back + -- the minors.++ go CENTER = V2 (hdist HCENTER) (vdist VCENTER)+ go ORIGIN = zeroVec+ go NN = V2 (hdist HCENTER) (vdist VTOP)+ go SS = V2 (hdist HCENTER) (vdist VBASE)+ go EE = V2 (hdist HRIGHT) (vdist VCENTER)+ go WW = V2 (hdist HLEFT) (vdist VCENTER)+ go NE = V2 (hdist HRIGHT) (vdist VTOP)+ go SE = V2 (hdist HRIGHT) (vdist VBASE)+ go SW = V2 (hdist HLEFT) (vdist VBASE)+ go NW = V2 (hdist HLEFT) (vdist VTOP)+ go BLL = V2 (hdist HLEFT) 0+ go BLC = V2 (hdist HCENTER) 0+ go BLR = V2 (hdist HRIGHT) 0 ++ -- > [..o..^.....] , o -> ^+ --+ hdist HCENTER = if xmin < xmaj then hw - xmin else negate (xmin - hw)++ -- > [..o..^.....] , o -> [+ --+ hdist HLEFT = negate xmin+ + -- > [..o..^.....] , o -> ]+ --+ hdist HRIGHT = xmaj++ vdist VCENTER = if ymin < ymaj then hh - ymin else negate (ymin - hh)+ vdist VBASE = negate ymin+ vdist VTOP = ymaj+++vtoOrigin :: (Fractional u, Ord u) + => RectAddress -> Orientation u -> Vec2 u+vtoOrigin addr ortt = negateV $ vtoRectAddress ortt addr++-- | Calculate the bounding box formed by locating the 'Orientation'+-- at the supplied point.+-- +orientationBounds :: Num u + => Orientation u -> Point2 u -> BoundingBox u+orientationBounds (Orientation xmin xmaj ymin ymaj) (P2 x y) = BBox llc urc+ where+ llc = P2 (x-xmin) (y-ymin)+ urc = P2 (x+xmaj) (y+ymaj)+++-- | Height of the orientation.+--+orientationWidth :: Num u => Orientation u -> u+orientationWidth (Orientation xmin xmaj _ _) = xmin + xmaj++-- | Height of the orientation.+--+orientationHeight :: Num u => Orientation u -> u+orientationHeight (Orientation _ _ ymin ymaj) = ymin + ymaj++--------------------------------------------------------------------------------+-- Rotation++-- | Rotate an Orientation about its origin (locus).+--+rotateOrientation :: (Real u, Floating u, Ord u) + => Radian -> Orientation u -> Orientation u+rotateOrientation ang (Orientation { or_x_minor = xmin+ , or_x_major = xmaj+ , or_y_minor = ymin+ , or_y_major = ymaj }) = + orthoOrientation bl br tl tr + where+ bl = rotateAbout ang zeroPt $ P2 (-xmin) (-ymin)+ br = rotateAbout ang zeroPt $ P2 xmaj (-ymin)+ tr = rotateAbout ang zeroPt $ P2 xmaj ymaj+ tl = rotateAbout ang zeroPt $ P2 (-xmin) ymaj+ ++orthoOrientation :: (Num u, Ord u)+ => Point2 u -> Point2 u -> Point2 u -> Point2 u + -> Orientation u+orthoOrientation (P2 x0 y0) (P2 x1 y1) (P2 x2 y2) (P2 x3 y3) = + Orientation { or_x_minor = abs $ min4 x0 x1 x2 x3+ , or_x_major = max4 x0 x1 x2 x3+ , or_y_minor = abs $ min4 y0 y1 y2 y3+ , or_y_major = max4 y0 y1 y2 y3+ }+++min4 :: Ord u => u -> u -> u -> u -> u+min4 a b c d = min (min a b) (min c d)++max4 :: Ord u => u -> u -> u -> u -> u+max4 a b c d = max (max a b) (max c d)+++--------------------------------------------------------------------------------+-- Extending an arm of the orientation++extendOrientation :: Num u + => u -> u -> u -> u -> Orientation u -> Orientation u+extendOrientation dxl dxr dyd dyu (Orientation xmin xmaj ymin ymaj) = + Orientation (xmin+dxl) (xmaj+dxr) (ymin+dyd) (ymaj+dyu)++extendOLeft :: Num u => u -> Orientation u -> Orientation u+extendOLeft u (Orientation xmin xmaj ymin ymaj) = + Orientation (u+xmin) xmaj ymin ymaj+++extendORight :: Num u => u -> Orientation u -> Orientation u+extendORight u (Orientation xmin xmaj ymin ymaj) = + Orientation xmin (u+xmaj) ymin ymaj++extendODown :: Num u => u -> Orientation u -> Orientation u+extendODown u (Orientation xmin xmaj ymin ymaj) = + Orientation xmin xmaj (u+ymin) ymaj++extendOUp :: Num u => u -> Orientation u -> Orientation u+extendOUp u (Orientation xmin xmaj ymin ymaj) = + Orientation xmin xmaj ymin (u+ymaj)+++--------------------------------------------------------------------------------+-- Note these are fills not pads...+++fillHEven :: (Fractional u, Ord u) + => u -> Orientation u -> Orientation u+fillHEven w ortt@(Orientation xmin xmaj _ _) = + if w > ow then ortt { or_x_minor = xmin + hdx+ , or_x_major = xmaj + hdx } + else ortt+ where+ ow = xmin + xmaj+ hdx = 0.5 * (w - ow)+++fillXMinor :: (Num u, Ord u) + => u -> Orientation u -> Orientation u+fillXMinor w ortt@(Orientation xmin xmaj _ _) = + if w > ow then ortt { or_x_minor = xmin + dx } else ortt+ where+ ow = xmin + xmaj+ dx = w - ow++fillXMajor :: (Num u, Ord u)+ => u -> Orientation u -> Orientation u+fillXMajor w ortt@(Orientation xmin xmaj _ _) = + if w > ow then ortt { or_x_major = xmaj + dx } else ortt+ where+ ow = xmin + xmaj+ dx = w - ow++fillVEven :: (Fractional u, Ord u) + => u -> Orientation u -> Orientation u+fillVEven h ortt@(Orientation _ _ ymin ymaj) = + if h > oh then ortt { or_y_minor = ymin + hdy+ , or_y_major = ymaj + hdy } + else ortt+ where+ oh = ymin + ymaj+ hdy = 0.5 * (h - oh)++fillYMinor :: (Num u, Ord u) + => u -> Orientation u -> Orientation u+fillYMinor h ortt@(Orientation _ _ ymin ymaj) = + if h > oh then ortt { or_y_minor = ymin + dy } else ortt+ where+ oh = ymin + ymaj+ dy = h - oh+++fillYMajor :: (Num u, Ord u) + => u -> Orientation u -> Orientation u+fillYMajor h ortt@(Orientation _ _ ymin ymaj) = + if h > oh then ortt { or_y_major = ymaj + dy } else ortt+ where+ oh = ymin + ymaj+ dy = h - oh+++--------------------------------------------------------------------------------+-- Combining Orientation++-- Note - there are lots of concatenations (due to alignment) +-- we need a consistent name scheme...+++-- | Second Orientation is moved /to the right/ of the first along+-- the /spine/ i.e the baseline.+--+spineRight :: (Num u, Ord u) + => Orientation u -> Orientation u -> Orientation u+spineRight (Orientation xmin0 xmaj0 ymin0 ymaj0) + (Orientation xmin1 xmaj1 ymin1 ymaj1) = + Orientation { or_x_minor = xmin0+ , or_x_major = xmaj0 + xmin1 + xmaj1 + , or_y_minor = max ymin0 ymin1+ , or_y_major = max ymaj0 ymaj1+ }+++-- | Second Orientation is moved /below/ the first along the spine+-- i.e. the vertical point between the left minor and right major+-- (not the same as the horizontal center).+--+spineBelow :: (Num u, Ord u) + => Orientation u -> Orientation u -> Orientation u+spineBelow (Orientation xmin0 xmaj0 ymin0 ymaj0) + (Orientation xmin1 xmaj1 ymin1 ymaj1) = + Orientation { or_x_minor = max xmin0 xmin1+ , or_x_major = max xmaj0 xmaj1+ , or_y_minor = ymin0 + ymaj1 + ymin1+ , or_y_major = ymaj0+ }+++-- | xmin and xmaj same as left.+--+halignBottomO :: (Num u, Ord u) + => Orientation u -> Orientation u -> Orientation u+halignBottomO (Orientation xmin0 xmaj0 ymin0 ymaj0) + (Orientation xmin1 xmaj1 ymin1 ymaj1) = + let hr = ymin1 + ymaj1+ in Orientation { or_x_minor = xmin0+ , or_x_major = xmaj0 + xmin1 + xmaj1+ , or_y_minor = ymin0+ , or_y_major = max ymaj0 (hr - ymin0)+ }++++++-- | xmin same as left.+--+halignCenterO :: (Fractional u, Ord u) + => Orientation u -> Orientation u -> Orientation u+halignCenterO (Orientation xmin0 xmaj0 ymin0 ymaj0) + (Orientation xmin1 xmaj1 ymin1 ymaj1) = + let hl = ymin0 + ymaj0+ hr = ymin1 + ymaj1+ half_diff = 0.5 * (hr - hl)+ in Orientation + { or_x_minor = xmin0+ , or_x_major = xmaj0 + xmin1 + xmaj1+ , or_y_minor = if hl >= hr then ymin0 else (ymin0 + half_diff)+ , or_y_major = if hl >= hr then ymaj0 else (ymaj0 + half_diff)+ }++++-- | xmin and ymaj same as left.+--+halignTopO :: (Num u, Ord u) + => Orientation u -> Orientation u -> Orientation u+halignTopO (Orientation xmin0 xmaj0 ymin0 ymaj0) + (Orientation xmin1 xmaj1 ymin1 ymaj1) = + let hr = ymin1 + ymaj1+ in Orientation { or_x_minor = xmin0+ , or_x_major = xmaj0 + xmin1 + xmaj1+ , or_y_minor = max ymin0 (hr - ymaj0)+ , or_y_major = ymaj0+ }++-- | Align second below - xmin and ymaj are same as left.+--+valignLeftO :: (Fractional u, Ord u) + => Orientation u -> Orientation u -> Orientation u+valignLeftO (Orientation xmin0 xmaj0 ymin0 ymaj0) + (Orientation xmin1 xmaj1 ymin1 ymaj1) = + let wr = xmin1 + xmaj1+ in Orientation { or_x_minor = xmin0+ , or_x_major = max xmaj0 (wr - xmin0)+ , or_y_minor = ymin0 + ymin1 + ymaj1+ , or_y_major = ymaj0+ }++++-- | Align second below - ymaj same as left.+--+valignCenterO :: (Fractional u, Ord u) + => Orientation u -> Orientation u -> Orientation u+valignCenterO (Orientation xmin0 xmaj0 ymin0 ymaj0) + (Orientation xmin1 xmaj1 ymin1 ymaj1) = + let wl = xmin0 + xmaj0+ wr = xmin1 + xmaj1+ half_diff = 0.5 * (wr - wl)+ in Orientation + { or_x_minor = if wl >= wr then xmin0 else (xmin0 + half_diff)+ , or_x_major = if wl >= wr then xmaj0 else (xmaj0 + half_diff)+ , or_y_minor = ymin0 + ymin1 + ymaj1+ , or_y_major = ymaj0 + }+++-- | Align second below - xmaj and ymaj are same as left.+--+valignRightO :: (Fractional u, Ord u) + => Orientation u -> Orientation u -> Orientation u+valignRightO (Orientation xmin0 xmaj0 ymin0 ymaj0) + (Orientation xmin1 xmaj1 ymin1 ymaj1) = + let wr = xmin1 + xmaj1+ in Orientation { or_x_minor = max xmin0 (wr - xmaj0)+ , or_x_major = xmaj0 + , or_y_minor = ymin0 + ymin1 + ymaj1+ , or_y_major = ymaj0 + }+++--------------------------------------------------------------------------------+-- Binary start pos displacement++-- Note - these can be made a lot clearer...++upDown :: Num u => u -> u -> u+upDown u d = u - d++downUp :: Num u => u -> u -> u+downUp d u = negate d + u++-- | Move second right.+--+spinemoveH :: Num u => Orientation u -> Orientation u -> Vec2 u+spinemoveH op0 op1 = V2 hdist 0+ where+ hdist = or_x_major op0 + or_x_minor op1++-- | Move second below.+--+spinemoveV :: Num u => Orientation u -> Orientation u -> Vec2 u+spinemoveV op0 op1 = V2 0 (negate vdist)+ where+ vdist = or_y_minor op0 + or_y_major op1+ +++binmoveHBottom :: Num u => Orientation u -> Orientation u -> Vec2 u+binmoveHBottom op0 op1 = V2 hdist vdist+ where+ hdist = or_x_major op0 + or_x_minor op1+ vdist = downUp (or_y_minor op0) (or_y_minor op1)+ ++binmoveHCenter :: (Fractional u, Ord u) + => Orientation u -> Orientation u -> Vec2 u+binmoveHCenter (Orientation _ xmaj0 ymin0 ymaj0) + (Orientation xmin1 _ ymin1 ymaj1) = + V2 hdist vdist+ where+ h0 = ymin0 + ymaj0+ h1 = ymin1 + ymaj1+ half_diff = abs $ 0.5 * (h1 - h0)+ hdist = xmaj0 + xmin1+ vdist = if h0 >= h1 then downUp ymin0 (half_diff + ymin1)+ else upDown (ymaj0 + half_diff) ymaj1++++binmoveHTop :: Num u => Orientation u -> Orientation u -> Vec2 u+binmoveHTop op0 op1 = V2 hdist vdist+ where+ hdist = or_x_major op0 + or_x_minor op1+ vdist = upDown (or_y_major op0) (or_y_major op1)+++leftRight :: Num u => u -> u -> u+leftRight l r = negate l + r+++rightLeft :: Num u => u -> u -> u+rightLeft r l = r - l+++binmoveVLeft :: Num u => Orientation u -> Orientation u -> Vec2 u+binmoveVLeft op0 op1 = V2 hdist vdist+ where+ hdist = leftRight (or_x_minor op0) (or_x_minor op1)+ vdist = negate $ or_y_minor op0 + or_y_major op1+++binmoveVCenter :: (Fractional u, Ord u) + => Orientation u -> Orientation u -> Vec2 u+binmoveVCenter (Orientation xmin0 xmaj0 ymin0 _) + (Orientation xmin1 xmaj1 _ ymaj1) = + V2 hdist vdist+ where+ w0 = xmin0 + xmaj0+ w1 = xmin1 + xmaj1+ half_diff = abs $ 0.5 * (w1 - w0)+ hdist = if w0 >= w1 then leftRight xmin0 (half_diff + xmin1)+ else rightLeft (xmaj0 + half_diff) xmaj1+ vdist = negate $ ymin0 + ymaj1++++binmoveVRight :: Num u => Orientation u -> Orientation u -> Vec2 u+binmoveVRight op0 op1 = V2 hdist vdist+ where+ hdist = rightLeft (or_x_major op0) (or_x_major op1)+ vdist = negate $ or_y_minor op0 + or_y_major op1+
− src/Wumpus/Basic/Kernel/Objects/TraceDrawing.hs
@@ -1,338 +0,0 @@-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Basic.Kernel.Objects.TraceDrawing--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : stephen.tetley@gmail.com--- Stability : unstable--- Portability : GHC ------ Drawing with /trace/ - a Writer like monad collecting --- intermediate graphics - and /drawing context/ - a reader monad--- of attributes - font_face, fill_colour etc.--------------------------------------------------------------------------------------module Wumpus.Basic.Kernel.Objects.TraceDrawing- (-- TraceDrawing- , DTraceDrawing- , TraceDrawingT- , DTraceDrawingT-- , runTraceDrawing- , execTraceDrawing- , evalTraceDrawing- , runTraceDrawingT- , execTraceDrawingT- , evalTraceDrawingT-- , liftToPictureU- , liftToPictureMb- , mbPictureU- -- , query- , draw- , xdraw- , drawi- , drawi_- , xdrawi- , xdrawi_-- , node- , nodei-- ) where---import Wumpus.Basic.Kernel.Base.BaseDefs-import Wumpus.Basic.Kernel.Base.ContextFun-import Wumpus.Basic.Kernel.Base.DrawingContext-import Wumpus.Basic.Kernel.Base.WrappedPrimitive-import Wumpus.Basic.Kernel.Objects.BaseObjects-import Wumpus.Basic.Kernel.Objects.Graphic--import Wumpus.Core -- package: wumpus-core--import Control.Applicative-import Control.Monad-import Data.Monoid------ Note - TraceDrawing run \once\ - it is supplied with the starting--- environment (DrawingContext) and returns a Picture.------ Other Wumpus monads (e.g. Turtle) will typically be run inside--- the TraceDrawing monad as a local effect, rather than built into a --- transformer stack.------newtype TraceDrawing u a = TraceDrawing { - getTraceDrawing :: DrawingContext -> (a, HPrim u) }--newtype TraceDrawingT u m a = TraceDrawingT { - getTraceDrawingT :: DrawingContext -> m (a, HPrim u) }---type DTraceDrawing a = TraceDrawing Double a-type DTraceDrawingT m a = TraceDrawingT Double m a----type instance MonUnit (TraceDrawing u) = u-type instance MonUnit (TraceDrawingT u m) = u------ Functor--instance Functor (TraceDrawing u) where- fmap f ma = TraceDrawing $ \ctx -> - let (a,w) = getTraceDrawing ma ctx in (f a,w)---instance Monad m => Functor (TraceDrawingT u m) where- fmap f ma = TraceDrawingT $ \ctx -> - getTraceDrawingT ma ctx >>= \(a,w) -> return (f a,w)------ Applicative--instance Applicative (TraceDrawing u) where- pure a = TraceDrawing $ \_ -> (a, mempty)- mf <*> ma = TraceDrawing $ \ctx -> - let (f,w1) = getTraceDrawing mf ctx- (a,w2) = getTraceDrawing ma ctx- in (f a, w1 `mappend` w2)---instance Monad m => Applicative (TraceDrawingT u m) where- pure a = TraceDrawingT $ \_ -> return (a,mempty)- mf <*> ma = TraceDrawingT $ \ctx -> - getTraceDrawingT mf ctx >>= \(f,w1) ->- getTraceDrawingT ma ctx >>= \(a,w2) ->- return (f a, w1 `mappend` w2)---- Monad--instance Monad (TraceDrawing u) where- return a = TraceDrawing $ \_ -> (a, mempty)- ma >>= k = TraceDrawing $ \ctx -> - let (a,w1) = getTraceDrawing ma ctx- (b,w2) = (getTraceDrawing . k) a ctx- in (b,w1 `mappend` w2)- ----instance Monad m => Monad (TraceDrawingT u m) where- return a = TraceDrawingT $ \_ -> return (a, mempty)- ma >>= k = TraceDrawingT $ \ctx -> - getTraceDrawingT ma ctx >>= \(a,w1) ->- (getTraceDrawingT . k) a ctx >>= \(b,w2) -> - return (b, w1 `mappend` w2)- ------ TraceM ------ Note - @ state `mappend` a @ means the first expression in a --- monadic drawing is the first element in the output file. It is--- also \*\* at the back \*\* in the the Z-Order.------ Some control over the Z-Order, possibly by adding /layers/ to --- the drawing model would be valuable. --- --instance TraceM (TraceDrawing u) where- trace a = TraceDrawing $ \_ -> ((), a)---instance Monad m => TraceM (TraceDrawingT u m) where- trace a = TraceDrawingT $ \_ -> return ((), a)------ DrawingCtxM--instance DrawingCtxM (TraceDrawing u) where- askDC = TraceDrawing $ \ctx -> (ctx, mempty)- localize upd ma = TraceDrawing $ \ctx -> getTraceDrawing ma (upd ctx)----instance Monad m => DrawingCtxM (TraceDrawingT u m) where- askDC = TraceDrawingT $ \ctx -> return (ctx,mempty)- localize upd ma = TraceDrawingT $ \ctx -> getTraceDrawingT ma (upd ctx)------ Note - the result type of runTraceDrawing and friends needs more --- thought and may change. ------ Possibly a wrapped HPrim that only supports concat and safe--- extraction is best.------ Or it could generate a picture, but then separate drawings--- need the picture combinators to put them together. --- ---runTraceDrawing :: DrawingContext -> TraceDrawing u a -> (a, HPrim u)-runTraceDrawing ctx ma = getTraceDrawing ma ctx---- | Run the drawing returning only the output it produces, drop--- any answer from the monadic computation.----execTraceDrawing :: DrawingContext -> TraceDrawing u a -> HPrim u-execTraceDrawing ctx ma = snd $ runTraceDrawing ctx ma---- | Run the drawing ignoring the output it produces, return the --- answer from the monadic computation.------ Note - this useful for testing, generally one would want the --- opposite behaviour (return the drawing, ignore than the --- answer).--- -evalTraceDrawing :: DrawingContext -> TraceDrawing u a -> a-evalTraceDrawing ctx ma = fst $ runTraceDrawing ctx ma----runTraceDrawingT :: Monad m - => DrawingContext -> TraceDrawingT u m a -> m (a, HPrim u) -runTraceDrawingT ctx ma = getTraceDrawingT ma ctx--execTraceDrawingT :: Monad m - => DrawingContext -> TraceDrawingT u m a -> m (HPrim u)-execTraceDrawingT ctx ma = liftM snd $ runTraceDrawingT ctx ma---evalTraceDrawingT :: Monad m - => DrawingContext -> TraceDrawingT u m a -> m a-evalTraceDrawingT ctx ma = liftM fst $ runTraceDrawingT ctx ma------ | /Unsafe/ promotion of @HPrim@ to @Picture@.------ If the HPrim is empty, a run-time error is thrown.--- -liftToPictureU :: (Real u, Floating u, FromPtSize u) => HPrim u -> Picture u-liftToPictureU hf = - let prims = hprimToList hf in if null prims then errK else frame prims- where- errK = error "toPictureU - empty prims list."---- | /Safe/ promotion of @HPrim@ to @(Maybe Picture)@.------ If the HPrim is empty, then @Nothing@ is returned.--- -liftToPictureMb :: (Real u, Floating u, FromPtSize u) - => HPrim u -> Maybe (Picture u)-liftToPictureMb hf = let prims = hprimToList hf in - if null prims then Nothing else Just (frame prims)------ | /Unsafe/ promotion of @(Maybe Picture)@ to @Picture@.------ This is equivalent to:------ > fromMaybe (error "empty") $ pic------ This function is solely a convenience, using it saves one --- import and a few characters.------ If the supplied value is @Nothing@ a run-time error is thrown.--- -mbPictureU :: (Real u, Floating u, FromPtSize u) - => Maybe (Picture u) -> Picture u-mbPictureU Nothing = error "mbPictureU - empty picture."-mbPictureU (Just a) = a---- Note - need an equivalent to Parsec\`s parseTest that provides--- a very simple way to run graphics without concern for return --- type or initial drawing context.------------------------------------------------------------------------------------query :: DrawingCtxM m => CF a -> m a-query df = askDC >>= \ctx -> return $ runCF ctx df----- | Draw a Graphic taking the drawing style from the --- /drawing context/. ------ This operation is analogeous to @tell@ in a Writer monad.--- -draw :: (TraceM m, DrawingCtxM m, u ~ MonUnit m) => Graphic u -> m ()-draw gf = askDC >>= \ctx -> trace (collectH $ snd $ runCF ctx gf)---- | Hyperlink version of 'draw'.----xdraw :: (TraceM m, DrawingCtxM m, u ~ MonUnit m) - => XLink -> Graphic u -> m ()-xdraw xl gf = draw (hyperlink xl gf)------ | Draw an Image taking the drawing style from the --- /drawing context/. ------ The graphic representation of the Image is drawn in the Trace --- monad, and the result is returned.--- -drawi :: (TraceM m, DrawingCtxM m, u ~ MonUnit m) => Image u a -> m a-drawi img = askDC >>= \ctx -> - let (a,o) = runCF ctx img in trace (collectH o) >> return a---- | Forgetful 'drawi'.----drawi_ :: (TraceM m, DrawingCtxM m, MonUnit m ~ u) => Image u a -> m ()-drawi_ img = drawi img >> return ()----- | Hyperlink version of 'drawi'.----xdrawi :: (TraceM m, DrawingCtxM m, MonUnit m ~ u) - => XLink -> Image u a -> m a-xdrawi xl img = drawi (hyperlink xl img)----- | Forgetful 'xdrawi'.----xdrawi_ :: (TraceM m, DrawingCtxM m, MonUnit m ~ u)- => XLink -> Image u a -> m ()-xdrawi_ xl img = xdrawi xl img >> return ()-----node :: (TraceM m, DrawingCtxM m, PointSupplyM m, MonUnit m ~ u) - => LocGraphic u -> m ()-node gf = askDC >>= \ctx -> - position >>= \pt -> - let (_,prim) = runCF1 ctx pt gf in trace (collectH prim)---nodei :: (TraceM m, DrawingCtxM m, PointSupplyM m, MonUnit m ~ u) - => LocImage u a -> m a-nodei imgL = askDC >>= \ctx -> - position >>= \pt -> - let (a,o) = runCF ctx (apply1R1 imgL pt)- in trace (collectH o) >> return a-
+ src/Wumpus/Basic/Kernel/Objects/Trail.hs view
@@ -0,0 +1,1042 @@+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.Kernel.Objects.Trial+-- Copyright : (c) Stephen Tetley 2011+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : highly unstable+-- Portability : GHC +--+-- /Trails/ - prototype paths. Less resource heavy than the Path+-- object in Wumpus-Drawing.+-- +-- @CatTrail@ supports concatenation. @AnaTrail@ supports +-- /initial displacement/ - this can account for drawing +-- rectangles from their center, for example.+--+--------------------------------------------------------------------------------++module Wumpus.Basic.Kernel.Objects.Trail+ (++ -- * Trail types+ TrailSegment(..)+ , CatTrail+ , AnaTrail++ -- * Trail operations+ , renderAnaTrail+ , renderCatTrail++ , destrAnaTrail+ , destrCatTrail++ , anaCatTrail+ , modifyAna+ + , trailIterateLocus++ , anaTrailPoints+++ , catline+ , catcurve+ , orthoCatTrail++ , diffCurve+ , diffLines+ ++ -- * Shape trails+ , rectangleTrail+ , diamondTrail+ , polygonTrail+ , wedgeTrail+++ -- * Named Trail constructors+ , trail_up+ , trail_down+ , trail_left+ , trail_right++ , trail_north+ , trail_south+ , trail_east+ , trail_west+ , trail_north_east+ , trail_north_west+ , trail_south_east+ , trail_south_west++ , trail_up_left+ , trail_up_right+ , trail_down_left+ , trail_down_right++ , trail_para+ , trail_perp++ , trail_theta_up+ , trail_theta_down+ , trail_theta_left+ , trail_theta_right++ , trail_theta_north+ , trail_theta_south+ , trail_theta_east+ , trail_theta_west+ , trail_theta_north_east+ , trail_theta_north_west+ , trail_theta_south_east+ , trail_theta_south_west++ , trail_theta_up_left+ , trail_theta_up_right+ , trail_theta_down_left+ , trail_theta_down_right++ , trail_theta_adj_grazing+ , trail_theta_bkwd_adj_grazing++++ , semicircleTrail+ , semiellipseTrail+ , minorCircleSweep+ , circleSweep+ , circularArc++ , sineWave+ , sineWave1+ , squareWave+ , sawtoothWave+ , squiggleWave+ , semicircleWave++ , triCurve+ , rectCurve+ , trapCurve+ , bowCurve+ , wedgeCurve+ , loopCurve++ ) where++import Wumpus.Basic.Kernel.Base.BaseDefs+import Wumpus.Basic.Kernel.Base.QueryDC+import Wumpus.Basic.Kernel.Objects.Displacement+import Wumpus.Basic.Kernel.Objects.DrawingPrimitives+import Wumpus.Basic.Kernel.Objects.Image+import Wumpus.Basic.Kernel.Objects.LocImage+import Wumpus.Basic.Utils.HList++import Wumpus.Core -- package: wumpus-core++import Data.AffineSpace -- package: vector-space+import Data.VectorSpace++import Data.List ( unfoldr )+import Data.Monoid+++--------------------------------------------------------------------------------+-- Trail types ++-- | Trail with an initial (undrawn) displacement - an anacrusis.+--+-- This allows trails to represent centered objects.+--+data AnaTrail u = AnaTrail+ { pt_init_vec :: Vec2 u+ , pt_segments :: [TrailSegment u]+ }+ deriving (Eq,Ord,Show)++type instance DUnit (AnaTrail u) = u++-- | Trail supporting concatenation.+--+newtype CatTrail u = CatTrail { getCatTrail :: H (TrailSegment u) }++type instance DUnit (CatTrail u) = u+++-- | Trail segment - trails are /prototype/ paths, so the are +-- built from the usual straight lines and Bezier curves.+--+data TrailSegment u = TLine (Vec2 u)+ | TCurve (Vec2 u) (Vec2 u) (Vec2 u)+ deriving (Eq,Ord,Show)++type instance DUnit (TrailSegment u) = u+++instance Functor TrailSegment where+ fmap f (TLine v1) = TLine $ fmap f v1+ fmap f (TCurve v1 v2 v3) = TCurve (fmap f v1) (fmap f v2) (fmap f v3)+++instance Monoid (CatTrail u) where+ mempty = CatTrail emptyH+ a `mappend` b = CatTrail $ getCatTrail a `appendH` getCatTrail b+++--------------------------------------------------------------------------------+-- Trail operations++-- | Render a 'CatTrail' to make a drawable 'LocGraphic'.+--+renderCatTrail :: InterpretUnit u => PathMode -> CatTrail u -> LocGraphic u+renderCatTrail mode (CatTrail ct) = promoteLoc $ \pt -> + drawTrailBody mode (toListH ct) pt +++-- | Render an 'AnaTrail' to make a drawable 'LocGraphic'.+--+renderAnaTrail :: InterpretUnit u => PathMode -> AnaTrail u -> LocGraphic u+renderAnaTrail mode (AnaTrail v0 xs) = promoteLoc $ \pt -> + drawTrailBody mode xs (pt .+^ v0)+++-- | Note - this optimizes contiguous lines that share the same +-- direction. +--+drawTrailBody :: InterpretUnit u + => PathMode -> [TrailSegment u] -> Point2 u -> Graphic u+drawTrailBody mode ts pt = + normalizeCtxF pt >>= \dpt -> + mapM normalizeCtxF ts >>= \dxs ->+ dcPath mode $ relPrimPath dpt $ stepA id dxs+ where+ stepA f [] = toListH f+ stepA f (TLine v1:ys) = stepB f (vdirection v1) v1 ys+ stepA f (TCurve v1 v2 v3:ys) = stepA (f `snocH` relCurveTo v1 v2 v3) ys++ stepB f dir v0 (TLine v1:ys) + | vdirection v1 == dir = stepB f dir (v0 ^+^ v1) ys+ stepB f _ v0 ys = stepA (f `snocH` relLineTo v0) ys+++-- | /Destructor/ for the opaque 'AnaTrail' type.+--+destrAnaTrail :: AnaTrail u -> (Vec2 u, [TrailSegment u])+destrAnaTrail (AnaTrail v0 ss) = (v0,ss)++-- | /Destructor/ for the opaque 'CatTrail' type.+--+destrCatTrail :: CatTrail u -> [TrailSegment u]+destrCatTrail = toListH . getCatTrail++++-- | Turn a 'CatTrail' into a 'AnaTrail'.+--+anaCatTrail :: Vec2 u -> CatTrail u -> AnaTrail u+anaCatTrail vinit cat = AnaTrail { pt_init_vec = vinit+ , pt_segments = getCatTrail cat []+ }+++modifyAna :: (Vec2 u -> Vec2 u) -> AnaTrail u -> AnaTrail u+modifyAna upd (AnaTrail v1 body) = AnaTrail (upd v1) body++-- | Create a AnaTrail from the vector list - each vector in the +-- input list iterates to the start point rather then the +-- cumulative tip.+--+-- When the AnaTrail is run, the supplied point is the /locus/ of +-- the path and it does not form part of the path proper.+-- +-- Like 'trailStartIsLocus', this constructor is typically used to +-- make /shape paths/. Some shapes are easier to express as +-- iterated displacements of the center rather than +-- /turtle drawing/. +-- +trailIterateLocus :: Num u => [Vec2 u] -> AnaTrail u+trailIterateLocus [] = AnaTrail zeroVec []+trailIterateLocus (v0:xs) = AnaTrail v0 (step v0 xs)+ where+ step v1 [] = [ TLine (v0 ^-^ v1) ]+ step v1 (v2:vs) = TLine (v2 ^-^ v1) : step v2 vs+++anaTrailPoints :: InterpretUnit u => AnaTrail u -> LocQuery u [Point2 u]+anaTrailPoints (AnaTrail v0 ts) = qpromoteLoc $ \pt -> + return $ step (pt .+^ v0) ts+ where+ step p1 [] = [p1]+ step p1 (TLine v1:xs) = p1 : step (p1 .+^ v1) xs+ step p1 (TCurve v1 v2 v3 :xs) = let p2 = p1 .+^ v1+ p3 = p2 .+^ v2+ p4 = p3 .+^ v3 + in p1 : p2 : p3 : step p4 xs+++catline :: Vec2 u -> CatTrail u +catline = CatTrail . wrapH . TLine+++-- | Alternative to @catline@, specifying the vector components +-- rather the vector itself.+--+-- (cf. orthoVec from Wumpus-Core)+--+orthoCatTrail :: Floating u => u -> u -> Radian -> CatTrail u +orthoCatTrail x y ang = catline (orthoVec x y ang)+++catcurve :: Vec2 u -> Vec2 u -> Vec2 u -> CatTrail u+catcurve v1 v2 v3 = CatTrail $ wrapH $ TCurve v1 v2 v3++-- | Form a Bezier CatTrail from the vectors between four control +-- points.+--+diffCurve :: Num u + => Point2 u -> Point2 u -> Point2 u -> Point2 u -> CatTrail u+diffCurve p0 p1 p2 p3 = + catcurve (pvec p0 p1) (pvec p1 p2) (pvec p2 p3)++++-- | Form a CatTrail from the linear segment joining the list of +-- points.+-- +-- Some configurations of vectors seem easier to specify using +-- located points then making them coordinate free by taking +-- the joining vectors.+--+diffLines :: Num u => [Point2 u] -> CatTrail u+diffLines [] = mempty+diffLines (x:xs) = step mempty x xs + where+ step ac a (b:bs) = step (ac `mappend` catline (pvec a b)) b bs+ step ac _ [] = ac++++--------------------------------------------------------------------------------+-- Shape Trails++-- | 'rectangleTrail' : @ width * height -> AnaTrail @+--+rectangleTrail :: Fractional u => u -> u -> AnaTrail u+rectangleTrail w h = + AnaTrail { pt_init_vec = ctr_to_bl + , pt_segments = map TLine spec+ }+ where+ ctr_to_bl = vec (negate $ 0.5*w) (negate $ 0.5*h)+ spec = [ go_right w, go_up h, go_left w, go_down h ]+++++-- | 'diamondTrail' : @ half_width * half_height -> AnaTrail @+--+diamondTrail :: Num u => u -> u -> AnaTrail u+diamondTrail hw hh = trailIterateLocus [ vs,ve,vn,vw ]+ where+ vs = vvec (-hh)+ ve = hvec hw+ vn = vvec hh+ vw = hvec (-hw)+++-- | 'polygonTrail' : @ num_points * radius -> AnaTrail @ +--+polygonTrail :: Floating u => Int -> u -> AnaTrail u+polygonTrail n radius = trailIterateLocus $ unfoldr phi (0,top)+ where+ top = 0.5*pi+ theta = (2*pi) / fromIntegral n+ + phi (i,ang) | i < n = Just (avec ang radius, (i+1,ang+theta))+ | otherwise = Nothing++++-- | wedgeTrail : radius * apex_angle+-- +-- Wedge is drawn at the apex.+--+wedgeTrail :: (Real u, Floating u) + => u -> Radian -> Radian -> AnaTrail u+wedgeTrail radius ang theta = + anaCatTrail zeroVec $ line_in `mappend` w_arc `mappend` line_out+ where+ half_ang = 0.5 * ang + line_in = catline $ avec (theta + half_ang) radius+ line_out = catline $ avec (theta - half_ang) (-radius)+ w_arc = circularArcCW ang radius (theta - half_pi)++++--------------------------------------------------------------------------------+-- Named Trail constructors++trail_up :: Num u => u -> CatTrail u+trail_up = catline . go_up++trail_down :: Num u => u -> CatTrail u+trail_down = catline . go_down++trail_left :: Num u => u -> CatTrail u+trail_left = catline . go_left++trail_right :: Num u => u -> CatTrail u+trail_right = catline . go_right+++trail_north :: Num u => u -> CatTrail u+trail_north = trail_up++trail_south :: Num u => u -> CatTrail u+trail_south = catline . go_down++trail_east :: Num u => u -> CatTrail u+trail_east = catline . go_right++trail_west :: Num u => u -> CatTrail u+trail_west = catline . go_left+++trail_north_east :: Floating u => u -> CatTrail u+trail_north_east = catline . go_north_east++trail_north_west :: Floating u => u -> CatTrail u+trail_north_west = catline . go_north_west++trail_south_east :: Floating u => u -> CatTrail u+trail_south_east = catline . go_south_east++trail_south_west :: Floating u => u -> CatTrail u+trail_south_west = catline . go_south_west+++trail_up_left :: Num u => u -> CatTrail u+trail_up_left = catline . go_up_left++trail_up_right :: Num u => u -> CatTrail u+trail_up_right = catline . go_up_right++trail_down_left :: Num u => u -> CatTrail u+trail_down_left = catline . go_down_left++trail_down_right :: Num u => u -> CatTrail u+trail_down_right = catline . go_down_right+++trail_perp :: Floating u => u -> Radian -> CatTrail u+trail_perp = trail_theta_up++trail_para :: Floating u => u -> Radian -> CatTrail u+trail_para = trail_theta_right+++trail_theta_up :: Floating u => u -> Radian -> CatTrail u+trail_theta_up u = catline . theta_up u++trail_theta_down :: Floating u => u -> Radian -> CatTrail u+trail_theta_down u = catline . theta_down u++trail_theta_left :: Floating u => u -> Radian -> CatTrail u+trail_theta_left u = catline . theta_left u++trail_theta_right :: Floating u => u -> Radian -> CatTrail u+trail_theta_right u = catline . theta_right u+++trail_theta_north :: Floating u => u -> Radian -> CatTrail u+trail_theta_north = trail_theta_up++trail_theta_south :: Floating u => u -> Radian -> CatTrail u+trail_theta_south = trail_theta_down++trail_theta_east :: Floating u => u -> Radian -> CatTrail u+trail_theta_east = trail_theta_right++trail_theta_west :: Floating u => u -> Radian -> CatTrail u+trail_theta_west = trail_theta_left+++trail_theta_north_east :: Floating u => u -> Radian -> CatTrail u+trail_theta_north_east u = catline . theta_north_east u++trail_theta_north_west :: Floating u => u -> Radian -> CatTrail u+trail_theta_north_west u = catline . theta_north_west u++trail_theta_south_east :: Floating u => u -> Radian -> CatTrail u+trail_theta_south_east u = catline . theta_south_east u++trail_theta_south_west :: Floating u => u -> Radian -> CatTrail u+trail_theta_south_west u = catline . theta_south_west u+++trail_theta_up_left :: Floating u => u -> Radian -> CatTrail u+trail_theta_up_left u = catline . theta_up_left u++trail_theta_up_right :: Floating u => u -> Radian -> CatTrail u+trail_theta_up_right u = catline . theta_up_right u++trail_theta_down_left :: Floating u => u -> Radian -> CatTrail u+trail_theta_down_left u = catline . theta_down_left u++trail_theta_down_right :: Floating u => u -> Radian -> CatTrail u+trail_theta_down_right u = catline . theta_down_right u++++-- | Return the line @a-o@ when supplied length of @b-o@ and the +-- grazing angle @boa@:+--+-- > a+-- > .\+-- > . \+-- > ..b..o+--+-- This is useful for building arrowhead vectors.+--+trail_theta_adj_grazing :: Floating u => u -> Radian -> Radian -> CatTrail u +trail_theta_adj_grazing adj_len ang = + catline . theta_adj_grazing adj_len ang+++-- | Return the line @o-c@ when supplied length of @b-o@ and the +-- grazing angle @boc@:+--+--+-- > ..b..o+-- > . /+-- > ./+-- > c+--+-- This is useful for building arrowhead vectors.+--+trail_theta_bkwd_adj_grazing :: Floating u => u -> Radian -> Radian -> CatTrail u +trail_theta_bkwd_adj_grazing adj_len ang = + catline . theta_bkwd_adj_grazing adj_len ang +++--------------------------------------------------------------------------------++--+-- DESIGN NOTE+--+-- Angle, unit width and number of repetitions (plus height etc.) +-- seems the best API, although this make fitting an issue.+--+++sineWave :: (Real u, Floating u) => Int -> u -> Radian -> CatTrail u+sineWave i unit ang = + mconcat $ replicate i $ sineWave1 (0.25 * unit) unit ang+++-- | One-phase sine wave. Height is parametric.+--+sineWave1 :: (Real u, Floating u)+ => u -> u -> Radian -> CatTrail u+sineWave1 h unit ang = + catcurve v1 (vdiff v1 v2) (vdiff v2 v3)+ `mappend` catcurve (vdiff v3 v4) (vdiff v4 v5) (vdiff v5 v6)+ `mappend` catcurve (vdiff v6 v7) (vdiff v7 v8) (vdiff v8 v9)+ `mappend` catcurve (vdiff v9 v10) (vdiff v10 v11) (vdiff v11 v12)+ where+ base1 = unit / 12+ h2 = h * (pi / 6)+ v1 = orthoVec base1 h2 ang+ v2 = orthoVec (2*base1) h ang+ v3 = orthoVec (3*base1) h ang+ v4 = orthoVec (4*base1) h ang+ v5 = orthoVec (5*base1) h2 ang+ v6 = orthoVec (6*base1) 0 ang+ v7 = orthoVec (7*base1) (-h2) ang+ v8 = orthoVec (8*base1) (-h) ang+ v9 = orthoVec (9*base1) (-h) ang+ v10 = orthoVec (10*base1) (-h) ang+ v11 = orthoVec (11*base1) (-h2) ang+ v12 = orthoVec (12*base1) 0 ang++++kappa :: Floating u => u+kappa = 4 * ((sqrt 2 - 1) / 3)+++--+-- DESIGN NOTE +--+-- The API seems better exposing ClockDirection as an argument +-- rather than providing two different functions for CW and CCW +-- (even though some functions are defined by independent CW and +-- CCW versions).+--+++-- | 'semicircleCW' : @ base_vector -> CatTrail @ +-- +-- Make an open semicircle from two Bezier curves. +--+-- Although this function produces an approximation of a +-- semicircle, the approximation seems fine in practice.+--+semicircleTrail :: (Real u, Floating u) + => ClockDirection -> Vec2 u -> CatTrail u+semicircleTrail CW = semicircleCW+semicircleTrail _ = semicircleCCW++-- | 'semicircleCW' : @ base_vector -> CatTrail @ +-- +-- Make a clockwise semicircle from two Bezier curves. Although +-- this function produces an approximation of a semicircle, the +-- approximation seems fine in practice.+--+semicircleCW :: (Real u, Floating u) => Vec2 u -> CatTrail u+semicircleCW base_vec =+ catcurve v1 (vdiff v1 v2) (vdiff v2 v3)+ `mappend` catcurve (vdiff v3 v4) (vdiff v4 v5) (vdiff v5 v6)+ where+ circum = vlength base_vec+ radius = 0.5 * circum+ ang = vdirection base_vec+ rl = radius * kappa+ + v1 = orthoVec 0 rl ang+ v2 = orthoVec (radius - rl) radius ang+ v3 = orthoVec radius radius ang++ v4 = orthoVec (radius + rl) radius ang+ v5 = orthoVec circum rl ang+ v6 = orthoVec circum 0 ang+++-- | 'semicircleCCW' : @ base_vector_vector -> CatTrail @ +-- +-- Make a counter-clockwise semicircle from two Bezier curves. +-- Although this function produces an approximation of a +-- semicircle, the approximation seems fine in practice.+--+semicircleCCW :: (Real u, Floating u) => Vec2 u -> CatTrail u+semicircleCCW base_vec =+ catcurve v1 (vdiff v1 v2) (vdiff v2 v3)+ `mappend` catcurve (vdiff v3 v4) (vdiff v4 v5) (vdiff v5 v6)+ where+ circum = vlength base_vec+ radius = 0.5 * circum+ ang = vdirection base_vec+ rl = radius * kappa+ + v1 = orthoVec 0 (-rl) ang+ v2 = orthoVec (radius - rl) (-radius) ang+ v3 = orthoVec radius (-radius) ang++ v4 = orthoVec (radius + rl) (-radius) ang+ v5 = orthoVec circum (-rl) ang+ v6 = orthoVec circum 0 ang+++-- | 'semicircleTrail' : @ clock_direction * ry * base_vector -> CatTrail @ +-- +-- Make an open semiellipse from two Bezier curves. +--+-- Although this function produces an approximation of a +-- semiellipse, the approximation seems fine in practice.+--+semiellipseTrail :: (Real u, Floating u) + => ClockDirection -> u -> Vec2 u -> CatTrail u+semiellipseTrail CW = semiellipseBasis theta_up+semiellipseTrail _ = semiellipseBasis theta_down++++-- | theta_up for CW, theta_down for CCW...+--+semiellipseBasis :: (Real u, Floating u) + => (u -> Radian -> Vec2 u) -> u -> Vec2 u -> CatTrail u+semiellipseBasis perpfun ry base_vec = + catcurve (pvec p00 c01) (pvec c01 c02) (pvec c02 p03)+ `mappend` catcurve (pvec p03 c04) (pvec c04 c05) (pvec c05 p06) + where+ rx = 0.5 * vlength base_vec+ ang = vdirection base_vec+ lrx = rx * kappa+ lry = ry * kappa+ para = theta_right `flip` ang+ perp = perpfun `flip` ang++ p00 = zeroPt .+^ theta_left rx ang+ c01 = p00 .+^ perp lry+ c02 = p03 .+^ para (-lrx)++ p03 = zeroPt .+^ perpfun ry ang + c04 = p03 .+^ para lrx+ c05 = p06 .+^ perp lry++ p06 = zeroPt .+^ theta_right rx ang+++-- | 'minorCircleSweep' : @ clock_direction * angle * radius +-- * inclination -> CatTrail @+--+-- > ang should be in the range 0 < ang <= 90deg.+--+minorCircleSweep :: (Real u, Floating u)+ => ClockDirection -> Radian -> u -> Radian -> CatTrail u+minorCircleSweep CW = minorCircleSweepCW +minorCircleSweep _ = minorCircleSweepCCW+++-- | 'minorCircleSweepCW' : @ angle * radius * inclination -> CatTrail @+--+-- > ang should be in the range 0 < ang <= 90deg.+--+minorCircleSweepCW :: (Real u, Floating u)+ => Radian -> u -> Radian -> CatTrail u+minorCircleSweepCW ang radius theta = + catcurve (pvec p0 p1) (pvec p1 p2) (pvec p2 p3)+ where+ kfactor = fromRadian $ ang / (0.5*pi)+ rl = kfactor * radius * kappa+ totang = circularModulo $ theta + (half_pi - ang)++ p0 = displace (theta_up radius theta) zeroPt+ p1 = displace (theta_right rl theta) p0+ p2 = displace (theta_up rl totang) p3+ p3 = displace (avec totang radius) zeroPt+++-- | 'minorCircleSweepCCW' : @ angle * radius * inclination -> CatTrail @+--+-- > ang should be in the range 0 < ang <= 90deg.+--+minorCircleSweepCCW :: (Real u, Floating u)+ => Radian -> u -> Radian -> CatTrail u+minorCircleSweepCCW ang radius theta = + catcurve (pvec p0 p1) (pvec p1 p2) (pvec p2 p3)+ where+ kfactor = fromRadian $ ang / (0.5*pi)+ rl = kfactor * radius * kappa+ totang = circularModulo $ theta - half_pi + ang++ p0 = displace (theta_down radius theta) zeroPt+ p1 = displace (theta_right rl theta) p0+ p2 = displace (theta_down rl totang) p3+ p3 = displace (avec totang radius) zeroPt+++-- | 'circleSweep' : @ clock_direction * apex_angle * radius +-- * inclination -> CatTrail @+--+-- > ang should be in the range 0 < ang < 360deg.+--+-- > if 0 < ang <= 90 returns 1 segment+-- > if 90 < ang <= 180 returns 2 segments+-- > if 180 < ang <= 270 returns 3 segments+-- > if 270 < ang < 360 returns 4 segmenets+--+circleSweep :: (Real u, Floating u)+ => ClockDirection -> Radian -> u -> Radian -> CatTrail u+circleSweep CW = circleSweepCW+circleSweep _ = circleSweepCCW+++-- | 'circleSweepCW' : @ apex_angle * radius * inclination -> CatTrail @+--+-- > ang should be in the range 0 < ang < 360deg.+--+-- > if 0 < ang <= 90 returns 1 segment+-- > if 90 < ang <= 180 returns 2 segments+-- > if 180 < ang <= 270 returns 3 segments+-- > if 270 < ang < 360 returns 4 segmenets+--+circleSweepCW :: (Real u, Floating u)+ => Radian -> u -> Radian -> CatTrail u+circleSweepCW ang radius theta = go (circularModulo ang)+ where+ go a | a <= half_pi = wedge1 a+ | a <= pi = wedge2 (a/2)+ | a <= 1.5*pi = wedge3 (a/3)+ | otherwise = wedge4 (a/4)+ + wedge1 a = minorCircleSweepCW a radius theta++ wedge2 a = minorCircleSweepCW a radius theta+ `mappend` minorCircleSweepCW a radius (theta-a)++ wedge3 a = minorCircleSweepCW a radius theta+ `mappend` minorCircleSweepCW a radius (theta - a)+ `mappend` minorCircleSweepCW a radius (theta - 2*a)+ + wedge4 a = minorCircleSweepCW a radius theta+ `mappend` minorCircleSweepCW a radius (theta - a)+ `mappend` minorCircleSweepCW a radius (theta - 2*a)+ `mappend` minorCircleSweepCW a radius (theta - 3*a)+++++-- | 'circleSweepCCW' : @ apex_angle * radius * inclination -> CatTrail @+--+-- > ang should be in the range 0 < ang < 360deg.+--+-- > if 0 < ang <= 90 returns 1 segment+-- > if 90 < ang <= 180 returns 2 segments+-- > if 180 < ang <= 270 returns 3 segments+-- > if 270 < ang < 360 returns 4 segmenets+--+circleSweepCCW :: (Real u, Floating u)+ => Radian -> u -> Radian -> CatTrail u+circleSweepCCW ang radius theta = go (circularModulo ang)+ where+ go a | a <= half_pi = wedge1 a+ | a <= pi = wedge2 (a/2)+ | a <= 1.5*pi = wedge3 (a/3)+ | otherwise = wedge4 (a/4)+ + wedge1 a = minorCircleSweepCCW a radius theta++ wedge2 a = minorCircleSweepCCW a radius theta+ `mappend` minorCircleSweepCCW a radius (theta+a)++ wedge3 a = minorCircleSweepCCW a radius theta+ `mappend` minorCircleSweepCCW a radius (theta+a)+ `mappend` minorCircleSweepCCW a radius (theta+a+a)+ + wedge4 a = minorCircleSweepCCW a radius theta+ `mappend` minorCircleSweepCCW a radius (theta+a)+ `mappend` minorCircleSweepCCW a radius (theta+a+a)+ `mappend` minorCircleSweepCCW a radius (theta+a+a+a)++circularArc :: (Real u, Floating u) + => ClockDirection -> Radian -> u -> Radian -> CatTrail u +circularArc CW = circularArcCW+circularArc _ = circularArcCCW+++-- | inclination is the inclination of the chord.+--+circularArcCW :: (Real u, Floating u) => Radian -> u -> Radian -> CatTrail u +circularArcCW apex_ang radius inclin = + circleSweepCW apex_ang radius (inclin + 0.5 * apex_ang)+++-- | inclination is the inclination of the chord.+--+circularArcCCW :: (Real u, Floating u) => Radian -> u -> Radian -> CatTrail u +circularArcCCW apex_ang radius inclin = + circleSweepCCW apex_ang radius (inclin - 0.5 * apex_ang)+++-- | Proper semicircles do not make a good squiggle (it needs a +-- bit of pinch).+--+squiggleWave :: (Real u, Floating u) => Int -> u -> Radian -> CatTrail u+squiggleWave i unit ang = mconcat $ replicate i $ squiggle1 unit ang+ +squiggle1 :: (Real u, Floating u) => u -> Radian -> CatTrail u+squiggle1 unit ang = + catcurve v1 (vdiff v1 v2) (vdiff v2 v3)+ `mappend` catcurve (vdiff v3 v4) (vdiff v4 v5) (vdiff v5 v6)+ `mappend` catcurve (vdiff v6 v7) (vdiff v7 v8) (vdiff v8 v9)+ `mappend` catcurve (vdiff v9 v10) (vdiff v10 v11) (vdiff v11 v12)+ where+ four_radius = unit+ radius = 0.25 * four_radius+ two_radius = 0.5 * four_radius+ three_radius = 0.75 * four_radius+ rl = radius * kappa+ micro = 0.33 * rl -- seems good+ + v1 = orthoVec micro rl ang+ v2 = orthoVec (radius - rl) radius ang+ v3 = orthoVec radius radius ang++ v4 = orthoVec (radius + rl) radius ang+ v5 = orthoVec (two_radius - micro) rl ang+ v6 = orthoVec two_radius 0 ang++ v7 = orthoVec (two_radius + micro) (-rl) ang+ v8 = orthoVec (three_radius - rl) (-radius) ang+ v9 = orthoVec three_radius (-radius) ang++ v10 = orthoVec (three_radius + rl) (-radius) ang+ v11 = orthoVec (four_radius - micro) (-rl) ang+ v12 = orthoVec four_radius 0 ang++ ++squareWave :: Floating u => Int -> u -> Radian -> CatTrail u +squareWave n unit ang + | n > 0 = monPreRepeatPost up_half (n - 1,kont) fin+ | otherwise = mempty+ where+ up_half = catline $ theta_up (0.25 * unit) ang+ up_one = catline $ theta_up (0.5 * unit) ang+ down_one = catline $ theta_down (0.5 * unit) ang+ right_half = catline $ theta_right (0.5 * unit) ang++ kont = right_half `mappend` down_one `mappend` right_half+ `mappend` up_one++ fin = right_half `mappend` down_one `mappend` right_half+ `mappend` up_half+++++-- |+-- +sawtoothWave :: (Real u, Floating u) => Int -> u -> Radian -> CatTrail u +sawtoothWave n unit ang + | n > 0 = monPreRepeatPost up_half (n - 1,kont) fin+ | otherwise = mempty+ where+ up_half = catline $ theta_up_right (0.25 * unit) ang+ up_one = catline $ theta_up_right (0.5 * unit) ang+ down_one = catline $ theta_down_right (0.5 * unit) ang++ kont = down_one `mappend` up_one+ fin = down_one `mappend` up_half++++semicircleWave :: (Real u, Floating u) + => ClockDirection -> Int -> u -> Radian -> CatTrail u+semicircleWave cdir i unit ang = + mconcat $ replicate i $ fn cdir (avec ang unit)+ where+ fn CCW = semicircleCCW+ fn _ = semicircleCW++++--------------------------------------------------------------------------------++-- | 'triCurve' : @ clock_direction * base_width * height * +-- base_inclination -> CatTrail @+-- +-- Curve in a triangle - base_width and height are expected to +-- be positive.+-- +triCurve :: Floating u => ClockDirection -> u -> u -> Radian -> CatTrail u+triCurve CW bw h ang = ctriCW bw h ang+triCurve CCW bw h ang = ctriCW bw (-h) ang+++-- | Curve in a triangle.+-- +ctriCW :: Floating u => u -> u -> Radian -> CatTrail u+ctriCW bw h ang = catcurve v1 zeroVec v2+ where+ v1 = orthoVec (0.5 * bw) h ang+ v2 = orthoVec (0.5 * bw) (-h) ang+++-- | 'rectCurve' : @ clock_direction * base_width * height * +-- base_inclination -> CatTrail @+-- +-- Curve in a rectangle.+-- +rectCurve :: Floating u => ClockDirection -> u -> u -> Radian -> CatTrail u+rectCurve CW bw h ang = crectCW bw h ang+rectCurve CCW bw h ang = crectCW bw (-h) ang+++-- | Curve in a rectangle.+-- +crectCW :: Floating u => u -> u -> Radian -> CatTrail u+crectCW bw h ang = catcurve v1 v2 v3+ where+ v1 = orthoVec 0 h ang+ v2 = orthoVec bw 0 ang+ v3 = orthoVec 0 (-h) ang++++-- | Curve in a trapezium.+-- +trapCurve :: Floating u + => ClockDirection -> u -> u -> Radian -> Radian -> CatTrail u+trapCurve CW = ctrapCW+trapCurve CCW = ctrapCCW++-- | Curve in a trapezium (CW).+-- +-- h must be positive.+--+ctrapCW :: Floating u => u -> u -> Radian -> Radian -> CatTrail u+ctrapCW bw h interior_ang ang = catcurve v1 v2 v3+ where+ minor_bw = h / (fromRadian $ tan interior_ang)+ v1 = orthoVec minor_bw h ang+ v2 = orthoVec (bw - (2 * minor_bw)) 0 ang+ v3 = orthoVec minor_bw (-h) ang++-- | Curve in a trapezium (CCW).+-- +-- h must be positive.+--+ctrapCCW :: Floating u => u -> u -> Radian -> Radian -> CatTrail u+ctrapCCW bw h interior_ang ang = catcurve v1 v2 v3+ where+ minor_bw = h / (fromRadian $ tan interior_ang)+ v1 = orthoVec minor_bw (-h) ang+ v2 = orthoVec (bw - (2 * minor_bw)) 0 ang+ v3 = orthoVec minor_bw h ang++-- | Curve in half a /bowtie/.+-- +bowCurve :: Floating u + => ClockDirection -> u -> u -> Radian -> CatTrail u+bowCurve CW bw h ang = cbowCW bw h ang+bowCurve CCW bw h ang = cbowCW bw (-h) ang++-- | Curve in half a /bowtie/.+-- +cbowCW :: Floating u => u -> u -> Radian -> CatTrail u+cbowCW bw h ang = catcurve v1 v2 v3+ where+ v1 = orthoVec 0 h ang+ v2 = orthoVec bw (-h) ang+ v3 = orthoVec 0 h ang+++-- | Wedge curve formed inside a bowtie rotated by 90deg.+-- +wedgeCurve :: Floating u + => ClockDirection -> u -> u -> Radian -> CatTrail u+wedgeCurve CW bw h ang = cwedgeCW bw h ang+wedgeCurve CCW bw h ang = cwedgeCW bw (-h) ang++-- | Wedge curve clockwise.+-- +cwedgeCW :: Floating u => u -> u -> Radian -> CatTrail u+cwedgeCW bw h ang = catcurve v1 v2 v3+ where+ v1 = orthoVec bw h ang+ v2 = orthoVec (-bw) 0 ang+ v3 = orthoVec bw (-h) ang+++-- | Variation of wedge curve that draws a loop.+-- +loopCurve :: Floating u + => ClockDirection -> u -> u -> Radian -> CatTrail u+loopCurve CW bw h ang = cloopCW bw h ang+loopCurve CCW bw h ang = cloopCW bw (-h) ang+++-- | loop curve clockwise.+-- +cloopCW :: Floating u => u -> u -> Radian -> CatTrail u+cloopCW bw h ang = catcurve v1 v2 v3+ where+ ww = 2.0 * bw + v1 = orthoVec (1.5 * bw) h ang+ v2 = orthoVec (-ww) 0 ang+ v3 = orthoVec (1.5 * bw) (-h) ang
+ src/Wumpus/Basic/System/FontLoader.hs view
@@ -0,0 +1,143 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.System.FontLoader+-- Copyright : (c) Stephen Tetley 2011-2012+-- License : BSD3+--+-- Maintainer : stephen.tetley@gmail.com+-- Stability : highly unstable+-- Portability : GHC +--+-- Top level module for font loading...+--+--------------------------------------------------------------------------------++module Wumpus.Basic.System.FontLoader+ (+ FontLoader+ , afmLoaderByEnv+ , gsLoaderByEnv+ , simpleFontLoader++ , default_font_loader_help++ ) where++import Wumpus.Basic.Kernel.Base.FontSupport +import Wumpus.Basic.System.FontLoader.AfmTopLevel+import Wumpus.Basic.System.FontLoader.GSTopLevel++import Control.Monad+import Control.Exception ( try )+import System.Environment+-- import System.IO.Error+++++-- | A FontLoader is an action from a list of fonts to a+-- 'FontLoadResult' returned in @IO@.+-- +-- Fonts are supplied in a list of @Either FontDef FontFamily@,+-- this is a little cumbersome but it allows the loader to load+-- individual fonts and \/ or a whole families with a single API+-- call.+--+type FontLoader = [Either FontDef FontFamily] -> IO FontLoadResult++++++-- | Environment variable pointing to the GhostScript font+-- directory.+-- +-- > WUMPUS_GS_FONT_DIR+--+wumpus_gs_font_dir :: String+wumpus_gs_font_dir = "WUMPUS_GS_FONT_DIR"+++-- | Environment variable pointing to the diretory containing +-- the Adobe Font Metrics files.+-- +-- > WUMPUS_AFM_FONT_DIR+--+wumpus_afm_font_dir :: String+wumpus_afm_font_dir = "WUMPUS_AFM_FONT_DIR"++++afmLoaderByEnv :: IO (Maybe FontLoader)+afmLoaderByEnv = do + mb <- envLookup wumpus_afm_font_dir+ case mb of + Nothing -> return Nothing+ Just path -> return $ Just (\xs -> loadAfmFontMetrics path $ fontList xs)+++gsLoaderByEnv :: IO (Maybe FontLoader)+gsLoaderByEnv = do+ mb <- envLookup wumpus_gs_font_dir+ case mb of+ Nothing -> return Nothing+ Just path -> return $ Just (\xs -> loadGSFontMetrics path $ fontList xs)+++-- | Tries to find the GhostScript metrics first...+--+-- Runs the IO action on the loader if it finds one.+--+-- Either of one of the environment variables +-- @WUMPUS_AFM_FONT_DIR@ or @WUMPUS_GS_FONT_DIR@ must be defined+-- and point to their respective directory. +-- +simpleFontLoader :: (FontLoader -> IO a) -> IO (Maybe a)+simpleFontLoader mf = + gsLoaderByEnv >>= maybe fk1 sk + where+ fk1 = afmLoaderByEnv >>= maybe fk2 sk+ fk2 = putStrLn default_font_loader_help >> return Nothing+ sk loader = mf loader >>= return . Just+++envLookup :: String -> IO (Maybe String)+envLookup name = liftM fn $ try $ getEnv name+ where+ fn :: Either IOError String -> Maybe String+ fn (Left _) = Nothing+ fn (Right a) = Just a++ ++fontList :: [Either FontDef FontFamily] -> [FontDef]+fontList = foldr fn []+ where+ fn (Left a) acc = a:acc+ fn (Right b) acc = let f1 = maybe id (\a -> (a:)) $ ff_bold b+ f2 = maybe id (\a -> (a:)) $ ff_italic b+ f3 = maybe id (\a -> (a:)) $ ff_bold_italic b+ in ff_regular b : (f1 $ f2 $ f3 acc)+++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)."+ , ""+ , "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."+ , ""+ , "If you have both environment variables set, the GhostScript loader"+ , "will be used."+ ]+
− src/Wumpus/Basic/System/FontLoader/Afm.hs
@@ -1,82 +0,0 @@-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Basic.System.FontLoader.Afm--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : Stephen Tetley <stephen.tetley@gmail.com>--- Stability : highly unstable--- Portability : GHC------ Font loader / import shim for the Adobe \"Core 14\" glyph --- metrics.------ Use this loader if you have the Adode glyph metrics set--- (AFM v4.1). This metrics set is avaiable from the Adobe --- website.--------------------------------------------------------------------------------------module Wumpus.Basic.System.FontLoader.Afm- ( - loadAfmMetrics- - ) where--import Wumpus.Basic.Kernel-import Wumpus.Basic.System.FontLoader.Base.AfmV4Dot1Parser-import Wumpus.Basic.System.FontLoader.Base.Datatypes-import Wumpus.Basic.System.FontLoader.Base.FontLoadMonad---import Wumpus.Core -- package: wumpus-core--import Control.Monad-import Data.Monoid---- The file names of the Afm fonts match there PostScript names,--- the only difference is the addition of a @.afm@ extension.------- | 'loadAfmMetrics' : --- @ path_to_afm_fonts * [font_name] -> IO (metrics, messages) @ --- --- Load the supplied list of fonts. --- --- Note - if a font fails to load a message is written to the --- log and monospaced /fallback metrics/ are used.----loadAfmMetrics :: FilePath -> [FontName] -> IO (GlyphMetrics, [String])-loadAfmMetrics font_dir_path ns = - liftM post $ runFontLoadIO $ sequenceAll $ map mkFun ns- where- mkFun = afmLoadFontCalcs font_dir_path - - post (Left err,ss) = (mempty, ss ++ [err]) -- unreachable...- post (Right xs,ss) = (foldr insertFont mempty xs, ss)-----afmLoadFontCalcs :: FilePath -> FontName -> FontLoadIO FontMetricsOps-afmLoadFontCalcs font_dir_path name = do- logLoadMsg $ "Loading " ++ name- path <- checkFontPath font_dir_path (name ++ ".afm")- ans <- runParserFLIO path afmV4Dot1Parser- props <- buildAfmFontProps afm_mono_defaults_4_1 ans- return $ FontMetricsOps name (buildMetricsOps afmUnitScale props)------ | These are values extracted from Courier in the core 14 fonts.----afm_mono_defaults_4_1 :: MonospaceDefaults AfmUnit-afm_mono_defaults_4_1 = - MonospaceDefaults { default_letter_bbox = bbox- , default_cap_height = 562- , default_char_width = V2 600 0- }- where- bbox = BBox (P2 (-23) (-250)) (P2 715 805)
+ src/Wumpus/Basic/System/FontLoader/AfmParserBase.hs view
@@ -0,0 +1,225 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.System.FontLoader.AfmParserBase+-- Copyright : (c) Stephen Tetley 2010-2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Common parsers for AFM files.+-- +--------------------------------------------------------------------------------++module Wumpus.Basic.System.FontLoader.AfmParserBase+ ( ++ afmFileParser+ , runQuery+ , textQuery++ , getFontBBox+ , getEncodingScheme+ , getCapHeight+ + , charBBox+ , metric+ , keyStringPair+ , versionNumber + , startCharMetrics++ , keyName+ , newlineOrEOF+ , name+ , name1+ , semi+ , uptoNewline+ , number+ , cint+ , hexInt+ , octInt++ , lexeme+ , symbol+ , integer+ , int+ , double++ ) where++import Wumpus.Basic.System.FontLoader.Datatypes++import Wumpus.Basic.Utils.ParserCombinators+import qualified Wumpus.Basic.Utils.TokenParsers as P++import Wumpus.Core -- package: wumpus-core++import Control.Applicative++import Data.Char+import qualified Data.Map as Map+++afmFileParser :: CharParser AfmGlyphMetrics -> CharParser AfmFile+afmFileParser pgm = do + info <- (versionNumber *> globalInfo) + cms <- (startCharMetrics *> many pgm)+ return $ AfmFile + { afm_encoding = getEncodingScheme info+ , afm_letter_bbox = getFontBBox info+ , afm_cap_height = getCapHeight info+ , afm_descender = getDescender info+ , afm_underline_position = getUlPosition info+ , afm_underline_thickness = getUlThickness info+ , afm_glyph_metrics = cms+ }++globalInfo :: CharParser GlobalInfo+globalInfo = (foldr (\(k,v) a -> Map.insert k v a) Map.empty) + <$> manyTill keyStringPair (peek startCharMetrics)+++ +runQuery :: String -> CharParser a -> GlobalInfo -> Maybe a+runQuery field_name p table = + Map.lookup field_name table >>= extr . runParser p+ where+ extr (Okay a _) = Just a+ extr _ = Nothing++textQuery :: String -> GlobalInfo -> Maybe String+textQuery = Map.lookup+++-- | Strictly speaking a fontBBox is measured in integer units.+--+getFontBBox :: GlobalInfo -> Maybe AfmBoundingBox+getFontBBox = runQuery "FontBBox" fontBBox++getEncodingScheme :: GlobalInfo -> Maybe String+getEncodingScheme = textQuery "EncodingScheme"++getCapHeight :: GlobalInfo -> Maybe AfmUnit+getCapHeight = runQuery "CapHeight" number++getDescender :: GlobalInfo -> Maybe AfmUnit+getDescender = runQuery "Descender" number+++-- | Expected to be negative...+--+getUlPosition :: GlobalInfo -> Maybe AfmUnit+getUlPosition = runQuery "UnderlinePosition" number++getUlThickness :: GlobalInfo -> Maybe AfmUnit+getUlThickness = runQuery "UnderlineThickness" number+++charBBox :: CharParser AfmBoundingBox+charBBox = symbol "B" *> fontBBox <* semi++fontBBox :: CharParser AfmBoundingBox+fontBBox = (\llx lly urx ury -> boundingBox (P2 llx lly) (P2 urx ury))+ <$> number <*> number <*> number <*> number++++metric :: String -> a -> CharParser a -> CharParser a+metric iden dfault p = option dfault go+ where+ go = symbol iden *> p <* semi++++keyStringPair :: CharParser (AfmKey,String)+keyStringPair = (,) <$> keyName <*> uptoNewline <* newlineOrEOF + <?> "key-value line"++versionNumber :: CharParser String+versionNumber = + symbol "StartFontMetrics" *> many1 (digit <|> char '.') <* newlineOrEOF+ <?> "StartFontMetrics"+++startCharMetrics :: CharParser Int+startCharMetrics = symbol "StartCharMetrics" *> int <* newlineOrEOF+ <?> "StartCharMetrics failed"++++--------------------------------------------------------------------------------+++keyName :: CharParser AfmKey+keyName = lexeme (many1 $ satisfy isAlphaNum) +++newlineOrEOF :: CharParser ()+newlineOrEOF = skipOne (lexeme newline) <|> eof+++uptoNewline :: CharParser String+uptoNewline = many1 (noneOf ['\n'])+++name :: CharParser String+name = lexeme $ many (noneOf ";\n")++name1 :: CharParser String+name1 = lexeme $ many (noneOf "; \t\n")++++semi :: CharParser Char+semi = lexeme $ char ';'+++++number :: CharParser AfmUnit+number = liftA realToFrac double+++cint :: CharParser Int+cint = hexInt <|> octInt <|> int+++hexInt :: CharParser Int+hexInt = lexeme $ between (char '<') (char '>') P.hexBase+++octInt :: CharParser Int+octInt = lexeme $ char '\\' *> P.octBase++++--------------------------------------------------------------------------------++-- no newline in whitespace+++lp :: P.LexemeParser+lp = P.commentLineLexemeParser "Comment" [' ', '\t']+++lexeme :: CharParser a -> CharParser a+lexeme = P.lexeme lp++symbol :: String -> CharParser String+symbol = lexeme . string++-- whiteSpace :: CharParser ()+-- whiteSpace = P.whiteSpace lp+++integer :: CharParser Integer+integer = lexeme P.integer++int :: CharParser Int+int = fromIntegral <$> integer++double :: CharParser Double+double = lexeme P.double
+ src/Wumpus/Basic/System/FontLoader/AfmTopLevel.hs view
@@ -0,0 +1,113 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.System.FontLoader.AfmTopLevel+-- Copyright : (c) Stephen Tetley 2010-2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Top-level AFM V4.1 font loader. +--+-- Use this module to build a font loader if you want to work with +-- the Adobe metrics sets, but find the @simpleFontLoader@ in +-- @Wumpus.Basic.System.FontLoader@ too inflexible.+-- +--------------------------------------------------------------------------------++module Wumpus.Basic.System.FontLoader.AfmTopLevel+ ( ++ loadAfmFontMetrics++ , loadAfmFont1 ++ ) where++import Wumpus.Basic.Kernel+import Wumpus.Basic.System.FontLoader.AfmV4Dot1Parser+import Wumpus.Basic.System.FontLoader.Datatypes+import Wumpus.Basic.System.FontLoader.FontLoadMonad+++import Wumpus.Core -- package: wumpus-core++import Control.Monad+import Data.Monoid++-- The file names of the Afm fonts match there PostScript names,+-- the only difference is the addition of a @.afm@ extension.+--++-- | 'loadAfmFontMetrics' : +-- @ path_to_afm_fonts * [font_name] -> IO FontLoadResult @ +-- +-- Load the supplied list of fonts. +-- +-- Note - if a font fails to load a message is written to the +-- log and monospaced /fallback metrics/ are used.+--+loadAfmFontMetrics :: FilePath -> [FontDef] -> IO FontLoadResult+loadAfmFontMetrics font_dir_path ds = + liftM post $ runFontLoadIO $ sequenceAll $ map mkFun ds+ where+ mkFun = afmLoadFontMetrics font_dir_path + + post (Left err,msgs) = let errs = fontLoadMsg err `mappend` msgs+ in FontLoadResult mempty errs+ post (Right xs,msgs) = let body = foldr fn mempty xs+ in FontLoadResult body msgs+ + fn (name,metrics) table = insertFont name metrics table+++-- TODO - need a one font version...+++-- | 'loadAfmFont1' : +-- @ path_to_afm_fonts * font_def -> IO FontLoadResult @ +-- +-- Load a single AFM font. +-- +-- Note - if the font fails to load a message is written to the +-- log and monospaced /fallback metrics/ are used.+--+loadAfmFont1 :: FilePath -> FontDef -> IO FontLoadResult+loadAfmFont1 font_dir_path font_def =+ liftM post $ runFontLoadIO $ afmLoadFontMetrics font_dir_path font_def+ where+ post (Left err,msgs) = let errs = fontLoadMsg err `mappend` msgs+ in FontLoadResult mempty errs+ post (Right (a,b),msgs) = let body = insertFont a b mempty+ in FontLoadResult body msgs+ +++afmLoadFontMetrics :: FilePath -> FontDef -> FontLoadIO (FontName,FontMetrics)+afmLoadFontMetrics font_dir_path font_def = do+ tellLoadMsg $ "Loading " ++ afm_file+ path <- checkFontPath font_dir_path afm_file+ ans <- runParserFLIO path afmV4Dot1Parser+ props <- buildAfmFontProps afm_mono_defaults_4_1 ans+ return (name, buildMetricsOps afmValue props)+ where+ afm_file = afm_file_name font_def+ name = ps_font_name $ font_def_face font_def+++-- | These are values extracted from Courier in the core 14 fonts.+--+afm_mono_defaults_4_1 :: MonospaceDefaults AfmUnit+afm_mono_defaults_4_1 = + MonospaceDefaults { default_letter_bbox = bbox+ , default_cap_height = 562+ , default_descender = (-157)+ , default_underline_position = (-100)+ , default_underline_thickness = 50 + , default_char_width = V2 600 0+ }+ where+ bbox = BBox (P2 (-23) (-250)) (P2 715 805)
+ src/Wumpus/Basic/System/FontLoader/AfmV2Parser.hs view
@@ -0,0 +1,65 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.System.FontLoader.AfmV2Parser+-- Copyright : (c) Stephen Tetley 2010-2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- AFM file parser for Version 2.0.+--+-- Note - AFM Version 2.0 used by GhostScript and Version 3.0++-- have numerous differences. +-- +--------------------------------------------------------------------------------++module Wumpus.Basic.System.FontLoader.AfmV2Parser+ ( + afmV2Parser++ ) where++import Wumpus.Basic.System.FontLoader.AfmParserBase+import Wumpus.Basic.System.FontLoader.Datatypes+import Wumpus.Basic.Utils.ParserCombinators++import Wumpus.Core -- package: wumpus-core++import Control.Applicative+++++--------------------------------------------------------------------------------+-- parser+++afmV2Parser :: CharParser AfmFile+afmV2Parser = afmFileParser charMetricsV2+++charMetricsV2 :: CharParser AfmGlyphMetrics+charMetricsV2 = AfmGlyphMetrics <$>+ metric "C" (-1) cint+ <*> widthVector+ <*> metric "N" "" name1+ <* charBBox+ <* many (symbol "L" *> ligature_body <* semi)+ <* newlineOrEOF+ where+ ligature_body = ((,) <$> name <*> name)+ +widthVector :: CharParser (Vec2 AfmUnit)+widthVector = (symbol "WX" *> ((\w -> vec w 0) <$> number) <* semi)+ <|> (symbol "W" *> (vec <$> number <*> number) <* semi)+++--------------------------------------------------------------------------------++++
+ src/Wumpus/Basic/System/FontLoader/AfmV4Dot1Parser.hs view
@@ -0,0 +1,63 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.System.FontLoader.AfmV4Dot1Parser+-- Copyright : (c) Stephen Tetley 2010-2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- AFM file parser for Version 4.1.+--+-- Adobe distributes font metrics for the /Core 14/ fonts as+-- AFM Version 4.1 files. +--+--------------------------------------------------------------------------------++module Wumpus.Basic.System.FontLoader.AfmV4Dot1Parser+ ( + + afmV4Dot1Parser + ) where++import Wumpus.Basic.System.FontLoader.AfmParserBase+import Wumpus.Basic.System.FontLoader.Datatypes+import Wumpus.Basic.Utils.ParserCombinators++import Wumpus.Core -- package: wumpus-core++import Control.Applicative+++afmV4Dot1Parser :: CharParser AfmFile+afmV4Dot1Parser = afmFileParser charMetricsV4Dot1+++charMetricsV4Dot1 :: CharParser AfmGlyphMetrics+charMetricsV4Dot1 = AfmGlyphMetrics <$>+ characterCode+ <*> widthVector+ <*> metric "N" "" name1+ <* charBBox+ <* many (symbol "L" *> ligature_body <* semi)+ <* newlineOrEOF+ where+ ligature_body = ((,) <$> name <*> name)+++-- Note - there are many variants for width vectors in in 4.1.+-- Wumpus needs some thought about what to do for them, it also+-- needs some facility to tell how successful the parse has been.+ +widthVector :: CharParser (Vec2 AfmUnit)+widthVector = (symbol "WX" *> ((\w -> vec w 0) <$> number) <* semi)+ <|> (symbol "W" *> (vec <$> number <*> number) <* semi)+++-- V4.1 allows C int or CH \<hex\>+--+characterCode :: CharParser Int+characterCode = metric "CH" (-1) hexInt <|> metric "C" (-1) cint
− src/Wumpus/Basic/System/FontLoader/Base/AfmParserBase.hs
@@ -1,210 +0,0 @@-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Basic.System.FontLoader.Base.AfmParserBase--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : Stephen Tetley <stephen.tetley@gmail.com>--- Stability : highly unstable--- Portability : GHC------ Common parsers for AFM files.--- -----------------------------------------------------------------------------------module Wumpus.Basic.System.FontLoader.Base.AfmParserBase- ( -- afmFileParser- , runQuery- , textQuery-- , getFontBBox- , getEncodingScheme- , getCapHeight- - , charBBox- , metric- , keyStringPair- , versionNumber - , startCharMetrics-- , keyName- , newlineOrEOF- , name- , name1- , semi- , uptoNewline- , number- , cint- , hexInt- , octInt-- , lexeme- , symbol- , integer- , int- , double-- ) where--import Wumpus.Basic.System.FontLoader.Base.Datatypes--import Wumpus.Basic.Utils.ParserCombinators-import qualified Wumpus.Basic.Utils.TokenParsers as P--import Wumpus.Core -- package: wumpus-core--import Control.Applicative--import Data.Char-import qualified Data.Map as Map---afmFileParser :: CharParser AfmGlyphMetrics -> CharParser AfmFile-afmFileParser pgm = do - info <- (versionNumber *> globalInfo) - cms <- (startCharMetrics *> many pgm)- return $ AfmFile - { afm_encoding = getEncodingScheme info- , afm_letter_bbox = getFontBBox info- , afm_cap_height = getCapHeight info- , afm_glyph_metrics = cms- }--globalInfo :: CharParser GlobalInfo-globalInfo = (foldr (\(k,v) a -> Map.insert k v a) Map.empty) - <$> manyTill keyStringPair (peek startCharMetrics)--- -runQuery :: String -> CharParser a -> GlobalInfo -> Maybe a-runQuery field_name p table = - Map.lookup field_name table >>= extr . runParser p- where- extr (Okay a _) = Just a- extr _ = Nothing--textQuery :: String -> GlobalInfo -> Maybe String-textQuery = Map.lookup----- | Strictly speaking a fontBBox is measured in integer units.----getFontBBox :: GlobalInfo -> Maybe AfmBoundingBox-getFontBBox = runQuery "FontBBox" fontBBox--getEncodingScheme :: GlobalInfo -> Maybe String-getEncodingScheme = textQuery "EncodingScheme"--getCapHeight :: GlobalInfo -> Maybe AfmUnit-getCapHeight = runQuery "CapHeight" number---charBBox :: CharParser AfmBoundingBox-charBBox = symbol "B" *> fontBBox <* semi--fontBBox :: CharParser AfmBoundingBox-fontBBox = (\llx lly urx ury -> boundingBox (P2 llx lly) (P2 urx ury))- <$> number <*> number <*> number <*> number----metric :: String -> a -> CharParser a -> CharParser a-metric iden dfault p = option dfault go- where- go = symbol iden *> p <* semi----keyStringPair :: CharParser (AfmKey,String)-keyStringPair = (,) <$> keyName <*> uptoNewline <* newlineOrEOF - <?> "key-value line"--versionNumber :: CharParser String-versionNumber = - symbol "StartFontMetrics" *> many1 (digit <|> char '.') <* newlineOrEOF- <?> "StartFontMetrics"---startCharMetrics :: CharParser Int-startCharMetrics = symbol "StartCharMetrics" *> int <* newlineOrEOF- <?> "StartCharMetrics failed"---------------------------------------------------------------------------------------keyName :: CharParser AfmKey-keyName = lexeme (many1 $ satisfy isAlphaNum) ---newlineOrEOF :: CharParser ()-newlineOrEOF = skipOne (lexeme newline) <|> eof---uptoNewline :: CharParser String-uptoNewline = many1 (noneOf ['\n'])---name :: CharParser String-name = lexeme $ many (noneOf ";\n")--name1 :: CharParser String-name1 = lexeme $ many (noneOf "; \t\n")----semi :: CharParser Char-semi = lexeme $ char ';'-----number :: CharParser AfmUnit-number = liftA realToFrac double---cint :: CharParser Int-cint = hexInt <|> octInt <|> int---hexInt :: CharParser Int-hexInt = lexeme $ between (char '<') (char '>') P.hexBase---octInt :: CharParser Int-octInt = lexeme $ char '\\' *> P.octBase---------------------------------------------------------------------------------------- no newline in whitespace---lp :: P.LexemeParser-lp = P.commentLineLexemeParser "Comment" [' ', '\t']---lexeme :: CharParser a -> CharParser a-lexeme = P.lexeme lp--symbol :: String -> CharParser String-symbol = lexeme . string---- whiteSpace :: CharParser ()--- whiteSpace = P.whiteSpace lp---integer :: CharParser Integer-integer = lexeme P.integer--int :: CharParser Int-int = fromIntegral <$> integer--double :: CharParser Double-double = lexeme P.double
− src/Wumpus/Basic/System/FontLoader/Base/AfmV2Parser.hs
@@ -1,65 +0,0 @@-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Basic.System.FontLoader.Internal.AfmV2Parser--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : Stephen Tetley <stephen.tetley@gmail.com>--- Stability : highly unstable--- Portability : GHC------ AFM file parser for Version 2.0.------ Note - AFM Version 2.0 used by GhostScript and Version 3.0+--- have numerous differences. --- -----------------------------------------------------------------------------------module Wumpus.Basic.System.FontLoader.Base.AfmV2Parser- ( - afmV2Parser-- ) where--import Wumpus.Basic.System.FontLoader.Base.AfmParserBase-import Wumpus.Basic.System.FontLoader.Base.Datatypes-import Wumpus.Basic.Utils.ParserCombinators--import Wumpus.Core -- package: wumpus-core--import Control.Applicative---------------------------------------------------------------------------------------- parser---afmV2Parser :: CharParser AfmFile-afmV2Parser = afmFileParser charMetricsV2---charMetricsV2 :: CharParser AfmGlyphMetrics-charMetricsV2 = AfmGlyphMetrics <$>- metric "C" (-1) cint- <*> widthVector- <*> metric "N" "" name1- <* charBBox- <* many (symbol "L" *> ligature_body <* semi)- <* newlineOrEOF- where- ligature_body = ((,) <$> name <*> name)- -widthVector :: CharParser (Vec2 AfmUnit)-widthVector = (symbol "WX" *> ((\w -> vec w 0) <$> number) <* semi)- <|> (symbol "W" *> (vec <$> number <*> number) <* semi)---------------------------------------------------------------------------------------
− src/Wumpus/Basic/System/FontLoader/Base/AfmV4Dot1Parser.hs
@@ -1,63 +0,0 @@-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Basic.System.FontLoader.Base.AfmV4Dot1Parser--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : Stephen Tetley <stephen.tetley@gmail.com>--- Stability : highly unstable--- Portability : GHC------ AFM file parser for Version 4.1.------ Adobe distributes font metrics for the /Core 14/ fonts as--- AFM Version 4.1 files. --------------------------------------------------------------------------------------module Wumpus.Basic.System.FontLoader.Base.AfmV4Dot1Parser- ( - - afmV4Dot1Parser - ) where--import Wumpus.Basic.System.FontLoader.Base.AfmParserBase-import Wumpus.Basic.System.FontLoader.Base.Datatypes-import Wumpus.Basic.Utils.ParserCombinators--import Wumpus.Core -- package: wumpus-core--import Control.Applicative---afmV4Dot1Parser :: CharParser AfmFile-afmV4Dot1Parser = afmFileParser charMetricsV4Dot1---charMetricsV4Dot1 :: CharParser AfmGlyphMetrics-charMetricsV4Dot1 = AfmGlyphMetrics <$>- characterCode- <*> widthVector- <*> metric "N" "" name1- <* charBBox- <* many (symbol "L" *> ligature_body <* semi)- <* newlineOrEOF- where- ligature_body = ((,) <$> name <*> name)----- Note - there are many variants for width vectors in in 4.1.--- Wumpus needs some thought about what to do for them, it also--- needs some facility to tell how successful the parse has been.- -widthVector :: CharParser (Vec2 AfmUnit)-widthVector = (symbol "WX" *> ((\w -> vec w 0) <$> number) <* semi)- <|> (symbol "W" *> (vec <$> number <*> number) <* semi)----- V4.1 allows C int or CH \<hex\>----characterCode :: CharParser Int-characterCode = metric "CH" (-1) hexInt <|> metric "C" (-1) cint
− src/Wumpus/Basic/System/FontLoader/Base/Datatypes.hs
@@ -1,178 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Basic.System.FontLoader.Base.Datatypes--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : Stephen Tetley <stephen.tetley@gmail.com>--- Stability : highly unstable--- Portability : GHC------ Datatypes--- -----------------------------------------------------------------------------------module Wumpus.Basic.System.FontLoader.Base.Datatypes- (-- -- * Afm Unit- AfmUnit- , afmValue- , afmUnitScale- - -- * Glyph metrics-- , PSCharCode- , PSEncodingScheme- , AfmBoundingBox-- , AfmKey- , GlobalInfo- , AfmFile(..)- , AfmGlyphMetrics(..)-- , MonospaceDefaults(..)-- , FontProps(..)- , buildMetricsOps- - ) where---import Wumpus.Basic.Kernel.Base.GlyphMetrics--import Wumpus.Core -- package: wumpus-core--import qualified Data.IntMap as IntMap-import qualified Data.Map as Map------ | Wrapped Double representing 1\/1000 of the scale factor--- (Point size) of a font. AFM files encode all measurements --- as these units. --- -newtype AfmUnit = AfmUnit { getAfmUnit :: Double } - deriving (Eq,Ord,Num,Floating,Fractional,Real,RealFrac,RealFloat)--instance Show AfmUnit where- showsPrec p d = showsPrec p (getAfmUnit d)----- | Compute the size of a measurement in Afm units scaled by the--- point size of the font.----afmValue :: FromPtSize u => AfmUnit -> PtSize -> u-afmValue u pt = fromPtSize $ (realToFrac $ getAfmUnit u) * (pt / 1000)--afmUnitScale :: AfmUnit -> PtSize -afmUnitScale u = (realToFrac $ getAfmUnit u / 1000)--------------------------------------------------------------------------------------- | Afm files index glyphs by /PostScript character code/. This --- is not the same as Unicode, ASCII...------ It is expected to be determined by @EncodingScheme@ in the--- Global Font Information Section.----type PSCharCode = Int--type PSEncodingScheme = String--type AfmBoundingBox = BoundingBox AfmUnit--type AfmKey = String-type GlobalInfo = Map.Map AfmKey String------ | Wumpus needs a very small subset of AFM files, common to both--- version 2.0 and version 4.1.------ Note - Bounding Box is mandatory for AFM versions 3.0 and 4.1--- --- Cap Height is optional in AFM versions 3.0 and 4.1. As Wumpus --- uses cap height in calculations, glyph metrics must be build --- with an arbitrary value if it is not present.------ Encoding Scheme is optional in AFM files.----data AfmFile = AfmFile - { afm_encoding :: Maybe String- , afm_letter_bbox :: Maybe AfmBoundingBox- , afm_cap_height :: Maybe AfmUnit- , afm_glyph_metrics :: [AfmGlyphMetrics]- }- deriving (Show) - --- Note - for AfmFile BBox is a required field for version 4.1, --- but it appears to be optional for version 2.0.------data AfmGlyphMetrics = AfmGlyphMetrics- { afm_char_code :: !PSCharCode- , afm_width_vector :: !(Vec2 AfmUnit)- , afm_char_name :: !String- }- deriving (Eq,Show)----- | Monospace defaults are used if the font loader fails to --- extract the necessary fields.--- --- The values are taken from the font correpsonding to Courier --- in the distributed font files.----data MonospaceDefaults cu = MonospaceDefaults - { default_letter_bbox :: BoundingBox cu- , default_cap_height :: cu- , default_char_width :: Vec2 cu- }- deriving (Eq,Show)------ | The metrics read from a font file by a font loader. --- --- NOTE - FontProps is parametric on @cu@ - /Character Unit/ and --- not on the usual @u@. A typical character unit is 'AfmUnit', --- the unit of measurement for AFM files (1000th of a point).------ The is the initial representation used by Wumpus-Basic as an--- syntax tree when loading font files. ----data FontProps cu = FontProps- { fp_bounding_box :: BoundingBox cu - , fp_default_adv_vec :: Vec2 cu- , fp_adv_vecs :: IntMap.IntMap (Vec2 cu)- , fp_cap_height :: cu- }----- | Build a MetricsOps function table, from a character unit--- scaling function and FontProps read from a file.----buildMetricsOps :: (cu -> PtSize) -> FontProps cu -> MetricsOps-buildMetricsOps fn (FontProps (BBox ll ur) (V2 vx vy) - vec_table cap_height) = - MetricsOps- { get_bounding_box = \sz -> BBox (scalePt sz ll) (scalePt sz ur)- , get_cw_table = \sz i -> - maybe (defaultAV sz) (scaleVec sz) $ IntMap.lookup i vec_table - , get_cap_height = \sz -> upscale sz (fn cap_height)- }- where- upscale sz d = fromPtSize $ sz * d - - defaultAV sz = V2 (upscale sz $ fn vx) (upscale sz $ fn vy) - scalePt sz (P2 cx cy) = P2 (upscale sz $ fn cx) (upscale sz $ fn cy) - scaleVec sz (V2 cx cy) = V2 (upscale sz $ fn cx) (upscale sz $ fn cy) ---
− src/Wumpus/Basic/System/FontLoader/Base/FontLoadMonad.hs
@@ -1,205 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Basic.System.FontLoader.Base.FontLoadMonad--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : Stephen Tetley <stephen.tetley@gmail.com>--- Stability : highly unstable--- Portability : GHC------ Font load monad handling IO (file system access), failure and --- logging.--------------------------------------------------------------------------------------module Wumpus.Basic.System.FontLoader.Base.FontLoadMonad- (- FontLoadErr- , FontLoadIO- , runFontLoadIO- , evalFontLoadIO- , loadError- , logLoadMsg- , promoteIO- , promoteEither- , runParserFLIO-- , sequenceAll-- -- * Font loading-- , buildAfmFontProps- , checkFontPath- - ) where--import Wumpus.Basic.System.FontLoader.Base.Datatypes-import Wumpus.Basic.Utils.HList-import Wumpus.Basic.Utils.ParserCombinators---import Wumpus.Core -- package: wumpus-core-import Wumpus.Core.Text.GlyphIndices---import Control.Monad-import qualified Data.IntMap as IntMap-import qualified Data.Map as Map-import Data.Monoid-import System.Directory-import System.FilePath--------------------------------------------------------------------------------------- FontLoadIO monad - IO plus Error----type FontLoadErr = String--newtype FontLoadLog = FontLoadLog { getFontLoadLog :: H String }---instance Monoid FontLoadLog where- mempty = FontLoadLog $ emptyH- a `mappend` b = FontLoadLog $ getFontLoadLog a `appendH` getFontLoadLog b----newtype FontLoadIO a = FontLoadIO { - getFontLoadIO :: IO (Either FontLoadErr a, FontLoadLog ) }--instance Functor FontLoadIO where- fmap f ma = FontLoadIO $ getFontLoadIO ma >>= \(a,w) -> return (fmap f a, w)- -instance Monad FontLoadIO where- return a = FontLoadIO $ return (Right a, mempty)- m >>= k = FontLoadIO $ getFontLoadIO m >>= fn - where- fn (Left err, w) = return (Left err, w)- fn (Right a, w1) = getFontLoadIO (k a) >>= \(b,w2) -> - return (b, w1 `mappend` w2)--runFontLoadIO :: FontLoadIO a -> IO (Either FontLoadErr a,[String])-runFontLoadIO ma = liftM post $ getFontLoadIO ma - where- post (ans,w) = (ans, toListH $ getFontLoadLog w)---evalFontLoadIO :: FontLoadIO a -> IO (Either FontLoadErr a)-evalFontLoadIO ma = liftM post $ getFontLoadIO ma- where- post (ans,_) = ans---loadError :: FontLoadErr -> FontLoadIO a-loadError msg = FontLoadIO $ return (Left msg, mempty)--logLoadMsg :: String -> FontLoadIO ()-logLoadMsg msg = FontLoadIO $ return (Right (), message1 msg ) ---message1 :: String -> FontLoadLog -message1 = FontLoadLog . wrapH----- | aka liftIO-promoteIO :: IO a -> FontLoadIO a-promoteIO ma = FontLoadIO $ ma >>= \a -> return (Right a, mempty)--promoteEither :: Either FontLoadErr a -> FontLoadIO a-promoteEither = either loadError return --runParserFLIO :: FilePath -> Parser Char a -> FontLoadIO a-runParserFLIO filepath p = - promoteIO (readFile filepath) >>= promoteEither . runParserEither p----- | The standard monadic @sequence@ would finish on first fail--- for the FontLoadIO monad. As we want to be able to sequence--- the loading of a list of fonts, this is not really the --- behaviour we want for Wumpus. Instead we prefer to use fallback --- metrics and produce an inaccurate drawing on a font load error--- rather than fail and produce no drawing.----sequenceAll :: [FontLoadIO a] -> FontLoadIO [a]-sequenceAll = FontLoadIO . step- where- step [] = return (Right [], mempty)- step (m:ms) = liftM2 cons (getFontLoadIO m) (step ms) --cons :: (Either FontLoadErr a, FontLoadLog)- -> (Either FontLoadErr [a], FontLoadLog)- -> (Either FontLoadErr [a], FontLoadLog)-cons (Right a, w1) (Right as, w2) = - (Right $ a:as, w1 `mappend` w2)--cons (Right a, w1) (Left e2, w2) = - (Right [a], w1 `mappend` w2 `mappend` message1 e2)--cons (Left e1, w1) (Right as, w2) = - (Right as, w1 `mappend` message1 e1 `mappend` w2)--cons (Left e1, w1) (Left e2, w2) = - (Right [], w1 `mappend` message1 e1 `mappend` w2 `mappend` message1 e2)----------------------------------------------------------------------------------------- | Afm files do not have a default advance vec so use the --- monospace default.--- --- Afm files hopefully have @CapHeight@ and @FontBBox@ properties--- in the header. Use the monospace default only if they are --- missing.--- -buildAfmFontProps :: MonospaceDefaults AfmUnit - -> AfmFile - -> FontLoadIO (FontProps AfmUnit)-buildAfmFontProps defaults afm = do - cap_height <- extractCapHeight defaults afm- bbox <- extractFontBBox defaults afm - return $ FontProps - { fp_bounding_box = bbox- , fp_default_adv_vec = default_char_width defaults- , fp_adv_vecs = char_widths- , fp_cap_height = cap_height- } - where- char_widths = foldr fn IntMap.empty $ afm_glyph_metrics afm- - fn (AfmGlyphMetrics _ v ss) table = case Map.lookup ss ps_glyph_indices of- Nothing -> table- Just i -> IntMap.insert i v table---extractCapHeight :: MonospaceDefaults AfmUnit -> AfmFile -> FontLoadIO AfmUnit-extractCapHeight defaults afm = maybe errk return $ afm_cap_height afm- where- errk = logLoadMsg "WARNING - Could not extract CapHeight" >> - return (default_cap_height defaults)---extractFontBBox :: MonospaceDefaults AfmUnit -> AfmFile - -> FontLoadIO (BoundingBox AfmUnit)-extractFontBBox defaults afm = maybe errk return $ afm_letter_bbox afm- where- errk = logLoadMsg "WARNING - Could not extract CapHeight" >> - return (default_letter_bbox defaults)----checkFontPath :: FilePath -> FilePath -> FontLoadIO FilePath-checkFontPath path_root font_file_name = - let full_path = normalise (path_root </> font_file_name)- in do { check <- promoteIO (doesFileExist full_path)- ; if check then return full_path- else loadError $ "Could not resolve path: " ++ full_path- }
− src/Wumpus/Basic/System/FontLoader/Base/GSFontMap.hs
@@ -1,108 +0,0 @@-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Basic.System.FontLoader.Base.GSFontMap--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : Stephen Tetley <stephen.tetley@gmail.com>--- Stability : highly unstable--- Portability : GHC------ GhostScript Font map.------ GhostScript aliases the /Core 14/ PostScript fonts to fonts--- it can freely distribute. This module provides aliases to --- Wumpus so the font loader can find the equivalent GhostScript--- files to the Core 14 set.------ The data in this file matches GhostScript 8.63. Other versions--- of GhostScript may need different aliases.--- -----------------------------------------------------------------------------------module Wumpus.Basic.System.FontLoader.Base.GSFontMap- (- - GSFontMap(..)- , gsMetricsFile- , gsFontAlias- , gsVersionNumber- , ghostscript_fontmap_8_54-- ) where---import Data.Map ( Map )-import qualified Data.Map as Map----- | GhostScript version that the aliases were derived from.--- --- ghostscript_version :: String--- ghostscript_version = "gs8.54"----- | A map from standard /Adode PostScript/ font name to the--- equivalent GhostScript font and AFM file name.------ It is expected that all GhostScript AFM files will be located--- in the same directory.----data GSFontMap = GSFontMap- { ghostscript_version :: String- , ghostscript_fontmap :: Map String (String, FilePath)- }----- | Get the @.afm@ metrics file.------ Note this return only the file name and not the path to it.--- The full path must be resolved in client code.----gsMetricsFile :: GSFontMap -> String -> Maybe FilePath-gsMetricsFile mp name = fmap snd $ Map.lookup name (ghostscript_fontmap mp)---- | Get the GhostScript font name alias.----gsFontAlias :: GSFontMap -> String -> Maybe String-gsFontAlias mp name = fmap fst $ Map.lookup name (ghostscript_fontmap mp)----- | Get the GhostScript version number that the FontMap --- represents.----gsVersionNumber :: GSFontMap -> String-gsVersionNumber = ghostscript_version ----- | Map from PostScript font name to the corresponding --- GhostScript name and file.------ Naming is correct for GhostSCript version 8.54.--- -ghostscript_fontmap_8_54 :: GSFontMap-ghostscript_fontmap_8_54 = GSFontMap "8.54" body- where- body = Map.fromList $ - [ ("Courier", ("NimbusMonL-Regu", "n022003l.afm"))- , ("Courier-Oblique", ("NimbusMonL-ReguObli", "n022023l.afm"))- , ("Courier-Bold", ("NimbusMonL-Bold", "n022004l.afm"))- , ("Courier-BoldOblique", ("NimbusMonL-BoldObli", "n022024l.afm"))- - , ("Helvetica", ("NimbusSanL-Regu", "n019003l.afm"))- , ("Helvetica-Oblique", ("NimbusSanL-ReguItal", "n019023l.afm"))- , ("Helvetica-Bold", ("NimbusSanL-Bold", "n019004l.afm"))- , ("Helvetica-BoldOblique", ("NimbusSanL-BoldItal", "n019024l.afm"))-- , ("Times-Roman", ("NimbusRomNo9L-Regu", "n021003l.afm"))- , ("Times-Italic", ("NimbusRomNo9L-ReguItal", "n021023l.afm"))- , ("Times-Bold", ("NimbusRomNo9L-Medi", "n021004l.afm"))- , ("Times-BoldItalic", ("NimbusRomNo9L-MediItal", "n021024l.afm"))-- , ("Symbol", ("StandardSymL", "s050000l.afm"))-- , ("ZapfDingbats", ("Dingbats", "d050000l.afm"))- ]-
+ src/Wumpus/Basic/System/FontLoader/Datatypes.hs view
@@ -0,0 +1,166 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.System.FontLoader.Datatypes+-- Copyright : (c) Stephen Tetley 2010-2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Datatypes+-- +--------------------------------------------------------------------------------++module Wumpus.Basic.System.FontLoader.Datatypes+ (+ + -- * Glyph metrics++ PSCharCode+ , PSEncodingScheme+ , AfmBoundingBox++ , AfmKey+ , GlobalInfo+ , AfmFile(..)+ , AfmGlyphMetrics(..)++ , MonospaceDefaults(..)++ , FontProps(..)+ , buildMetricsOps+ + ) where+++import Wumpus.Basic.Kernel.Base.FontSupport++import Wumpus.Core -- package: wumpus-core++import qualified Data.IntMap as IM+import qualified Data.Map as M+++++++++--------------------------------------------------------------------------------++-- | Afm files index glyphs by /PostScript character code/. This +-- is not the same as Unicode, ASCII...+--+-- It is expected to be determined by @EncodingScheme@ in the+-- Global Font Information Section.+--+type PSCharCode = Int++type PSEncodingScheme = String++type AfmBoundingBox = BoundingBox AfmUnit++type AfmKey = String+type GlobalInfo = M.Map AfmKey String++++-- | Wumpus needs a very small subset of AFM files, common to both+-- version 2.0 and version 4.1.+--+-- Note - Bounding Box is mandatory for AFM versions 3.0 and 4.1+-- +-- Cap Height is optional in AFM versions 3.0 and 4.1. As Wumpus +-- uses cap height in calculations, glyph metrics must be build +-- with an arbitrary value if it is not present.+--+-- Encoding Scheme is optional in AFM files.+--+data AfmFile = AfmFile + { afm_encoding :: Maybe String+ , afm_letter_bbox :: Maybe AfmBoundingBox+ , afm_cap_height :: Maybe AfmUnit+ , afm_descender :: Maybe AfmUnit+ , afm_underline_position :: Maybe AfmUnit+ , afm_underline_thickness :: Maybe AfmUnit+ , afm_glyph_metrics :: [AfmGlyphMetrics]+ }+ deriving (Show) + +-- Note - for AfmFile BBox is a required field for version 4.1, +-- but it appears to be optional for version 2.0.+--+++data AfmGlyphMetrics = AfmGlyphMetrics+ { afm_char_code :: !PSCharCode+ , afm_width_vector :: !(Vec2 AfmUnit)+ , afm_char_name :: !String+ }+ deriving (Eq,Show)+++-- | Monospace defaults are used if the font loader fails to +-- extract the necessary fields.+-- +-- The values are taken from the font correpsonding to Courier +-- in the distributed font files.+--+data MonospaceDefaults cu = MonospaceDefaults + { default_letter_bbox :: BoundingBox cu+ , default_cap_height :: cu+ , default_descender :: cu+ , default_underline_position :: cu+ , default_underline_thickness :: cu+ , default_char_width :: Vec2 cu+ }+ deriving (Eq,Show)++++-- | The metrics read from a font file by a font loader. +-- +-- NOTE - FontProps is parametric on @cu@ - /Character Unit/ and +-- not on the usual @u@. A typical character unit is 'AfmUnit', +-- the unit of measurement for AFM files (1000th of a point).+--+-- The is the initial representation used by Wumpus-Basic as an+-- syntax tree when loading font files. +--+data FontProps cu = FontProps+ { fp_bounding_box :: BoundingBox cu + , fp_default_adv_vec :: Vec2 cu+ , fp_adv_vecs :: IM.IntMap (Vec2 cu)+ , fp_cap_height :: cu+ , fp_descender :: cu+ , fp_underline_position :: cu+ , fp_underline_thickness :: cu+ }+++-- | Build a MetricsOps function table, from a character unit+-- scaling function and FontProps read from a file.+--+buildMetricsOps :: (FontSize -> cu -> Double) -> FontProps cu -> FontMetrics+buildMetricsOps fn font@(FontProps { fp_bounding_box = BBox ll ur+ , fp_default_adv_vec = V2 vx vy }) = + FontMetrics+ { get_bounding_box = \sz -> BBox (scalePt sz ll) (scalePt sz ur)+ , get_cw_table = \sz i -> + maybe (defaultAV sz) (scaleVec sz) $ IM.lookup i (fp_adv_vecs font)+ , get_cap_height = \sz -> fn sz (fp_cap_height font)+ , get_descender = \sz -> fn sz (fp_descender font)+ , get_underline_position = \sz -> fn sz (fp_underline_position font)+ , get_underline_thickness = \sz -> fn sz (fp_underline_thickness font)+ }+ where+ defaultAV sz = V2 (fn sz vx) (fn sz vy) + scalePt sz (P2 cx cy) = P2 (fn sz cx) (fn sz cy) + scaleVec sz (V2 cx cy) = V2 (fn sz cx) (fn sz cy) +++
+ src/Wumpus/Basic/System/FontLoader/FontLoadMonad.hs view
@@ -0,0 +1,218 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.System.FontLoader.FontLoadMonad+-- Copyright : (c) Stephen Tetley 2010-2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Font load monad handling IO (file system access), failure and +-- logging.+--+--------------------------------------------------------------------------------++module Wumpus.Basic.System.FontLoader.FontLoadMonad+ (+ FontLoadIO+ , runFontLoadIO+ , evalFontLoadIO+ , loadError+ , tellLoadMsg+ , promoteIO+ , promoteEither+ , runParserFLIO++ , sequenceAll++ -- * Font loading++ , buildAfmFontProps+ , checkFontPath+ + ) where++import Wumpus.Basic.Kernel+import Wumpus.Basic.System.FontLoader.Datatypes+import Wumpus.Basic.Utils.ParserCombinators+++import Wumpus.Core -- package: wumpus-core+import Wumpus.Core.Text.GlyphIndices+++import Control.Monad+import qualified Data.IntMap as IntMap+import qualified Data.Map as Map+import Data.Monoid+import System.Directory+import System.FilePath++++--------------------------------------------------------------------------------+-- FontLoadIO monad - IO plus Error++++newtype FontLoadIO a = FontLoadIO { + getFontLoadIO :: IO (Either FontLoadMsg a, FontLoadLog ) }++instance Functor FontLoadIO where+ fmap f ma = FontLoadIO $ getFontLoadIO ma >>= \(a,w) -> return (fmap f a, w)+ +instance Monad FontLoadIO where+ return a = FontLoadIO $ return (Right a, mempty)+ m >>= k = FontLoadIO $ getFontLoadIO m >>= fn + where+ fn (Left err, w) = return (Left err, w)+ fn (Right a, w1) = getFontLoadIO (k a) >>= \(b,w2) -> + return (b, w1 `mappend` w2)++runFontLoadIO :: FontLoadIO a -> IO (Either FontLoadMsg a, FontLoadLog)+runFontLoadIO ma = getFontLoadIO ma +++evalFontLoadIO :: FontLoadIO a -> IO (Either FontLoadMsg a)+evalFontLoadIO ma = liftM post $ getFontLoadIO ma+ where+ post (ans,_) = ans+++loadError :: FontLoadMsg -> FontLoadIO a+loadError msg = FontLoadIO $ return (Left msg, mempty)++tellLoadMsg :: String -> FontLoadIO ()+tellLoadMsg msg = FontLoadIO $ return (Right (), fontLoadMsg msg ) ++++-- | Promote an @IO@ action into the the @FontLoadIO@ monad.+--+-- This function is equivalent to @liftIO@.+--+promoteIO :: IO a -> FontLoadIO a+promoteIO ma = FontLoadIO $ ma >>= \a -> return (Right a, mempty)++promoteEither :: Either FontLoadMsg a -> FontLoadIO a+promoteEither = either loadError return ++runParserFLIO :: FilePath -> Parser Char a -> FontLoadIO a+runParserFLIO filepath p = + promoteIO (readFile filepath) >>= promoteEither . runParserEither p+++-- | The standard monadic @sequence@ would finish on first fail+-- for the FontLoadIO monad. As we want to be able to sequence+-- the loading of a list of fonts, this is not really the +-- behaviour we want for Wumpus. Instead we prefer to use fallback +-- metrics and produce an inaccurate drawing on a font load error+-- rather than fail and produce no drawing.+--+sequenceAll :: [FontLoadIO a] -> FontLoadIO [a]+sequenceAll = FontLoadIO . step+ where+ step [] = return (Right [], mempty)+ step (m:ms) = liftM2 cons (getFontLoadIO m) (step ms) ++cons :: (Either FontLoadMsg a, FontLoadLog)+ -> (Either FontLoadMsg [a], FontLoadLog)+ -> (Either FontLoadMsg [a], FontLoadLog)+cons (Right a, w1) (Right as, w2) = + (Right $ a:as, w1 `mappend` w2)++cons (Right a, w1) (Left e2, w2) = + (Right [a], w1 `mappend` w2 `mappend` fontLoadMsg e2)++cons (Left e1, w1) (Right as, w2) = + (Right as, w1 `mappend` fontLoadMsg e1 `mappend` w2)++cons (Left e1, w1) (Left e2, w2) = + (Right [], w1 `mappend` fontLoadMsg e1 `mappend` w2 `mappend` fontLoadMsg e2)++++--------------------------------------------------------------------------------+++-- | Afm files do not have a default advance vec so use the +-- monospace default.+-- +-- Afm files hopefully have @CapHeight@ and @FontBBox@ properties+-- in the header. Use the monospace default only if they are +-- missing.+-- +buildAfmFontProps :: MonospaceDefaults AfmUnit + -> AfmFile + -> FontLoadIO (FontProps AfmUnit)+buildAfmFontProps defaults afm = do + cap_height <- extractCapHeight defaults afm+ desc_depth <- extractDescender defaults afm+ ul_position <- extractUlPosition defaults afm+ ul_thickness <- extractUlThickness defaults afm+ bbox <- extractFontBBox defaults afm + return $ FontProps + { fp_bounding_box = bbox+ , fp_default_adv_vec = default_char_width defaults+ , fp_adv_vecs = char_widths+ , fp_cap_height = cap_height+ , fp_descender = desc_depth+ , fp_underline_position = ul_position+ , fp_underline_thickness = ul_thickness+ } + where+ char_widths = foldr fn IntMap.empty $ afm_glyph_metrics afm+ + fn (AfmGlyphMetrics _ v ss) table = case Map.lookup ss ps_glyph_indices of+ Nothing -> table+ Just i -> IntMap.insert i v table+++extractCapHeight :: MonospaceDefaults AfmUnit -> AfmFile -> FontLoadIO AfmUnit+extractCapHeight defaults afm = maybe errk return $ afm_cap_height afm+ where+ errk = tellLoadMsg "WARNING - Could not extract CapHeight" >> + return (default_cap_height defaults)++++extractDescender :: MonospaceDefaults AfmUnit -> AfmFile -> FontLoadIO AfmUnit+extractDescender defaults afm = maybe errk return $ afm_descender afm+ where+ errk = tellLoadMsg "WARNING - Could not extract Descender" >> + return (default_descender defaults)++extractUlPosition :: MonospaceDefaults AfmUnit -> AfmFile -> FontLoadIO AfmUnit+extractUlPosition defaults afm = + maybe errk return $ afm_underline_position afm+ where+ errk = tellLoadMsg "WARNING - Could not extract UnderlinePosition" >> + return (default_underline_position defaults)++extractUlThickness :: MonospaceDefaults AfmUnit -> AfmFile -> FontLoadIO AfmUnit+extractUlThickness defaults afm = + maybe errk return $ afm_underline_thickness afm+ where+ errk = tellLoadMsg "WARNING - Could not extract UnderlineThickness" >> + return (default_underline_thickness defaults)+++extractFontBBox :: MonospaceDefaults AfmUnit -> AfmFile + -> FontLoadIO (BoundingBox AfmUnit)+extractFontBBox defaults afm = maybe errk return $ afm_letter_bbox afm+ where+ errk = tellLoadMsg "WARNING - Could not extract CapHeight" >> + return (default_letter_bbox defaults)++++checkFontPath :: FilePath -> FilePath -> FontLoadIO FilePath+checkFontPath path_root font_file_name = + let full_path = normalise (path_root </> font_file_name)+ in do { check <- promoteIO (doesFileExist full_path)+ ; if check then return full_path+ else loadError $ "Could not resolve path: " ++ full_path+ }
+ src/Wumpus/Basic/System/FontLoader/GSTopLevel.hs view
@@ -0,0 +1,110 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Wumpus.Basic.System.FontLoader.GSTopLevel+-- Copyright : (c) Stephen Tetley 2010-2011+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : GHC+--+-- Top-level GhostScript font loader. +--+-- Use this module to build a font loader if you want to work with +-- GhostScript, but find the @simpleFontLoader@ in +-- @Wumpus.Basic.System.FontLoader@ too inflexible.+-- +--------------------------------------------------------------------------------++module Wumpus.Basic.System.FontLoader.GSTopLevel+ ( ++ loadGSFontMetrics++ , loadGSFont1 + + ) where++import Wumpus.Basic.Kernel+import Wumpus.Basic.System.FontLoader.AfmV2Parser+import Wumpus.Basic.System.FontLoader.Datatypes+import Wumpus.Basic.System.FontLoader.FontLoadMonad++import Wumpus.Core -- package: wumpus-core++import Control.Monad+import Data.Monoid+++-- | 'loadGSFontMetrics' : +-- @ path_to_gs_fonts * [font_name] -> IO FontLoadResult @ +-- +-- Load the supplied list of fonts. +-- +-- Note - if a font fails to load a message is written to the +-- log and monospaced /fallback metrics/ are used.+--+loadGSFontMetrics :: FilePath -> [FontDef] -> IO FontLoadResult+loadGSFontMetrics font_dir_path ds = + liftM post $ runFontLoadIO $ sequenceAll $ map mkFun ds+ where+ mkFun = gsLoadFontMetrics font_dir_path ++ post (Left err,msgs) = let errs = fontLoadMsg err `mappend` msgs+ in FontLoadResult mempty errs + post (Right xs,msgs) = let body = foldr fn mempty xs+ in FontLoadResult body msgs++ fn (name,metrics) table = insertFont name metrics table+++-- | 'loadGSFont1' : +-- @ path_to_gs_fonts * font_name -> IO FontLoadResult @ +-- +-- Load a single GhostScript font. +-- +-- Note - if the font fails to load a message is written to the +-- log and monospaced /fallback metrics/ are used.+--+loadGSFont1 :: FilePath -> FontDef -> IO FontLoadResult+loadGSFont1 font_dir_path font_def = + liftM post $ runFontLoadIO $ gsLoadFontMetrics font_dir_path font_def+ where+ post (Left err,msgs) = let errs = fontLoadMsg err `mappend` msgs+ in FontLoadResult mempty errs + post (Right (a,b),msgs) = let body = insertFont a b mempty+ in FontLoadResult body msgs++++gsLoadFontMetrics :: FilePath -> FontDef+ -> FontLoadIO (FontName,FontMetrics)+gsLoadFontMetrics font_dir_path font_def = do+ tellLoadMsg $ "Loading " ++ gs_file+ path <- checkFontPath font_dir_path gs_file+ ans <- runParserFLIO path afmV2Parser+ props <- buildAfmFontProps ghostscript_mono_defaults_8_54 ans+ return (name, buildMetricsOps afmValue props)+ where+ gs_file = gs_file_name font_def+ name = ps_font_name $ font_def_face font_def++++-- | These are values extracted from the file @n022003l.afm@+-- which is the font @NimbusMonL-Regu@, GhostScript\'s eqivalent +-- font for the core 14 font Courier.+--+ghostscript_mono_defaults_8_54 :: MonospaceDefaults AfmUnit+ghostscript_mono_defaults_8_54 = + MonospaceDefaults { default_letter_bbox = bbox+ , default_cap_height = 563+ , default_descender = (-186)+ , default_underline_position = (-100)+ , default_underline_thickness = 50 + , default_char_width = V2 600 0+ }+ where+ bbox = BBox (P2 (-46) (-273)) (P2 650 820)
− src/Wumpus/Basic/System/FontLoader/GhostScript.hs
@@ -1,86 +0,0 @@-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Basic.System.FontLoader.GhostScript--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : Stephen Tetley <stephen.tetley@gmail.com>--- Stability : highly unstable--- Portability : GHC------ Font loader / import shim for GhostScript glyph metrics.------ Use this loader if you have GhostScript installed and you want --- to use the (AFM v2.0) metrics that are distributed with --- GhostScript.--- -----------------------------------------------------------------------------------module Wumpus.Basic.System.FontLoader.GhostScript- ( -- loadGSMetrics- - ) where--import Wumpus.Basic.Kernel-import Wumpus.Basic.System.FontLoader.Base.AfmV2Parser-import Wumpus.Basic.System.FontLoader.Base.Datatypes-import Wumpus.Basic.System.FontLoader.Base.FontLoadMonad-import Wumpus.Basic.System.FontLoader.Base.GSFontMap--import Wumpus.Core -- package: wumpus-core--import Control.Monad-import Data.Monoid----- | 'loadGSMetrics' : --- @ path_to_gs_fonts * [font_name] -> IO (metrics, messages) @ --- --- Load the supplied list of fonts. --- --- Note - if a font fails to load a message is written to the --- log and monospaced /fallback metrics/ are used.----loadGSMetrics :: FilePath -> [FontName] -> IO (GlyphMetrics, [String])-loadGSMetrics font_dir_path ns = - liftM post $ runFontLoadIO $ sequenceAll $ map mkFun ns- where- mkFun = gsLoadFontCalcs font_dir_path ghostscript_fontmap_8_54 - - post (Left err,ss) = (mempty, ss ++ [err]) -- unreachable...- post (Right xs,ss) = (foldr insertFont mempty xs, ss)---gsLoadFontCalcs :: FilePath -> GSFontMap -> FontName - -> FontLoadIO FontMetricsOps-gsLoadFontCalcs font_dir_path fm name = do- logLoadMsg $ "Loading " ++ name- font_file <- resolveFontFile fm name - path <- checkFontPath font_dir_path font_file- ans <- runParserFLIO path afmV2Parser- props <- buildAfmFontProps ghostscript_mono_defaults_8_54 ans- return $ FontMetricsOps name (buildMetricsOps afmUnitScale props)---resolveFontFile :: GSFontMap -> FontName -> FontLoadIO FilePath-resolveFontFile fm name = maybe errk return $ gsMetricsFile fm name- where- errk = loadError $ "Could note resolve GhostScript alias for " ++ name----- | These are values extracted from the file @n022003l.afm@--- which is the font @NimbusMonL-Regu@, GhostScript\'s eqivalent --- font for the core 14 font Courier.----ghostscript_mono_defaults_8_54 :: MonospaceDefaults AfmUnit-ghostscript_mono_defaults_8_54 = - MonospaceDefaults { default_letter_bbox = bbox- , default_cap_height = 563- , default_char_width = V2 600 0- }- where- bbox = BBox (P2 (-46) (-273)) (P2 650 820)
− src/Wumpus/Basic/Utils/FormatCombinators.hs
@@ -1,430 +0,0 @@-{-# OPTIONS -Wall #-}------------------------------------------------------------------------------------- |--- Module : Wumpus.Basic.Utils.FormatCombinators--- Copyright : (c) Stephen Tetley 2010--- License : BSD3------ Maintainer : Stephen Tetley <stephen.tetley@gmail.com>--- Stability : highly unstable--- Portability : GHC------ Formatting combinators - pretty printers without the fitting.------ Note - indentation support is very limited. Generally one --- should use a proper pretty printing library.--- -----------------------------------------------------------------------------------module Wumpus.Basic.Utils.FormatCombinators- (- Doc- , DocS- , Format(..)- , empty- , showsDoc- , (<>)- , (<+>) - , vconcat- , separate- , hcat- , hsep- , vcat-- , text- , char- , int- , integer- , integral- , float- , double- , hex4-- , space- , comma- , semicolon- , line-- , fill- , fillStringR- , fillStringL-- , punctuate- , enclose- , squotes- , dquotes- , parens- , brackets- , braces- , angles-- , lparen- , rparen- , lbracket- , rbracket- , lbrace- , rbrace- , langle- , rangle-- , list- , tupled- , semiBraces-- , indent-- , writeDoc- - ) where--import Data.Monoid-import Numeric---- | Doc is a Join List ...----data Doc = Doc1 ShowS - | Join Doc Doc- | Line- | Indent !Int Doc ---type DocS = Doc -> Doc----- Join could be improved...----unDoc :: Doc -> ShowS-unDoc = step 0 id- where- step _ acc (Doc1 sf) = acc . sf- step n acc (Join a b) = let acc' = step n acc a in step n acc' b- step n acc Line = acc . showChar '\n' . indentS n- step n acc (Indent i d) = step (n+i) (acc . (indentS i)) d---indentS :: Int -> ShowS-indentS i | i < 1 = id- | otherwise = showString $ replicate i ' '--runDoc :: Doc -> String-runDoc = ($ "") . unDoc---instance Show Doc where- show = runDoc--instance Monoid Doc where- mempty = empty- mappend = (<>)---class Format a where format :: a -> Doc----------------------------------------------------------------------------------- -infixr 6 <>, <+>------ | Create an empty, zero length document.----empty :: Doc-empty = Doc1 id---- | Create a document from a ShowS function.----showsDoc :: ShowS -> Doc-showsDoc = Doc1----- | Horizontally concatenate two documents with no space --- between them.--- -(<>) :: Doc -> Doc -> Doc-a <> b = Join a b ----- | Horizontally concatenate two documents with a single space --- between them.--- -(<+>) :: Doc -> Doc -> Doc-a <+> b = Join a (Join space b)---- | Vertical concatenate two documents with a line break.--- -vconcat :: Doc -> Doc -> Doc-vconcat a b = a <> Line <> b----separate :: Doc -> [Doc] -> Doc-separate _ [] = empty-separate sep (a:as) = step a as- where- step acc [] = acc- step acc (x:xs) = step (acc <> sep <> x) xs---- | Horizontally concatenate a list of documents with @(\<\>)@.----hcat :: [Doc] -> Doc-hcat = foldr (<>) empty---- | Horizontally concatenate a list of documents with @(\<+\>)@.----hsep :: [Doc] -> Doc-hsep = separate space---- | Vertically concatenate a list of documents, with a line --- break between each doc.----vcat :: [Doc] -> Doc-vcat [] = empty-vcat (x:xs) = step x xs - where- step acc (z:zs) = step (acc `vconcat` z) zs- step acc [] = acc---- | Create a document from a literal string.--- --- The string should not contain newlines (though this is not --- enforced). ----text :: String -> Doc-text = Doc1 . showString----- | Create a document from a literal character.------ The char should not be a tab or newline. ----char :: Char -> Doc-char = Doc1 . showChar---- | Show the Int as a Doc.------ > int = text . show----int :: Int -> Doc-int = Doc1 . showInt---- | Show the Integer as a Doc.----integer :: Integer -> Doc-integer = Doc1 . showInt---- | Show an \"integral value\" as a Doc via 'fromIntegral'.----integral :: Integral a => a -> Doc-integral = Doc1 . showInt---- | Show the Float as a Doc.----float :: Double -> Doc-float = Doc1 . showFloat---- | Show the Double as a Doc.----double :: Double -> Doc-double = Doc1 . showFloat---- | Show the Int as hexadecimal, padding up to 4 digits if --- necessary.------ No trucation occurs if the value has more than 4 digits.----hex4 :: Int -> Doc-hex4 n | n < 0x0010 = text "000" <> showsDoc (showHex n)- | n < 0x0100 = text "00" <> showsDoc (showHex n)- | n < 0x1000 = text "0" <> showsDoc (showHex n)- | otherwise = showsDoc (showHex n)- --- | Create a Doc containing a single space character.----space :: Doc-space = char ' '---- | Create a Doc containing a comma, \",\".----comma :: Doc-comma = char ','---- | Create a Doc containing a semi colon, \";\".----semicolon :: Doc-semicolon = char ';'---- | Create a Doc containing newline, \"\\n\".----line :: Doc -line = char '\n'--------------------------------------------------------------------------------------- | Fill a doc to the supplied length, padding the right-hand--- side with spaces.------ Note - this function is expensive - it unrolls the functional--- representation of the String. --- --- Also it should only be used for single line Doc\'s.--- -fill :: Int -> Doc -> Doc-fill i d = Doc1 (padr i ' ' $ unDoc d) --padr :: Int -> Char -> ShowS -> ShowS-padr i c df = step (length $ df []) - where- step len | len >= i = df- | otherwise = df . showString (replicate (i-len) c)---- | String version of 'fill'.------ This is more efficient than 'fill' as the input is a string--- so its length is more accesible.------ Padding is the space character appended to the right.--- -fillStringR :: Int -> String -> Doc-fillStringR i s = step (length s)- where- step n | n >= i = text s- step n = text s <> text (replicate (i-n) ' ')---- | Left-padding version of 'fillStringR'.----fillStringL :: Int -> String -> Doc-fillStringL i s = step (length s)- where- step n | n >= i = text s- step n = text (replicate (i-n) ' ') <> text s-------------------------------------------------------------------------------------- | Punctuate the Doc list with the separator, producing a Doc. ----punctuate :: Doc -> [Doc] -> Doc-punctuate _ [] = empty-punctuate _ [x] = x-punctuate s (x:xs) = x <> s <> punctuate s xs----- | Enclose the final Doc within the first two.------ There are no spaces between the documents:------ > enclose l r d = l <> d <> r----enclose :: Doc -> Doc -> Doc -> Doc-enclose l r d = l <> d <> r------ | Enclose the Doc within single quotes.----squotes :: Doc -> Doc-squotes = enclose (char '\'') (char '\'')---- | Enclose the Doc within double quotes.----dquotes :: Doc -> Doc-dquotes = enclose (char '"') (char '"')---- | Enclose the Doc within parens @()@.----parens :: Doc -> Doc-parens = enclose lparen rparen---- | Enclose the Doc within square brackets @[]@.----brackets :: Doc -> Doc-brackets = enclose lbracket rbracket---- | Enclose the Doc within curly braces @{}@.----braces :: Doc -> Doc-braces = enclose lbrace rbrace---- | Enclose the Doc within angle brackets @\<\>@.----angles :: Doc -> Doc-angles = enclose langle rangle------ | Create a Doc containing a left paren, \'(\'.----lparen :: Doc-lparen = char '('---- | Create a Doc containing a right paren, \')\'.----rparen :: Doc-rparen = char ')'---- | Create a Doc containing a left square bracket, \'[\'.----lbracket :: Doc-lbracket = char '['---- | Create a Doc containing a right square bracket, \']\'.----rbracket :: Doc-rbracket = char ']'---- | Create a Doc containing a left curly brace, \'{\'.----lbrace :: Doc-lbrace = char '{'---- | Create a Doc containing a right curly brace, \'}\'.----rbrace :: Doc-rbrace = char '}'---- | Create a Doc containing a left angle bracket, \'\<\'.----langle :: Doc-langle = char '<'---- | Create a Doc containing a right angle bracket, \'\>\'.----rangle :: Doc-rangle = char '>'---- | Comma separate the list of documents and enclose in square--- brackets.----list :: [Doc] -> Doc-list = brackets . punctuate comma---- | Comma separate the list of documents and enclose in parens.----tupled :: [Doc] -> Doc-tupled = parens . punctuate comma---- | Separate the list with a semicolon and enclose in curly --- braces.----semiBraces :: [Doc] -> Doc-semiBraces = braces . punctuate semicolon----- | Horizontally indent a Doc.------ Note - this space-prefixes the Doc on /the current line/. It--- does not indent subsequent lines if the Doc spans multiple --- lines.----indent :: Int -> Doc -> Doc-indent i d | i < 1 = d- | otherwise = Indent i d--------------------------------------------------------------------------------------- | Write a Doc to file.----writeDoc :: FilePath -> Doc -> IO ()-writeDoc filepath d = writeFile filepath $ show d
src/Wumpus/Basic/Utils/HList.hs view
@@ -29,6 +29,7 @@ , concatH , toListH+ , prefixListH , fromListH @@ -79,6 +80,9 @@ toListH :: H a -> [a] toListH = ($ [])++prefixListH :: H a -> [a] -> [a]+prefixListH hf xs = hf $ xs fromListH :: [a] -> H a fromListH xs = (xs++)
src/Wumpus/Basic/Utils/JoinList.hs view
@@ -135,7 +135,7 @@ -- | Convert a join list to a regular list. -- toList :: JoinList a -> [a]-toList = joinfoldl (flip (:)) []+toList = joinfoldr (:) []
src/Wumpus/Basic/VersionNumber.hs view
@@ -3,7 +3,7 @@ -------------------------------------------------------------------------------- -- | -- Module : Wumpus.Basic.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,15,0)+-- > (0,24,0) -- wumpus_basic_version :: (Int,Int,Int)-wumpus_basic_version = (0,15,0)+wumpus_basic_version = (0,24,0)
wumpus-basic.cabal view
@@ -1,5 +1,5 @@ name: wumpus-basic-version: 0.15.0+version: 0.24.0 license: BSD3 license-file: LICENSE copyright: Stephen Tetley <stephen.tetley@gmail.com>@@ -9,8 +9,8 @@ synopsis: Basic objects and system code built on Wumpus-Core. description: .- Kernel code for higher-level drawing built on Wumpus-Core.- This package provides font loader code (limited to AFM font + Kernel code for higher-level drawing built on Wumpus-Core. This + package provides font loader code (currently limited to AFM font files) and a various /drawing objects/ intended to be a higher-level basis to make vector drawings than the primitives (paths, text labels) provided by Wumpus-Core. @@ -29,29 +29,76 @@ . . Changelog:+ . + v0.23.0 to v0.24.0: .- v0.14.0 to v0.15.0:+ * Changes to type funs to work with GHC 7.4. . - * Split previous @Wumpus-Basic@ package into two packages:- @Wumpus-Basic@ and @Wumpus-Drawing@. This is a pratical move - to separate the developed (although not yet polished) @Kernel@ - and @FontLoader@ code from the prototypical @Drawing@ code.+ * Removed FormatCombinators module. .- * Renamed the @Drawing@ object to @CtxPicture@. Although - @CtxPicture@ is a less pleasant name, it should be less - confusing. A @CtxPicture@ is the essentially the @Picture@ - type from Wumpus-Core with an implicit context - - @ContextPicture@ is simply too long and @CtxPicture@ is - almost jibberish but the previous unrelated name @Drawing@ - was not helpful.+ v0.22.0 to v0.23.0: .- * @Kernel.Base.ScalingContext@ has been simplified. - ScalingContexts are no longer manipulated via a custom Reader- monad or transformer as the type signatures were too unwieldy.+ * Re-worked the Chain module and API. .- * Added @TextMargin@ to the @DrawingContext@ - Wumpus-Drawing - can now calculate more appealing bounding boxes for text.+ * Replaced @ZDeco@ enumeration with @ZOrder@. .+ * Removed the @Basic.Geometry@ modules, they are superseded in + Wumpus-Drawing. Some of the equivalent functionality is now+ internal to the respective modules in Wumpus-Drawing - Wumpus+ has scaled back the amount of /geometric/ types and operations+ it wants to expose. + .+ * Renamed the @PlacedTrail@ object to @AnaTrail@ - the prefix + vector is considered an /anacrusis/. Tidied up the API of the + @Trail@ module.+ .+ * Moved @bezierArcPoints@ and @BezierMinorArc@ from + @Basic.Geometry@ and marked the @Basic.Geometry@ code as + obsolute.+ .+ v0.21.0 to v0.22.0:+ .+ * Reverted argument order of @run@ functions they back to the + @monadLib@ style (args * monadic_operation) rather than @MTL@+ style (monadic_operation * args). Typographically this seems + better.+ .+ * Removed module @Geometry.Paths@ it is superseded in+ Wumpus-Drawing.+ .+ * Added user state to PosObject and moved it into + @Kernel.Drawing@.+ .+ * Changed the enumeration @DrawStyle@ to @DrawMode@. Added + another enumeration @PathMode@.+ .+ * Changed Chain so that first item in the chain is the start + point (previously the start point was iterated by the chain).+ .+ * Removed @ConnectorProps@ from the @DrawingContext@ , they are + now used \"locally\" in Wumpus-Drawing.+ .+ * Added more vector constructors to + @Kernel.Objects.Displacement@. + .+ * Added extra methods to the @Decorate@ class for annotating + SVG elements.+ .+ v0.20.0 to v0.21.0:+ .+ * The code from the @Kernel.Base.UserState@ module has been + moved to the module @Kernel.Drawing.Basis@.+ .+ * Moved @Image@ and @Query@ from @Objects.Basis@ to new module+ @Objects.Image@. Moved @Chain@, @CtxPicture@ and + @TraceDrawing@ into the new @Drawing@ folder.+ .+ * Added @Wumpus.Basic.Kernel.Drawing.LocDrawing@ module.+ . + * Rationalized the combinators exported by the drawing objects + @Image@, @LocImage@, @LocThetaImage@ and @Connector@.+ . + . build-type: Simple stability: highly unstable cabal-version: >= 1.2@@ -59,7 +106,10 @@ extra-source-files: CHANGES, LICENSE,- demo/FontDeltaPic.hs+ demo/AltUnits.hs,+ demo/FontDeltaPic.hs,+ demo/SimpleAdvGraphic.hs,+ demo/SimplePosObject.hs library hs-source-dirs: src@@ -68,37 +118,47 @@ 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-core >= 0.52.0 && < 0.53.0 exposed-modules: Wumpus.Basic.Kernel,- Wumpus.Basic.Kernel.Base.Anchors, Wumpus.Basic.Kernel.Base.BaseDefs,- Wumpus.Basic.Kernel.Base.ContextFun, Wumpus.Basic.Kernel.Base.DrawingContext,- Wumpus.Basic.Kernel.Base.GlyphMetrics,+ Wumpus.Basic.Kernel.Base.FontSupport, Wumpus.Basic.Kernel.Base.QueryDC,- Wumpus.Basic.Kernel.Base.ScalingContext,+ Wumpus.Basic.Kernel.Base.Units, Wumpus.Basic.Kernel.Base.UpdateDC, Wumpus.Basic.Kernel.Base.WrappedPrimitive,- Wumpus.Basic.Kernel.Objects.AdvanceGraphic,- Wumpus.Basic.Kernel.Objects.BaseObjects,+ Wumpus.Basic.Kernel.Drawing.Basis,+ Wumpus.Basic.Kernel.Drawing.Chain,+ Wumpus.Basic.Kernel.Drawing.CtxPicture,+ Wumpus.Basic.Kernel.Drawing.LocDrawing,+ Wumpus.Basic.Kernel.Drawing.LocTrace,+ Wumpus.Basic.Kernel.Drawing.PosObject,+ Wumpus.Basic.Kernel.Drawing.TraceDrawing,+ Wumpus.Basic.Kernel.Objects.AdvObject,+ Wumpus.Basic.Kernel.Objects.Anchors,+ Wumpus.Basic.Kernel.Objects.Basis, Wumpus.Basic.Kernel.Objects.Bounded,+ Wumpus.Basic.Kernel.Objects.Concat, Wumpus.Basic.Kernel.Objects.Connector,- Wumpus.Basic.Kernel.Objects.CtxPicture,- Wumpus.Basic.Kernel.Objects.Graphic,- Wumpus.Basic.Kernel.Objects.TraceDrawing,- Wumpus.Basic.System.FontLoader.Afm,- Wumpus.Basic.System.FontLoader.GhostScript,- Wumpus.Basic.System.FontLoader.Base.AfmParserBase,- Wumpus.Basic.System.FontLoader.Base.AfmV2Parser,- Wumpus.Basic.System.FontLoader.Base.AfmV4Dot1Parser,- Wumpus.Basic.System.FontLoader.Base.Datatypes,- Wumpus.Basic.System.FontLoader.Base.FontLoadMonad,- Wumpus.Basic.System.FontLoader.Base.GSFontMap,+ Wumpus.Basic.Kernel.Objects.Displacement,+ Wumpus.Basic.Kernel.Objects.DrawingPrimitives,+ Wumpus.Basic.Kernel.Objects.Image,+ Wumpus.Basic.Kernel.Objects.LocImage,+ Wumpus.Basic.Kernel.Objects.LocThetaImage,+ Wumpus.Basic.Kernel.Objects.Orientation,+ Wumpus.Basic.Kernel.Objects.Trail,+ Wumpus.Basic.System.FontLoader,+ Wumpus.Basic.System.FontLoader.AfmParserBase,+ Wumpus.Basic.System.FontLoader.AfmTopLevel,+ Wumpus.Basic.System.FontLoader.AfmV2Parser,+ Wumpus.Basic.System.FontLoader.AfmV4Dot1Parser,+ Wumpus.Basic.System.FontLoader.Datatypes,+ Wumpus.Basic.System.FontLoader.FontLoadMonad,+ Wumpus.Basic.System.FontLoader.GSTopLevel, Wumpus.Basic.Utils.HList,- Wumpus.Basic.Utils.FormatCombinators, Wumpus.Basic.Utils.JoinList, Wumpus.Basic.Utils.ParserCombinators, Wumpus.Basic.Utils.TokenParsers,