Portability | GHC |
---|---|

Stability | unstable |

Maintainer | Stephen Tetley <stephen.tetley@gmail.com> |

This module re-exports types and functions from Wumpus.Core.PictureInternal but makes them opaque. Contructors are provided by Wumpus.Core.Picture.

- data Picture u
- type DPicture = Picture Double
- data PrimElement u
- type DPrimElement = PrimElement Double
- data FontCtx
- data Primitive u
- type DPrimitive = Primitive Double
- data XLink
- data PrimPath u
- type DPrimPath = PrimPath Double
- data PrimPathSegment u
- type DPrimPathSegment = PrimPathSegment Double
- data PrimLabel u
- type DPrimLabel = PrimLabel Double
- type KerningChar u = (u, EncodedChar)
- type DKerningChar = KerningChar Double
- data PathProps
- data LabelProps
- data EllipseProps
- rotatePrim :: (Real u, Floating u) => Radian -> PrimElement u -> PrimElement u
- scalePrim :: Num u => u -> u -> PrimElement u -> PrimElement u
- uniformScalePrim :: Num u => u -> PrimElement u -> PrimElement u
- translatePrim :: Num u => u -> u -> PrimElement u -> PrimElement u
- class Num a => PSUnit a where

# Picture types

Picture is a leaf attributed tree - where attributes are colour, line-width etc. It is parametric on the unit type of points (typically Double).

Wumpus's leaf attributed tree, is not directly matched to PostScript's picture representation, which might be considered a node attributed tree (if you consider graphics state changes less imperatively - setting attributes rather than global state change).

Considered as a node-attributed tree PostScript precolates graphics state updates downwards in the tree (vis-a-vis inherited attributes in an attibute grammar), where a graphics state change deeper in the tree overrides a higher one.

Wumpus on the other hand, simply labels each leaf with its drawing attributes - there is no attribute inheritance. When it draws the PostScript picture it does some optimization to avoid generating excessive graphics state changes in the PostScript code.

Omitting some details, Picture is a simple non-empty leaf-labelled rose tree via:

tree = Leaf [primitive] | Picture [tree]

The additional constructors are convenience:

`Clip`

nests a picture (tree) inside a clipping path.

The `Group`

constructor allows local shared graphics state
updates for the SVG renderer - in some instances this can
improve the code size of the generated SVG.

data PrimElement u Source

To represent XLink hyperlinks, Primitives in a Leaf are actualy encoded in a tree rather a list.

As a design, this is rather unfortunate as it demands an extra wrapper for ever element regardless of whether hyperlinks are actually used. But it does mean that one hyperlink can cover a complex graphic element - for example an arrow might be drawn with one or more paths, plus extra (filled or stroked) paths for the tip and tail, but each element should be within the link.

Show u => Show (PrimElement u) | |

PSUnit u => Format (PrimElement u) | |

(Real u, Floating u, FromPtSize u) => Boundary (PrimElement u) |

type DPrimElement = PrimElement DoubleSource

Set the font *delta* for SVG rendering.

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.

Wumpus's drawings are built from two fundamental primitives: paths (straight line segments and Bezier curves) and labels (single lines of text).

Ellipses are a included as a primitive only for optimization
- drawing a reasonable circle with Bezier curves needs at
least eight curves. This is inconvenient for drawing dots
which can otherwise be drawn with a single `arc`

command.

Wumpus does not follow PostScript employing arc as a general path primitive - arcs are used only to draw ellipses. This is because arcs do not enjoy the nice properties of Bezier curves, whereby the affine transformation of a Bezier curve can simply be achieved by the affine transformation of it's control points.

Ellipses are represented by their center, half-width and half-height. Half-width and half-height are used so the bounding box can be calculated using only multiplication, and thus initially only obliging a Num constraint on the unit. Though typically for affine transformations a Fractional constraint is also obliged.

type DPrimitive = Primitive DoubleSource

Primitives can be grouped with hyperlinks in SVG output.

PrimPath - start point and a list of path segments.

data PrimPathSegment u Source

PrimPathSegment - either a cubic Bezier curve or a line.

Eq u => Eq (PrimPathSegment u) | |

Show u => Show (PrimPathSegment u) | |

PSUnit u => Format (PrimPathSegment u) |

Label - represented by *baseline* left point and text.

type DPrimLabel = PrimLabel DoubleSource

type KerningChar u = (u, EncodedChar)Source

A Char (possibly escaped) paired with is displacement from the previous KerningChar.

type DKerningChar = KerningChar DoubleSource

# Drawing styles

Note when drawn *filled* and drawn *stroked* the same
polygon will have (slightly) different size:

- A filled shape fills
*within*the boundary of the shape - A stroked shape draws a pen line around the boundary of the shape. The actual size depends on the thickness of the line (stroke width).

data LabelProps Source

Font rendering properties for a PrimLabel.

Eq LabelProps | |

Ord LabelProps | |

Show LabelProps | |

Format LabelProps |

data EllipseProps Source

Ellipses and circles are always closed.

Eq EllipseProps | |

Show EllipseProps | |

Format EllipseProps |

# Transformations on PrimElements

rotatePrim :: (Real u, Floating u) => Radian -> PrimElement u -> PrimElement uSource

Rotate a Primitive.

Note - this is not an affine transformation as Primitives are not regarded as being within an affine frame.

- Paths are rotated about their start point.
- Labels are rotated about the bottom-left corner.
- Ellipses are rotated about the center.

For Primitives and Ellipses applying a rotation and or a scale will generate an additional matrix transformation in the generated PostScript. For Paths all transformations are "cost-free".

scalePrim :: Num u => u -> u -> PrimElement u -> PrimElement uSource

Scale a Primitive.

Note - this is not an affine transformation as Primitives are not regarded as being within an affine frame.

An affine scaling uniformly scales all the elements in a Picture. It is just a change of the Picture's basis vectors. The elements within the Picture are unchanged - though obviously rendering changes according to the transformation.

By contrast, the scaling operation on Primitives changes the properties of the object as it is applied - e.g. for a path the vector between the start point and all subsequent points is changed with respect to the x,y scaling factors; for an ellipse the half-width and half-height of the ellipse is scaled.

For Primitives and Ellipses applying a rotation and or a scale will generate an additional matrix transformation in the generated PostScript. For Paths all transformations are "cost-free".

uniformScalePrim :: Num u => u -> PrimElement u -> PrimElement uSource

Apply a uniform scale to a Primitive.

translatePrim :: Num u => u -> u -> PrimElement u -> PrimElement uSource

Translate a primitive.

Translation is essentially "cost-free" for the generated PostScript or SVG. Paths are translated before the PostScript is generated. For Ellipses and Labels, translation will either move the bottom-left origin (Label) or center (Ellipse); or if they are also scaled or rotated the translation will be concatenated into the matrix operation in the generated output.