|
From: Jae-Joon L. <lee...@gm...> - 2009-03-23 19:17:51
Attachments:
path-alpha_test.py
path-alpha_test.png
|
Hi,
When drawing a patch, the alpha value of its edgeolor is ignored. The
following command draw a circle whose edgecolor has alpha=1, instead
of 0.1.
gca().add_patch(Circle((0.5, 0.5), 0.3,
ec=(1,0,0,0.1), fc="none"))
Attached is a little test script and its output.
It seems that the edgecolor always has an alpha value of the face color.
I'm not sure if this behavior is intended, but I personally think this
is a bug.
Regards,
-JJ
|
|
From: Eric F. <ef...@ha...> - 2009-03-23 19:32:52
|
Jae-Joon Lee wrote: > Hi, > > When drawing a patch, the alpha value of its edgeolor is ignored. The > following command draw a circle whose edgecolor has alpha=1, instead > of 0.1. > > > gca().add_patch(Circle((0.5, 0.5), 0.3, > ec=(1,0,0,0.1), fc="none")) > > > Attached is a little test script and its output. > It seems that the edgecolor always has an alpha value of the face color. > I'm not sure if this behavior is intended, but I personally think this > is a bug. Jae-Joon, I can't look at this specifically now, but I suspect it is a side effect of the way that alpha support has evolved, resulting in a confusing mess. Some things are RGB, others are RGBA; alpha defaults get set in various places, and there is no clear way of keeping track of what is just a default and should be overridden, versus what has been set deliberately and should *not* be overridden. I dimly remember looking into it a few months ago, thinking that it could be cleaned up with some simple changes, but failing. I really wish we could make some sweeps through the mpl code base and systematically clean up some of these messes. I don't know how many there are, but certainly more than one. Dpi is another area of perennial confusion, for example. Eric |
|
From: Jae-Joon L. <lee...@gm...> - 2009-03-23 20:24:45
Attachments:
path-alpha_test.py
path-alpha_test.png
|
The example (e) in my previous script have a code and a text label mismatched.
I'm attaching the corrected one.
I took a more look on how a patch is drawn.
the draw() method of a patch calls draw_path method of the renderer,
which seems to be responsible for both "fill", and "stroke". But there
is only one alpha value (gc.alpha). The rgbFace is a tuple of r,g,b
and does not have a alpha value.
renderer.draw_path(gc, tpath, affine, rgbFace)
Thus, it seems that is is not possible to have different alpha for
"fill" and "stroke".
One of the easiest solution would be call the draw_path twice, each
for fill and stroke.
>
> I can't look at this specifically now, but I suspect it is a side effect of
> the way that alpha support has evolved, resulting in a confusing mess. Some
> things are RGB, others are RGBA; alpha defaults get set in various places,
> and there is no clear way of keeping track of what is just a default and
> should be overridden, versus what has been set deliberately and should *not*
> be overridden. I dimly remember looking into it a few months ago, thinking
> that it could be cleaned up with some simple changes, but failing. I really
> wish we could make some sweeps through the mpl code base and systematically
> clean up some of these messes. I don't know how many there are, but
> certainly more than one. Dpi is another area of perennial confusion, for
> example.
>
> Eric
>
>
Thanks for the explanation.
I personally think that the default behavior for setting alpha is
better to be multiplying with the previous one, instead of overriding.
We may introduce override keyword in set_alpha method to to force the
override. I think it should be considered a bug If there are more than
one alpha defaults involved. Just a thought.
And, yes, dealing with the dpi is always confusing!
-JJ
|
|
From: John H. <jd...@gm...> - 2009-03-23 21:27:20
|
On Mon, Mar 23, 2009 at 3:24 PM, Jae-Joon Lee <lee...@gm...> wrote: > The example (e) in my previous script have a code and a text label > mismatched. > I'm attaching the corrected one. > > I took a more look on how a patch is drawn. > the draw() method of a patch calls draw_path method of the renderer, > which seems to be responsible for both "fill", and "stroke". But there > is only one alpha value (gc.alpha). The rgbFace is a tuple of r,g,b > and does not have a alpha value. > > renderer.draw_path(gc, tpath, affine, rgbFace) > > Thus, it seems that is is not possible to have different alpha for > "fill" and "stroke". > One of the easiest solution would be call the draw_path twice, each > for fill and stroke. I think we would pay a significant performance hit in some cases to make the call twice. Perhaps this is a good time to rethink the draw_path and gc signature, since as Eric notes these have evolved over time from the early days when mpl didn't have alpha at all. One possibility would be to have a facecolor/edgecolor property on the gc itself, which would be rgba tuples. Since the gc is almost entirely internal, we can revamp it w/o affecting userland code, though it would be nice to support legacy methods (eg gc.set_alpha could warn and then proceed to set the edge and face alpha channel). Then we would drop the rgbFace argument entirely. Obviously this would require hacking through a bunch of backend code to fix, but the changes would be fairly straightforward and of the busy-work variety. JDH |
|
From: Eric F. <ef...@ha...> - 2009-03-23 21:57:16
|
John Hunter wrote: > > > On Mon, Mar 23, 2009 at 3:24 PM, Jae-Joon Lee <lee...@gm... > <mailto:lee...@gm...>> wrote: > > The example (e) in my previous script have a code and a text label > mismatched. > I'm attaching the corrected one. > > I took a more look on how a patch is drawn. > the draw() method of a patch calls draw_path method of the renderer, > which seems to be responsible for both "fill", and "stroke". But there > is only one alpha value (gc.alpha). The rgbFace is a tuple of r,g,b > and does not have a alpha value. > > renderer.draw_path(gc, tpath, affine, rgbFace) > > Thus, it seems that is is not possible to have different alpha for > "fill" and "stroke". > One of the easiest solution would be call the draw_path twice, each > for fill and stroke. > > > I think we would pay a significant performance hit in some cases to make > the call twice. Perhaps this is a good time to rethink the draw_path > and gc signature, since as Eric notes these have evolved over time from > the early days when mpl didn't have alpha at all. One possibility would > be to have a facecolor/edgecolor property on the gc itself, which would > be rgba tuples. Since the gc is almost entirely internal, we can revamp > it w/o affecting userland code, though it would be nice to support > legacy methods (eg gc.set_alpha could warn and then proceed to set the > edge and face alpha channel). Then we would drop the rgbFace argument > entirely. Obviously this would require hacking through a bunch of > backend code to fix, but the changes would be fairly straightforward and > of the busy-work variety. > > JDH It may be nearly orthogonal to the lower-level changes you are suggesting, John, but before I completely forget about it I would like to toss out a very vague idea about reform at a higher level, with apologies that I have not thought it through: Maybe we need an MplColorSpec class. At present, functions and methods typically accept colors and/or color arrays in a wide variety of forms. This is good. My thought is that these should then be converted by the accepting function or method to instances of the new class, and that instances of the new class should be accepted as color inputs along with all the old forms. I suspect that this refactoring might, without loss of backwards compatibility, make it possible to considerably simplify, clarify, and generalize the handling of colors (again, both single values and arrays), and provide a less-confusing framework for setting and overriding defaults. I think that as things are now, color spec checking and/or conversion are often done repeatedly in a single pipeline. With the class, all this would happen only the first time a color spec is encountered. The class might include mapping, or the present color mapping might yield an instance of the class; I have not thought about this aspect. Eric |
|
From: Jouni K. S. <jk...@ik...> - 2009-03-24 08:24:42
|
Eric Firing <ef...@ha...> writes:
> John Hunter wrote:
>> One possibility would be to have a facecolor/edgecolor property on
>> the gc itself, which would be rgba tuples. Since the gc is almost
>> entirely internal, we can revamp it w/o affecting userland code,
>> though it would be nice to support legacy methods (eg gc.set_alpha
>> could warn and then proceed to set the edge and face alpha channel).
>> Then we would drop the rgbFace argument entirely. Obviously this
>> would require hacking through a bunch of backend code to fix, but the
>> changes would be fairly straightforward and of the busy-work variety.
This sounds like a good idea. In the pdf backend, GraphicsContextPdf
already defines a _fillcolor attribute, and for example draw_path does
def draw_path(self, gc, path, transform, rgbFace=None):
self.check_gc(gc, rgbFace)
# ...
where check_gc just temporarily sets gc._fillcolor to the value of
rgbFace and issues the pdf commands to change the graphics state to
reflect gc. If rgbFace is absorbed into gc, at least the pdf backend
should be easy to change accordingly, and should become less complex in
the process. Currently the same alpha value (gc._alpha) is used for both
strokes and painting operations, but this too should be easy to change
if we decide to drop the _alpha attribute from GraphicsContext and use
the fourth component of the stroke and fill colors for alpha.
By the way, the PDF imaging model has much richer support for
transparency than just specifying an alpha value for each operation; the
Transparency chapter takes up 64 pages in the PDF spec¹. One thing that
I imagine somebody just might want to have support for in matplotlib are
transparency groups², i.e., blending some objects together and then
blending the group with the background. But I wonder if that's possible
in Agg - I guess we will want to stay pretty close to the greatest
common denominator of Agg, SVG and PDF, and let people with special
needs use other software such as Illustrator to postprocess the files.
¹ http://www.adobe.com/devnet/pdf/pdf_reference_archive.html
² http://itext.ugent.be/library/com/lowagie/examples/directcontent/colors/transparency.pdf
> Maybe we need an MplColorSpec class. At present, functions and methods
> typically accept colors and/or color arrays in a wide variety of forms.
> This is good. My thought is that these should then be converted by
> the accepting function or method to instances of the new class, and that
> instances of the new class should be accepted as color inputs along with
> all the old forms.
I haven't thought about this carefully, but I think it could help
improve the cohesion of the code.
Perhaps a more "object-oriented" way to deal with color specifications
would be to let ColorSpec objects be constructed by the user, and
require these objects to be used in the OO api: instead of
ax.bar(x, y, color='0.5', edgecolor='r')
require
ax.bar(x, y, color=ColorSpec('0.5'), edgecolor=ColorSpec('r'))
or perhaps even something like
ax.bar(x, y,
color=ColorSpec(grayscale=0.5),
edgecolor=ColorSpec(named='r'))
to avoid parsing strings in the API - replacing the current hack, neat
as it is, where a string representation of a decimal number means a
grayscale color, a string beginning with # means a hexadecimal color,
etc. The pyplot API should of course continue to work as it does now.
IMHO this would be more Pythonic (in the vein of "Explicit is better
than implicit" and "In the face of ambiguity, refuse the temptation to
guess"), although a departure from how matplotlib currently works. It
would encourage the users of the OO API to select their colors once and
put them in variables, so the example above would really read
facecolor = ColorSpec(grayscale=0.5)
edgecolor = ColorSpec(named='red')
# ...
ax.bar(x, y, color=facecolor, edgecolor=edgecolor)
which - as a part of a longer program - would likely be easier to
maintain than the first version of the example.
> I suspect that this refactoring might, without loss
> of backwards compatibility, make it possible to considerably simplify,
> clarify, and generalize the handling of colors (again, both single
> values and arrays), and provide a less-confusing framework for setting
> and overriding defaults. I think that as things are now, color spec
> checking and/or conversion are often done repeatedly in a single
> pipeline. With the class, all this would happen only the first time a
> color spec is encountered.
>
> The class might include mapping, or the present color mapping might
> yield an instance of the class; I have not thought about this aspect.
>
> Eric
--
Jouni K. Seppänen
http://www.iki.fi/jks
|
|
From: Michael D. <md...@st...> - 2009-03-24 12:24:58
|
Jouni K. Seppänen wrote: > Eric Firing <ef...@ha...> writes: > >> John Hunter wrote: >> >>> One possibility would be to have a facecolor/edgecolor property on >>> the gc itself, which would be rgba tuples. Since the gc is almost >>> entirely internal, we can revamp it w/o affecting userland code, >>> though it would be nice to support legacy methods (eg gc.set_alpha >>> could warn and then proceed to set the edge and face alpha channel). >>> Then we would drop the rgbFace argument entirely. Obviously this >>> would require hacking through a bunch of backend code to fix, but the >>> changes would be fairly straightforward and of the busy-work variety. >>> > > One open question is whether set_alpha (even if deprecated) should set or multiply the alpha of the fill and edge color. But I think I'm in favor of creating "one way to do it", which would be to have alpha as the fourth component of any color -- that option also scales well to individual colors in a collection, in a way that any of the more global options don't. It strikes me that if none of us find the time for this, this task would be a good initial GSoC task... it's not enough work for an entire summer by any means, but it's "busy work" that touches a lot of parts of the code, and therefore a good introduction to the code base. The other related task is to create a gc-like object for collections so that the arguments to draw_collection don't have to change in every backend every time a new feature is added. > This sounds like a good idea. In the pdf backend, GraphicsContextPdf > already defines a _fillcolor attribute, and for example draw_path does > > def draw_path(self, gc, path, transform, rgbFace=None): > self.check_gc(gc, rgbFace) > # ... > > where check_gc just temporarily sets gc._fillcolor to the value of > rgbFace and issues the pdf commands to change the graphics state to > reflect gc. If rgbFace is absorbed into gc, at least the pdf backend > should be easy to change accordingly, and should become less complex in > the process. Currently the same alpha value (gc._alpha) is used for both > strokes and painting operations, but this too should be easy to change > if we decide to drop the _alpha attribute from GraphicsContext and use > the fourth component of the stroke and fill colors for alpha. > > By the way, the PDF imaging model has much richer support for > transparency than just specifying an alpha value for each operation; the > Transparency chapter takes up 64 pages in the PDF spec¹. One thing that > I imagine somebody just might want to have support for in matplotlib are > transparency groups², i.e., blending some objects together and then > blending the group with the background. But I wonder if that's possible > in Agg - I guess we will want to stay pretty close to the greatest > common denominator of Agg, SVG and PDF, and let people with special > needs use other software such as Illustrator to postprocess the files. > > ¹ http://www.adobe.com/devnet/pdf/pdf_reference_archive.html > ² http://itext.ugent.be/library/com/lowagie/examples/directcontent/colors/transparency.pdf > > >> Maybe we need an MplColorSpec class. At present, functions and methods >> typically accept colors and/or color arrays in a wide variety of forms. >> This is good. My thought is that these should then be converted by >> the accepting function or method to instances of the new class, and that >> instances of the new class should be accepted as color inputs along with >> all the old forms. >> > > replacing the current hack, neat > as it is, where a string representation of a decimal number means a > grayscale color, a string beginning with # means a hexadecimal color, > etc. The pyplot API should of course continue to work as it does now. > > I really like Eric's suggestion here, as it fits in well with my desire to verify arguments early and consistently. But I don't think we need to throw out the convenient string forms of colors to achieve it. Those are really handy, and fairly well known from HTML/CSS/SVG etc., and I worry forcing the user to provide an instance of a particular class to do something as common as setting a color would be annoying verbosity. Of course, they should be free to do so if there's other maintenance advantages as you suggested. Mike -- Michael Droettboom Science Software Branch Operations and Engineering Division Space Telescope Science Institute Operated by AURA for NASA |
|
From: Eric F. <ef...@ha...> - 2009-03-23 20:59:20
|
Jae-Joon Lee wrote: > > Thanks for the explanation. > I personally think that the default behavior for setting alpha is > better to be multiplying with the previous one, instead of overriding. This was causing havoc in contourf; I don't see the logic of multiplying a previous alpha by a new one. > We may introduce override keyword in set_alpha method to to force the > override. I think it should be considered a bug If there are more than > one alpha defaults involved. Just a thought. Yes, it is a bug, but I don't think another kwarg in set_alpha will necessarily solve it. Eric > And, yes, dealing with the dpi is always confusing! > > -JJ > > > ------------------------------------------------------------------------ > |
|
From: Jae-Joon L. <lee...@gm...> - 2009-03-23 22:46:33
|
> > This was causing havoc in contourf; I don't see the logic of multiplying a > previous alpha by a new one. Consider a following example that you want to have different alpha for edgecolor and face color (of course this does not work as of now), Circle((1, 1), 0.5, ec=(1, 1, 1, 0.2), fc=(1, 0, 0, 1), alpha=1) If alpha always overrides, it will override both alpha values of the edge color and facecolor, and we can't have different alphas for edge and face. And, in this case, I think the alphas should be multiplied. Similarly, Circle((1, 1), 0.5, ec=(1, 1, 1, 0.2), fc=(1, 0, 0, 1), alpha=0.5) I think it is natural to expect that the resulting alpha of ec to be 0.2*0.5. For the case of contourf, the problem in my point of view is not that the alpha is not overridden, but that contourf applies the same alpha twice. Whether set_alpha() method override or not would be an implementation detail, but I guess the above cases at least make some sense. -JJ |
|
From: Eric F. <ef...@ha...> - 2009-03-24 19:09:59
|
Michael Droettboom wrote: > Jouni K. Seppänen wrote: >> Eric Firing <ef...@ha...> writes: >> >>> John Hunter wrote: >>> >>>> One possibility would be to have a facecolor/edgecolor property on >>>> the gc itself, which would be rgba tuples. Since the gc is almost >>>> entirely internal, we can revamp it w/o affecting userland code, >>>> though it would be nice to support legacy methods (eg gc.set_alpha >>>> could warn and then proceed to set the edge and face alpha channel). >>>> Then we would drop the rgbFace argument entirely. Obviously this >>>> would require hacking through a bunch of backend code to fix, but the >>>> changes would be fairly straightforward and of the busy-work variety. >>>> >> > One open question is whether set_alpha (even if deprecated) should set > or multiply the alpha of the fill and edge color. But I think I'm in > favor of creating "one way to do it", which would be to have alpha as > the fourth component of any color -- that option also scales well to > individual colors in a collection, in a way that any of the more global > options don't. I agree. To the extent that we retain alpha= kwargs and set_alpha, it seems to me they should have the obvious meaning: "set_x" means *set*, not multiply; if you want to multiply, use "scale_alpha", or "fade", or something like that. These sorts of operations could be done naturally as ColorSpec methods. > > It strikes me that if none of us find the time for this, this task would > be a good initial GSoC task... it's not enough work for an entire > summer by any means, but it's "busy work" that touches a lot of parts of > the code, and therefore a good introduction to the code base. The other > related task is to create a gc-like object for collections so that the > arguments to draw_collection don't have to change in every backend every > time a new feature is added. > >> This sounds like a good idea. In the pdf backend, GraphicsContextPdf >> already defines a _fillcolor attribute, and for example draw_path does >> >> def draw_path(self, gc, path, transform, rgbFace=None): >> self.check_gc(gc, rgbFace) >> # ... >> >> where check_gc just temporarily sets gc._fillcolor to the value of >> rgbFace and issues the pdf commands to change the graphics state to >> reflect gc. If rgbFace is absorbed into gc, at least the pdf backend >> should be easy to change accordingly, and should become less complex in >> the process. Currently the same alpha value (gc._alpha) is used for both >> strokes and painting operations, but this too should be easy to change >> if we decide to drop the _alpha attribute from GraphicsContext and use >> the fourth component of the stroke and fill colors for alpha. >> >> By the way, the PDF imaging model has much richer support for >> transparency than just specifying an alpha value for each operation; the >> Transparency chapter takes up 64 pages in the PDF spec¹. One thing that >> I imagine somebody just might want to have support for in matplotlib are >> transparency groups², i.e., blending some objects together and then >> blending the group with the background. But I wonder if that's possible >> in Agg - I guess we will want to stay pretty close to the greatest >> common denominator of Agg, SVG and PDF, and let people with special >> needs use other software such as Illustrator to postprocess the files. >> >> ¹ http://www.adobe.com/devnet/pdf/pdf_reference_archive.html >> ² http://itext.ugent.be/library/com/lowagie/examples/directcontent/colors/transparency.pdf >> >> >>> Maybe we need an MplColorSpec class. At present, functions and methods >>> typically accept colors and/or color arrays in a wide variety of forms. >>> This is good. My thought is that these should then be converted by >>> the accepting function or method to instances of the new class, and that >>> instances of the new class should be accepted as color inputs along with >>> all the old forms. >>> >> replacing the current hack, neat >> as it is, where a string representation of a decimal number means a >> grayscale color, a string beginning with # means a hexadecimal color, >> etc. The pyplot API should of course continue to work as it does now. >> >> > I really like Eric's suggestion here, as it fits in well with my desire > to verify arguments early and consistently. But I don't think we need > to throw out the convenient string forms of colors to achieve it. Those > are really handy, and fairly well known from HTML/CSS/SVG etc., and I > worry forcing the user to provide an instance of a particular class to > do something as common as setting a color would be annoying verbosity. > Of course, they should be free to do so if there's other maintenance > advantages as you suggested. The way I envision backwards compatibility, above some level in the API, a color-like kwarg would be handled something like this: def color_using_method(self, ..., carg=None,...) if carg is None: cspec = self.default_carg_cspec else: cspec = as_color_spec(carg) ... def as_color_spec(arg): "like asarray..." if isinstance(arg, ColorSpec): #No duck-typing here, please. return arg else: return ColorSpec(arg) The ColorSpec.__init__() would then have all our present magic for figuring out the various types of arguments, as well as explicit specifications based on kwargs as suggested by Jouni. An API level (e.g. backends) could be specified below which only a ColorSpec is accepted. Eric > > Mike > > |