patches have incorrect alpha values

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

path-alpha_test.py (1.26 KB)

path-alpha_test.png

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

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

path-alpha_test.py (1.23 KB)

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

------------------------------------------------------------------------

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

···

On Mon, Mar 23, 2009 at 3:24 PM, Jae-Joon Lee <lee.j.joon@…149…> 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.

John Hunter 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

···

On Mon, Mar 23, 2009 at 3:24 PM, Jae-Joon Lee <lee.j.joon@...149... > <mailto:lee.j.joon@…149…>> wrote:

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