diagrams-core-1.2.0.5: Core libraries for diagrams EDSL

Copyright(c) 2011 diagrams-core team (see LICENSE)
LicenseBSD-style (see LICENSE)
Maintainerdiagrams-discuss@googlegroups.com
Safe HaskellNone
LanguageHaskell2010

Diagrams.Core

Contents

Description

The core library of primitives forming the basis of an embedded domain-specific language for describing and rendering diagrams. Normal users of the diagrams library should almost never need to import anything from this package directly; instead, import modules (especially Diagrams.Prelude) from the diagrams-lib package, which re-exports most things of value to users.

For most library code needing access to core internals, it should be sufficient to import this module, which simply re-exports useful functionality from other modules in the core library. Library writers needing finer-grained access or functionality may occasionally find it useful to directly import one of the constituent core modules.

The diagrams library relies heavily on custom types and classes. Many of the relevant definitions are in the Diagrams.Core.Types module. Indeed the definition of the diagram type QDiagram is contained in: QDiagram.

The best place to start when learning about diagrams' types is the user manual: http://projects.haskell.org/diagrams/doc/manual.html#type-reference The following list shows which types are contained in each module of Diagrams.Core.

Synopsis

Associated vector spaces

type family V a :: * Source

Many sorts of objects have an associated vector space in which they "live". The type function V maps from object types to the associated vector space.

Instances

type V Double = Double 
type V Rational = Rational 
type V [a] = V a 
type V (Set a) = V a 
type V (Split m) = V m 
type V (Deletable m) = V m 
type V (Option a) = V a 
type V (Point v) = v 
type V (TransInv t) = V t 
type V (Transformation v) = v 
type V (Style v) = v 
type V (Attribute v) = v 
type V (Trace v) = v 
type V (Envelope v) = v 
type V (Measure v) = v 
type V (a -> b) = V b 
type V (a, b) = V a 
type V (Map k a) = V a 
type V ((:+:) m n) = V m 
type V (Query v m) = v 
type V (Prim b v) = v 
type V (a, b, c) = V a 
type V (SubMap b v m) = v 
type V (Subdiagram b v m) = v 
type V (QDiagram b v m) = v 

Points

data Point v :: * -> *

Point is a newtype wrapper around vectors used to represent points, so we don't get them mixed up. The distinction between vectors and points is important: translations affect points, but leave vectors unchanged. Points are instances of the AffineSpace class from Data.AffineSpace.

Instances

Functor Point 
Eq v => Eq (Point v) 
Data v => Data (Point v) 
Ord v => Ord (Point v) 
Read v => Read (Point v) 
Show v => Show (Point v) 
AdditiveGroup v => AffineSpace (Point v) 
VectorSpace v => HasOrigin (Point v) 
HasLinearMap v => Transformable (Point v) 
(Ord (Scalar v), VectorSpace v) => Traced (Point v)

The trace of a single point is the empty trace, i.e. the one which returns no intersection points for every query. Arguably it should return a single finite distance for vectors aimed directly at the given point, but due to floating-point inaccuracy this is problematic. Note that the envelope for a single point is not the empty envelope (see Diagrams.Core.Envelope).

(OrderedField (Scalar v), InnerSpace v) => Enveloped (Point v) 
Typeable (* -> *) Point 
type Diff (Point v) = v 
type V (Point v) = v 

origin :: AdditiveGroup v => Point v

The origin of the vector space v.

(*.) :: VectorSpace v => Scalar v -> Point v -> Point v

Scale a point by a scalar.

_relative :: AffineSpace (Point v) => Point v -> Iso' (Point v) v Source

An isomorphism between points and vectors, given a reference point. This is provided for defining new lenses on points.

Transformations

Utilities

basis :: forall v. HasLinearMap v => [v] Source

Get the matrix equivalent of the basis of the vector space v as a list of columns.

dimension :: forall a. HasLinearMap (V a) => a -> Int Source

Get the dimension of an object whose vector space is an instance of HasLinearMap, e.g. transformations, paths, diagrams, etc.

determinant :: (HasLinearMap v, Num (Scalar v)) => Transformation v -> Scalar v Source

The determinant of a Transformation.

Invertible linear transformations

data u :-: v infixr 7 Source

(v1 :-: v2) is a linear map paired with its inverse.

Instances

HasLinearMap v => Monoid ((:-:) v v)

Invertible linear maps from a vector space to itself form a monoid under composition.

HasLinearMap v => Semigroup ((:-:) v v) 

(<->) :: (HasLinearMap u, HasLinearMap v) => (u -> v) -> (v -> u) -> u :-: v Source

Create an invertible linear map from two functions which are assumed to be linear inverses.

linv :: (u :-: v) -> v :-: u Source

Invert a linear map.

lapp :: (VectorSpace v, Scalar u ~ Scalar v, HasLinearMap u) => (u :-: v) -> u -> v Source

Apply a linear map to a vector.

General transformations

data Transformation v Source

General (affine) transformations, represented by an invertible linear map, its transpose, and a vector representing a translation component.

By the transpose of a linear map we mean simply the linear map corresponding to the transpose of the map's matrix representation. For example, any scale is its own transpose, since scales are represented by matrices with zeros everywhere except the diagonal. The transpose of a rotation is the same as its inverse.

The reason we need to keep track of transposes is because it turns out that when transforming a shape according to some linear map L, the shape's normal vectors transform according to L's inverse transpose. This is exactly what we need when transforming bounding functions, which are defined in terms of perpendicular (i.e. normal) hyperplanes.

For more general, non-invertable transformations, see Diagrams.Deform (in diagrams-lib).

Instances

HasLinearMap v => Monoid (Transformation v) 
HasLinearMap v => Semigroup (Transformation v)

Transformations are closed under composition; t1 <> t2 is the transformation which performs first t2, then t1.

HasLinearMap v => HasOrigin (Transformation v) 
HasLinearMap v => Transformable (Transformation v) 
(HasLinearMap v, (~) * v (V a), Transformable a) => Action (Transformation v) a

Transformations can act on transformable things.

type V (Transformation v) = v 

inv :: HasLinearMap v => Transformation v -> Transformation v Source

Invert a transformation.

