Copyright | (c) 2013 diagrams-lib team (see LICENSE) |
---|---|

License | BSD-style (see LICENSE) |

Maintainer | diagrams-discuss@googlegroups.com |

Safe Haskell | None |

Language | Haskell2010 |

"Envelopes", aka functional bounding regions. See Diagrams.Core.Envelope for internal implementation details.

- data Envelope v n :: (* -> *) -> * -> *
- class (Metric (V a), OrderedField (N a)) => Enveloped a
- envelope :: (OrderedField n, Metric v, Monoid' m) => Lens' (QDiagram b v n m) (Envelope v n)
- setEnvelope :: (OrderedField n, Metric v, Monoid' m) => Envelope v n -> QDiagram b v n m -> QDiagram b v n m
- withEnvelope :: (InSpace v n a, Metric v, OrderedField n, Monoid' m, Enveloped a) => a -> QDiagram b v n m -> QDiagram b v n m
- phantom :: (InSpace v n a, Monoid' m, Enveloped a, Traced a) => a -> QDiagram b v n m
- pad :: (Metric v, OrderedField n, Monoid' m) => n -> QDiagram b v n m -> QDiagram b v n m
- extrudeEnvelope :: (Metric v, OrderedField n, Monoid' m) => v n -> QDiagram b v n m -> QDiagram b v n m
- intrudeEnvelope :: (Metric v, OrderedField n, Monoid' m) => v n -> QDiagram b v n m -> QDiagram b v n m
- envelopeVMay :: Enveloped a => Vn a -> a -> Maybe (Vn a)
- envelopeV :: Enveloped a => Vn a -> a -> Vn a
- envelopePMay :: ((~) (* -> *) (V a) v, (~) * (N a) n, Enveloped a) => v n -> a -> Maybe (Point v n)
- envelopeP :: ((~) (* -> *) (V a) v, (~) * (N a) n, Enveloped a) => v n -> a -> Point v n
- diameter :: ((~) (* -> *) (V a) v, (~) * (N a) n, Enveloped a) => v n -> a -> n
- radius :: ((~) (* -> *) (V a) v, (~) * (N a) n, Enveloped a) => v n -> a -> n

# Types

data Envelope v n :: (* -> *) -> * -> *

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.

Action Name (Envelope v n) | |

Show (Envelope v n) | |

Ord n => Semigroup (Envelope v n) | |

Ord n => Monoid (Envelope v n) | |

(Metric v, OrderedField n) => Juxtaposable (Envelope v n) | |

(Metric v, OrderedField n) => Enveloped (Envelope v n) | |

(Metric v, Floating n) => Transformable (Envelope v n) | |

(Metric v, Fractional n) => HasOrigin (Envelope v n) | The local origin of an envelope is the point with respect to
which bounding queries are made, |

Wrapped (Envelope v n) | |

(Metric v, OrderedField n) => Alignable (Envelope v n) | |

Rewrapped (Envelope v n) (Envelope v' n') | |

type V (Envelope v n) = v | |

type N (Envelope v n) = n | |

type Unwrapped (Envelope v n) = Option (v n -> Max n) |

class (Metric (V a), OrderedField (N a)) => Enveloped a

`Enveloped`

abstracts over things which have an envelope.

Enveloped b => Enveloped [b] | |

Enveloped b => Enveloped (Set b) | |

Enveloped t => Enveloped (TransInv t) | |

Enveloped a => Enveloped (Located a) | The envelope of a |

(Enveloped a, Enveloped b, (~) (* -> *) (V a) (V b), (~) * (N a) (N b)) => Enveloped (a, b) | |

Enveloped b => Enveloped (Map k b) | |

(Metric v, OrderedField n) => Enveloped (Envelope v n) | |

(OrderedField n, Metric v) => Enveloped (Point v n) | |

(Metric v, OrderedField n) => Enveloped (FixedSegment v n) | |

(Metric v, OrderedField n) => Enveloped (Trail v n) | |

(Metric v, OrderedField n) => Enveloped (Path v n) | |

(Metric v, Traversable v, OrderedField n) => Enveloped (BoundingBox v n) | |

(Metric v, OrderedField n) => Enveloped (Segment Closed v n) | The envelope for a segment is based at the segment's start. |

(Metric v, OrderedField n) => Enveloped (Trail' l v n) | The envelope for a trail is based at the trail's start. |

(Metric v, OrderedField n, Monoid' m) => Enveloped (QDiagram b v n m) | |

(OrderedField n, Metric v, Monoid' m) => Enveloped (Subdiagram b v n m) |

# Diagram envelopes

setEnvelope :: (OrderedField n, Metric v, Monoid' m) => Envelope v n -> QDiagram b v n m -> QDiagram b v n m

Replace the envelope of a diagram.

withEnvelope :: (InSpace v n a, Metric v, OrderedField n, Monoid' m, Enveloped a) => a -> QDiagram b v n m -> QDiagram b v n m Source

Use the envelope from some object as the envelope for a diagram, in place of the diagram's default envelope.

sqNewEnv = circle 1 # fc green ||| ( c # dashingG [0.1,0.1] 0 # lc white <> square 2 # withEnvelope (c :: D V2 Double) # fc blue ) c = circle 0.8 withEnvelopeEx = sqNewEnv # centerXY # pad 1.5

phantom :: (InSpace v n a, Monoid' m, Enveloped a, Traced a) => a -> QDiagram b v n m Source

`phantom x`

produces a "phantom" diagram, which has the same
envelope and trace as `x`

but produces no output.

pad :: (Metric v, OrderedField n, Monoid' m) => n -> QDiagram b v n m -> QDiagram b v n m Source

`pad s`

"pads" a diagram, expanding its envelope by a factor of
`s`

(factors between 0 and 1 can be used to shrink the envelope).
Note that the envelope will expand with respect to the local
origin, so if the origin is not centered the padding may appear
"uneven". If this is not desired, the origin can be centered
(using, e.g., `centerXY`

for 2D diagrams) before applying `pad`

.

extrudeEnvelope :: (Metric v, OrderedField n, Monoid' m) => v n -> QDiagram b v n m -> QDiagram b v n m Source

`extrudeEnvelope v d`

asymmetrically "extrudes" the envelope of
a diagram in the given direction. All parts of the envelope
within 90 degrees of this direction are modified, offset outwards
by the magnitude of the vector.

This works by offsetting the envelope distance proportionally to the cosine of the difference in angle, and leaving it unchanged when this factor is negative.

intrudeEnvelope :: (Metric v, OrderedField n, Monoid' m) => v n -> QDiagram b v n m -> QDiagram b v n m Source

`intrudeEnvelope v d`

asymmetrically "intrudes" the envelope of
a diagram away from the given direction. All parts of the envelope
within 90 degrees of this direction are modified, offset inwards
by the magnitude of the vector.

Note that this could create strange inverted envelopes, where
` diameter v d < 0 `

.

# Querying envelopes

envelopeVMay :: Enveloped a => Vn a -> a -> Maybe (Vn a)

Compute the vector from the local origin to a separating
hyperplane in the given direction, or `Nothing`

for the empty
envelope.

envelopeV :: Enveloped a => Vn a -> a -> Vn a

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

envelopePMay :: ((~) (* -> *) (V a) v, (~) * (N a) n, Enveloped a) => v n -> a -> Maybe (Point v n)

Compute the point on a separating hyperplane in the given
direction, or `Nothing`

for the empty envelope.

envelopeP :: ((~) (* -> *) (V a) v, (~) * (N a) n, Enveloped a) => v n -> a -> Point v n

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