Diagrams

Annotations

Monoidal annotations which travel up the diagram tree, i.e. which
are aggregated from component diagrams to the whole:

envelopes (see Graphics.Rendering.Diagrams.Envelope).
The envelopes are "deletable" meaning that at any point we can
throw away the existing envelope and replace it with a new one;
sometimes we want to consider a diagram as having a different
envelope unrelated to its "natural" envelope.

The fundamental diagram type is represented by trees of
primitives with various monoidal annotations. The Q in
QDiagram stands for "Queriable", as distinguished from
Diagram, a synonym for QDiagram with the query type
specialized to Any.

Diagrams form a monoid since each of their components do: the
empty diagram has no primitives, an empty envelope, no named
points, and a constantly empty query function.

Diagrams compose by aligning their respective local origins. The
new diagram has all the primitives and all the names from the two
diagrams combined, and query functions are combined pointwise.
The first diagram goes on top of the second. "On top of"
probably only makes sense in vector spaces of dimension lower
than 3, but in theory it could make sense for, say, 3-dimensional
diagrams when viewed by 4-dimensional beings.

The default sort of diagram is one where querying at a point
simply tells you whether that point is occupied or not.
Transforming a default diagram into one with a more interesting
query can be done via the Functor instance of QDiagram b.

Given a name and a diagram transformation indexed by a located
envelope, perform the transformation using the most recent
located envelope associated with (some qualification of) the
name, or perform the identity transformation if the name does not
exist.

Given a name and a diagram transformation indexed by a list of
located envelopes, perform the transformation using the
collection of all such located envelopes associated with (some
qualification of) the given name.

Given a list of names and a diagram transformation indexed by a
list of located envelopes, perform the transformation using the
list of most recent envelopes associated with (some qualification
of) each name. Do nothing (the identity transformation) if any
of the names do not exist.

Other

By default, diagram attributes are not affected by
transformations. This means, for example, that lw 0.01 circle
and scale 2 (lw 0.01 circle) will be drawn with lines of the
same width, and scaleY 3 circle will be an ellipse drawn with
a uniform line. Once a diagram is frozen, however,
transformations do affect attributes, so, for example, scale 2
(freeze (lw 0.01 circle)) will be drawn with a line twice as
thick as lw 0.01 circle, and scaleY 3 (freeze circle) will be
drawn with a "stretched", variable-width line.

Another way of thinking about it is that pre-freeze, we are
transforming the "abstract idea" of a diagram, and the
transformed version is then drawn; when doing a freeze, we
produce a concrete drawing of the diagram, and it is this visual
representation itself which is acted upon by subsequent
transformations.

Primtives

Ultimately, every diagram is essentially a collection of
primitives, basic building blocks which can be rendered by
backends. However, not every backend must be able to render every
type of primitive; the collection of primitives a given backend
knows how to render is determined by instances of Renderable.

Backends

Abstract diagrams are rendered to particular formats by
backends. Each backend/vector space combination must be an
instance of the Backend class. A minimal complete definition
consists of the three associated types and implementations for
withStyle and doRender.

The type of rendering operations used by this backend, which
must be a monoid. For example, if Render b v = M () for some
monad M, a monoid instance can be made with mempty = return
() and mappend = (>>).

adjustDia allows the backend to make adjustments to the final
diagram (e.g. to adjust the size based on the options) before
rendering it. It can also make adjustments to the options
record, usually to fill in incompletely specified size
information. A default implementation is provided which makes
no adjustments. See the diagrams-lib package for other useful
implementations.

Null backend

A null backend which does no actual rendering. It is provided
mainly for convenience in situations where you must give a
diagram a concrete, monomorphic type, but don't actually care
which one. See D for more explanation and examples.

It is courteous, when defining a new primitive P, to make an instance

instance Renderable P NullBackend where
render _ _ = mempty

This ensures that the trick with D annotations can be used for
diagrams containing your primitive.

The D type is provided for convenience in situations where you
must give a diagram a concrete, monomorphic type, but don't care
which one. Such situations arise when you pass a diagram to a
function which is polymorphic in its input but monomorphic in its
output, such as width, height, phantom, or names. Such
functions compute some property of the diagram, or use it to
accomplish some other purpose, but do not result in the diagram
being rendered. If the diagram does not have a monomorphic type,
GHC complains that it cannot determine the diagram's type.

For example, here is the error we get if we try to compute the
width of a radius-1 circle (this example requires
diagrams-lib):

GHC complains that it cannot find an instance for "Backend b0
R2"; what is really going on is that it does not have enough
information to decide which backend to use for the circle (hence
the type variable b0). This is annoying because we know that
the choice of backend cannot possibly affect the width of the
circle; but there is no way for GHC to know that.