packages feed

wumpus-core 0.30.0 → 0.31.0

raw patch · 28 files changed

+1006/−434 lines, 28 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Wumpus.Core.GraphicsState: DashPattern :: DashPattern -> StrokeAttr
- Wumpus.Core.GraphicsState: LineCap :: LineCap -> StrokeAttr
- Wumpus.Core.GraphicsState: LineJoin :: LineJoin -> StrokeAttr
- Wumpus.Core.GraphicsState: LineWidth :: Double -> StrokeAttr
- Wumpus.Core.GraphicsState: MiterLimit :: Double -> StrokeAttr
- Wumpus.Core.GraphicsState: gs_dash_pattern :: GraphicsState -> DashPattern
- Wumpus.Core.GraphicsState: gs_line_cap :: GraphicsState -> LineCap
- Wumpus.Core.GraphicsState: gs_line_join :: GraphicsState -> LineJoin
- Wumpus.Core.GraphicsState: gs_line_width :: GraphicsState -> Double
- Wumpus.Core.GraphicsState: gs_miter_limit :: GraphicsState -> Double
- Wumpus.Core.Picture: class Ellipse t
- Wumpus.Core.Picture: class Fill t
- Wumpus.Core.Picture: class Stroke t
- Wumpus.Core.Picture: class TextLabel t
- Wumpus.Core.Picture: ellipse :: (Ellipse t, Fractional u) => t -> u -> u -> Point2 u -> Primitive u
- Wumpus.Core.Picture: instance Ellipse ()
- Wumpus.Core.Picture: instance Ellipse (RGBi, StrokeAttr)
- Wumpus.Core.Picture: instance Ellipse (RGBi, XLink)
- Wumpus.Core.Picture: instance Ellipse (RGBi, [StrokeAttr])
- Wumpus.Core.Picture: instance Ellipse (RGBi, [StrokeAttr], XLink)
- Wumpus.Core.Picture: instance Ellipse (StrokeAttr, XLink)
- Wumpus.Core.Picture: instance Ellipse ([StrokeAttr], XLink)
- Wumpus.Core.Picture: instance Ellipse RGBi
- Wumpus.Core.Picture: instance Ellipse StrokeAttr
- Wumpus.Core.Picture: instance Ellipse XLink
- Wumpus.Core.Picture: instance Ellipse [StrokeAttr]
- Wumpus.Core.Picture: instance Fill ()
- Wumpus.Core.Picture: instance Fill (RGBi, XLink)
- Wumpus.Core.Picture: instance Fill RGBi
- Wumpus.Core.Picture: instance Fill XLink
- Wumpus.Core.Picture: instance Stroke ()
- Wumpus.Core.Picture: instance Stroke (RGBi, StrokeAttr)
- Wumpus.Core.Picture: instance Stroke (RGBi, StrokeAttr, XLink)
- Wumpus.Core.Picture: instance Stroke (RGBi, XLink)
- Wumpus.Core.Picture: instance Stroke (RGBi, [StrokeAttr])
- Wumpus.Core.Picture: instance Stroke (RGBi, [StrokeAttr], XLink)
- Wumpus.Core.Picture: instance Stroke (StrokeAttr, XLink)
- Wumpus.Core.Picture: instance Stroke ([StrokeAttr], XLink)
- Wumpus.Core.Picture: instance Stroke RGBi
- Wumpus.Core.Picture: instance Stroke StrokeAttr
- Wumpus.Core.Picture: instance Stroke XLink
- Wumpus.Core.Picture: instance Stroke [StrokeAttr]
- Wumpus.Core.Picture: instance TextLabel ()
- Wumpus.Core.Picture: instance TextLabel (FontAttr, XLink)
- Wumpus.Core.Picture: instance TextLabel (RGBi, FontAttr)
- Wumpus.Core.Picture: instance TextLabel (RGBi, FontAttr, XLink)
- Wumpus.Core.Picture: instance TextLabel (RGBi, XLink)
- Wumpus.Core.Picture: instance TextLabel FontAttr
- Wumpus.Core.Picture: instance TextLabel RGBi
- Wumpus.Core.Picture: instance TextLabel XLink
- Wumpus.Core.WumpusTypes: data GSUpdate
+ Wumpus.Core.GraphicsState: StrokeAttr :: Double -> Double -> LineCap -> LineJoin -> DashPattern -> StrokeAttr
+ Wumpus.Core.GraphicsState: dash_pattern :: StrokeAttr -> DashPattern
+ Wumpus.Core.GraphicsState: default_stroke_attr :: StrokeAttr
+ Wumpus.Core.GraphicsState: gs_stroke_attr :: GraphicsState -> StrokeAttr
+ Wumpus.Core.GraphicsState: line_cap :: StrokeAttr -> LineCap
+ Wumpus.Core.GraphicsState: line_join :: StrokeAttr -> LineJoin
+ Wumpus.Core.GraphicsState: line_width :: StrokeAttr -> Double
+ Wumpus.Core.GraphicsState: miter_limit :: StrokeAttr -> Double
+ Wumpus.Core.Picture: bordered :: (Num u) => RGBi -> StrokeAttr -> RGBi -> PrimPath u -> Primitive u
+ Wumpus.Core.Picture: borderedEllipse :: (Num u) => RGBi -> StrokeAttr -> RGBi -> u -> u -> Point2 u -> Primitive u
+ Wumpus.Core.Picture: fillEllipse :: (Num u) => RGBi -> u -> u -> Point2 u -> Primitive u
+ Wumpus.Core.Picture: fontDeltaContext :: FontAttr -> Picture u -> Picture u
+ Wumpus.Core.Picture: hkernlabel :: (Num u) => RGBi -> FontAttr -> [KerningChar u] -> Point2 u -> Primitive u
+ Wumpus.Core.Picture: kernEscInt :: u -> Int -> KerningChar u
+ Wumpus.Core.Picture: kernEscName :: u -> String -> KerningChar u
+ Wumpus.Core.Picture: kernchar :: u -> Char -> KerningChar u
+ Wumpus.Core.Picture: strokeEllipse :: (Num u) => RGBi -> StrokeAttr -> u -> u -> Point2 u -> Primitive u
+ Wumpus.Core.Picture: vkernlabel :: (Num u) => RGBi -> FontAttr -> [KerningChar u] -> Point2 u -> Primitive u
+ Wumpus.Core.Picture: wumpus_default_font :: FontAttr
+ Wumpus.Core.Picture: xbordered :: (Num u) => RGBi -> StrokeAttr -> RGBi -> XLink -> PrimPath u -> Primitive u
+ Wumpus.Core.Picture: xcstroke :: (Num u) => RGBi -> StrokeAttr -> XLink -> PrimPath u -> Primitive u
+ Wumpus.Core.Picture: xfill :: (Num u) => RGBi -> XLink -> PrimPath u -> Primitive u
+ Wumpus.Core.Picture: xfillEllipse :: (Num u) => RGBi -> XLink -> u -> u -> Point2 u -> Primitive u
+ Wumpus.Core.Picture: xhkernlabel :: (Num u) => RGBi -> FontAttr -> XLink -> [KerningChar u] -> Point2 u -> Primitive u
+ Wumpus.Core.Picture: xostroke :: (Num u) => RGBi -> StrokeAttr -> XLink -> PrimPath u -> Primitive u
+ Wumpus.Core.Picture: xstrokeEllipse :: (Num u) => RGBi -> StrokeAttr -> XLink -> u -> u -> Point2 u -> Primitive u
+ Wumpus.Core.Picture: xtextlabel :: (Num u) => RGBi -> FontAttr -> XLink -> String -> Point2 u -> Primitive u
+ Wumpus.Core.Picture: xvkernlabel :: (Num u) => RGBi -> FontAttr -> XLink -> [KerningChar u] -> Point2 u -> Primitive u
+ Wumpus.Core.WumpusTypes: data FontCtx
+ Wumpus.Core.WumpusTypes: data XLink
+ Wumpus.Core.WumpusTypes: type DKerningChar = KerningChar Double
+ Wumpus.Core.WumpusTypes: type KerningChar u = (u, EncodedChar)
- Wumpus.Core.FontSize: textBounds :: (Fractional u, Ord u, FromPtSize u) => FontSize -> Point2 u -> CharCount -> BoundingBox u
+ Wumpus.Core.FontSize: textBounds :: (Num u, Ord u, FromPtSize u) => FontSize -> Point2 u -> CharCount -> BoundingBox u
- Wumpus.Core.GraphicsState: GraphicsState :: RGBi -> Int -> FontFace -> Double -> Double -> LineCap -> LineJoin -> DashPattern -> GraphicsState
+ Wumpus.Core.GraphicsState: GraphicsState :: RGBi -> Int -> FontFace -> StrokeAttr -> GraphicsState
- Wumpus.Core.Picture: cstroke :: (Stroke t, Num u) => t -> PrimPath u -> Primitive u
+ Wumpus.Core.Picture: cstroke :: (Num u) => RGBi -> StrokeAttr -> PrimPath u -> Primitive u
- Wumpus.Core.Picture: fill :: (Fill t, Num u) => t -> PrimPath u -> Primitive u
+ Wumpus.Core.Picture: fill :: (Num u) => RGBi -> PrimPath u -> Primitive u
- Wumpus.Core.Picture: ostroke :: (Stroke t, Num u) => t -> PrimPath u -> Primitive u
+ Wumpus.Core.Picture: ostroke :: (Num u) => RGBi -> StrokeAttr -> PrimPath u -> Primitive u
- Wumpus.Core.Picture: textlabel :: (TextLabel t, Num u) => t -> String -> Point2 u -> Primitive u
+ Wumpus.Core.Picture: textlabel :: (Num u) => RGBi -> FontAttr -> String -> Point2 u -> Primitive u

Files

