Closed Javran closed 7 years ago
Unfortunately this isn't possible at the moment, the only way to combine an attribute is by using the semigroup structure and FillTexture
uses Last
. You could write your own fill attribute that uses First
which would do what you want but then the backend wouldn't know what to do with it.
Once we have a style traversal, replacing a fill colour could be done with something like:
myGreenCircle = circle 1 # fc red & styles . _fillTexture ?= solid green
I've got a working version in my rewrite but it isn't possible to do a valid style traversal with the current DUALTree
internals. So it might be a while before this makes it to the release version.
@cchalmers I'm a bit confused here, what does the use of Last
suppose to do? I think for Maybe
or Option
it's taking the last non-empty one appended, but for circle 1 # fc red # fc green
, or fc green (fc red $ circle 1)
it sounds more intuitive to me that fc green
is the last.
A diagram is really a tree with annotations, whenever you apply an attribute it's applied to the root of a the tree. So your diagram would look something like
fc green
|
fc red
|
circle 1
When a diagram is rendered you start from the top of the tree and accumulate the style (and transformation). Once you reach the leaf primitive (the Path
given by circle 1
in this case) you render it with the accumulated style (and transformation). In this case the accumulated style would be
Last green <> Last red = red
Another way you can think about it is by writing it as
x # applyStyle a # applyStyle b = (applyStyle b . applyStyle a) x
And since applyStyle
is a Monoid homomorphism
applyStyle b . applyStyle a = applyStyle (b <> a)
Just to be clear, this was a deliberate choice: you say "the second application should have precedence", but I think a good argument can be made that sometimes you really want the first application to have precedence, and that in particular this is a more sensible default. In particular, it means you can specify colors for some parts of a diagram and leave colors unspecified on other parts; then applying a color to the entire diagram has the effect of setting the color of only the parts with no color specified, instead of simply making the entire diagram the new color. The manual does mention this in a few places (e.g. "In general, inner attributes (that is, attributes applied earlier) override outer ones."), though I don't blame you for missing it.
Ultimately, I agree the right solution will be to have style traversals. For now, if you want to allow later overriding of colors you could do something like
myCircle :: (Diagram B -> Diagram B) -> Diagram B
myCircle styleFun = circle 1 # styleFun # fc red
... later ...
myCircle id -- circle with default (red) style
myCircle (fc green) -- circle with overridden color
It's not necessarily ideal but it works.
thanks for the explanation, this behavior now makes sense to me. I'm still new to diagrams and incorrectly assumed when you modifies an attribute the old one gets overwritten - haven't got to the point of being introduced about the notion of inner
and outer
attributes.
depending on the usage I think there are plenty of workarounds, @byorgey has just gave one, but I'm good with just keeping track of color somewhere else and apply it later. as in my use case color is probably the only thing that could require a change later.
Yeah, separately keeping track of the color, updating it in whatever way you want, and then applying it at the very end is another option. It's also not ideal since you have to manually keep track of which colors go with which diagrams --- but in simple enough cases this can work fine.
I'll close this issue for now---feel free to re-open if there's anything more to discuss.
The following code results in a red circle, rather than a green one (any backend should work),
I don't recall seeing anything saying I can't apply color on same diagram twice, and the second application (i.e.
# fc green
) should have precedence.