Copyright | Marco Zocca 2017 |
---|---|
License | BSD3 |
Maintainer | Marco Zocca <zocca marco gmail> |
Safe Haskell | None |
Language | Haskell2010 |
`plot-light` provides functionality for rendering vector graphics in SVG format. It is geared in particular towards scientific plotting, and it is termed "light" because it only requires a few common Haskell dependencies and no external libraries.
Usage
To use this project you just need import Graphics.Rendering.Plot.Light
. If GHC complains of name clashes you can import the module in "qualified" form.
If you wish to try out the examples in this page, you will need to have these additional import statements:
import Text.Blaze.Svg.Renderer.String (renderSvg)
import qualified Data.Colour.Names as C
- rect :: (Show a, RealFrac a) => a -> a -> a -> Maybe (Colour Double) -> Maybe (Colour Double) -> Point a -> Svg
- rectCentered :: (Show a, RealFrac a) => a -> a -> a -> Maybe (Colour Double) -> Maybe (Colour Double) -> Point a -> Svg
- circle :: (Real a1, Real a) => Point a1 -> a -> a -> Maybe (Colour Double) -> Maybe (Colour Double) -> Svg
- line :: (Show a, RealFrac a) => Point a -> Point a -> a -> LineStroke_ a -> Colour Double -> Svg
- text :: (Show a, Real a) => a -> Int -> Colour Double -> TextAnchor_ -> Text -> V2 a -> Point a -> Svg
- polyline :: (Foldable t, Show a1, Show a, RealFrac a, RealFrac a1) => a1 -> LineStroke_ a -> StrokeLineJoin_ -> Colour Double -> t (Point a) -> Svg
- filledPolyline :: (Foldable t, Show a, Real o) => Colour Double -> o -> t (Point a) -> Svg
- filledBand :: (Foldable t, Real o, Show a) => Colour Double -> o -> (LabeledPoint l a -> a) -> (LabeledPoint l a -> a) -> t (LabeledPoint l a) -> Svg
- candlestick :: (Show a, RealFrac a) => (a -> a -> Bool) -> (l -> a) -> (l -> a) -> (l -> a) -> (l -> a) -> a -> a -> Colour Double -> Colour Double -> Colour Double -> LabeledPoint l a -> Svg
- toPlot :: (Functor t, Foldable t, Show a, RealFrac a) => FigureData a -> (l -> Text) -> (l -> Text) -> a -> a -> a -> Colour Double -> Maybe (t (LabeledPoint l a)) -> Maybe (t (LabeledPoint l a)) -> (t (LabeledPoint l a) -> Svg) -> t (LabeledPoint l a) -> Svg
- data FigureData a = FigureData {
- figWidth :: a
- figHeight :: a
- figLeftMFrac :: a
- figRightMFrac :: a
- figTopMFrac :: a
- figBottomMFrac :: a
- figLabelFontSize :: Int
- data LineStroke_ a
- = Continuous
- | Dashed [a]
- data StrokeLineJoin_
- data TextAnchor_
- svgHeader :: Real a => Frame a -> Svg -> Svg
- translateSvg :: Show a => Point a -> Svg -> Svg
- data Frame a = Frame {}
- data Point a = Point {}
- data LabeledPoint l a = LabeledPoint {}
- labelPoint :: (Point a -> l) -> Point a -> LabeledPoint l a
- mapLabel :: (l1 -> l2) -> LabeledPoint l1 a -> LabeledPoint l2 a
- data Axis
- data V2 a = V2 a a
- data Mat2 a = Mat2 a a a a
- data DiagMat2 a = DMat2 a a
- diagMat2 :: Num a => a -> a -> DiagMat2 a
- origin :: Num a => Point a
- e1 :: Num a => V2 a
- e2 :: Num a => V2 a
- norm2 :: (Hermitian v, Floating n, n ~ InnerProduct v) => v -> n
- normalize2 :: (InnerProduct v ~ Scalar v, Floating (Scalar v), Hermitian v) => v -> v
- v2fromEndpoints :: Num a => Point a -> Point a -> V2 a
- v2fromPoint :: Num a => Point a -> V2 a
- movePoint :: Num a => V2 a -> Point a -> Point a
- moveLabeledPointV2 :: Num a => V2 a -> LabeledPoint l a -> LabeledPoint l a
- moveLabeledPointBwFrames :: Fractional a => Frame a -> Frame a -> Bool -> Bool -> LabeledPoint l a -> LabeledPoint l a
- (-.) :: Num a => Point a -> Point a -> V2 a
- toSvgFrame :: Fractional a => Frame a -> Frame a -> Bool -> Point a -> Point a
- toSvgFrameLP :: Fractional a => Frame a -> Frame a -> Bool -> LabeledPoint l a -> LabeledPoint l a
- pointRange :: (Fractional a, Integral n) => n -> Point a -> Point a -> [Point a]
- frameToFrame :: Fractional a => Frame a -> Frame a -> Bool -> Bool -> V2 a -> V2 a
- frameFromPoints :: (Ord a, Foldable t, Functor t) => t (Point a) -> Frame a
- frameFromFigData :: Num a => FigureData a -> Frame a
- mkFrame :: Point a -> Point a -> Frame a
- mkFrameOrigin :: Num a => a -> a -> Frame a
- width :: Num a => Frame a -> a
- height :: Num a => Frame a -> a
- class AdditiveGroup v where
- class AdditiveGroup v => VectorSpace v where
- class VectorSpace v => Hermitian v where
- type InnerProduct v :: *
- class Hermitian v => LinearMap m v where
- class MultiplicativeSemigroup m where
- class LinearMap m v => MatrixGroup m v where
- class Eps a where
- toFloat :: Scientific -> Float
- wholeDecimal :: (Integral a, RealFrac b) => b -> (a, b)
Plot elements
Geometrical primitives
:: (Show a, RealFrac a) | |
=> a | Width |
-> a | Height |
-> a | Stroke width |
-> Maybe (Colour Double) | Stroke colour |
-> Maybe (Colour Double) | Fill colour |
-> Point a | Corner point coordinates |
-> Svg |
A rectangle, defined by its anchor point coordinates and side lengths
> putStrLn $ renderSvg $ rect (Point 100 200) 30 60 2 Nothing (Just C.aquamarine) <rect x="100.0" y="200.0" width="30.0" height="60.0" fill="#7fffd4" stroke="none" stroke-width="2.0" />
:: (Show a, RealFrac a) | |
=> a | Width |
-> a | Height |
-> a | Stroke width |
-> Maybe (Colour Double) | Stroke colour |
-> Maybe (Colour Double) | Fill colour |
-> Point a | Center coordinates |
-> Svg |
A rectangle, defined by its center coordinates and side lengths
> putStrLn $ renderSvg $ rectCentered (Point 20 30) 15 30 (Just C.blue) (Just C.red) <g transform="translate(12.5 15.0)"><rect width="15.0" height="30.0" fill="#ff0000" stroke="#0000ff" /></g>
:: (Real a1, Real a) | |
=> Point a1 | Center |
-> a | Radius |
-> a | Stroke width |
-> Maybe (Colour Double) | Stroke colour |
-> Maybe (Colour Double) | Fill colour |
-> Svg |
A circle
> putStrLn $ renderSvg $ circle (Point 20 30) 15 (Just C.blue) (Just C.red) <circle cx="20.0" cy="30.0" r="15.0" fill="#ff0000" stroke="#0000ff" />
:: (Show a, RealFrac a) | |
=> Point a | First point |
-> Point a | Second point |
-> a | Stroke width |
-> LineStroke_ a | Stroke type |
-> Colour Double | Stroke colour |
-> Svg |
Line segment between two Point
s
> putStrLn $ renderSvg $ line (Point 0 0) (Point 1 1) 0.1 Continuous C.blueviolet <line x1="0.0" y1="0.0" x2="1.0" y2="1.0" stroke="#8a2be2" stroke-width="0.1" />
> putStrLn $ renderSvg (line (Point 0 0) (Point 1 1) 0.1 (Dashed [0.2, 0.3]) C.blueviolet) <line x1="0.0" y1="0.0" x2="1.0" y2="1.0" stroke="#8a2be2" stroke-width="0.1" stroke-dasharray="0.2, 0.3" />
:: (Show a, Real a) | |
=> a | Rotation angle of the textbox |
-> Int | Font size |
-> Colour Double | Font colour |
-> TextAnchor_ | How to anchor the text to the point |
-> Text | Text |
-> V2 a | Displacement w.r.t. rotated textbox |
-> Point a | Initial position of the text box (i.e. before rotation and displacement) |
-> Svg |
text
renders text onto the SVG canvas
Conventions
The Point
argument p
refers to the lower-left corner of the text box.
The text box can be rotated by rot
degrees around p
and then anchored at either its beginning, middle or end to p
with the TextAnchor_
flag.
The user can supply an additional V2
displacement which will be applied after rotation and anchoring and refers to the rotated text box frame.
> putStrLn $ renderSvg $ text (-45) C.green TAEnd "blah" (V2 (- 10) 0) (Point 250 0) <text x="-10.0" y="0.0" transform="translate(250.0 0.0)rotate(-45.0)" fill="#008000" text-anchor="end">blah</text>
:: (Foldable t, Show a1, Show a, RealFrac a, RealFrac a1) | |
=> a1 | Stroke width |
-> LineStroke_ a | Stroke type |
-> StrokeLineJoin_ | Stroke join type |
-> Colour Double | Stroke colour |
-> t (Point a) | Data |
-> Svg |
Polyline (piecewise straight line)
> putStrLn $ renderSvg (polyline [Point 100 50, Point 120 20, Point 230 50] 4 (Dashed [3, 5]) Round C.blueviolet) <polyline points="100.0,50.0 120.0,20.0 230.0,50.0" fill="none" stroke="#8a2be2" stroke-width="4.0" stroke-linejoin="round" stroke-dasharray="3.0, 5.0" />
:: (Foldable t, Show a, Real o) | |
=> Colour Double | Fill colour |
-> o | Fill opacity |
-> t (Point a) | Contour point coordinates |
-> Svg |
A filled polyline
> putStrLn $ renderSvg $ filledPolyline C.coral 0.3 [(Point 0 1), (Point 10 40), Point 34 50, Point 30 5] <polyline points="0,1 10,40 34,50 30,5" fill="#ff7f50" fill-opacity="0.3" />
Composite plot elements
:: (Foldable t, Real o, Show a) | |
=> Colour Double | Fill colour |
-> o | Fill opacity |
-> (LabeledPoint l a -> a) | Band maximum value |
-> (LabeledPoint l a -> a) | Band minimum value |
-> t (LabeledPoint l a) | Centerline points |
-> Svg |
A filled band of colour, given the coordinates of its center line
This element can be used to overlay uncertainty ranges (e.g. the first standard deviation) associated with a given data series.
:: (Show a, RealFrac a) | |
=> (a -> a -> Bool) | If True, fill the box with the first colour, otherwise with the second |
-> (l -> a) | Box maximum value |
-> (l -> a) | Box minimum value |
-> (l -> a) | Line maximum value |
-> (l -> a) | Line minimum value |
-> a | Box width |
-> a | Stroke width |
-> Colour Double | First box colour |
-> Colour Double | Second box colour |
-> Colour Double | Line stroke colour |
-> LabeledPoint l a | Data point |
-> Svg |
A candlestick
glyph for time series plots. This is a type of box glyph, commonly used in plotting financial time series.
Some financial market quantities such as currency exchange rates are aggregated over some time period (e.g. a day) and summarized by various quantities, for example opening and closing rates, as well as maximum and minimum over the period.
By convention, the candlestick
colour depends on the derivative sign of one such quantity (e.g. it is green if the market closes higher than it opened, and red otherwise).
Plot utilities
:: (Functor t, Foldable t, Show a, RealFrac a) | |
=> FigureData a | |
-> (l -> Text) | X tick label |
-> (l -> Text) | Y tick label |
-> a | X label rotation angle |
-> a | Y label rotation angle |
-> a | Stroke width |
-> Colour Double | Stroke colour |
-> Maybe (t (LabeledPoint l a)) | X axis labels |
-> Maybe (t (LabeledPoint l a)) | Y axis labels |
-> (t (LabeledPoint l a) -> Svg) | Data rendering function |
-> t (LabeledPoint l a) | Data |
-> Svg |
toPlot
performs a number of related operations:
- Maps the dataset to the figure frame
- Renders the X, Y axes
- Renders the transformed dataset onto the newly created plot canvas
data FigureData a Source #
Figure data
FigureData | |
|
Eq a => Eq (FigureData a) Source # | |
Show a => Show (FigureData a) Source # | |
Element attributes
data LineStroke_ a Source #
Specify a continuous or dashed stroke
Continuous | |
Dashed [a] |
Eq a => Eq (LineStroke_ a) Source # | |
Show a => Show (LineStroke_ a) Source # | |
data StrokeLineJoin_ Source #
Specify the type of connection between line segments
data TextAnchor_ Source #
Specify at which end should the text be anchored to its current point
SVG utilities
Types
A frame, i.e. a bounding box for objects
A Point
defines a point in R2
data LabeledPoint l a Source #
A LabeledPoint
carries a "label" (i.e. any additional information such as a text tag, or any other data structure), in addition to position information. Data points on a plot are LabeledPoint
s.
labelPoint :: (Point a -> l) -> Point a -> LabeledPoint l a Source #
Given a labelling function and a Point
p
, returned a LabeledPoint
containing p
and the computed label
mapLabel :: (l1 -> l2) -> LabeledPoint l1 a -> LabeledPoint l2 a Source #
Apply a function to the label
Geometry
Vectors
V2 is a vector in R^2
V2 a a |
Eq a => Eq (V2 a) Source # | |
Show a => Show (V2 a) Source # | |
Num a => Monoid (V2 a) Source # | Vectors form a monoid w.r.t. vector addition |
Eps (V2 Double) Source # | |
Eps (V2 Float) Source # | |
Num a => Hermitian (V2 a) Source # | |
Num a => VectorSpace (V2 a) Source # | |
Num a => AdditiveGroup (V2 a) Source # | Vectors form an additive group |
Fractional a => MatrixGroup (DiagMat2 a) (V2 a) Source # | Diagonal matrices can always be inverted |
Num a => LinearMap (DiagMat2 a) (V2 a) Source # | |
Num a => LinearMap (Mat2 a) (V2 a) Source # | |
type InnerProduct (V2 a) Source # | |
type Scalar (V2 a) Source # | |
Matrices
A Mat2 can be seen as a linear operator that acts on points in the plane
Mat2 a a a a |
Diagonal matrices in R2 behave as scaling transformations
DMat2 a a |
Eq a => Eq (DiagMat2 a) Source # | |
Show a => Show (DiagMat2 a) Source # | |
Num a => Monoid (DiagMat2 a) Source # | Diagonal matrices form a monoid w.r.t. matrix multiplication and have the identity matrix as neutral element |
Num a => MultiplicativeSemigroup (DiagMat2 a) Source # | |
Fractional a => MatrixGroup (DiagMat2 a) (V2 a) Source # | Diagonal matrices can always be inverted |
Num a => LinearMap (DiagMat2 a) (V2 a) Source # | |
Primitive elements
Vector norm operations
normalize2 :: (InnerProduct v ~ Scalar v, Floating (Scalar v), Hermitian v) => v -> v Source #
Normalize a V2 w.r.t. its Euclidean norm
Vector construction
v2fromEndpoints :: Num a => Point a -> Point a -> V2 a Source #
Create a V2 v
from two endpoints p1, p2. That is v
can be seen as pointing from p1
to p2
Operations on points
moveLabeledPointV2 :: Num a => V2 a -> LabeledPoint l a -> LabeledPoint l a Source #
Move a LabeledPoint
along a vector
moveLabeledPointBwFrames Source #
:: Fractional a | |
=> Frame a | Initial frame |
-> Frame a | Final frame |
-> Bool | Flip L-R in [0,1] x [0,1] |
-> Bool | Flip U-D in [0,1] x [0,1] |
-> LabeledPoint l a | Initial |
-> LabeledPoint l a |
(-.) :: Num a => Point a -> Point a -> V2 a Source #
Create a V2 v
from two endpoints p1, p2. That is v
can be seen as pointing from p1
to p2
:: Fractional a | |
=> Frame a | Initial frame |
-> Frame a | Final frame |
-> Bool | Flip L-R in [0,1] x [0,1] |
-> Point a | Point in the initial frame |
-> Point a |
Move point to the SVG frame of reference (for which the origing is a the top-left corner of the screen)
toSvgFrameLP :: Fractional a => Frame a -> Frame a -> Bool -> LabeledPoint l a -> LabeledPoint l a Source #
Move LabeledPoint to the SVG frame of reference (uses toSvgFrame
)
pointRange :: (Fractional a, Integral n) => n -> Point a -> Point a -> [Point a] Source #
`pointRange n p q` returns a list of `n+1` equi-spaced Point
s between p
and q
(i.e. the input points are included as the first and last points in the list)
Operations on vectors
:: Fractional a | |
=> Frame a | Initial frame |
-> Frame a | Final frame |
-> Bool | Flip L-R in [0,1] x [0,1] |
-> Bool | Flip U-D in [0,1] x [0,1] |
-> V2 a | Initial vector |
-> V2 a |
Given two frames F1
and F2
, returns a function f
that maps an arbitrary vector v
contained within F1
onto one contained within F2
.
This function is composed of three affine maps :
- map
v
into a vectorv01
that points within the unit square, - map
v01
ontov01'
. This transformation serves to e.g. flip the dataset along the y axis (since the origin of the SVG canvas is the top-left corner of the screen). If this is not needed one can just supply the identity matrix and the zero vector, - map
v01'
onto the target frameF2
.
NB: we do not check that v
is actually contained within the F1
, nor that v01'
is still contained within [0,1] x [0, 1]. This has to be supplied correctly by the user.
Operations on frames
frameFromFigData :: Num a => FigureData a -> Frame a Source #
mkFrameOrigin :: Num a => a -> a -> Frame a Source #
Build a frame rooted at the origin (0, 0)
Typeclasses
class AdditiveGroup v where Source #
Additive group :
v ^+^ zero == zero ^+^ v == v
v ^-^ v == zero
Identity element
Group action ("sum")
Inverse group action ("subtraction")
Num a => AdditiveGroup (V2 a) Source # | Vectors form an additive group |
class AdditiveGroup v => VectorSpace v where Source #
Vector space : multiplication by a scalar quantity
Num a => VectorSpace (V2 a) Source # | |
class VectorSpace v => Hermitian v where Source #
Hermitian space : inner product
type InnerProduct v :: * Source #
(<.>) :: v -> v -> InnerProduct v Source #
Inner product
class Hermitian v => LinearMap m v where Source #
Linear maps, i.e. linear transformations of vectors
class MultiplicativeSemigroup m where Source #
Multiplicative matrix semigroup ("multiplying" two matrices together)
Num a => MultiplicativeSemigroup (DiagMat2 a) Source # | |
Num a => MultiplicativeSemigroup (Mat2 a) Source # | |
class LinearMap m v => MatrixGroup m v where Source #
The class of invertible linear transformations
Fractional a => MatrixGroup (DiagMat2 a) (V2 a) Source # | Diagonal matrices can always be inverted |
Numerical equality
Helpers
toFloat :: Scientific -> Float Source #
wholeDecimal :: (Integral a, RealFrac b) => b -> (a, b) Source #
Separate whole and decimal part of a fractional number e.g.
> wholeDecimal