transp :: Transformation v -> v :-: v Source

Get the transpose of a transformation (ignoring the translation component).

transl :: Transformation v -> v Source

Get the translational component of a transformation.

dropTransl :: AdditiveGroup v => Transformation v -> Transformation v Source

Drop the translational component of a transformation, leaving only the linear part.

apply :: HasLinearMap v => Transformation v -> v -> v Source

Apply a transformation to a vector. Note that any translational component of the transformation will not affect the vector, since vectors are invariant under translation.

papply :: HasLinearMap v => Transformation v -> Point v -> Point v Source

Apply a transformation to a point.

fromLinear :: AdditiveGroup v => (v :-: v) -> (v :-: v) -> Transformation v Source

Create a general affine transformation from an invertible linear transformation and its transpose. The translational component is assumed to be zero.

Some specific transformations

translation :: HasLinearMap v => v -> Transformation v Source

Create a translation.

translate :: (Transformable t, HasLinearMap (V t)) => V t -> t -> t Source

Translate by a vector.

moveTo :: HasOrigin t => Point (V t) -> t -> t Source

Translate the object by the translation that sends the origin to the given point. Note that this is dual to moveOriginTo, i.e. we should have

  moveTo (origin .^+ v) === moveOriginTo (origin .^- v)
  

For types which are also Transformable, this is essentially the same as translate, i.e.

  moveTo (origin .^+ v) === translate v
  

place :: HasOrigin t => t -> Point (V t) -> t Source

A flipped variant of moveTo, provided for convenience. Useful when writing a function which takes a point as an argument, such as when using withName and friends.

scaling :: (HasLinearMap v, Fractional (Scalar v)) => Scalar v -> Transformation v Source

Create a uniform scaling transformation.

scale :: (Transformable t, Fractional (Scalar (V t)), Eq (Scalar (V t))) => Scalar (V t) -> t -> t Source

Scale uniformly in every dimension by the given scalar.

avgScale :: (HasLinearMap v, Floating (Scalar v)) => Transformation v -> Scalar v Source

Compute the "average" amount of scaling performed by a transformation. Satisfies the properties

  avgScale (scaling k) == k
  avgScale (t1 <> t2)  == avgScale t1 * avgScale t2
  

The Transformable class

class HasLinearMap (V t) => Transformable t where Source

Type class for things t which can be transformed.

Methods

transform :: Transformation (V t) -> t -> t Source

Apply a transformation to an object.

Instances

Transformable Double 
Transformable Rational 
Transformable t => Transformable [t] 
(Transformable t, Ord t) => Transformable (Set t) 
Transformable m => Transformable (Deletable m) 
HasLinearMap v => Transformable (Point v) 
Transformable t => Transformable (TransInv t) 
HasLinearMap v => Transformable (Transformation v) 
HasLinearMap v => Transformable (Style v) 
HasLinearMap v => Transformable (Attribute v) 
HasLinearMap v => Transformable (Trace v) 
(HasLinearMap v, InnerSpace v, Floating (Scalar v)) => Transformable (Envelope v) 
(HasLinearMap v, Floating (Scalar v)) => Transformable (Measure v) 
(HasBasis (V b), HasTrie (Basis (V b)), Transformable a, Transformable b, (~) * (V b) (V a)) => Transformable (a -> b) 
(Transformable a, Transformable b, (~) * (V a) (V b)) => Transformable (a, b) 
Transformable t => Transformable (Map k t) 
HasLinearMap v => Transformable (Query v m) 
HasLinearMap v => Transformable (Prim b v)

The Transformable instance for Prim just pushes calls to transform down through the Prim constructor.

(Transformable a, Transformable b, Transformable c, (~) * (V a) (V b), (~) * (V a) (V c)) => Transformable (a, b, c) 
(InnerSpace v, Floating (Scalar v), HasLinearMap v) => Transformable (SubMap b v m) 
(HasLinearMap v, InnerSpace v, Floating (Scalar v)) => Transformable (Subdiagram b v m) 
(HasLinearMap v, OrderedField (Scalar v), InnerSpace v, Semigroup m) => Transformable (QDiagram b v m)

Diagrams can be transformed by transforming each of their components appropriately.

Translational invariance

newtype TransInv t Source

TransInv is a wrapper which makes a transformable type translationally invariant; the translational component of transformations will no longer affect things wrapped in TransInv.

Constructors

TransInv t 

Names

data AName Source

Atomic names. AName is just an existential wrapper around things which are Typeable, Ord and Show.

data Name Source

A (qualified) name is a (possibly empty) sequence of atomic names.

Instances

Eq Name 
Ord Name 
Show Name 
Monoid Name 
Semigroup Name 
Wrapped Name 
Qualifiable Name

Of course, names can be qualified using (.>).

IsName Name 
Typeable * Name 
Rewrapped Name Name 
Action Name a => Action Name (Deletable a) 
Action Name (Trace v) 
Action Name (Envelope v) 
Action Name (Query v m) 
Action Name (SubMap b v m)

A name acts on a name map by qualifying every name in it.

type Unwrapped Name = [AName] 

class (Typeable a, Ord a, Show a) => IsName a where Source

Class for those types which can be used as names. They must support Typeable (to facilitate extracting them from existential wrappers), Ord (for comparison and efficient storage) and Show.

To make an instance of IsName, you need not define any methods, just declare it.

WARNING: it is not recommended to use GeneralizedNewtypeDeriving in conjunction with IsName, since in that case the underlying type and the newtype will be considered equivalent when comparing names. For example:

    newtype WordN = WordN Int deriving (Show, Ord, Eq, Typeable, IsName)
  

is unlikely to work as intended, since (1 :: Int) and (WordN 1) will be considered equal as names. Instead, use

    newtype WordN = WordN Int deriving (Show, Ord, Eq, Typeable, IsName)
    instance IsName WordN
  

Minimal complete definition

Nothing

Methods

toName :: a -> Name Source

class Qualifiable q where Source

Instances of Qualifiable are things which can be qualified by prefixing them with a name.

Methods

(|>) :: IsName a => a -> q -> q infixr 5 Source

Qualify with the given name.

Instances

Qualifiable Name

