Repository: matplotlib/matplotlib

Issue:
[Bug]: PathCollection using alpha ignores 'none' facecolors
### Bug summary

As title, using alpha parameter in PathCollection, either as a list or as a scalar value, causes 'none' facecolors to be ignored / filled

### Code for reproduction

```Python
import matplotlib.pyplot as plt
from matplotlib.collections import PathCollection
from matplotlib.path import Path

# https://matplotlib.org/stable/gallery/shapes_and_collections/compound_path.html

paths = [
    Path(
        [(1, 1), (1, 2), (2, 2), (2, 1), (0, 0)],
        [Path.MOVETO] + [Path.LINETO] * 3 + [Path.CLOSEPOLY]
    ),
    Path(
        [(4, 4), (5, 5), (5, 4), (0, 0)],
        [Path.MOVETO] + [Path.LINETO] * 2 + [Path.CLOSEPOLY]
    ),
]

fig, ax = plt.subplots()
collection = PathCollection(
    paths,
    edgecolors='black',
    facecolors=['blue', 'none'],
    alpha=[1, 1],
    # alpha=1
)
collection.set_transform(ax.transData)
ax.add_artist(collection)
ax.set(xlim=(0, 5), ylim=(0, 5))
plt.show()
```


### Actual outcome

![actual](https://github.com/matplotlib/matplotlib/assets/109676812/28dd3e05-c218-4274-b586-c86639cd8c60)


### Expected outcome

![expected](https://github.com/matplotlib/matplotlib/assets/109676812/390c44e5-0fe3-40b5-97f4-b57e1a7cc81e)


### Additional information

_No response_

### Operating system

Windows

### Matplotlib Version

3.8.3

### Matplotlib Backend

_No response_

### Python version

3.12

### Jupyter version

_No response_

### Installation

pip


Discussion:
When you set alpha = 1, `collections::set_alpha()` calls `_set_facecolor()`, which further calls `to_rgba_array()` in `collections.py`, Line 766: `self._facecolors = mcolors.to_rgba_array(c, self._alpha)` in which `c` is a list of strings: ["blue", "none"], and alpha is [1, 1].

In`colors::to_rgba_array()`, Line 509 - 510, the alpha param is used to replace the default alpha values in the rgba sequence:
```
if alpha is not None:
    rgba[:, 3] = alpha
```
resulting in the following change:
```
facecolor rgba before applying alpha 
[[0. 0. 1. 1.]       # this is blue
 [0. 0. 0. 0.]]      # this has no color
facecolor rgba after applying alpha 
[[0. 0. 1. 1.]       # this is still blue
 [0. 0. 0. 1.]]      # this becomes black
```

To produce the expected outcome, try the following code:

```
import matplotlib.pyplot as plt
from matplotlib.collections import PathCollection
from matplotlib.path import Path

# https://matplotlib.org/stable/gallery/shapes_and_collections/compound_path.html

paths = [
    Path(
        [(1, 1), (1, 2), (2, 2), (2, 1), (0, 0)],
        [Path.MOVETO] + [Path.LINETO] * 3 + [Path.CLOSEPOLY]
    ),
    Path(
        [(4, 4), (5, 5), (5, 4), (0, 0)],
        [Path.MOVETO] + [Path.LINETO] * 2 + [Path.CLOSEPOLY]
    ),
]

fig, ax = plt.subplots()
collection = PathCollection(
    paths,
    edgecolors='black',
    facecolors=['blue', (0,0,0,0)]    # represent the desired face color using a explicit rgba tuple
)
collection.set_transform(ax.transData)
ax.add_artist(collection)
ax.set(xlim=(0, 5), ylim=(0, 5))
plt.show()
```
![screenshot-matplotlib-issue-27839](https://github.com/matplotlib/matplotlib/assets/90495101/f3c11ff0-d480-4081-9579-a4cb1a666fe0)





Patch:
diff --git a/lib/matplotlib/colors.py b/lib/matplotlib/colors.py
index 9a493c928fdb..c0f86ea75bc2 100644
--- a/lib/matplotlib/colors.py
+++ b/lib/matplotlib/colors.py
@@ -506,6 +506,11 @@ def to_rgba_array(c, alpha=None):
 
     if alpha is not None:
         rgba[:, 3] = alpha
+        if isinstance(c, Sequence):
+            # ensure that an explicit alpha does not overwrite full transparency
+            # for "none"
+            none_mask = [cbook._str_equal(cc, "none") for cc in c]
+            rgba[:, 3][none_mask] = 0
     return rgba
 
 


Test patch:
diff --git a/lib/matplotlib/tests/test_colors.py b/lib/matplotlib/tests/test_colors.py
index 2d71fea83472..b8af0c69b5aa 100644
--- a/lib/matplotlib/tests/test_colors.py
+++ b/lib/matplotlib/tests/test_colors.py
@@ -19,6 +19,7 @@
 import matplotlib.scale as mscale
 from matplotlib.rcsetup import cycler
 from matplotlib.testing.decorators import image_comparison, check_figures_equal
+from matplotlib.colors import to_rgba_array
 
 
 @pytest.mark.parametrize('N, result', [
@@ -1660,3 +1661,13 @@ def test_set_cmap_mismatched_name():
     cmap_returned = plt.get_cmap("wrong-cmap")
     assert cmap_returned == cmap
     assert cmap_returned.name == "wrong-cmap"
+
+
+def test_to_rgba_array_none_color_with_alpha_param():
+    # effective alpha for color "none" must always be 0 to achieve a vanishing color
+    # even explicit alpha must be ignored
+    c = ["blue", "none"]
+    alpha = [1, 1]
+    assert_array_equal(
+        to_rgba_array(c, alpha), [[0., 0., 1., 1.], [0., 0., 0., 0.]]
+    )