Contents

1 The issue

Most often one wants to be able to describe a diagram consisting of a collection of shapes, some of them obtained by transforming (e.g. rotating, shearing, scaling, translating) other shapes, and then draw the whole thing using a consistent line width. It would be strange if, say, a large circle was drawn with a thicker line just because it happened to be generated by scaling a smaller circle. Diagrams are supposed to be scale-invariant but in actuality it would be surprising for them to be truly scale-invariant with regards to line width.

Another way of thinking about it is that we want to think of diagrams as 'abstract geometric entities' (e.g. perfect circles, etc.) with the details of how they are actually drawn being a separate issue.

...On the other hand, sometimes we really do want, say, scaling a diagram to scale the line width proportionally! It really depends on the situation.

Note that other things in addition to line width may have the same issue, e.g. patterns/textures like gradients, fill patterns, etc. (once we add support for those).

2 Solution #1: transformations always affect attributes

One possible "solution" is to say, in essence, "To heck with thinking of diagrams as abstract geometric entities; transformations will always transform attributes, period." This forces the user to place attribute annotations carefully in order to produce the desired behavior. For example, in order to produce a scene consisting of variously scaled shapes, all drawn with a consistent line width, the user must apply the line width attribute after doing all the requisite scaling.

Put more simply, the following snippets of code would produce different output:

foo # scale 2 # lw 1

foo # lw 1 # scale 2

This is certainly possible, but has some drawbacks. Most notable is that if a user obtains several diagrams, each of which has already had a line width applied to it, it may be practically impossible to assemble them into a single diagram with consistent-looking lines.

3 Solution #2: 'freeze'

Another solution (the one currently adopted by diagrams) is to have a primitive operation called freeze such that transformations normally do not affect attributes such as line width, but after applying freeze, they do. Intuitively, freeze can be thought of as taking a "snapshot" of the physical realization of a diagram, and from then on transformations apply to the realization/drawing rather than to the abstract geometric ideal underlying it.

For example, under this scheme

foo # scale 2 # lw 1
foo # lw 1 # scale 2

have the same meaning, but

foo # lw 1 # freeze # scale 2

is different.

4 Semantics of 'freeze'

5 Implementation of 'freeze'

6 Rough notes dumped here, need to edit

The reason this is difficult is that some of the transformations

should* apply to styles and some *shouldn't*. But the

transformations and styles occur in the tree all mixed up. (Also,
transformations can affect some attributes!) The way we handled this in
the past was to never give any transformations directly to a backend.
We only give *fully transformed primitives*. e.g. the cairo backend's
implementation of withStyle works by *first* performing the given
rendering operation under *no* transformations (since it is given
fully transformed primitives), then apply just the transformations
above freeze and do the actual stroking.

If we want to be able to apply transformations incrementally while
we're walking down the tree this gets tricky. I suppose we could do

inverse* transformations before doing the stroking corresponding to

the transformations *underneath* the freeze...

Even just continuing to fully apply the transformations to primitives
and allowing styles to be applied early would go a long way, I think.
We could also do some work to coalesce styles. e.g. consider

foo1 = hcat (map circle [1,2,3,4,5]) # fc blue

foo2 = hcat (map (fc blue . circle) [1,2,3,4,5])

It would be nice if these actually generated the same output, i.e. the
programmer didn't have to worry about making such changes to their
code in order to "optimize" it.