Of course, names can be qualified using (.>).

Qualifiable a => Qualifiable [a] 
(Ord a, Qualifiable a) => Qualifiable (Set a) 
Qualifiable a => Qualifiable (TransInv a) 
Qualifiable a => Qualifiable (b -> a) 
(Qualifiable a, Qualifiable b) => Qualifiable (a, b) 
Qualifiable a => Qualifiable (Map k a) 
(Qualifiable a, Qualifiable b, Qualifiable c) => Qualifiable (a, b, c) 
Qualifiable (SubMap b v m)

SubMaps are qualifiable: if ns is a SubMap, then a |> ns is the same SubMap except with every name qualified by a.

(HasLinearMap v, InnerSpace v, OrderedField (Scalar v), Semigroup m) => Qualifiable (QDiagram b v m)

Diagrams can be qualified so that all their named points can now be referred to using the qualification prefix.

(.>) :: (IsName a1, IsName a2) => a1 -> a2 -> Name infixr 5 Source

Convenient operator for writing qualified names with atomic components of different types. Instead of writing toName a1 <> toName a2 <> toName a3 you can just write a1 .> a2 .> a3.

Subdiagram maps

newtype SubMap b v m Source

A SubMap is a map associating names to subdiagrams. There can be multiple associations for any given name.

Constructors

SubMap (Map Name [Subdiagram b v m]) 

Instances

Action Name (SubMap b v m)

A name acts on a name map by qualifying every name in it.

Functor (SubMap b v) 
Monoid (SubMap b v m)

SubMaps form a monoid with the empty map as the identity, and map union as the binary operation. No information is ever lost: if two maps have the same name in their domain, the resulting map will associate that name to the concatenation of the information associated with that name.

Semigroup (SubMap b v m) 
Wrapped (SubMap b v m) 
(OrderedField (Scalar v), InnerSpace v, HasLinearMap v) => HasOrigin (SubMap b v m) 
(InnerSpace v, Floating (Scalar v), HasLinearMap v) => Transformable (SubMap b v m) 
Qualifiable (SubMap b v m)

SubMaps are qualifiable: if ns is a SubMap, then a |> ns is the same SubMap except with every name qualified by a.

