{-# 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 -- -- Graphic type - this is largely equivalent to Primitive in -- Wumpus-Core, but drawing attributes are implicitly supplied -- by the DrawingContext. -- -- -------------------------------------------------------------------------------- module Wumpus.Basic.Kernel.Objects.DrawingPrimitives ( -- * Prim Paths locPP , emptyLocPP , vertexPP , curvePP , dcOpenPath , dcClosedPath -- * Text , dcTextlabel , dcRTextlabel , dcEscapedlabel , dcREscapedlabel , KernChar , hkernLine , vkernLine -- * Lines , straightLine , locStraightLine , curvedLine , straightConnector -- * Circles , dcCircle -- * Ellipses , dcEllipse , dcREllipse -- * Rectangles , dcRectangle -- * Disks , dcDisk , dcEllipseDisk ) where import Wumpus.Basic.Kernel.Base.BaseDefs import Wumpus.Basic.Kernel.Base.QueryDC import Wumpus.Basic.Kernel.Base.WrappedPrimitive 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.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)... -- -- | '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 :: DrawStyle -> PrimPath -> Graphic u dcClosedPath FILL pp = fillPrim (\rgb -> fill rgb pp) dcClosedPath STROKE pp = strokePrim (\rgb attr -> cstroke rgb attr pp) dcClosedPath 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) -- | Unit parametric version of KerningChar from Wumpus-Core. -- type KernChar u = (u,EscapedChar) 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 -> zapQuery (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 -> zapQuery (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 = zapQuery (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 -> zapLocQuery (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 = zapQuery (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 -> zapQuery (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 => DrawStyle -> u -> LocGraphic u dcCircle style r = promoteLoc $ \pt -> zapLocQuery (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 => DrawStyle -> u -> u -> LocGraphic u dcEllipse style rx ry = promoteLoc $ \pt -> zapLocQuery (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 => DrawStyle -> u -> u -> LocThetaGraphic u dcREllipse style rx ry = promoteLocTheta $ \pt ang -> zapLocThetaQuery (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' : @ 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 => DrawStyle -> u -> u -> LocGraphic u dcRectangle style w h = promoteLoc $ \pt -> zapLocQuery (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 => DrawStyle -> u -> LocGraphic u dcDisk FILL radius = normalizeCtx radius >>= \r -> fillLoc (\rgb pt -> fillEllipse rgb r r pt) dcDisk STROKE radius = normalizeCtx radius >>= \r -> strokeLoc (\rgb attr pt -> strokeEllipse rgb attr r r pt) dcDisk 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 => DrawStyle -> u -> u -> LocGraphic u dcEllipseDisk style rx ry = normalizeCtx rx >>= \drx -> normalizeCtx ry >>= \dry -> case style of FILL -> fillLoc (\rgb pt -> fillEllipse rgb drx dry pt) STROKE -> strokeLoc (\rgb attr pt -> strokeEllipse rgb attr drx dry pt) FILL_STROKE -> fillStrokeLoc $ (\frgb attr srgb pt -> fillStrokeEllipse frgb attr srgb drx dry pt)