[diagrams] Attributes

Brent Yorgey byorgey at seas.upenn.edu
Thu Nov 18 12:27:26 EST 2010

Hi Ryan, thanks for writing up these thoughts.  Some of my own
thoughts are below.

On Tue, Nov 16, 2010 at 07:44:22AM -0500, Ryan Yates wrote:
> It would be useful for some attributes to be subject to some
> transformations.  As an example lets consider line width.  I think there are
> two straight forward ways that line width can be represented:
>     A) Have the value constant and represent the line width of the final
> diagram.
>     B) Have the value scale with the diagram.
> Given line widths of option A we can think of scaling as manipulating the
> diagram or redrawing it on a bigger piece of paper.  With option B we can
> think of scaling as changing the view or moving the "camera" closer or
> further away.  It would also be useful to have a function that (for lack of
> a better term right now) I'll call !freeze :
>     freeze :: Diagram b -> Diagram b

Indeed!  I've thought about this too, and I had actually thought of
having such a primitive called 'freeze' as well.  Given the type-based
attribute framework we have, it also shouldn't be too hard to do
selective freezing/thawing, although I guess it would require doing
something funny like passing a dummy attribute of the desired type
(whose value would be ignored!).  i.e.

  freezeThingsLikeThis :: AttrClass a => a -> Diagram b -> Diagram b

Another interesting possibility the type-based system opens up is
selective attribute modification:

  modifyAttr :: AttrClass a => (a -> a) -> Diagram b -> Diagram b

modifyAttr is easy to write and would be useful independently of
whatever we decide to do wrt. freezing.  And perhaps it gives us
enough power to not need the "dependent attributes" that you
describe.  That is, perhaps it is enough to just give the user the
power to modify attributes in whatever way they please (perhaps
defining their own custom transformation functions which combine a
transformation with some related attribute modifications) rather than
trying to set up the correspondence at the outset.

One problem that arises is exactly what the semantics of transformable
attributes would be.  For example, if you have a line width of 2 and
apply a scale of 2 in the x-axis and 0.5 in the y-axis, what exactly
should the new line width be??  For this reason I propose making
attributes transformable simply by pairing them with an associated
transformation.  The transformations can then be accumulated for
frozen attributes (and ignored for non-frozen ones), leaving the final
decision of how to interpret the transformation up to each backend.
For example, I think Cairo can combine line drawing with actual
transformations in such a way that the line will be thicker or thinner
in various places depending on (say) a non-uniform scale -- precisely
as you would expect for a "frozen" diagram.  But other backends might
not be able to do this, and might settle for simply sampling the
transformation to compute some sort of average scaling factor and use
that to transform the line width.  And so on.

> In another direction, are the two options (A and B) just two points in a
> continuum of ways to treat attributes?  In the case of line width A is a
> constant scaling and B can be determined by the relationship between the
> bounds when the attribute is first applied and the bounds when the diagram
> is rendered (or "frozen").  Maybe then the generalization of B is:
>     newtype B = B (AttributeClass a => Bounds b -> Bounds b -> a)
> or perhaps:
>     newtype B = B (AttributeClass a => Diagram b -> Diagram b -> a)
> which would lead to:
>     mkDependentAttribute :: (AttributeClass a) => a -> (a -> Diagram b ->
> Diagram b -> a) -> B
>     scalingLineWidth :: LineWidth -> Diagram b -> Diagram b -> LineWidth
>     scalingLineWidth (LineWidth w) original new = LineWidth (w * ratio)
>         where ratio = ... -- in terms of original and new.

I agree that options A and B seem like two points at the ends of a
space of possibilities, but the above seems much too complicated to
me...  One place I can think of where I would want something other
than just options A or B is that sometimes when scaling a diagram,
keeping the same absolute line width looks bad (because it gets too
thick or thin relative to the overall diagram), but scaling the line
width by the same amount as the diagram looks bad too! (because
e.g. it gets so thin it can hardly be seen).  I have actually run
across this in practice.  In this case you want some sort of
intermediate scaling.  However this is the sort of thing that should
be done on a case-by-case basis, not by establishing some formula or
ratio that says how to scale the line width relative to the diagram
scaling; in this case I would have been happy just to have a function
I could use to modify the line width.


More information about the diagrams mailing list