CHANGES view
@@ -1,4 +1,78 @@ +0.30.0 to 0.31.0:++  * Extended the label type to optionally handle horizontal+    or vertical \"kerning\". This can be efficiently implemented +    in SVG.++  * Major API change - the Primitive constructors defined in +    @Core.PictureLanguage@ are no longer overloaded (@cstroke@, +    @ostroke@, @fill@, @textlabel@, @ellipse@). The respective +    classes (@Stroke@, @TextLabel@, @Ellipse@) have been removed+    and the constructors now have monomorphic types. This is +    because Wumpus-Core now only has one colour type and stroke+    attributes are now a single type, so polymorphism became+    less useful (and the simplicity of monomorphic types became+    more favourable). @ellipse@ is no longer a constructor +    instead there variants @fillEllipse@ and @strokeEllipse@.++  * Added support for /bordered/ closed paths - i.e. paths that+    are both stroked and filled. These can be efficiently drawn +    in SVG with a single @path@ element.++  * Changed StrokeAttr representation to be a regular data type+    rather than a list of /deltas/ - i.e. differences to the +    graphics state.++  * Added SVG font attribute /delta optimizations/ to help reduce +    SVG code size - via the @fontDeltaContext@ function in +    @Core.Picture@. This functionality was partially inplemented +    in the last release using the @GSUpdate@ datatype - this +    datatype it is now called @FontCtx@.++  * Some internal work documenting text encoding.++0.23.0 to 0.30.0:++  * Substantial changes - the output machinery has been heavily +    modified, the intention was to reduce the use of matrix +    transformations in SVG output. Though this goal has not been +    achieved, some code-size improvements in the SVG output have +    been made. ++  * The code generation monads have been changed - Wumpus no +    longer uses a writer monad for code generation, but it will+    keep the Wumpus name.++  * Added support for hyperlinks in the SVG output.++  * Improved internal pretty printing and added the function +    @printPicture@ for debugging.++  * The @Core.Colour@ module has been simplified to support just +    one (new) type - RGBi, and the conversion type class +    @PSColour@ has been eliminated.++  * @Core.Picture@ - the type of the @frame@ function has been +    changed to take a list of Primitives rather than a single +    Primitive. The function @frameMulti@ which previously +    provided this functionality has been removed. SVG hyperlinks+    are now supported for Primitives. + +  * The default font size has been changed to 14 pt. ++  * The Path datatype has been renamed PrimPath, likewise Ellipse +    is now PrimEllispe and Label becomes PrimLabel.++  * @Core.Geometry@ now longer defines an affine frame datatype.++  * @Core.BoundingBox@ module changed significantly. The corner +    functions (@boundaryBottomLeft@, etc.) have been removed, +    along with the plane functions (@leftPlane@, etc.). Some +    functions have been given more verbose names - @corners@ is +    now @boundaryCorners@, @within@ is now @withinBoundary@ and +    @union@ is now @boundaryUnion@. + 0.22.0 TO 0.23.0:     * @Basic.GraphicsState@ - extracted the font face fields from @@ -41,7 +115,6 @@    * Exposed the PSUnit type class.  - 0.20.0 to 0.21.0:    * Removed the Primitive instances of the Affine transformation @@ -70,7 +143,6 @@   * Removed the Ord superclass constriant from the @Stroke@ and     @Fill@ classes and derived operations (e.g. @zostroke@). - 0.19.0 to 0.20.0:      * Removed the modules:@@ -93,7 +165,6 @@      * Guide updated regarding the new package @wumpus-basic@.   - 0.18.0 to 0.19.0:      * Added @Transform@ type class to apply a matrix transformation@@ -149,7 +220,6 @@     missing with the generated file @WorldFrame.eps@ incorrectly      included instead. - 0.16.0 to 0.17.0:      * Added Core.WumpusTypes to export opaque versions of@@ -176,7 +246,6 @@      * Removed dependency on \'old-time\'. - 0.15.0 to 0.16.0:    * Additions to Core.Geometry (direction, pvec, vangle, @@ -216,12 +285,10 @@      * Removed DList dependency. - 0.13.0 to 0.13.1:    * Wumpus.Core.SVG changed path_s to path_c - \'S\' was the      wrongSVG command to match PostScript\'s @curveto@.-  0.12.0 to 0.13.0: 
demo/AffineTestBase.hs view
@@ -110,16 +110,16 @@ --------------------------------------------------------------------------------
 
 rgbLabel :: RGBi -> DPrimitive
-rgbLabel rgb = textlabel rgb "Wumpus!" zeroPt
+rgbLabel rgb = textlabel rgb wumpus_default_font "Wumpus!" zeroPt
 
 rgbCircle :: RGBi -> DPrimitive
-rgbCircle rgb = ellipse rgb 60 60 zeroPt
+rgbCircle rgb = fillEllipse rgb 60 60 zeroPt
 
 rgbEllipse :: RGBi -> DPrimitive
-rgbEllipse rgb = ellipse rgb 60 30 zeroPt
+rgbEllipse rgb = fillEllipse rgb 60 30 zeroPt
 
 rgbPath :: RGBi -> DPrimitive
-rgbPath rgb = ostroke rgb $ dog_kennel
+rgbPath rgb = ostroke rgb default_stroke_attr $ dog_kennel
 --------------------------------------------------------------------------------
 -- Demo - draw a dog kennel...
 
