With this module, you can compose collages in a more automatic way. Instead of shifting collages manually, this module figures out the dimensions of each part of your drawing and places them seamlessly next to each other, above each other or on top of each other. This is all possible by keeping track of the envelope or bounding box of each collage.
Collages are positioned relative to their internal origin. By default, a collage's internal origin lays in its exact center. By shifting a collage, we change its internal origin and the way it composes with other collages. Another method to change the origin is by using alignments.
All these ideas are based on the Diagrams library for Haskell and the Scalable Graphics library for Clean. You can regard this module as a simplified version of above libraries.
An envelope defines the bounding box of a collage. It is similar to a bounding box. Envelopes can become quite complex when we calculate them in all possible directions. Here, we restrict ourselves to four: up, down, right and left.
Envelopes answer the question: “If I want to put my collage in a rectangular envelope, what are the distances from its local origin to the envelope edges?”
envelope : Direction -> Collage msg -> Basics.Float
Calculate the envelope of a collage relative to its internal origin.
The figure below illustrates the four distances that can be calculated.
We represent the origin with (X)
.
When calling, for example, envelope Up
,
we calculate the distance from (X)
to the upper edge of the rectangle.
+–––––––––––––––+
| ˄ |
| Up | |
| | Right |
| ˂––––(X)––––˃ |
| Left | |
| | Down |
| ˅ |
+–––––––––––––––+
The same holds for the other three directions.
The four different directions in which we can calculate an envelope.
facing : Direction -> Direction
Calculate the facing direction.
Up <-> Down
Left <-> Right
distances : Collage msg -> Distances
Calculate the envelope in all four directions at once.
The result is a Distances
record with toTop
, toBottom
, toLeft
, and toRight
fields.
Use this function if you need envelopes in multiple directions at the same time.
{ toTop, toBottom } = distances collage
...use up and down...
{ toTop : Basics.Float
, toBottom : Basics.Float
, toRight : Basics.Float
, toLeft : Basics.Float
}
Type alias collecting envelope distances in all four directions.
width : Collage msg -> Basics.Float
Calculates the width of a collage.
The width is equivalent to the envelopes in the left and right directions:
width collage == envelope Left collage + envelope Right collage
height : Collage msg -> Basics.Float
Calculates the height of a collage.
The height is equivalent to the envelopes in the up and down directions:
height collage == envelope Up collage + envelope Down collage
Collages can be composed in three ways:
A more advanced way of combining collages is by imposition: placing one collage on top of a background and forgetting about the dimensions of the foreground. All ways of combining are expressed used one basic placing function, which you can use in some extreme cases where you need to place a collage but not combine it with others.
horizontal : List (Collage msg) -> Collage msg
Place a list of collages next to each other, such that their origins are along a horizontal line. The first element in the list will be on the left, the last on the right.
horizontal [a, b, c]
+–––+–––+–––+
|(a)| b | c |
+–––+–––+–––+
The new origin will be the origin of the first element in the list.
hcat
in Diagrams.vertical : List (Collage msg) -> Collage msg
Place a list of collages next to each other, such that their origins are along a vertical line. The first element in the list will be on the top, the last on the bottom.
vertical [a, b, c]
+–––+
|(a)|
+–––+
| b |
+–––+
| c |
+–––+
The new origin will be the origin of the first element in the list.
vcat
in Diagrams.stack : List (Collage msg) -> Collage msg
Place a list of collages on top of each other, with their origin points stacked on the "out of page" axis.
The first collage in the list is on top.
This actually is the same as the group
operation in the Collage module.
stack [a, b, c]
+–––+
|(a)|
+–––+
(Yes, b
and c
are somewhere below a
...)
The new origin will be the origin of the first element in the list.
Note: this is called concat
in Diagrams.
Note: when we create an operator (<>)
like
(<>) a b =
stack [ a, b ]
then (<>)
forms a monoid together with empty
.
(<>)
is called atop
in Diagrams.
impose : Collage msg -> Collage msg -> Collage msg
Impose a collage on a background.
The call
impose fore back
stacks fore
on back
.
The envelope of fore
will be "forgotten"
and back
will be used to calculate the envelope of the resulting collage.
+–––––––––––+
|(b)+–––+ |
| | a | | <-- new envelope
| +–––+ |
+–––––––––––+
Obviously, this also works with the background having a smaller envelope than the foreground.
The new origin will be the origin of the background.
beside : Direction -> Collage msg -> Collage msg -> Collage msg
Place a collage beside another one in the given direction and combine them into a new one.
Most of the time it is way nicer to use horizontal
or vertical
to express your layout,
but beside dir
can come into hand as a curried function.
The new origin will be the origin of the first argument.
beside dir
forms a monoid with empty
for every dir
.beside
in Diagrams.place : Direction -> Collage msg -> Collage msg -> Collage msg
Shift a collage in a direction to precisely that position where it would touch the first one, without composing them.
Use this to position a collage next to another collage without actually composing them.
juxtapose
in Diagrams.What is placing collages without being able to space them accordingly? These two collages are invisible and only take up some space. (Or they even don't...)
spacer : Basics.Float -> Basics.Float -> Collage msg
Create an empty collage of given width and height.
This is useful for getting your spacing right and for making borders.
hspace = spacer 10 0
horizontal <| List.intersperse hspace [a, b, c]
+–––+ +–––+ +–––+
| a | | b | | c |
+–––+ +–––+ +–––+
empty : Collage msg
A collage that takes up no space. Good for things that appear conditionally:
horizontal [ a, if showMore then b else empty ]
+–––+– – – – – –+
|(a)| maybe b? |
+–––+– – – – – –+
align : Anchor msg -> Collage msg -> Collage msg
Shift a collage such that the origin is on the given anchor.
Use this, for example, when you like to align some collages to the top:
[a, b, c]
|> List.map (align top)
|> horizontal
+–(X)–+––––X––––+–X–+
| a | b | c |
| +–––––––––+ |
+–––––+ | |
+–––+
Anchors are created by the functions from the section below.
at : Anchor msg -> Collage msg -> Collage msg -> Collage msg
Stack a collage on top of a specified anchor of a host.
Makes placing objects on a collage a lot easier:
collage
|> at bottom dot
|> at upperRight dot
+–––––––––0
| collage |
+––––0––––+
instead of:
stack
[ dot
, align upperRight <|
stack
[ dot
, align bottom collage
]
]
This does not change the origin of collage
.
center : Collage msg -> Collage msg
Shift a collage such that the envelope in all directions is equal.
This is the same as aligning on the base anchor:
center collage == align base collage
Collage msg -> Collage.Point
Anchors are functions which calculate a point relative to the origin of a given collage.
top : Collage msg -> Collage.Point
+(X)+
| |
+–––+
topRight : Collage msg -> Collage.Point
+––(X)
| |
+–––+
right : Collage msg -> Collage.Point
+–––+
| (X)
+–––+
bottomRight : Collage msg -> Collage.Point
+–––+
| |
+––(X)
bottom : Collage msg -> Collage.Point
+–––+
| |
+(X)+
bottomLeft : Collage msg -> Collage.Point
+–––+
| |
(X)––+
left : Collage msg -> Collage.Point
+–––+
(X) |
+–––+
topLeft : Collage msg -> Collage.Point
(X)––+
| |
+–––+
base : Collage msg -> Collage.Point
+–––+
|(X)|
+–––+
Warning!
name : String -> Collage msg -> Collage msg
Give a name to (a part of) a collage in order to locate it after composition.
locate : String -> Anchor msg -> Collage msg -> Maybe Collage.Point
Locate a named part of a collage and calculate the coordinates using the given anchor in the new coordinate system.
First be sure to give a name to a sub collage using name
,
only after that you can retrieve the collage using this function.
Well, you cannot retrieve the whole collage,
but a point calculated relative to its internal origin using an anchor.
This point is then subjected to the samen transformations as all the groups above it.
When a collage part could not be found, we display a message on the console for your convenience.
connect : List ( String, Anchor msg ) -> Collage.LineStyle -> Collage msg -> Collage msg
Connect a list of points which are located inside a collage.
For named parts that could not be found, the result will be ignored.
names : Collage msg -> Dict String (Collage msg)
Return a dictionary with all named parts of given collage.
These two functions can aid in discovering how collages are composed.
showOrigin : Collage msg -> Collage msg
Draw a red dot at the local origin of the collage.
showEnvelope : Collage msg -> Collage msg
Draw a red dotted box around the collage representing the envelope.
debug : Collage msg -> Collage msg
Show both the envelope and the origin of a collage.
We can imagine some possible extensions which could aid in designing composable drawings:
baseX
, and baseY
(horizontalBase
and vertialBase
)?centerX
, and centerY
too?