Rewrapped (SubMap b v m) (SubMap b' v' m') 
type Unwrapped (SubMap b v m) = Map Name [Subdiagram b v m] 
type V (SubMap b v m) = v 

fromNames :: IsName a => [(a, Subdiagram b v m)] -> SubMap b v m Source

Construct a SubMap from a list of associations between names and subdiagrams.

rememberAs :: IsName a => a -> QDiagram b v m -> SubMap b v m -> SubMap b v m Source

Add a name/diagram association to a submap.

lookupSub :: IsName n => n -> SubMap b v m -> Maybe [Subdiagram b v m] Source

Look for the given name in a name map, returning a list of subdiagrams associated with that name. If no names match the given name exactly, return all the subdiagrams associated with names of which the given name is a suffix.

Attributes and styles

class (Typeable a, Semigroup a) => AttributeClass a Source

Every attribute must be an instance of AttributeClass, which simply guarantees Typeable and Semigroup constraints. The Semigroup instance for an attribute determines how it will combine with other attributes of the same type.

data Attribute v :: * Source

An existential wrapper type to hold attributes. Some attributes are simply inert/static; some are affected by transformations; and some are affected by transformations and can be modified generically.

Instances

Semigroup (Attribute v)

Attributes form a semigroup, where the semigroup operation simply returns the right-hand attribute when the types do not match, and otherwise uses the semigroup operation specific to the (matching) types.

HasLinearMap v => Transformable (Attribute v) 
type V (Attribute v) = v 

mkAttr :: AttributeClass a => a -> Attribute v Source

Wrap up an attribute.

mkTAttr :: (AttributeClass a, Transformable a, V a ~ v) => a -> Attribute v Source

Wrap up a transformable attribute.

mkGTAttr :: (AttributeClass a, Data a, Transformable a, V a ~ v) => a -> Attribute v Source

Wrap up a transformable and generic attribute.

unwrapAttr :: AttributeClass a => Attribute v -> Maybe a Source

Unwrap an unknown Attribute type, performing a dynamic (but safe) check on the type of the result. If the required type matches the type of the attribute, the attribute value is returned wrapped in Just; if the types do not match, Nothing is returned.

data Style v Source

A Style is a heterogeneous collection of attributes, containing at most one attribute of any given type.

Instances

Monoid (Style v)

The empty style contains no attributes; composition of styles is a union of attributes; if the two styles have attributes of the same type they are combined according to their semigroup structure.

Semigroup (Style v) 
Wrapped (Style v) 
HasLinearMap v => Transformable (Style v) 
HasStyle (Style v) 
Action (Style v) m

Styles have no action on other monoids.

Rewrapped (Style v) (Style v') 
type Unwrapped (Style v) = Map String (Attribute v) 
type V (Style v) = v 

class HasStyle a where Source

Type class for things which have a style.

Methods

applyStyle :: Style (V a) -> a -> a Source

Apply a style by combining it (on the left) with the existing style.

Instances

HasStyle a => HasStyle [a] 
(HasStyle a, Ord a) => HasStyle (Set a) 
HasStyle (Style v) 
HasStyle b => HasStyle (a -> b) 
(HasStyle a, HasStyle b, (~) * (V a) (V b)) => HasStyle (a, b) 
HasStyle a => HasStyle (Map k a) 
(HasLinearMap v, InnerSpace v, OrderedField (Scalar v), Semigroup m) => HasStyle (QDiagram b v m) 

getAttr :: forall a v. AttributeClass a => Style v -> Maybe a Source

Extract an attribute from a style of a particular type. If the style contains an attribute of the requested type, it will be returned wrapped in Just; otherwise, Nothing is returned.

combineAttr :: AttributeClass a => a -> Style v -> Style v Source

Add a new attribute to a style that does not already contain an attribute of this type, or combine it on the left with an existing attribute.

applyAttr :: (AttributeClass a, HasStyle d) => a -> d -> d Source

Apply an attribute to an instance of HasStyle (such as a diagram or a style). If the object already has an attribute of the same type, the new attribute is combined on the left with the existing attribute, according to their semigroup structure.

applyTAttr :: (AttributeClass a, Transformable a, V a ~ V d, HasStyle d) => a -> d -> d Source

Apply a transformable attribute to an instance of HasStyle (such as a diagram or a style). If the object already has an attribute of the same type, the new attribute is combined on the left with the existing attribute, according to their semigroup structure.

applyGTAttr :: (AttributeClass a, Data a, Transformable a, V a ~ V d, HasStyle d) => a -> d -> d Source

Envelopes

newtype Envelope v Source

Every diagram comes equipped with an envelope. What is an envelope?

Consider first the idea of a bounding box. A bounding box expresses the distance to a bounding plane in every direction parallel to an axis. That is, a bounding box can be thought of as the intersection of a collection of half-planes, two perpendicular to each axis.

More generally, the intersection of half-planes in every direction would give a tight "bounding region", or convex hull. However, representing such a thing intensionally would be impossible; hence bounding boxes are often used as an approximation.

An envelope is an extensional representation of such a "bounding region". Instead of storing some sort of direct representation, we store a function which takes a direction as input and gives a distance to a bounding half-plane as output. The important point is that envelopes can be composed, and transformed by any affine transformation.

Formally, given a vector v, the envelope computes a scalar s such that

  • for every point u inside the diagram, if the projection of (u - origin) onto v is s' *^ v, then s' <= s.
    • s is the smallest such scalar.

There is also a special "empty envelope".

The idea for envelopes came from Sebastian Setzer; see http://byorgey.wordpress.com/2009/10/28/collecting-attributes/#comment-2030. See also Brent Yorgey, Monoids: Theme and Variations, published in the 2012 Haskell Symposium: http://www.cis.upenn.edu/~byorgey/pub/monoid-pearl.pdf; video: http://www.youtube.com/watch?v=X-8NCkD2vOw.

Constructors

Envelope (Option (v -> Max (Scalar v))) 

Instances

Action Name (Envelope v) 
Show (Envelope v) 
Ord (Scalar v) => Monoid (Envelope v) 
Ord (Scalar v) => Semigroup (Envelope v) 
Wrapped (Envelope v) 
(InnerSpace v, Fractional (Scalar v)) => HasOrigin (Envelope v)

The local origin of an envelope is the point with respect to which bounding queries are made, i.e. the point from which the input vectors are taken to originate.

(HasLinearMap v, InnerSpace v, Floating (Scalar v)) => Transformable (Envelope v) 
(InnerSpace v, OrderedField (Scalar v)) => Enveloped (Envelope v) 
(InnerSpace v, OrderedField (Scalar v)) => Juxtaposable (Envelope v) 
Rewrapped (Envelope v) (Envelope v') 
type Unwrapped (Envelope v) = Option (v -> Max (Scalar v)) 
type V (Envelope v) = v 

onEnvelope :: ((v -> Scalar v) -> v -> Scalar v) -> Envelope v -> Envelope v Source

mkEnvelope :: (v -> Scalar v) -> Envelope v Source

class (InnerSpace (V a), OrderedField (Scalar (V a))) => Enveloped a where Source

Enveloped abstracts over things which have an envelope.

Methods

getEnvelope :: a -> Envelope (V a) Source

Compute the envelope of an object. For types with an intrinsic notion of "local origin", the envelope will be based there. Other types (e.g. Trail) may have some other default reference point at which the envelope will be based; their instances should document what it is.

envelopeVMay :: Enveloped a => V a -> a -> Maybe (V a) Source

Compute the vector from the local origin to a separating hyperplane in the given direction, or Nothing for the empty envelope.

envelopeV :: Enveloped a => V a -> a -> V a Source

Compute the vector from the local origin to a separating hyperplane in the given direction. Returns the zero vector for the empty envelope.

envelopePMay :: Enveloped a => V a -> a -> Maybe (Point (V a)) Source

Compute the point on a separating hyperplane in the given direction, or Nothing for the empty envelope.

envelopeP :: Enveloped a => V a -> a -> Point (V a) Source

Compute the point on a separating hyperplane in the given direction. Returns the origin for the empty envelope.

diameter :: Enveloped a => V a -> a -> Scalar (V a) Source

Compute the diameter of a enveloped object along a particular vector. Returns zero for the empty envelope.

radius :: Enveloped a => V a -> a -> Scalar (V a) Source

Compute the "radius" (1/2 the diameter) of an enveloped object along a particular vector.

Traces

newtype Trace v Source

Every diagram comes equipped with a trace. Intuitively, the trace for a diagram is like a raytracer: given a line (represented as a base point and a direction vector), the trace computes a sorted list of signed distances from the base point to all intersections of the line with the boundary of the diagram.

Note that the outputs are not absolute distances, but multipliers relative to the input vector. That is, if the base point is p and direction vector is v, and one of the output scalars is s, then there is an intersection at the point p .+^ (s *^ v).

Constructors

Trace 

Fields

appTrace :: Point v -> v -> SortedList (Scalar v)
 

Instances

Action Name (Trace v) 
Show (Trace v) 
Ord (Scalar v) => Monoid (Trace v) 
Ord (Scalar v) => Semigroup (Trace v) 
Wrapped (Trace v) 
VectorSpace v => HasOrigin (Trace v) 
HasLinearMap v => Transformable (Trace v) 
(Ord (Scalar v), VectorSpace v) => Traced (Trace v) 
Rewrapped (Trace v) (Trace v') 
type Unwrapped (Trace v) = Point v -> v -> SortedList (Scalar v) 
type V (Trace v) = v 

data SortedList a Source

A newtype wrapper around a list which maintains the invariant that the list is sorted. The constructor is not exported; use the smart constructor mkSortedList (which sorts the given list) instead.

Instances

Ord a => Monoid (SortedList a)

SortedList forms a monoid with merge and the empty list.

Ord a => Semigroup (SortedList a)

SortedList forms a semigroup with merge as composition.

mkSortedList :: Ord a => [a] -> SortedList a Source

A smart constructor for the SortedList type, which sorts the input to ensure the SortedList invariant.

getSortedList :: SortedList a -> [a] Source

Project the (guaranteed sorted) list out of a SortedList wrapper.

mkTrace :: (Point v -> v -> SortedList (Scalar v)) -> Trace v Source

class (Ord (Scalar (V a)), VectorSpace (V a)) => Traced a where Source

Traced abstracts over things which have a trace.

Methods

getTrace :: a -> Trace (V a) Source

Compute the trace of an object.

Instances

Traced b => Traced [b] 
Traced b => Traced (Set b) 
(Ord (Scalar v), VectorSpace v) => Traced (Point v)

The trace of a single point is the empty trace, i.e. the one which returns no intersection points for every query. Arguably it should return a single finite distance for vectors aimed directly at the given point, but due to floating-point inaccuracy this is problematic. Note that the envelope for a single point is not the empty envelope (see Diagrams.Core.Envelope).

Traced t => Traced (TransInv t) 
(Ord (Scalar v), VectorSpace v) => Traced (Trace v) 
(Traced a, Traced b, (~) * (V a) (V b)) => Traced (a, b) 
Traced b => Traced (Map k b) 
(OrderedField (Scalar v), HasLinearMap v, InnerSpace v, Semigroup m) => Traced (Subdiagram b v m) 
(HasLinearMap v, VectorSpace v, Ord (Scalar v), InnerSpace v, Semigroup m, Fractional (Scalar v), Floating (Scalar v)) => Traced (QDiagram b v m) 

traceV :: Traced a => Point (V a) -> V a -> a -> Maybe (V a) Source

Compute the vector from the given point p to the "smallest" boundary intersection along the given vector v. The "smallest" boundary intersection is defined as the one given by p .+^ (s *^ v) for the smallest (most negative) value of s. Return Nothing if there is no intersection. See also traceP.

See also rayTraceV which uses the smallest positive intersection, which is often more intuitive behavior.

traceP :: Traced a => Point (V a) -> V a -> a -> Maybe (Point (V a)) Source

Compute the "smallest" boundary point along the line determined by the given point p and vector v. The "smallest" boundary point is defined as the one given by p .+^ (s *^ v) for the smallest (most negative) value of s. Return Nothing if there is no such boundary point. See also traceV.

See also rayTraceP which uses the smallest positive intersection, which is often more intuitive behavior.

maxTraceV :: Traced a => Point (V a) -> V a -> a -> Maybe (V a) Source

Like traceV, but computes a vector to the "largest" boundary point instead of the smallest. (Note, however, the "largest" boundary point may still be in the opposite direction from the given vector, if all the boundary points are, as in the third example shown below.)

maxTraceP :: Traced a => Point (V a) -> V a -> a -> Maybe (Point (V a)) Source

Like traceP, but computes the "largest" boundary point instead of the smallest. (Note, however, the "largest" boundary point may still be in the opposite direction from the given vector, if all the boundary points are.)

rayTraceV :: (Traced a, Num (Scalar (V a))) => Point (V a) -> V a -> a -> Maybe (V a) Source

Compute the vector from the given point to the closest boundary point of the given object in the given direction, or Nothing if there is no such boundary point (as in the third example below). Note that unlike traceV, only positive boundary points are considered, i.e. boundary points corresponding to a positive scalar multiple of the direction vector. This is intuitively the "usual" behavior of a raytracer, which only considers intersections "in front of" the camera. Compare the second example diagram below with the second example shown for traceV.

rayTraceP :: (Traced a, Num (Scalar (V a))) => Point (V a) -> V a -> a -> Maybe (Point (V a)) Source

Compute the boundary point on an object which is closest to the given base point in the given direction, or Nothing if there is no such boundary point. Note that unlike traceP, only positive boundary points are considered, i.e. boundary points corresponding to a positive scalar multiple of the direction vector. This is intuitively the "usual" behavior of a raytracer, which only considers intersection points "in front of" the camera.

maxRayTraceV :: (Traced a, Num (Scalar (V a))) => Point (V a) -> V a -> a -> Maybe (V a) Source

Like rayTraceV, but computes a vector to the "largest" boundary point instead of the smallest. Considers only positive boundary points.

maxRayTraceP :: (Traced a, Num (Scalar (V a))) => Point (V a) -> V a -> a -> Maybe (Point (V a)) Source

Like rayTraceP, but computes the "largest" boundary point instead of the smallest. Considers only positive boundary points.

Things with local origins

class VectorSpace (V t) => HasOrigin t where Source

Class of types which have an intrinsic notion of a "local origin", i.e. things which are not invariant under translation, and which allow the origin to be moved.

One might wonder why not just use Transformable instead of having a separate class for HasOrigin; indeed, for types which are instances of both we should have the identity

  moveOriginTo (origin .^+ v) === translate (negateV v)
  

The reason is that some things (e.g. vectors, Trails) are transformable but are translationally invariant, i.e. have no origin.

Methods

moveOriginTo :: Point (V t) -> t -> t Source

Move the local origin to another point.

Note that this function is in some sense dual to translate (for types which are also Transformable); moving the origin itself while leaving the object "fixed" is dual to fixing the origin and translating the diagram.

Instances

HasOrigin a => HasOrigin [a] 
(HasOrigin a, Ord a) => HasOrigin (Set a) 
VectorSpace v => HasOrigin (Point v) 
VectorSpace (V t) => HasOrigin (TransInv t) 
HasLinearMap v => HasOrigin (Transformation v) 
VectorSpace v => HasOrigin (Trace v) 
(InnerSpace v, Fractional (Scalar v)) => HasOrigin (Envelope v)

The local origin of an envelope is the point with respect to which bounding queries are made, i.e. the point from which the input vectors are taken to originate.

(HasOrigin a, HasOrigin b, (~) * (V a) (V b)) => HasOrigin (a, b) 
HasOrigin a => HasOrigin (Map k a) 
VectorSpace v => HasOrigin (Query v m) 
(OrderedField (Scalar v), InnerSpace v, HasLinearMap v) => HasOrigin (SubMap b v m) 
(HasLinearMap v, InnerSpace v, OrderedField (Scalar v)) => HasOrigin (Subdiagram b v m) 
(HasLinearMap v, InnerSpace v, OrderedField (Scalar v), Semigroup m) => HasOrigin (QDiagram b v m)

Every diagram has an intrinsic "local origin" which is the basis for all combining operations.

moveOriginBy :: HasOrigin t => V t -> t -> t Source

Move the local origin by a relative vector.

Juxtaposable things

class Juxtaposable a where Source

Class of things which can be placed "next to" other things, for some appropriate notion of "next to".

Methods

juxtapose :: V a -> a -> a -> a Source

juxtapose v a1 a2 positions a2 next to a1 in the direction of v. In particular, place a2 so that v points from the local origin of a1 towards the old local origin of a2; a1's local origin becomes a2's new local origin. The result is just a translated version of a2. (In particular, this operation does not combine a1 and a2 in any way.)

juxtaposeDefault :: (Enveloped a, HasOrigin a) => V a -> a -> a -> a Source

Default implementation of juxtapose for things which are instances of Enveloped and HasOrigin. If either envelope is empty, the second object is returned unchanged.

Queries

newtype Query v m Source

A query is a function that maps points in a vector space to values in some monoid. Queries naturally form a monoid, with two queries being combined pointwise.

The idea for annotating diagrams with monoidal queries came from the graphics-drawingcombinators package, http://hackage.haskell.org/package/graphics-drawingcombinators.

Constructors

Query 

Fields

runQuery :: Point v -> m
 

Instances

Action Name (Query v m) 
Functor (Query v) 
Applicative (Query v) 
Monoid m => Monoid (Query v m) 
Semigroup m => Semigroup (Query v m) 
Wrapped (Query v m) 
VectorSpace v => HasOrigin (Query v m) 
HasLinearMap v => Transformable (Query v m) 
Rewrapped (Query v m) (Query v' m') 
type Unwrapped (Query v m) = Point v -> m 
type V (Query v m) = v 

Primtives

data Prim b v where Source

A value of type Prim b v is an opaque (existentially quantified) primitive which backend b knows how to render in vector space v.

Constructors

Prim :: (Transformable p, Typeable p, Renderable p b) => p -> Prim b (V p) 

Instances

HasLinearMap v => Transformable (Prim b v)

The Transformable instance for Prim just pushes calls to transform down through the Prim constructor.

HasLinearMap v => Renderable (Prim b v) b

The Renderable instance for Prim just pushes calls to render down through the Prim constructor.

type V (Prim b v) = v 

Diagrams

data QDiagram b v m Source

The fundamental diagram type. The type variables are as follows:

  • b represents the backend, such as SVG or Cairo. Note that each backend also exports a type synonym B for itself, so the type variable b may also typically be instantiated by B, meaning "use whatever backend is in scope".
  • v represents the vector space of the diagram. Typical instantiations include R2 (for a two-dimensional diagram) or R3 (for a three-dimensional diagram).
  • m is the monoidal type of "query annotations": each point in the diagram has a value of type m associated to it, and these values are combined according to the Monoid instance for m. Most often, m is simply instantiated to Any, associating a simple Bool value to each point indicating whether the point is inside the diagram; Diagram is a synonym for QDiagram with m thus instantiated to Any.

Diagrams can be combined via their Monoid instance, transformed via their Transformable instance, and assigned attributes via their HasStyle instance.

Note that the Q in QDiagram stands for "Queriable", as distinguished from Diagram, where m is fixed to Any. This is not really a very good name, but it's probably not worth changing it at this point.

Instances

Functor (QDiagram b v) 
Typeable (* -> * -> * -> *) QDiagram 
(HasLinearMap v, InnerSpace v, OrderedField (Scalar v), Semigroup m) => Monoid (QDiagram b v m)

Diagrams form a monoid since each of their components do: the empty diagram has no primitives, an empty envelope, an empty trace, no named subdiagrams, 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.

(HasLinearMap v, InnerSpace v, OrderedField (Scalar v), Semigroup m) => Semigroup (QDiagram b v m) 
Wrapped (QDiagram b v m) 
(HasLinearMap v, InnerSpace v, OrderedField (Scalar v), Semigroup m) => HasOrigin (QDiagram b v m)

Every diagram has an intrinsic "local origin" which is the basis for all combining operations.

(HasLinearMap v, OrderedField (Scalar v), InnerSpace v, Semigroup m) => Transformable (QDiagram b v m)

Diagrams can be transformed by transforming each of their components appropriately.

(HasLinearMap v, InnerSpace v, OrderedField (Scalar v), Semigroup m) => Qualifiable (QDiagram b v m)

Diagrams can be qualified so that all their named points can now be referred to using the qualification prefix.

(HasLinearMap v, InnerSpace v, OrderedField (Scalar v), Semigroup m) => HasStyle (QDiagram b v m) 
(HasLinearMap v, VectorSpace v, Ord (Scalar v), InnerSpace v, Semigroup m, Fractional (Scalar v), Floating (Scalar v)) => Traced (QDiagram b v m) 
(HasLinearMap v, InnerSpace v, OrderedField (Scalar v), Monoid' m) => Enveloped (QDiagram b v m) 
(HasLinearMap v, InnerSpace v, OrderedField (Scalar v), Monoid' m) => Juxtaposable (QDiagram b v m) 
Rewrapped (QDiagram b v m) (QDiagram b' v' m') 
type Unwrapped (QDiagram b v m) = DUALTree (DownAnnots v) (UpAnnots b v m) Annotation (QDiaLeaf b v m) 
type V (QDiagram b v m) = v 

type Diagram b v = QDiagram b v Any Source

Diagram b v is a synonym for QDiagram b v Any. That is, the default sort of diagram is one where querying at a point simply tells you whether the diagram contains that point or not. Transforming a default diagram into one with a more interesting query can be done via the Functor instance of QDiagram b or the value function.

mkQD :: Prim b v -> Envelope v -> Trace v -> SubMap b v m -> Query v m -> QDiagram b v m Source

Create a diagram from a single primitive, along with an envelope, trace, subdiagram map, and query function.

pointDiagram :: (Fractional (Scalar v), InnerSpace v) => Point v -> QDiagram b v m Source

Create a "point diagram", which has no content, no trace, an empty query, and a point envelope.

envelope :: forall b v m. (OrderedField (Scalar v), InnerSpace v, HasLinearMap v, Monoid' m) => Lens' (QDiagram b v m) (Envelope v) Source

Get the envelope of a diagram.

trace :: (InnerSpace v, HasLinearMap v, OrderedField (Scalar v), Semigroup m) => Lens' (QDiagram b v m) (Trace v) Source

Get the trace of a diagram.

subMap :: (HasLinearMap v, InnerSpace v, Semigroup m, OrderedField (Scalar v)) => Lens' (QDiagram b v m) (SubMap b v m) Source

Get the subdiagram map (i.e. an association from names to subdiagrams) of a diagram.

names :: (HasLinearMap v, InnerSpace v, Semigroup m, OrderedField (Scalar v)) => QDiagram b v m -> [(Name, [Point v])] Source

Get a list of names of subdiagrams and their locations.

query :: Monoid m => QDiagram b v m -> Query v m Source

Get the query function associated with a diagram.

sample :: Monoid m => QDiagram b v m -> Point v -> m Source

Sample a diagram's query function at a given point.

value :: Monoid m => m -> QDiagram b v Any -> QDiagram b v m Source

Set the query value for True points in a diagram (i.e. points "inside" the diagram); False points will be set to mempty.

resetValue :: (Eq m, Monoid m) => QDiagram b v m -> QDiagram b v Any Source

Reset the query values of a diagram to True/False: any values equal to mempty are set to False; any other values are set to True.

clearValue :: QDiagram b v m -> QDiagram b v Any Source

Set all the query values of a diagram to False.

nameSub :: (IsName n, HasLinearMap v, InnerSpace v, OrderedField (Scalar v), Semigroup m) => (QDiagram b v m -> Subdiagram b v m) -> n -> QDiagram b v m -> QDiagram b v m Source

Attach an atomic name to a certain subdiagram, computed from the given diagram /with the mapping from name to subdiagram included/. The upshot of this knot-tying is that if d' = d # named x, then lookupName x d' == Just d' (instead of Just d).

withName :: (IsName n, HasLinearMap v, InnerSpace v, Semigroup m, OrderedField (Scalar v)) => n -> (Subdiagram b v m -> QDiagram b v m -> QDiagram b v m) -> QDiagram b v m -> QDiagram b v m Source

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

withNameAll :: (IsName n, HasLinearMap v, InnerSpace v, Semigroup m, OrderedField (Scalar v)) => n -> ([Subdiagram b v m] -> QDiagram b v m -> QDiagram b v m) -> QDiagram b v m -> QDiagram b v m Source

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

withNames :: (IsName n, HasLinearMap v, InnerSpace v, Semigroup m, OrderedField (Scalar v)) => [n] -> ([Subdiagram b v m] -> QDiagram b v m -> QDiagram b v m) -> QDiagram b v m -> QDiagram b v m Source

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

localize :: forall b v m. (HasLinearMap v, InnerSpace v, OrderedField (Scalar v), Semigroup m) => QDiagram b v m -> QDiagram b v m Source

"Localize" a diagram by hiding all the names, so they are no longer visible to the outside.

href :: (HasLinearMap v, InnerSpace v, OrderedField (Scalar v), Semigroup m) => String -> QDiagram b v m -> QDiagram b v m Source

Make a diagram into a hyperlink. Note that only some backends will honor hyperlink annotations.

setEnvelope :: forall b v m. (OrderedField (Scalar v), InnerSpace v, HasLinearMap v, Monoid' m) => Envelope v -> QDiagram b v m -> QDiagram b v m Source

Replace the envelope of a diagram.

setTrace :: forall b v m. (OrderedField (Scalar v), InnerSpace v, HasLinearMap v, Semigroup m) => Trace v -> QDiagram b v m -> QDiagram b v m Source

Replace the trace of a diagram.

atop :: (HasLinearMap v, OrderedField (Scalar v), InnerSpace v, Semigroup m) => QDiagram b v m -> QDiagram b v m -> QDiagram b v m infixl 6 Source

A convenient synonym for mappend on diagrams, designed to be used infix (to help remember which diagram goes on top of which when combining them, namely, the first on top of the second).

Subdiagrams

data Subdiagram b v m Source

A Subdiagram represents a diagram embedded within the context of a larger diagram. Essentially, it consists of a diagram paired with any accumulated information from the larger context (transformations, attributes, etc.).

Constructors

Subdiagram (QDiagram b v m) (DownAnnots v) 

mkSubdiagram :: QDiagram b v m -> Subdiagram b v m Source

Turn a diagram into a subdiagram with no accumulated context.

getSub :: (HasLinearMap v, InnerSpace v, Floating (Scalar v), Ord (Scalar v), Semigroup m) => Subdiagram b v m -> QDiagram b v m Source

Turn a subdiagram into a normal diagram, including the enclosing context. Concretely, a subdiagram is a pair of (1) a diagram and (2) a "context" consisting of an extra transformation and attributes. getSub simply applies the transformation and attributes to the diagram to get the corresponding "top-level" diagram.

rawSub :: Subdiagram b v m -> QDiagram b v m Source

Extract the "raw" content of a subdiagram, by throwing away the context.

location :: HasLinearMap v => Subdiagram b v m -> Point v Source

Get the location of a subdiagram; that is, the location of its local origin with respect to the vector space of its parent diagram. In other words, the point where its local origin "ended up".

subPoint :: (HasLinearMap v, InnerSpace v, OrderedField (Scalar v), Semigroup m) => Point v -> Subdiagram b v m Source

Create a "point subdiagram", that is, a pointDiagram (with no content and a point envelope) treated as a subdiagram with local origin at the given point. Note this is not the same as mkSubdiagram . pointDiagram, which would result in a subdiagram with local origin at the parent origin, rather than at the given point.

Measurements

data Measure v Source

Type of measurement units for attributes.

Constructors

Output (Scalar v) 
Normalized (Scalar v) 
Local (Scalar v) 
Global (Scalar v) 
MinM (Measure v) (Measure v) 
MaxM (Measure v) (Measure v) 
ZeroM 
NegateM (Measure v) 
PlusM (Measure v) (Measure v) 
ScaleM (Scalar v) (Measure v) 

Instances

Eq (Scalar v) => Eq (Measure v) 
(Typeable * v, Data v, Data (Scalar v)) => Data (Measure v) 
Ord (Scalar v) => Ord (Measure v) 
Show (Scalar v) => Show (Measure v) 
VectorSpace (Measure v) 
AdditiveGroup (Measure v) 
(HasLinearMap v, Floating (Scalar v)) => Transformable (Measure v) 
Typeable (* -> *) Measure 
type Scalar (Measure v) = Scalar v 
type V (Measure v) = v 

fromOutput :: Measure v -> Scalar v Source

Retrieve the Output value of a 'Measure v' or throw an exception. Only Ouput measures should be left in the RTree passed to the backend.

toOutput :: forall v. (Data v, Data (Scalar v), Num (Scalar v), Ord (Scalar v), Fractional (Scalar v)) => Scalar v -> Scalar v -> Measure v -> Measure v Source

Convert an aribrary Measure to Output units.

atMost :: Measure v -> Measure v -> Measure v Source

Compute the smaller of two Measures. Useful for setting upper bounds.

atLeast :: Measure v -> Measure v -> Measure v Source

Compute the larger of two Measures. Useful for setting lower bounds.

Backends

class HasLinearMap v => Backend b v where Source

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 Render, Result, Options, and renderRTree. However, most backends will want to implement adjustDia as well; the default definition does nothing. Some useful standard definitions are provided in the Diagrams.TwoD.Adjust module from the diagrams-lib package.

Minimal complete definition

renderRTree

Associated Types

data Render b v :: * Source

An intermediate representation used for rendering primitives. (Typically, this will be some sort of monad, but it need not be.) The Renderable class guarantees that a backend will be able to convert primitives into this type; how these rendered primitives are combined into an ultimate Result is completely up to the backend.

type Result b v :: * Source

The result of running/interpreting a rendering operation.

data Options b v :: * Source

Backend-specific rendering options.

Methods

adjustDia :: (Monoid' m, Num (Scalar v)) => b -> Options b v -> QDiagram b v m -> (Options b v, Transformation v, QDiagram b v m) Source

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 returns a modified options record, the transformation applied to the diagram (which can be used to convert attributes whose value is Measure, or transform e.g. screen coordinates back into local diagram coordinates), and the adjusted diagram itself.

See the diagrams-lib package (particularly the Diagrams.TwoD.Adjust module) for some useful implementations.

renderRTree :: b -> Options b v -> RTree b v Annotation -> Result b v Source

Given some options, take a representation of a diagram as a tree and render it. The RTree has already been simplified and has all measurements converted to Output units.

Instances

class Transformable t => Renderable t b where Source

The Renderable type class connects backends to primitives which they know how to render.

Methods

render :: b -> t -> Render b (V t) Source

Given a token representing the backend and a transformable object, render it in the appropriate rendering context.

Instances

HasLinearMap v => Renderable (Prim b v) b

The Renderable instance for Prim just pushes calls to render down through the Prim constructor.

renderDia :: (Backend b v, InnerSpace v, Data v, OrderedField (Scalar v), Data (Scalar v), Monoid' m) => b -> Options b v -> QDiagram b v m -> Result b v Source

Render a diagram.

renderDiaT :: (Backend b v, HasLinearMap v, InnerSpace v, Data v, OrderedField (Scalar v), Data (Scalar v), Monoid' m) => b -> Options b v -> QDiagram b v m -> (Transformation v, Result b v) Source

Render a diagram, returning also the transformation which was used to convert the diagram from its ("global") coordinate system into the output coordinate system. The inverse of this transformation can be used, for example, to convert output/screen coordinates back into diagram coordinates. See also adjustDia.

The null backend

data NullBackend Source

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.

type D v = Diagram NullBackend v Source

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 an image (this example requires diagrams-lib):

  ghci> width (image "foo.png" 200 200)
  <interactive>:8:8:
      No instance for (Renderable Diagrams.TwoD.Image.Image b0)
        arising from a use of image
      Possible fix:
        add an instance declaration for
        (Renderable Diagrams.TwoD.Image.Image b0)
      In the first argument of width, namely
        `(image "foo.png" 200 200)'
      In the expression: width (image "foo.png" 200 200)
      In an equation for it: it = width (image "foo.png" 200 200)
  

GHC complains that there is no instance for Renderable Image b0; what is really going on is that it does not have enough information to decide what backend to use (hence the uninstantiated b0). This is annoying because we know that the choice of backend cannot possibly affect the width of the image (it's 200! it's right there in the code!); but there is no way for GHC to know that.

The solution is to annotate the call to image with the type D R2, like so:

  ghci> width (image "foo.png" 200 200 :: D R2)
  200.00000000000006
  

(It turns out the width wasn't 200 after all...)

As another example, here is the error we get if we try to compute the width of a radius-1 circle:

  ghci> width (circle 1)
  <interactive>:4:1:
      Couldn't match type `V a0' with R2
      In the expression: width (circle 1)
      In an equation for it: it = width (circle 1)
  

There's even more ambiguity here. Whereas image always returns a Diagram, the circle function can produce any PathLike type, and the width function can consume any Enveloped type, so GHC has no idea what type to pick to go in the middle. However, the solution is the same:

  ghci> width (circle 1 :: D R2)
  1.9999999999999998
  

Convenience classes

class (HasBasis v, HasTrie (Basis v), VectorSpace v) => HasLinearMap v Source

HasLinearMap is a poor man's class constraint synonym, just to help shorten some of the ridiculously long constraint sets.

Instances

class (Fractional s, Floating s, Ord s, AdditiveGroup s) => OrderedField s Source

When dealing with envelopes we often want scalars to be an ordered field (i.e. support all four arithmetic operations and be totally ordered) so we introduce this class as a convenient shorthand.

Instances

class (Semigroup m, Monoid m) => Monoid' m

The Monoid' class is a synonym for things which are instances of both Semigroup and Monoid. Ideally, the Monoid class itself will eventually include a Semigroup superclass and we can get rid of this.

Instances

(Semigroup m, Monoid m) => Monoid' m