+ demo/DeltaPic.hs view
@@ -0,0 +1,50 @@+{-# LANGUAGE TypeFamilies               #-}+{-# OPTIONS -Wall #-}++module DeltaPic where++import Wumpus.Core++import System.Directory+++peru :: RGBi+peru = RGBi 205  133  63++black :: RGBi+black = RGBi 0 0 0 +++++main :: IO ()+main = do +    createDirectoryIfMissing True "./out/"+    writeEPS_latin1 "./out/delta_pic01.eps" pic1+    writeSVG_latin1 "./out/delta_pic01.svg" pic1+++pic1 :: DPicture+pic1 = fontDeltaContext delta_ctx $ frame +          [ helveticaLabel 18 "Optimized - size and face"  (P2 0 60)+          , helveticaLabel 14 "Optimized - face only"      (P2 0 40)+          , courierLabel      "No optimization"            (P2 0 20)+          ]+  where+   delta_ctx  = FontAttr 18 common_ff+ ++common_ff :: FontFace+common_ff = FontFace "Helvetica" "Helvetica" SVG_REGULAR+++-- Note - each label is fully attributed with the font style.+-- There really is not attribute inheritance.+--+helveticaLabel :: Int -> String -> DPoint2 -> DPrimitive+helveticaLabel sz ss pt = textlabel peru attrs ss pt+  where+    attrs = FontAttr sz common_ff++courierLabel :: String -> DPoint2 -> DPrimitive+courierLabel ss pt = textlabel black wumpus_default_font ss pt
demo/FontMetrics.hs view
@@ -57,18 +57,19 @@ bodyHeight  = vertLine peru $ fromPtSize courier48_numeral_height  agraveU     :: PrimF-agraveU     = textlabel (black, courier_attr) "&#Agrave"+agraveU     = textlabel black courier_attr "&#Agrave"  letter :: Char -> DPoint2 -> DPrimitive-letter ch pt = textlabel (black, courier_attr) [ch] pt+letter ch pt = textlabel black courier_attr [ch] pt   vertLine :: RGBi -> Double -> DPoint2 -> DPrimitive-vertLine rgb height pt = ostroke rgb $ vertexPath [pt, pt .+^ vvec height]+vertLine rgb height pt = +    ostroke rgb default_stroke_attr $ vertexPath [pt, pt .+^ vvec height]  haxis :: RGBi -> PtSize -> DPrimitive haxis rgb ypos = -    ostroke (rgb, dash_attr) $ vertexPath [ pt, pt .+^ hvec 440 ]+    ostroke rgb dash_attr $ vertexPath [ pt, pt .+^ hvec 440 ]   where-    dash_attr = DashPattern (Dash 0 [(2,2)])+    dash_attr = default_stroke_attr { dash_pattern = Dash 0 [(2,2)] }     pt        = P2 0 (fromPtSize ypos)
+ demo/KernPic.hs view
@@ -0,0 +1,53 @@+{-# OPTIONS -Wall #-}++module KernPic where++import Wumpus.Core+import Wumpus.Core.Colour++import System.Directory++++main :: IO ()+main = do +    createDirectoryIfMissing True "./out/"+    writeEPS_latin1 "./out/kern_pic01.eps" kern_pic+    writeSVG_latin1 "./out/kern_pic01.svg" kern_pic+++kern_pic :: DPicture+kern_pic = pic1 `picOver` pic2++pic1 :: DPicture+pic1 = frame [ helveticaLabelH mystere   (P2 0 40)+             , helveticaLabelH mystere   (P2 0 20)+             ]++pic2 :: DPicture+pic2 = illustrateBoundsPrim blue_violet $ +          helveticaLabelV mystere   (P2 100 140)+++mystere ::[DKerningChar]+mystere = [ kernchar 0  'm'+          , kernchar 15 'y'+          , kernchar 10 's'+          , kernchar 10 't'+          , kernEscInt 6 232+          , kernchar 10 'r'+          , kernchar 6 'e'+          ]++helveticaLabelH :: [KerningChar Double] -> DPoint2 -> DPrimitive+helveticaLabelH xs pt = hkernlabel black helvetica18 xs pt++helveticaLabelV :: [KerningChar Double] -> DPoint2 -> DPrimitive+helveticaLabelV xs pt = vkernlabel black helvetica18 xs pt+++helvetica18 :: FontAttr+helvetica18 = FontAttr 18 (FontFace "Helvetica" "Helvetica" SVG_REGULAR)++blue_violet             :: RGBi+blue_violet             = RGBi 0x8a 0x2b 0xe2
demo/LabelPic.hs view
@@ -12,7 +12,7 @@   drawBounds :: (Floating u, Real u, FromPtSize u) => Picture u -> Picture u-drawBounds p        = p `picOver` (frame [cstroke () ph])+drawBounds p        = p `picOver` (frame [zcstroke ph])   where     ph            = vertexPath $ [bl,br,tr,tl]     (bl,br,tr,tl) = boundaryCorners $ boundary p@@ -41,9 +41,9 @@  lbl1 :: Picture Double lbl1 = line1 `picBeside` line2 where-  line1 = frame [textlabel attrs "Hello" zeroPt]-  line2 = frame [textlabel attrs "World" zeroPt]-  attrs = (peru, FontAttr 12 (FontFace "Helvetica" "Helvetica" SVG_REGULAR)) +  line1 = frame [textlabel peru attrs "Hello" zeroPt]+  line2 = frame [textlabel peru attrs "World" zeroPt]+  attrs = FontAttr 12 (FontFace "Helvetica" "Helvetica" SVG_REGULAR)   demo01 :: IO ()@@ -90,9 +90,9 @@ bigT = bigLetter plum  'T'  bigLetter :: RGBi -> Char -> Picture Double-bigLetter col ch = uniformScale 5 $ frame [textlabel attrs [ch] zeroPt]+bigLetter rgb ch = uniformScale 5 $ frame [textlabel rgb attrs [ch] zeroPt]   where-    attrs = (col, FontAttr 12 (FontFace "Helvetica" "Helvetica" SVG_REGULAR)) +    attrs = FontAttr 12 (FontFace "Helvetica" "Helvetica" SVG_REGULAR)   -- | A should be above B, above T
+ demo/Latin1Pic.hs view
@@ -0,0 +1,34 @@+{-# OPTIONS -Wall #-}++module Latin1Pic where++import Wumpus.Core+import Wumpus.Core.Colour++import System.Directory++++main :: IO ()+main = do +    createDirectoryIfMissing True "./out/"+    writeEPS_latin1 "./out/latin1_pic01.eps" pic1+    writeSVG_latin1 "./out/latin1_pic01.svg" pic1+++-- | Provided the respective lookups can be found, Wumpus +-- supports escapes as either numbers or names...+--+pic1 :: DPicture+pic1 = frame [ helveticaLabel "mystère"      (P2 0 40)+             , helveticaLabel "myst&#egrave;re"   (P2 0 20)+             ]+ ++++helveticaLabel :: String -> DPoint2 -> DPrimitive+helveticaLabel ss pt = textlabel black helvetica18 ss pt++helvetica18 :: FontAttr+helvetica18 = FontAttr 18 (FontFace "Helvetica" "Helvetica" SVG_REGULAR)
demo/MultiPic.hs view
@@ -19,8 +19,8 @@  pic1 :: DPicture pic1 = uniformScale 2 $ frame $ -    [ ellipse blue 10 10 zeroPt-    , ellipse red 10 10 (P2 40 40)+    [ fillEllipse blue 10 10 zeroPt+    , fillEllipse red 10 10 (P2 40 40)     , ztextlabel "Wumpus!" (P2 40 20)     , square red 5 (P2 50 10)       ]
demo/Rotated.hs view
@@ -58,10 +58,10 @@   ellipseHH :: Fractional u => RGBi -> u -> Point2 u -> Primitive u-ellipseHH rgb radius ctr = ellipse rgb radius (0.5*radius) ctr+ellipseHH rgb radius ctr = fillEllipse rgb radius (0.5*radius) ctr   label :: Num u => RGBi -> Point2 u -> Primitive u-label rgb bl = textlabel rgb "Wumpus" bl+label rgb bl = textlabel rgb wumpus_default_font "Wumpus" bl   grey :: RGBi
demo/Scaled.hs view
@@ -51,10 +51,10 @@     [bl, bl .+^ hvec sidelen, bl .+^ V2 sidelen sidelen, bl .+^ vvec sidelen]  ellipseHH :: Fractional u => RGBi -> u -> Point2 u -> Primitive u-ellipseHH rgb radius ctr = ellipse rgb radius (0.5*radius) ctr+ellipseHH rgb radius ctr = fillEllipse rgb radius (0.5*radius) ctr   label :: Num u => RGBi -> Point2 u -> Primitive u-label rgb bl = textlabel rgb "Wumpus" bl+label rgb bl = textlabel rgb wumpus_default_font "Wumpus" bl   grey :: RGBi
demo/Translated.hs view
@@ -46,10 +46,10 @@     [bl, bl .+^ hvec sidelen, bl .+^ V2 sidelen sidelen, bl .+^ vvec sidelen]  ellipseHH :: Fractional u => RGBi -> u -> Point2 u -> Primitive u-ellipseHH rgb radius ctr = ellipse rgb radius (0.5*radius) ctr+ellipseHH rgb radius ctr = fillEllipse rgb radius (0.5*radius) ctr   label :: Num u => RGBi -> Point2 u -> Primitive u-label rgb bl = textlabel rgb "Wumpus" bl+label rgb bl = textlabel rgb wumpus_default_font "Wumpus" bl   grey :: RGBi
demo/ZOrderPic.hs view
@@ -28,8 +28,8 @@   prim_list :: DPoint2 -> [DPrimitive]-prim_list = sequence [ ellipse red   20 20-                     , \p -> ellipse green 20 20 (p .+^ hvec 20)-                     , \p -> ellipse blue  20 20 (p .+^ hvec 40)+prim_list = sequence [ fillEllipse red   20 20+                     , \p -> fillEllipse green 20 20 (p .+^ hvec 20)+                     , \p -> fillEllipse blue  20 20 (p .+^ hvec 40)                      ] 
doc-src/Guide.lhs view
@@ -95,11 +95,11 @@ 
 \item[\texttt{Wumpus.Core.Picture.}]
 Operations to build \emph{pictures} - paths and labels within
-an affine frame. Type classes overloading convenience 
-constructors for building paths, labels, ellipses... The 
-constructors are convenient in that attributes - colour, line
-width, etc. - may be specified or not. The technique is due to 
-Iavor S. Diatchki's XML-Light.
+an affine frame. Generally the functions here are convenience 
+constructors for types from the hidden module 
+\texttt{Wumpus.Core.PictureInternal}. The types from this
+module are exported as opaque signatures by 
+\texttt{Wumpus.Core.WumpusTypes}.
 
 \item[\texttt{Wumpus.Core.PtSize.}]
 Text size calculations in \texttt{Core.FontSize} use points 
doc-src/WorldFrame.hs view
@@ -27,11 +27,11 @@   makeLabelPrim :: String -> DPoint2 -> DPrimitive-makeLabelPrim = textlabel attrs +makeLabelPrim = textlabel black attrs    where-    attrs     = (black, FontAttr 10 (FontFace "Helvetica" "Helvetica" SVG_REGULAR))+    attrs     = FontAttr 10 (FontFace "Helvetica" "Helvetica" SVG_REGULAR)  makeLinePrim :: Double -> DPoint2 -> DPoint2 -> DPrimitive-makeLinePrim lw a b = ostroke attrs $ path a [lineTo b]+makeLinePrim lw a b = ostroke black attrs $ path a [lineTo b]   where-    attrs = (black, [LineWidth lw])+    attrs = default_stroke_attr {line_width=lw}
doc/Guide.pdf view

binary file changed (56990 → 56526 bytes)

src/Wumpus/Core/FontSize.hs view
@@ -209,7 +209,7 @@ -- For variable width fonts the calculated bounding box will  -- usually be too long. ---textBounds :: (Fractional u, Ord u, FromPtSize u) +textBounds :: (Num u, Ord u, FromPtSize u)             => FontSize -> Point2 u -> CharCount -> BoundingBox u textBounds sz body_bl n = bbox bl tr where     h           = fromPtSize $ textHeight sz
src/Wumpus/Core/GraphicsState.hs view
@@ -56,8 +56,9 @@   , FontFace(..)   , SVGFontStyle(..) -  -- ** Default graphic state+  -- * Initial graphic state   , zeroGS+  , default_stroke_attr    ) where @@ -73,29 +74,23 @@       { gs_draw_colour  :: RGBi       , gs_font_size    :: Int       , gs_font_face    :: FontFace-      , gs_line_width   :: Double-      , gs_miter_limit  :: Double-      , gs_line_cap     :: LineCap-      , gs_line_join    :: LineJoin-      , gs_dash_pattern :: DashPattern +      , gs_stroke_attr  :: StrokeAttr        }   deriving (Eq,Show) ---- | Stroke attributes are an algebriac type rather than a --- record type. This is for convenience when attributing paths ---- paths can be attibuted with just the differences from the --- default settings, rather than all the settings whether or not--- they are important.+-- | Stroke attributes for drawing paths. ---data StrokeAttr = LineWidth   Double-                | MiterLimit  Double-                | LineCap     LineCap-                | LineJoin    LineJoin-                | DashPattern DashPattern +data StrokeAttr = StrokeAttr+      { line_width      :: Double+      , miter_limit     :: Double+      , line_cap        :: LineCap+      , line_join       :: LineJoin+      , dash_pattern    :: DashPattern+      }   deriving (Eq,Show)  + -- | Line cap - default in output is butt. -- data LineCap = CapButt | CapRound | CapSquare@@ -159,11 +154,18 @@ zeroGS = GraphicsState { gs_draw_colour  = black                        , gs_font_size    = (-1)                        , gs_font_face    = unmatchable_face-                       , gs_line_width   = 1-                       , gs_miter_limit  = 1-                       , gs_line_cap     = CapButt-                       , gs_line_join    = JoinMiter-                       , gs_dash_pattern = Solid+                       , gs_stroke_attr  = default_stroke_attr                        }   where     unmatchable_face = FontFace "DONT_MATCH" "" SVG_BOLD_OBLIQUE+++-- | Default stroke attributes.+--+default_stroke_attr :: StrokeAttr+default_stroke_attr = StrokeAttr { line_width      = 1+                                 , miter_limit     = 1+                                 , line_cap        = CapButt+                                 , line_join       = JoinMiter+                                 , dash_pattern    = Solid+                                 }
src/Wumpus/Core/OutputPostScript.hs view
@@ -85,9 +85,9 @@     Just n  -> (Right n,s)     Nothing -> (Left $ ps_fallback r,s) -runLocalGS :: GSUpdate -> PsMonad a -> PsMonad a-runLocalGS upd mf = -    PsMonad $ \r s -> let (a,_) = getPsMonad mf r (getGSU upd s) in (a,s)+-- runLocalGS :: GSUpdate -> PsMonad a -> PsMonad a+-- runLocalGS upd mf = +--     PsMonad $ \r s -> let (a,_) = getPsMonad mf r (getGSU upd s) in (a,s)  getDrawColour       :: PsMonad RGBi getDrawColour       = PsMonad $ \_ s -> (gs_draw_colour s, s)@@ -107,38 +107,52 @@     getLineWidth        :: PsMonad Double-getLineWidth        = PsMonad $ \_ s -> (gs_line_width s, s)+getLineWidth        = PsMonad $ \_ s -> (line_width $ gs_stroke_attr s, s)  setLineWidth        :: Double -> PsMonad ()-setLineWidth a      = PsMonad $ \_ s -> ((), s { gs_line_width=a })-+setLineWidth a      = PsMonad $ \_ s -> +    ((), s { gs_stroke_attr = fn $ gs_stroke_attr s })+  where+    fn sa = sa { line_width = a }  getMiterLimit       :: PsMonad Double-getMiterLimit       = PsMonad $ \_ s -> (gs_miter_limit s,s)+getMiterLimit       = PsMonad $ \_ s -> (miter_limit $ gs_stroke_attr s,s)  setMiterLimit       :: Double -> PsMonad ()-setMiterLimit a     = PsMonad $ \_ s -> ((), s { gs_miter_limit=a })+setMiterLimit a     = PsMonad $ \_ s -> +    ((), s { gs_stroke_attr = fn $ gs_stroke_attr s })+  where+    fn sa = sa { miter_limit = a }   getLineCap          :: PsMonad LineCap-getLineCap          = PsMonad $ \_ s -> (gs_line_cap s,s)+getLineCap          = PsMonad $ \_ s -> (line_cap $ gs_stroke_attr s,s)  setLineCap          :: LineCap -> PsMonad ()-setLineCap a        = PsMonad $ \_ s -> ((), s { gs_line_cap=a })+setLineCap a        = PsMonad $ \_ s ->+    ((), s { gs_stroke_attr = fn $ gs_stroke_attr s })+  where+    fn sa = sa { line_cap = a }   getLineJoin         :: PsMonad LineJoin-getLineJoin         = PsMonad $ \_ s -> (gs_line_join s,s)+getLineJoin         = PsMonad $ \_ s -> (line_join $ gs_stroke_attr s,s)  setLineJoin         :: LineJoin -> PsMonad ()-setLineJoin a       = PsMonad $ \_ s -> ((), s { gs_line_join=a })+setLineJoin a       = PsMonad $ \_ s ->+    ((), s { gs_stroke_attr = fn $ gs_stroke_attr s })+  where+    fn sa = sa { line_join = a }   getDashPattern      :: PsMonad DashPattern-getDashPattern      = PsMonad $ \_ s -> (gs_dash_pattern s,s)+getDashPattern      = PsMonad $ \_ s -> (dash_pattern $ gs_stroke_attr s,s)  setDashPattern      :: DashPattern -> PsMonad ()-setDashPattern a    = PsMonad $ \_ s -> ((), s { gs_dash_pattern=a })+setDashPattern a    = PsMonad $ \_ s -> +    ((), s { gs_stroke_attr = fn $ gs_stroke_attr s })+  where+    fn sa = sa { dash_pattern = a }   --------------------------------------------------------------------------------@@ -237,13 +251,15 @@  -------------------------------------------------------------------------------- -+-- Note - PostScript ignotes any FontCtx changes via the @Group@+-- constructor.+-- picture :: (Real u, Floating u, PSUnit u) => Picture u -> PsMonad Doc picture (Leaf    (_,xs) ones)   = bracketTrafos xs $ revConcat primitive ones picture (Picture (_,xs) ones)   = bracketTrafos xs $ revConcat picture ones picture (Clip    (_,xs) cp pic) = bracketTrafos xs $                                     (vconcat <$> clipPath cp <*> picture pic)-picture (Group   (_,xs) fn pic) = bracketTrafos xs (runLocalGS fn (picture pic))+picture (Group   (_,xs) _ pic) = bracketTrafos xs (picture pic)   revConcat :: (a -> PsMonad Doc) -> OneList a -> PsMonad Doc@@ -310,10 +326,10 @@ primEllipse props (PrimEllipse center hw hh ctm) =     bracketPrimCTM center (scaleCTM 1 (hh/hw) ctm) (drawF props)   where-    drawF (EFill rgb)               pt = fillArcPath rgb hw pt-    drawF (EStroke attrs rgb)       pt = strokeArcPath rgb attrs hw pt-    drawF (EFillStroke fc attrs sc) pt = -        vconcat <$> fillArcPath fc hw pt <*>  strokeArcPath sc attrs hw pt+    drawF (EFill rgb)            pt = fillArcPath rgb hw pt+    drawF (EStroke sa rgb)       pt = strokeArcPath rgb sa hw pt+    drawF (EFillStroke fc sa sc) pt = +        vconcat <$> fillArcPath fc hw pt <*>  strokeArcPath sc sa hw pt                          @@ -329,15 +345,15 @@       <$> deltaDrawColour rgb  strokeArcPath :: PSUnit u -              => RGBi -> [StrokeAttr] -> u -> Point2 u -> PsMonad Doc-strokeArcPath rgb attrs radius pt =+              => RGBi -> StrokeAttr -> u -> Point2 u -> PsMonad Doc+strokeArcPath rgb sa radius pt =     (\rgbd attrd -> vcat [ rgbd                          , attrd                          , ps_newpath                          , ps_arc pt radius 0 360                          , ps_closepath                          , ps_stroke ])-      <$> deltaDrawColour rgb <*> deltaStrokeAttrs attrs+      <$> deltaDrawColour rgb <*> deltaStrokeAttrs sa   -- Note - for the otherwise case, the x-and-y coordinates are @@ -345,24 +361,56 @@ -- primLabel :: (Real u, Floating u, PSUnit u)            => LabelProps -> PrimLabel u -> PsMonad Doc-primLabel (LabelProps rgb font) (PrimLabel basept txt ctm) = +primLabel (LabelProps rgb font) (PrimLabel basept body ctm) =      bracketPrimCTM basept ctm mf   where-    mf pt = (\rgbd fontd showd -> vcat [ rgbd, fontd, ps_moveto pt, showd ]) +    mf pt = (\rgbd fontd showd -> vcat [ rgbd, fontd, showd ])                <$> deltaDrawColour rgb <*> deltaFontAttrs font -                                      <*> encodedText txt+                                      <*> labelBody pt body +labelBody :: PSUnit u => Point2 u -> LabelBody u -> PsMonad Doc+labelBody pt (StdLayout txt) = (\d1 -> ps_moveto pt `vconcat` d1) +                                 <$> encodedText txt+labelBody pt (KernTextH xs)  = kernTextH pt xs+labelBody pt (KernTextV xs)  = kernTextV pt xs++ encodedText :: EncodedText -> PsMonad Doc  encodedText etext = vcat <$> (mapM textChunk $ getEncodedText etext)   textChunk :: TextChunk -> PsMonad Doc-textChunk (SText s)  = pure (ps_show $ escapeSpecial s)-textChunk (EscStr s) = pure (ps_glyphshow s)-textChunk (EscInt i) = (either failk ps_glyphshow) <$> askCharCode i +textChunk (TextSpan s)    = pure (ps_show $ escapeSpecial s)+textChunk (TextEscName s) = pure (ps_glyphshow s)+textChunk (TextEscInt i)  = (either failk ps_glyphshow) <$> askCharCode i    where     failk gly_name = missingCharCode i gly_name +kernTextH :: PSUnit u => Point2 u -> [KerningChar u] -> PsMonad Doc+kernTextH pt0 xs = snd <$> F.foldlM fn (pt0,empty) xs+  where+    fn (P2 x y,acc) (dx,ch) = (\doc1 -> let pt = P2 (x+dx) y in+                                        (pt, vcat [acc, ps_moveto pt, doc1]))+                                <$> encodedChar ch++-- Note - vertical labels grow downwards...+--+kernTextV :: PSUnit u => Point2 u -> [KerningChar u] -> PsMonad Doc+kernTextV pt0 xs = snd <$> F.foldlM fn (pt0,empty) xs+  where+    fn (P2 x y,acc) (dy,ch) = (\doc1 -> let pt = P2 x (y-dy) in+                                        (pt, vcat [acc, ps_moveto pt, doc1]))+                                <$> encodedChar ch+++encodedChar :: EncodedChar -> PsMonad Doc+encodedChar (CharLiteral c) = pure (ps_show $ escapeSpecialChar c)+encodedChar (CharEscName s) = pure (ps_glyphshow s)+encodedChar (CharEscInt i)  = (either failk ps_glyphshow) <$> askCharCode i +  where+    failk gly_name = missingCharCode i gly_name++ -------------------------------------------------------------------------------- -- Stroke, font and drawing colour attribute delta @@ -389,74 +437,38 @@                else setDrawColour rgb >> return (ps_setrgbcolor rgb)  --- Note - the current way of handling Stroke Deltas for --- PostScript uses an awful lot of operations. --- --- The GraphicsState data types need some more thought -----data StrokeDelta = StrokeDelta-      { sd_line_width   :: Double-      , sd_miter_limit  :: Double-      , sd_line_cap     :: LineCap-      , sd_line_join    :: LineJoin-      , sd_dash_pattern :: DashPattern-      }---- | Note - the values here must be in- sync with zeroGS.----zeroSD :: StrokeDelta-zeroSD = StrokeDelta { sd_line_width   = 1-                     , sd_miter_limit  = 1-                     , sd_line_cap     = CapButt-                     , sd_line_join    = JoinMiter-                     , sd_dash_pattern = Solid-                     }---makeStrokeDelta :: [StrokeAttr] -> StrokeDelta-makeStrokeDelta = foldr fn zeroSD-  where-    fn (LineWidth a)    sd = sd { sd_line_width = a }-    fn (MiterLimit a)   sd = sd { sd_miter_limit = a }-    fn (LineCap a)      sd = sd { sd_line_cap = a }-    fn (LineJoin a)     sd = sd { sd_line_join = a }-    fn (DashPattern a)  sd = sd { sd_dash_pattern = a} ---deltaStrokeAttrs :: [StrokeAttr] -> PsMonad Doc-deltaStrokeAttrs xs = step $ makeStrokeDelta xs+deltaStrokeAttrs :: StrokeAttr -> PsMonad Doc+deltaStrokeAttrs sa = +    (\d1 d2 d3 d4 d5 -> hcat $ catMaybes [d1,d2,d3,d4,d5])  +      <$> lw <*> ml <*> lc <*> lj <*> dp   where-    step sd = (\d1 d2 d3 d4 d5 -> hcat $ catMaybes [d1,d2,d3,d4,d5])  -                <$> lw sd <*> ml sd <*> lc sd <*> lj sd <*> dp sd--    lw sd = let d = sd_line_width sd in -            getLineWidth >>= \inh -> -            if d == inh +    lw = let d = line_width sa in +         getLineWidth >>= \inh -> +         if d == inh                then return Nothing                else setLineWidth d >> return (Just $ ps_setlinewidth d) -    ml sd = let d = sd_miter_limit sd in -            getMiterLimit >>= \inh -> -            if d==inh +    ml = let d = miter_limit sa in +         getMiterLimit >>= \inh -> +         if d==inh                then return Nothing                else setMiterLimit d >> return (Just $ ps_setmiterlimit d)                             -    lc sd = let d = sd_line_cap sd in-            getLineCap >>= \inh -> -            if d==inh +    lc = let d = line_cap sa in+         getLineCap >>= \inh -> +         if d==inh                then return Nothing               else setLineCap d >> return (Just $ ps_setlinecap d)                       -    lj sd = let d = sd_line_join sd in -            getLineJoin >>= \inh -> -            if d==inh +    lj = let d = line_join sa in +         getLineJoin >>= \inh -> +         if d==inh                then return Nothing               else setLineJoin d >> return (Just $ ps_setlinejoin d) -    dp sd  = let d = sd_dash_pattern sd in-             getDashPattern >>= \inh -> -             if d==inh +    dp = let d = dash_pattern sa in+         getDashPattern >>= \inh -> +         if d==inh                 then return Nothing                else setDashPattern d >> return (Just $ ps_setdash d) 
src/Wumpus/Core/OutputSVG.hs view
@@ -56,6 +56,7 @@  import Control.Applicative hiding ( empty, some ) import qualified Data.Foldable as F+import Data.Maybe  -- SvgMonad is two Readers plus Int state for clip paths... --@@ -96,29 +97,35 @@ -- (and /graphics state/ is via a Reader), so it is the same as  -- local with a Reader monad. ---runLocalGS :: GSUpdate -> SvgMonad a -> SvgMonad a+runLocalGS :: (GraphicsState -> GraphicsState) -> SvgMonad a -> SvgMonad a runLocalGS upd mf = -    SvgMonad $ \r1 r2 s -> getSvgMonad mf r1 (getGSU upd r2) s+    SvgMonad $ \r1 r2 s -> getSvgMonad mf r1 (upd r2) s  +askGraphicsState :: SvgMonad GraphicsState+askGraphicsState = SvgMonad $ \_ r2 s -> (r2,s)++asksGraphicsState :: (GraphicsState -> a) -> SvgMonad a+asksGraphicsState fn = SvgMonad $ \_ r2 s -> (fn r2,s)+ askFontAttr :: SvgMonad FontAttr askFontAttr = -    SvgMonad $ \_ r2 s -> (FontAttr (gs_font_size r2) (gs_font_face r2), s)+    asksGraphicsState $ \r -> FontAttr (gs_font_size r) (gs_font_face r)  askLineWidth    :: SvgMonad Double-askLineWidth    = SvgMonad $ \_ r2 s -> (gs_line_width r2, s)+askLineWidth    = asksGraphicsState (line_width . gs_stroke_attr)  askMiterLimit   :: SvgMonad Double-askMiterLimit   = SvgMonad $ \_ r2 s -> (gs_miter_limit r2, s)+askMiterLimit   = asksGraphicsState (miter_limit . gs_stroke_attr)  askLineCap      :: SvgMonad LineCap-askLineCap      = SvgMonad $ \_ r2 s -> (gs_line_cap r2, s)+askLineCap      = asksGraphicsState (line_cap . gs_stroke_attr)  askLineJoin     :: SvgMonad LineJoin-askLineJoin     = SvgMonad $ \_ r2 s -> (gs_line_join r2, s)+askLineJoin     = asksGraphicsState (line_join . gs_stroke_attr)  askDashPattern  :: SvgMonad DashPattern-askDashPattern  = SvgMonad $ \_ r2 s -> (gs_dash_pattern r2, s)+askDashPattern  = asksGraphicsState (dash_pattern . gs_stroke_attr)  -------------------------------------------------------------------------------- @@ -170,7 +177,7 @@                           ; d2  <- picture pic                           ; return (vconcat d1 (elem_g (attr_clip_path lbl) d2))                           } -picture (Group   (_,xs) fn pic) = bracketTrafos xs (runLocalGS fn (picture pic))+picture (Group   (_,xs) fn pic) = bracketTrafos xs $ bracketGS fn (picture pic)   @@ -275,53 +282,105 @@  primLabel :: (Real u, Floating u, PSUnit u)        => LabelProps -> PrimLabel u -> SvgMonad Doc-primLabel (LabelProps rgb attrs) (PrimLabel pt etext ctm) = -    (\fa ca txt -> elem_text (fa <+> ca) txt)-      <$> deltaFontAttrs attrs <*> bracketPrimCTM pt ctm mkXY -                               <*> tspan rgb etext-  where-    mkXY (P2 x y) = pure $ attr_x x <+> attr_y y    +primLabel (LabelProps rgb attrs) (PrimLabel pt body ctm) = +    (\fa ca dtxt -> elem_text (fa <+> ca) (makeTspan rgb dtxt))+      <$> deltaFontAttrs attrs <*> bracketPrimCTM pt ctm (labelBodyCoords body)+                               <*> labelBodyText body+     -tspan :: RGBi -> EncodedText -> SvgMonad Doc-tspan rgb enctext = -    (\txt -> elem_tspan (attr_fill rgb) txt) -      <$> encodedText enctext +labelBodyCoords :: PSUnit u => LabelBody u -> Point2 u -> SvgMonad Doc+labelBodyCoords (StdLayout _)  pt = pure $ makeXY pt+labelBodyCoords (KernTextH xs) pt = pure $ makeXsY pt xs        +labelBodyCoords (KernTextV xs) pt = pure $ makeXYs pt xs++labelBodyText :: LabelBody u -> SvgMonad Doc+labelBodyText (StdLayout enctext) = encodedText enctext+labelBodyText (KernTextH xs)      = hcat <$> mapM kerningChar xs+labelBodyText (KernTextV xs)      = hcat <$> mapM kerningChar xs++ encodedText :: EncodedText -> SvgMonad Doc encodedText enctext = hcat <$> mapM textChunk (getEncodedText enctext) - textChunk :: TextChunk -> SvgMonad Doc-textChunk (SText s)  = pure $ text s-textChunk (EscInt i) = pure $ text $ escapeSpecial i-textChunk (EscStr s) = either text text <$> askGlyphName s +textChunk (TextSpan s)    = pure $ text s+textChunk (TextEscInt i)  = pure $ text $ escapeSpecial i+textChunk (TextEscName s) = either text text <$> askGlyphName s  +kerningChar :: KerningChar u -> SvgMonad Doc+kerningChar (_, CharLiteral c) = pure $ char c+kerningChar (_, CharEscInt i)  = pure $ text $ escapeSpecial i+kerningChar (_, CharEscName s) = either text text <$> askGlyphName s  ++makeTspan :: RGBi -> Doc -> Doc+makeTspan rgb body = elem_tspan (attr_fill rgb) body++makeXY :: PSUnit u => Point2 u -> Doc+makeXY (P2 x y) = attr_x x <+> attr_y y++-- This is for horizontal kerning text, the output is of the +-- form:+-- +-- > x="0 10 25 35" y="0"+--+makeXsY :: PSUnit u => Point2 u -> [KerningChar u] -> Doc+makeXsY (P2 x y) ks = attr_xs (step x ks) <+> attr_y y+  where +    step ax ((d,_):ds) = let a = ax+d in a : step a ds +    step _  []         = []+++-- This is for vertical kerning text, the output is of the +-- form:+-- +-- > x="0 0 0 0" y="0 10 25 35"+--+-- Note - this is different to the horizontal version as the +-- x-coord needs to be /realigned/ at each step.+--+makeXYs :: PSUnit u => Point2 u -> [KerningChar u] -> Doc+makeXYs (P2 x y) ks = attr_xs xcoords <+> attr_ys (step y ks)+  where +    xcoords            = replicate (length ks) x+    step ay ((d,_):ds) = let a = ay+d in a : step a ds +    step _  []         = []+    +    + -------------------------------------------------------------------------------- -- Stroke and font attribute delta -deltaStrokeAttrs :: [StrokeAttr] -> SvgMonad Doc-deltaStrokeAttrs xs = hsep <$> mapM df xs+deltaStrokeAttrs :: StrokeAttr -> SvgMonad Doc+deltaStrokeAttrs sa = +    (\d1 d2 d3 d4 d5 -> hsep $ catMaybes [d1,d2,d3,d4,d5])  +      <$> lw <*> ml <*> lc <*> lj <*> dp   where-    df (LineWidth d)    = (\inh -> if d==inh then empty -                                         else attr_stroke_width d) -                            <$> askLineWidth--    df (MiterLimit d)   = (\inh -> if d==inh then empty -                                         else attr_stroke_miterlimit d)-                            <$> askMiterLimit+    lw = let d = line_width sa in+         askLineWidth >>= \inh -> +         if d==inh then return Nothing +                   else return (Just $ attr_stroke_width d) +         +    ml = let d = miter_limit sa in+         askMiterLimit >>= \inh -> +         if d==inh then return Nothing +                   else return (Just $ attr_stroke_miterlimit d) -    df (LineCap d)      = (\inh -> if d==inh then empty -                                         else attr_stroke_linecap d)-                            <$> askLineCap+    lc = let d = line_cap sa in+         askLineCap >>= \inh -> +         if d==inh then return Nothing +                   else return (Just $ attr_stroke_linecap d) -    df (LineJoin d)     = (\inh -> if d==inh then empty -                                         else attr_stroke_linejoin d)-                            <$> askLineJoin+    lj = let d = line_join sa in+         askLineJoin >>= \inh -> +         if d==inh then return Nothing +                   else return (Just $ attr_stroke_linejoin d) -    df (DashPattern d)  = (\inh -> if d==inh then empty -                                             else makeDashPattern d) -                            <$> askDashPattern+    dp = let d = dash_pattern sa in +         askDashPattern >>= \inh -> +         if d==inh then return Nothing +                   else return (Just $ makeDashPattern d)   makeDashPattern :: DashPattern -> Doc makeDashPattern Solid       = attr_stroke_dasharray_none@@ -331,7 +390,7 @@   deltaFontAttrs :: FontAttr -> SvgMonad Doc-deltaFontAttrs fa  = +deltaFontAttrs fa =      (\inh -> if fa ==inh then empty else makeFontAttrs fa) <$> askFontAttr  makeFontAttrs :: FontAttr -> Doc@@ -353,6 +412,28 @@     suffix SVG_BOLD_OBLIQUE =          space <> attr_font_weight "bold" <+> attr_font_style "oblique" ++-- NOTE - as is only practical to delta the FontFace attributes +-- it might be good to specialize / simplify the graphics state+-- GSUpdate to a simpler type rather than a functional one...++bracketGS :: FontCtx -> SvgMonad Doc -> SvgMonad Doc+bracketGS (FontCtx new_font) mf = +    (\old body -> mkElem old body) +        <$> askGraphicsState <*> runLocalGS updateF mf+  where+    mkElem old body +      | fontMatch old new_font = elem_g_no_attrs body+      | otherwise              = let a = makeFontAttrs new_font+                                 in elem_g a body++    updateF s = s { gs_font_size = font_size new_font+                  , gs_font_face = font_face new_font }+                ++fontMatch :: GraphicsState -> FontAttr -> Bool+fontMatch gs fa = +   gs_font_size gs == font_size fa && gs_font_face gs == font_face fa   --------------------------------------------------------------------------------
src/Wumpus/Core/Picture.hs view
@@ -23,6 +23,7 @@   -- * Construction     frame   , multi+  , fontDeltaContext   , path   , lineTo   , curveTo@@ -31,19 +32,42 @@   , xlinkhref    -- * Constructing primitives-  , Stroke(..)+  , ostroke+  , cstroke+  , xostroke+  , xcstroke   , zostroke   , zcstroke-  , Fill(..)++  , fill+  , xfill   , zfill-  , Ellipse(..)-  , zellipse +  , bordered+  , xbordered   , clip -  , TextLabel(..)+  , textlabel+  , xtextlabel   , ztextlabel+  , wumpus_default_font +  , hkernlabel+  , vkernlabel+  , xhkernlabel+  , xvkernlabel+  , kernchar+  , kernEscInt+  , kernEscName++  , strokeEllipse+  , fillEllipse+  , xstrokeEllipse+  , xfillEllipse+  , zellipse+  , borderedEllipse+  +   -- * Operations   , extendBoundary   @@ -63,7 +87,7 @@ import Wumpus.Core.AffineTrans import Wumpus.Core.BoundingBox import Wumpus.Core.Colour-import Wumpus.Core.FormatCombinators+import Wumpus.Core.FormatCombinators hiding ( fill ) import Wumpus.Core.Geometry import Wumpus.Core.GraphicsState import Wumpus.Core.OneList@@ -112,7 +136,38 @@                     in ( boundary a `append` bb', cons a rest )  +-- | Update the font /delta/ attributes for SVG output.+--+-- Note - 'fontDeltaContext' does not set the font properties of+-- elements in the supplied Picture, it is solely a mechanism to+-- help reduce the code size of the generated SVG by factoring +-- common attributes into a group (g) element. For instance, +-- settting the font properties with 'fontDeltaContext' can +-- eliminate the repeated use of font-family and font-size in +-- this code:+--+-- > <text font-family="Helvetica" font-size="12"> ... </text>+-- > <text font-family="Helvetica" font-size="12"> ... </text>+-- > <text font-family="Helvetica" font-size="12"> ... </text>+--+-- With the appropriate font delta context, this code will be+-- generated:+-- +-- > <g font-family="Helvetica" font-size="12">+-- >   <text > ... </text>+-- >   <text > ... </text>+-- >   <text > ... </text>+-- > </g>+--+-- Wumpus ignores 'fontDeltaContext' directives when generating +-- PostScript. Unlike SVG, PostScript is not naturally nested, so +-- introducing nesting with @gsave@ and @grestore@ is not likely+-- to improve the PostScript Wumpus generates.+--+fontDeltaContext :: FontAttr -> Picture u -> Picture u+fontDeltaContext fa p = Group (boundary p, []) (FontCtx fa) p + -- | Create a Path from a start point and a list of PathSegments. -- path :: Point2 u -> [PrimPathSegment u] -> PrimPath u@@ -151,119 +206,108 @@     step _          = []  +-- | Create a hyperlink for SVG output.+-- xlinkhref :: String -> XLink xlinkhref = XLinkHRef ++ -------------------------------------------------------------------------------- -- Take Paths to Primitives  -- *** Stroke -ostrokePath :: Num u -            => RGBi -> [StrokeAttr] -> XLink -> PrimPath u -> Primitive u-ostrokePath rgb attrs xlink p = PPath (OStroke attrs rgb) xlink p+-- | Create a open, stroked path.+--+ostroke :: Num u +        => RGBi -> StrokeAttr -> PrimPath u -> Primitive u+ostroke rgb sa p = PPath (OStroke sa rgb) NoLink p -cstrokePath :: Num u -            => RGBi -> [StrokeAttr] -> XLink -> PrimPath u -> Primitive u-cstrokePath rgb attrs xlink p = PPath (CStroke attrs rgb) xlink p --- | Create a open, stroked path (@ostroke@) or a closed, stroked--- path (@cstroke@).+-- | Create a closed, stroked path. ----- @ostroke@ and @cstroke@ are overloaded to make attributing --- the path more convenient.--- -class Stroke t where-  ostroke :: Num u => t -> PrimPath u -> Primitive u-  cstroke :: Num u => t -> PrimPath u -> Primitive u+cstroke :: Num u +        => RGBi -> StrokeAttr -> PrimPath u -> Primitive u+cstroke rgb sa p = PPath (CStroke sa rgb) NoLink p -instance Stroke () where-  ostroke () = ostrokePath black [] NoLink-  cstroke () = cstrokePath black [] NoLink+-- | Create a open, stroked path with a hyperlink.+--+-- Note - hyperlinks are only rendered in SVG output.+--+xostroke :: Num u +         => RGBi -> StrokeAttr -> XLink -> PrimPath u -> Primitive u+xostroke rgb sa xlink p = PPath (OStroke sa rgb) xlink p -instance Stroke RGBi where-  ostroke rgb = ostrokePath rgb [] NoLink-  cstroke rgb = cstrokePath rgb [] NoLink -instance Stroke StrokeAttr where-  ostroke x = ostrokePath black [x] NoLink-  cstroke x = cstrokePath black [x] NoLink+-- | Create a closed, stroked path with a hyperlink.+--+--  Note - hyperlinks are only rendered in SVG output.+--+xcstroke :: Num u +         => RGBi -> StrokeAttr -> XLink -> PrimPath u -> Primitive u+xcstroke rgb sa xlink p = PPath (CStroke sa rgb) xlink p -instance Stroke [StrokeAttr] where-  ostroke xs = ostrokePath black xs NoLink-  cstroke xs = cstrokePath black xs NoLink -instance Stroke XLink where-  ostroke xlink = ostrokePath black [] xlink-  cstroke xlink = cstrokePath black [] xlink  -instance Stroke (RGBi,StrokeAttr) where-  ostroke (rgb,x) = ostrokePath rgb [x] NoLink-  cstroke (rgb,x) = cstrokePath rgb [x] NoLink+-- | Create an open, stroked path using the default stroke +-- attributes and coloured black.+--+zostroke :: Num u => PrimPath u -> Primitive u+zostroke = ostroke black default_stroke_attr+ +-- | Create a closed stroked path using the default stroke +-- attributes and coloured black.+--+zcstroke :: Num u => PrimPath u -> Primitive u+zcstroke = cstroke black default_stroke_attr -instance Stroke (RGBi,[StrokeAttr]) where-  ostroke (rgb,xs) = ostrokePath rgb xs NoLink-  cstroke (rgb,xs) = cstrokePath rgb xs NoLink+--------------------------------------------------------------------------------+-- *** Fill -instance Stroke (RGBi,XLink) where-  ostroke (rgb,xlink) = ostrokePath rgb [] xlink-  cstroke (rgb,xlink) = cstrokePath rgb [] xlink -instance Stroke (StrokeAttr,XLink) where-  ostroke (x,xlink) = ostrokePath black [x] xlink-  cstroke (x,xlink) = cstrokePath black [x] xlink--instance Stroke ([StrokeAttr],XLink) where-  ostroke (xs,xlink) = ostrokePath black xs xlink-  cstroke (xs,xlink) = cstrokePath black xs xlink--instance Stroke (RGBi,StrokeAttr,XLink) where-  ostroke (rgb,x,xlink) = ostrokePath rgb [x] xlink-  cstroke (rgb,x,xlink) = cstrokePath rgb [x] xlink--instance Stroke (RGBi,[StrokeAttr],XLink) where-  ostroke (rgb,xs,xlink) = ostrokePath rgb xs xlink-  cstroke (rgb,xs,xlink) = cstrokePath rgb xs xlink+-- | Create a filled path.+--+fill :: Num u => RGBi -> PrimPath u -> Primitive u+fill rgb p = PPath (CFill rgb) NoLink p  --- | Create an open stoke coloured black.+-- | Create a filled path with a hyperlink. ---zostroke :: Num u => PrimPath u -> Primitive u-zostroke = ostrokePath black [] NoLink- --- | Create a closed stroke coloured black.+--  Note - hyperlinks are only rendered in SVG output. ---zcstroke :: Num u => PrimPath u -> Primitive u-zcstroke = cstrokePath black [] NoLink+xfill :: Num u => RGBi -> XLink -> PrimPath u -> Primitive u+xfill rgb xlink p = PPath (CFill rgb) xlink p +-- | Create a filled path coloured black. +zfill :: Num u => PrimPath u -> Primitive u+zfill = fill black --- *** Fill -fillPath :: Num u => RGBi -> XLink -> PrimPath u -> Primitive u-fillPath rgb xlink p = PPath (CFill rgb) xlink p+--------------------------------------------------------------------------------+-- Bordered (closed) paths --- | Create a filled path (@fill@). Fills only have one --- property - colour. But there are various representations of --- colour.++-- | Create a closed path that is both filled and stroked (the fill+-- is below in the zorder). ----- @ fill () @ will fill with the default colour - black.--- -class Fill t where-  fill :: Num u => t -> PrimPath u -> Primitive u- +-- > fill colour * stroke attrs * stroke_colour * ...+--+bordered :: Num u +        => RGBi -> StrokeAttr -> RGBi -> PrimPath u -> Primitive u+bordered frgb sa srgb p = PPath (CFillStroke frgb sa srgb) NoLink p -instance Fill ()                where fill ()    = fillPath black NoLink-instance Fill RGBi              where fill rgb   = fillPath rgb   NoLink-instance Fill XLink             where fill xlink = fillPath black xlink -instance Fill (RGBi,XLink) where-  fill (rgb,xlink) = fillPath rgb xlink+-- | Create a bordered, closed path with a hyperlink.+--+--  Note - hyperlinks are only rendered in SVG output.+--+xbordered :: Num u +        => RGBi -> StrokeAttr -> RGBi -> XLink -> PrimPath u -> Primitive u+xbordered frgb sa srgb xlink p = PPath (CFillStroke frgb sa srgb) xlink p  --- | Create a filled path coloured black. -zfill :: Num u => PrimPath u -> Primitive u-zfill = fillPath black NoLink  -------------------------------------------------------------------------------- -- Clipping @@ -276,12 +320,37 @@ -------------------------------------------------------------------------------- -- Labels to primitive -mkTextLabel :: Num u -            => RGBi -> FontAttr -> XLink -> String -> Point2 u -> Primitive u-mkTextLabel rgb attr xlink txt pt = PLabel (LabelProps rgb attr) xlink lbl +-- | Create a text label. The string should not contain newline+-- or tab characters.+--+-- The supplied point is the left baseline.+--+textlabel :: Num u +          => RGBi -> FontAttr -> String -> Point2 u -> Primitive u+textlabel rgb attr txt = xtextlabel rgb attr NoLink txt +++-- | Create a text label with a hyperlink. The string should not +-- contain newline or tab characters.+--+-- The supplied point is the left baseline.+--+-- Note - hyperlinks are only rendered in SVG output.+--+xtextlabel :: Num u +           => RGBi -> FontAttr -> XLink -> String -> Point2 u -> Primitive u+xtextlabel rgb attr xlink txt pt = PLabel (LabelProps rgb attr) xlink lbl    where-    lbl = PrimLabel pt (lexLabel txt) identityCTM+    lbl = PrimLabel pt (StdLayout $ lexLabel txt) identityCTM ++-- | Create a label where the font is @Courier@, text size is 14pt+-- and colour is black.+--+ztextlabel :: Num u => String -> Point2 u -> Primitive u+ztextlabel = xtextlabel black wumpus_default_font NoLink++ -- | Constant for the default font, which is @Courier@ (aliased  -- to @Courier New@ for SVG) at 14 point. --@@ -294,124 +363,180 @@                     , svg_font_style    = SVG_REGULAR                     } +-------------------------------------------------------------------------------- --- | Create a text label. The string should not contain newline--- or tab characters. Use 'multilabel' to create text with --- multiple lines.--- --- @textlabel@ is overloaded to make attributing the label more --- convenient.+-- | Create a text label with horizontal /kerning/ for each +-- character.  ----- Unless a 'FontAttr' is specified, the label will use 14pt --- Courier.+-- Note - kerning is relative to the left baseline of the +-- previous character, it is \*not relative\* to the right-hand+-- boundary of the previous char. While the later would be more+-- obvious it would take a lot of effort to implement as it would +-- need access to the metrics encoded in font files.  ----- The supplied point is is the bottom left corner.+-- Characters are expected to be drawn left to right, so +-- displacements should not be negative. If the displacement is+-- zero the character will be drawn ontop of the previous char.+-- +-- The charcters should not contain newline or tab characters. ---class TextLabel t where -  textlabel :: Num u => t -> String -> Point2 u -> Primitive u+-- The supplied point is the left baseline.+--+hkernlabel :: Num u +            => RGBi -> FontAttr -> [KerningChar u] -> Point2 u +            -> Primitive u+hkernlabel rgb attr xs = xhkernlabel rgb attr NoLink xs   -instance TextLabel () where -    textlabel () = mkTextLabel black wumpus_default_font NoLink+ +-- | Create a horizontally kerned text label with a hyperlink.+--+-- The is the hyperlink version of 'hkernlabel'.+--+-- Note - hyperlinks are only rendered in SVG output.+--+xhkernlabel :: Num u +            => RGBi -> FontAttr -> XLink -> [KerningChar u] -> Point2 u +            -> Primitive u+xhkernlabel rgb attr xlink xs pt = PLabel (LabelProps rgb attr) xlink lbl +  where+    lbl = PrimLabel pt (KernTextH xs) identityCTM -instance TextLabel RGBi where-  textlabel rgb = mkTextLabel rgb wumpus_default_font NoLink -instance TextLabel FontAttr where-  textlabel a = mkTextLabel black a NoLink -instance TextLabel XLink where-    textlabel xlink = mkTextLabel black wumpus_default_font xlink+-- | Create a text label with vertical /kerning/ for each +-- character - the text is expected to grow downwards. +--+-- Note - /kerning/ here is the measure between baselines of +-- sucessive characters, it is \*not\* the distance between the +-- bottom of one chararter and the top of the next character.+-- +-- While the later maybe be more obvious from a drawing +-- perspective, it would take a lot of effort to implement as it +-- would need access to the metrics encoded in font files. +--+-- Characters are expected to be drawn downwards - a positive +-- number represents the downward displacement - so displacements +-- should not be negative. If the displacement is zero the +-- character will be drawn ontop of the previous char.+-- +-- The charcters should not contain newline or tab characters.+--+-- The supplied point is the left baseline of the top character.+--+vkernlabel :: Num u +            => RGBi -> FontAttr ->[KerningChar u] -> Point2 u +            -> Primitive u+vkernlabel rgb attr xs = xvkernlabel rgb attr NoLink xs   -instance TextLabel (RGBi,FontAttr) where-  textlabel (rgb,a) = mkTextLabel rgb a NoLink+-- | Create a vertically drawn text label with a hyperlink.+--+-- The is the hyperlink version of 'vkernlabel'.+--+-- Note - hyperlinks are only rendered in SVG output.+--+xvkernlabel :: Num u +            => RGBi -> FontAttr -> XLink -> [KerningChar u] -> Point2 u +            -> Primitive u+xvkernlabel rgb attr xlink xs pt = PLabel (LabelProps rgb attr) xlink lbl +  where+    lbl = PrimLabel pt (KernTextV xs) identityCTM -instance TextLabel (RGBi,XLink) where-  textlabel (rgb,xlink) = mkTextLabel rgb wumpus_default_font xlink -instance TextLabel (FontAttr,XLink) where-  textlabel (a,xlink) = mkTextLabel black a xlink -instance TextLabel (RGBi,FontAttr,XLink) where-  textlabel (rgb,a,xlink) = mkTextLabel rgb a xlink+-- | Construct a regular (i.e. non-special) Char along with its +-- displacement from the left-baseline of the previous Char.+--+kernchar :: u -> Char -> KerningChar u+kernchar u c = (u, CharLiteral c) --- | Create a label where the font is @Courier@, text size is 14pt--- and colour is black.+-- | Construct a Char by its character code along with its +-- displacement from the left-baseline of the previous Char. ---ztextlabel :: Num u => String -> Point2 u -> Primitive u-ztextlabel = mkTextLabel black wumpus_default_font NoLink+kernEscInt :: u -> Int -> KerningChar u+kernEscInt u i = (u, CharEscInt i) +-- | Construct a Char by its character name along with its +-- displacement from the left-baseline of the previous Char.+--+kernEscName :: u -> String -> KerningChar u+kernEscName u s = (u, CharEscName s)  -------------------------------------------------------------------------------- -mkEllipse :: Num u -          => EllipseProps -> XLink -> u -> u -> Point2 u -> Primitive u-mkEllipse props xlink hw hh pt = -    PEllipse props xlink (PrimEllipse pt hw hh identityCTM) --ellipseDefault :: EllipseProps-ellipseDefault = EFill black----- | Create an ellipse, the ellipse will be filled unless the --- supplied attributes /imply/ a stroked ellipse, e.g.:------ > ellipse (LineWidth 4) zeroPt 40 40 +-- | Create a stroked ellipse. -- -- Note - within Wumpus, ellipses are considered an unfortunate -- but useful /optimization/. Drawing good cicles with Beziers  -- needs at least eight curves, but drawing them with --- PostScript\'s @arc@ command is a single operation.  For +-- PostScript\'s @arc@ command needs a single operation. For  -- drawings with many dots (e.g. scatter plots) it seems sensible--- to employ this optimaztion.+-- to employ this optimization. -- -- A deficiency of Wumpus\'s ellipse is that (non-uniformly) -- scaling a stroked ellipse also (non-uniformly) scales the pen  -- it is drawn with. Where the ellipse is wider, the pen stroke  -- will be wider too.  ---class Ellipse t where-  ellipse :: Fractional u => t -> u -> u -> Point2 u -> Primitive u+-- Avoid non-uniform scaling stroked ellipses!+--+strokeEllipse :: Num u +             => RGBi -> StrokeAttr -> u -> u -> Point2 u -> Primitive u+strokeEllipse rgb sa = xstrokeEllipse rgb sa NoLink  -instance Ellipse ()             where ellipse () = zellipse+-- | Create a filled ellipse.+--+fillEllipse :: Num u +             => RGBi -> u -> u -> Point2 u -> Primitive u+fillEllipse rgb = xfillEllipse rgb NoLink -instance Ellipse RGBi where -  ellipse rgb = mkEllipse (EFill rgb) NoLink+-- | Create a stroked ellipse with a hyperlink.+--+-- Note - hyperlinks are only rendered in SVG output.+--+xstrokeEllipse :: Num u +             => RGBi -> StrokeAttr -> XLink -> u -> u -> Point2 u -> Primitive u+xstrokeEllipse rgb sa xlink hw hh pt = +    PEllipse (EStroke sa rgb) xlink (PrimEllipse pt hw hh identityCTM) -instance Ellipse StrokeAttr where-  ellipse x = mkEllipse (EStroke [x] black) NoLink+-- | Create a filled ellipse.+--+-- Note - hyperlinks are only rendered in SVG output.+-- +xfillEllipse :: Num u +             => RGBi -> XLink -> u -> u -> Point2 u -> Primitive u+xfillEllipse rgb xlink hw hh pt = +    PEllipse (EFill rgb) xlink (PrimEllipse pt hw hh identityCTM) -instance Ellipse [StrokeAttr] where-  ellipse xs = mkEllipse (EStroke xs black) NoLink -instance Ellipse XLink where -  ellipse xlink = mkEllipse (EFill black) xlink+-- | Create a black, filled ellipse. +zellipse :: Num u => u -> u -> Point2 u -> Primitive u+zellipse hw hh pt = xfillEllipse black NoLink hw hh pt -instance Ellipse (RGBi,StrokeAttr) where-  ellipse (rgb,x) = mkEllipse (EStroke [x] rgb) NoLink -instance Ellipse (RGBi,[StrokeAttr]) where-  ellipse (rgb,xs) = mkEllipse (EStroke xs rgb) NoLink+-- | Create a bordered (i.e. filled and stroked) ellipse.+--+borderedEllipse :: Num u +                => RGBi -> StrokeAttr -> RGBi -> u -> u -> Point2 u +                -> Primitive u+borderedEllipse frgb sa srgb = xborderedEllipse frgb sa srgb NoLink -instance Ellipse (RGBi,XLink) where-  ellipse (rgb,xlink) = mkEllipse (EFill rgb) xlink+-- | Create a bordered (i.e. filled and stroked) ellipse with a +-- hyperlink.+--+-- Note - hyperlinks are only rendered in SVG output.+--+xborderedEllipse :: Num u +                 => RGBi -> StrokeAttr -> RGBi -> XLink -> u -> u -> Point2 u +                 -> Primitive u+xborderedEllipse frgb sa srgb xlink hw hh pt = +    PEllipse (EFillStroke frgb sa srgb) xlink (PrimEllipse pt hw hh identityCTM) -instance Ellipse (StrokeAttr,XLink) where-  ellipse (x,xlink) = mkEllipse (EStroke [x] black) xlink -instance Ellipse ([StrokeAttr],XLink) where-  ellipse (xs,xlink) = mkEllipse (EStroke xs black) xlink -instance Ellipse (RGBi,[StrokeAttr],XLink) where-  ellipse (rgb,xs,xlink) = mkEllipse (EStroke xs rgb) xlink---- | Create a black, filled ellipse. -zellipse :: Num u => u -> u -> Point2 u -> Primitive u-zellipse hw hh pt = mkEllipse ellipseDefault NoLink hw hh pt- -------------------------------------------------------------------------------- -- Operations @@ -505,11 +630,12 @@ boundsPrims rgb a = [ bbox_rect, bl_to_tr, br_to_tl ]   where     (bl,br,tr,tl) = boundaryCorners $ boundary a-    bbox_rect     = cstroke (rgb, line_attr) $ vertexPath [bl,br,tr,tl]-    bl_to_tr      = ostroke (rgb, line_attr) $ vertexPath [bl,tr]-    br_to_tl      = ostroke (rgb, line_attr) $ vertexPath [br,tl]+    bbox_rect     = cstroke rgb line_attr $ vertexPath [bl,br,tr,tl]+    bl_to_tr      = ostroke rgb line_attr $ vertexPath [bl,tr]+    br_to_tl      = ostroke rgb line_attr $ vertexPath [br,tl] -    line_attr     = [LineCap $ CapRound, DashPattern $ Dash 0 [(1,2)]]+    line_attr     = default_stroke_attr { line_cap     = CapRound+                                        , dash_pattern = Dash 0 [(1,2)] }   -- | Generate the control points illustrating the Bezier @@ -546,7 +672,8 @@     step _ (PLineTo e:xs)        = step e xs     step s (PCurveTo c1 c2 e:xs) = mkLine s c1 : mkLine c2 e : step e xs  -    mkLine s e                   = ostroke rgb (PrimPath s [lineTo e]) +    mkLine s e                   = let pp = (PrimPath s [lineTo e]) in +                                   ostroke rgb default_stroke_attr pp    -- Generate lines illustrating the control points of an @@ -570,7 +697,7 @@     rest s (c1:c2:e:xs)  = mkLine s c1 : mkLine c2 e : rest e xs     rest _ _             = [] -    mkLine s e           = ostroke rgb (PrimPath s [lineTo e]) +    mkLine s e  = ostroke rgb default_stroke_attr (PrimPath s [lineTo e])    
src/Wumpus/Core/PictureInternal.hs view
@@ -23,7 +23,7 @@   , DPicture   , Locale   , AffineTrafo(..)-  , GSUpdate(..)+  , FontCtx(..)    , Primitive(..)   , DPrimitive@@ -37,6 +37,10 @@   , PrimLabel(..)   , DPrimLabel   , LabelProps(..)+  , LabelBody(..)+  , DLabelBody+  , KerningChar+  , DKerningChar     , PrimEllipse(..)   , EllipseProps(..)   , PrimCTM(..)@@ -124,21 +128,19 @@ data Picture u = Leaf     (Locale u)              (OneList (Primitive u))                | Picture  (Locale u)              (OneList (Picture u))                | Clip     (Locale u) (PrimPath u) (Picture u)-               | Group    (Locale u) GSUpdate     (Picture u)+               | Group    (Locale u) FontCtx      (Picture u)   deriving (Show)  --- | Update the graphics state for SVG rendering. +-- | Set the font /delta/ for SVG rendering.  -- --- Note - this does not change how any of the Primitives are --- drawn, nor does it change the default colour or font style. +-- Note - this does not change the default colour or font style.  -- It is solely a backdoor into the SVG renderer to potential  -- allow some code size reductions. ---newtype GSUpdate = GSUpdate { getGSU :: GraphicsState -> GraphicsState }+newtype FontCtx = FontCtx { getFontCtx :: FontAttr }+  deriving (Eq,Show) -instance Show GSUpdate where-  show _ = "*function*"  -- | Locale = (bounding box * current translation matrix) -- @@ -232,9 +234,9 @@ --   of the line (stroke width). -- data PathProps = CFill RGBi -               | CStroke [StrokeAttr] RGBi-               | OStroke [StrokeAttr] RGBi-               | CFillStroke RGBi [StrokeAttr] RGBi+               | CStroke StrokeAttr RGBi+               | OStroke StrokeAttr RGBi+               | CFillStroke RGBi StrokeAttr RGBi   deriving (Eq,Show)  @@ -242,13 +244,15 @@ -- data PrimLabel u = PrimLabel        { label_baseline_left :: Point2 u-      , label_text          :: EncodedText+      , label_body          :: LabelBody u       , label_ctm           :: PrimCTM u       }   deriving (Eq,Show)  type DPrimLabel = PrimLabel Double +-- | Font rendering properties for a PrimLabel.+-- data LabelProps   = LabelProps        { label_colour :: RGBi       , label_font   :: FontAttr@@ -256,6 +260,33 @@   deriving (Eq,Ord,Show)  +-- | Label can be draw with 3 layouts.+-- +-- The standard layout uses @show@ for PostScript and a single +-- initial point for SVG.+--+-- Kerned horizontal layout - each character is encoded with the+-- rightwards horizontal distance from the last charcaters left +-- base-line.+-- +-- Kerned vertical layout - each character is encoded with the+-- upwards distance from the last charcaters left base-line.+-- +data LabelBody u = StdLayout EncodedText+                 | KernTextH [KerningChar u]+                 | KernTextV [KerningChar u]+  deriving (Eq,Show)++type DLabelBody = LabelBody Double+++-- | A Char (possibly escaped) paired with is displacement from +-- the previous KerningChar.+--+type KerningChar u = (u,EncodedChar) ++type DKerningChar = KerningChar Double+ -- Ellipse represented by center and half_width * half_height -- data PrimEllipse u = PrimEllipse @@ -270,9 +301,9 @@ -- | Ellipses and circles are always closed. -- data EllipseProps = EFill RGBi-                  | EStroke [StrokeAttr] RGBi +                  | EStroke StrokeAttr RGBi                    -- Note - first colour fill, second colour stroke.-                  | EFillStroke RGBi [StrokeAttr] RGBi +                  | EFillStroke RGBi StrokeAttr RGBi    deriving (Eq,Show)  @@ -361,6 +392,12 @@             <+> text "ctm="           <> format ctm           ] +instance PSUnit u => Format (LabelBody u) where+  format (StdLayout enctext) = format enctext+  format (KernTextH xs)      = text "(KernH)" <+> hcat (map (format .snd) xs)+  format (KernTextV xs)      = text "(KernV)" <+> hcat (map (format .snd) xs)++ instance PSUnit u => Format (PrimEllipse u) where   format (PrimEllipse ctr hw hh ctm) = text "center="   <> format ctr                                    <+> text "hw="       <> dtruncFmt hw@@ -434,14 +471,55 @@  labelBoundary :: (Floating u, Real u, FromPtSize u)                => FontAttr -> PrimLabel u -> BoundingBox u-labelBoundary attr (PrimLabel (P2 x y) xs ctm) = +labelBoundary attr (PrimLabel (P2 x y) body ctm) =      retraceBoundary  (disp . (m33 *#)) untraf_bbox   where     disp        = (.+^ V2 x y)     m33         = matrixRepCTM ctm-    untraf_bbox = textBounds (font_size attr) zeroPt char_count-    char_count  = textLength xs+    untraf_bbox = labelBodyBoundary (font_size attr) body +labelBodyBoundary :: (Num u, Ord u, FromPtSize u) +                  => FontSize -> LabelBody u -> BoundingBox u+labelBodyBoundary sz (StdLayout etxt) = stdLayoutBB sz (textLength etxt)+labelBodyBoundary sz (KernTextH xs)   = hKerningBB sz xs+labelBodyBoundary sz (KernTextV xs)   = vKerningBB sz xs+++stdLayoutBB :: (Num u, Ord u, FromPtSize u) +            => FontSize -> CharCount -> BoundingBox u+stdLayoutBB sz len = textBounds sz zeroPt len++-- Note - this assumes positive deltas (and a nonempty list)...+--+-- Kern deltas are relative to the left basepoint, so they are+-- irrespective of the actual charater width. Thus to calculate+-- the bounding box Wumpus calculates the bounds of one character+-- then expands the right edge with the sum of the (rightwards)+-- displacements.+-- +hKerningBB :: (Num u, Ord u, FromPtSize u) +           => FontSize -> [(u,EncodedChar)] -> BoundingBox u+hKerningBB sz xs = rightGrow (sumDiffs xs) $ textBounds sz zeroPt 1+  where+    sumDiffs                          = foldr (\(u,_) i -> i+u)  0+    rightGrow u (BBox ll (P2 x1 y1))  = BBox ll (P2 (x1+u) y1)+++-- Note - likewise same assumptions as horizontal version.+-- (A postive distance represents a move downwards)...+--+-- The kern delta is the distance between baselines of successive+-- characters, so character height is irrespective when summing +-- the deltas.+-- +-- Also note, that the Label /grows/ downwards...+--+vKerningBB :: (Num u, Ord u, FromPtSize u) +           => FontSize -> [(u,EncodedChar)] -> BoundingBox u+vKerningBB sz xs = downGrow (sumDiffs xs) $ textBounds sz zeroPt 1+  where+    sumDiffs                                = foldr (\(u,_) i -> i+u)  0+    downGrow u (BBox (P2 x0 y0) (P2 x1 y1)) = BBox (P2 x0 (y0-u)) (P2 x1 y1)   -- | Ellipse bbox is the bounding rectangle, rotated as necessary 
src/Wumpus/Core/PostScriptDoc.hs view
@@ -18,6 +18,7 @@   (        escapeSpecial+  , escapeSpecialChar        , psHeader   , epsHeader@@ -88,6 +89,11 @@   where     f c ss | c `elem` ps_special = '\\' : c : ss            | otherwise           = c : ss++-- Note - this has to promote the Char to a String...+escapeSpecialChar :: Char -> String+escapeSpecialChar c | c `elem` ps_special = ['\\', c]+                    | otherwise           = [c]  ps_special :: [Char] ps_special = "\\()<>[]{}/%"
src/Wumpus/Core/SVGDoc.hs view
@@ -34,7 +34,9 @@    , attr_id   , attr_x+  , attr_xs   , attr_y+  , attr_ys   , attr_r   , attr_rx   , attr_ry@@ -199,10 +201,28 @@ attr_x :: PSUnit u => u -> Doc attr_x = svgAttr "x" . dtruncFmt ++-- | @ x=\"... ... ...\" @+--+-- /List/ version of attr_x+-- +attr_xs :: PSUnit u => [u] -> Doc+attr_xs = svgAttr "x" . hsep . map dtruncFmt++ -- | @ y=\"...\" @ -- attr_y :: PSUnit u => u -> Doc attr_y = svgAttr "y" . dtruncFmt+++-- | @ y=\"... ... ...\" @+--+-- /List/ version of attr_y+-- +attr_ys :: PSUnit u => [u] -> Doc+attr_ys = svgAttr "y" . hsep . map dtruncFmt+  -- | @ r=\"...\" @ --
src/Wumpus/Core/TextEncoder.hs view
@@ -17,20 +17,22 @@ -- -- > "regular ascii text &#egrave; more ascii text" --  --- i.e. character names and codes are delimited by @&#@ on the +-- i.e. character names and codes are delimited by @&\#@ on the  -- left and @;@ on the right. ----- In Wumpus strings both character names and character codes can--- be embedded - it seems conventional for PostScript to use --- names e.g.:  +-- In Wumpus both character names and character codes can+-- be embedded in strings - (e.g. @ &\#egrave; or &\#232; @). --+-- In the generated PostScript, Wumpus uses the character name, +-- e.g.:  +-- -- > (myst) show /egrave glyphshow (re) show -- --- ... and SVG to use codes, e.g.: +-- The generated SVG uses the numeric code, e.g.:  -- -- > myst&#232;re ----- To accommodate both Wumpus defines a TextEncoder record which +-- To accommodate both, Wumpus defines a TextEncoder record which -- provides a two-way mapping between character codes and glyph  -- names for a character set. --
src/Wumpus/Core/TextInternal.hs view
@@ -19,6 +19,7 @@      EncodedText(..)       , TextChunk(..)+  , EncodedChar(..)    , textLength   , lookupByCharCode  @@ -36,10 +37,20 @@ newtype EncodedText = EncodedText { getEncodedText :: [TextChunk] }   deriving (Eq,Show) +-- | Wumpus supports both escaped names e.g. @egrave@ and escaped+-- (numeric decimal) character codes in the input string for a +-- TextLabel.+-- +data TextChunk = TextSpan    String+               | TextEscInt  Int+               | TextEscName GlyphName+  deriving (Eq,Show) -data TextChunk = SText  String-               | EscInt Int-               | EscStr GlyphName+-- | For KernLabels Wumpus needs a Char version of TextChunk.+--+data EncodedChar = CharLiteral Char+                 | CharEscInt  Int+                 | CharEscName GlyphName   deriving (Eq,Show)  @@ -49,16 +60,22 @@   format = hcat . map format . getEncodedText  instance Format TextChunk where-  format (SText s)   = text s-  format (EscInt i)  = text "&#" <> int i  <> semicolon-  format (EscStr s)  = text "&#" <> text s <> semicolon+  format (TextSpan s)    = text s+  format (TextEscInt i)  = text "&#" <> int i  <> semicolon+  format (TextEscName s) = text "&#" <> text s <> semicolon +instance Format EncodedChar where+  format (CharLiteral c) = char c+  format (CharEscInt i)  = text "&#" <> int i  <> semicolon+  format (CharEscName s) = text "&#" <> text s <> semicolon++ --------------------------------------------------------------------------------  textLength :: EncodedText -> Int textLength = foldr add 0 . getEncodedText where -    add (SText s) n = n + length s-    add _         n = n + 1+    add (TextSpan s) n = n + length s+    add _            n = n + 1   lookupByCharCode :: CharCode -> TextEncoder -> Maybe GlyphName@@ -68,17 +85,27 @@ lookupByGlyphName i enc = (svg_lookup enc) i  --- | Output to PostScript as @ /egrave glyphshow @---- Output to SVG as an escaped decimal, e.g. @ &#232; @+-- | 'lexLabel' input is regular text and escaped glyph names or+-- decimal character codes. Escaping follows the SVG convention,+-- start with @&#@ (ampersand hash) end with @;@ (semicolon). --+-- Special chars are output to PostScript as:+--+-- > /egrave glyphshow+--+-- Special chars are output to SVG as an escaped decimal, e.g.:+--+-- > &#232;+-- -- Note, HTML entity names do not seem to be supported in SVG, -- @ &egrave; @ does not work in FireFox or Chrome.--+-- lexLabel :: String -> EncodedText lexLabel = EncodedText . lexer +-- Note - the lexer reads number spans with isDigit, so reads +-- decimals only.+--  lexer :: String -> [TextChunk] lexer []            = [] @@ -87,14 +114,14 @@     esc (c:cs) | isDigit c = let (s,cs') = span isDigit cs                               in  intval (c:s) cs'                | otherwise = let (s,cs') = span isAlpha cs -                             in EscStr (c:s) : optsemi cs'+                             in TextEscName (c:s) : optsemi cs'     esc []                 = []      optsemi (';':cs)   = lexer cs      -- let ill-formed go through     optsemi cs         = lexer cs      intval [] rest  = optsemi rest-    intval cs rest  = EscInt (read cs) : optsemi rest+    intval cs rest  = TextEscInt (read cs) : optsemi rest  lexer (x:xs)        = let (s,xs') = span (/= '&') xs -                      in SText (x:s) : lexer xs'+                      in TextSpan (x:s) : lexer xs'
src/Wumpus/Core/VersionNumber.hs view
@@ -22,7 +22,7 @@  -- | Version number. ----- > (0,30,0)+-- > (0,31,0) -- wumpus_core_version :: (Int,Int,Int)-wumpus_core_version = (0,30,0)+wumpus_core_version = (0,31,0)
src/Wumpus/Core/WumpusTypes.hs view
@@ -28,15 +28,18 @@   -- * Picture types     Picture   , DPicture-  , GSUpdate+  , FontCtx   , Primitive   , DPrimitive+  , XLink   , PrimPath   , DPrimPath   , PrimPathSegment   , DPrimPathSegment   , PrimLabel   , DPrimLabel+  , KerningChar+  , DKerningChar    -- * Drawing styles   , PathProps       
wumpus-core.cabal view
@@ -1,5 +1,5 @@ name:             wumpus-core-version:          0.30.0+version:          0.31.0 license:          BSD3 license-file:     LICENSE copyright:        Stephen Tetley <stephen.tetley@gmail.com>@@ -9,8 +9,6 @@ synopsis:         Pure Haskell PostScript and SVG generation.  description:   .-  Wumpus - (W)riter (M)onad (P)ost (S)cript. -  .   Wumpus is a kernel library for generating 2D vector pictures,    its salient feature is portability due to no FFI dependencies.    It can generate PostScript (EPS) files and SVG files. The @@ -21,7 +19,7 @@   Pictures in Wumpus are made from /paths/ and text /labels/.    Paths themselves are made from points. The usual affine    transformations (rotations, scaling, translations) can be-  applied to geometric objects. Unlike PostScript there is no +  applied to Pictures. Unlike PostScript there is no    notion of a current point, Wumpus builds pictures in a   coordinate-free style.    .@@ -37,12 +35,8 @@   sophisticated (e.g. how attributes like colour are handled,    and how the bounding boxes of text labels are calculated), so    Wumpus might be limited compared to other systems. However, -  the design permits a fairly simple implementation - which is -  a priority. Text encoding an exception - I\'m not sure how -  reasonable the design is. The current implementation -  appears okay for Latin 1 but may be inadequate for other -  character sets, so I may have to revise it significantly.-  .+  the design permits a fairly simple implementation, which is a +  priority.    .   \[1\] Because the output is simple, straight-line PostScript    code, it is possible to use GraphicsMagick or a similar tool @@ -51,6 +45,39 @@   .   Changelog:   .+  0.30.0 to 0.31.0:+  .+  * Extended the label type to optionally handle horizontal+    or vertical \"kerning\". This can be efficiently implemented +    in SVG.+  .+  * Major API change - the Primitive constructors defined in +    @Core.PictureLanguage@ are no longer overloaded (@cstroke@, +    @ostroke@, @fill@, @textlabel@, @ellipse@). The respective +    classes (@Stroke@, @TextLabel@, @Ellipse@) have been removed+    and the constructors now have monomorphic types. This is +    because Wumpus-Core now only has one colour type and stroke+    attributes are now a single type, so polymorphism became+    less useful (and the simplicity of monomorphic types became+    more favourable). @ellipse@ is no longer a constructor +    instead there variants @fillEllipse@ and @strokeEllipse@.+  .+  * Added support for /bordered/ closed paths - i.e. paths that+    are both stroked and filled. These can be efficiently drawn +    in SVG with a single @path@ element.+  .+  * Changed StrokeAttr representation to be a regular data type+    rather than a list of /deltas/ - i.e. differences to the +    graphics state.+  .+  * Added SVG font attribute /delta optimizations/ to help reduce +    SVG code size - via the @fontDeltaContext@ function in +    @Core.Picture@. This functionality was partially inplemented +    in the last release using the @GSUpdate@ datatype - this +    datatype it is now called @FontCtx@.+  .+  * Some internal work documenting text encoding.+  .   0.23.0 to 0.30.0:   .   * Substantial changes - the output machinery has been heavily @@ -92,29 +119,8 @@     now @boundaryCorners@, @within@ is now @withinBoundary@ and      @union@ is now @boundaryUnion@.    .-  0.22.0 TO 0.23.0:-  . -  * @Basic.GraphicsState@ - extracted the font face fields from -    @FontAttr@ datatype into a separate datatype @FontFace@.-  . -  * Added @PtSize@ a numeric type wrapping Double. This is -    specifically for text size calculations, vis the -    @Core.FontSize@ module.   .-  * Changed all functions in @Core.FontSize@ to return @PtSize@-    instead of a polymorphic type @u@ (where @u@ is an instance -    of Fractional). To get to another unit type rather than -    FontSize use an explicit conversion that scales the value -    accordingly.   .-  * Added FromPtSize class constraints to various functions in-    @Core.Picture@.-  .-  * Added @charWidth@ to @Core.FontSize@.-  .-  * Added @vlength@ to @Core.Geometry@-  .-  . build-type:         Simple stability:          unstable cabal-version:      >= 1.2@@ -126,8 +132,11 @@   demo/AffineTest02.hs,   demo/AffineTest03.hs,   demo/AffineTestBase.hs,-  demo/FontMetrics.hs+  demo/DeltaPic.hs,+  demo/FontMetrics.hs,+  demo/KernPic.hs,   demo/LabelPic.hs,+  demo/Latin1Pic.hs,   demo/MultiPic.hs,   demo/Rotated.hs,   demo/Scaled.hs,