2828 from matplotlib .text import Text
2929
3030 from plotnine import theme
31+ from plotnine .guides import guides
3132 from plotnine .scales .scale import scale
3233 from plotnine .typing import Side
3334
@@ -50,7 +51,12 @@ class guide_colorbar(guide):
5051 """
5152
5253 display : Literal ["gradient" , "rectangles" , "raster" ] = "gradient"
53- """How to render the colorbar."""
54+ """
55+ How to render the colorbar
56+
57+ SVG figures will always use "rectangles" to create gradients. This has
58+ better support across applications that render svg images.
59+ """
5460
5561 alpha : Optional [float ] = None
5662 """
@@ -76,6 +82,12 @@ def __post_init__(self):
7682 if self .nbin is None :
7783 self .nbin = 300 # if self.display == "gradient" else 300
7884
85+ def setup (self , guides : guides ):
86+ super ().setup (guides )
87+ # See: add_segmented_colorbar
88+ if guides .plot ._build_objs .meta .get ("figure_format" ) == "svg" :
89+ self .display = "rectangles"
90+
7991 def train (self , scale : scale , aesthetic = None ):
8092 self .nbin = cast ("int" , self .nbin )
8193 self .title = cast ("str" , self .title )
@@ -173,6 +185,7 @@ def draw(self):
173185 nbars = len (self .bar )
174186 elements = self .elements
175187 raster = self .display == "raster"
188+ alpha = self .alpha
176189
177190 colors = self .bar ["color" ].tolist ()
178191 labels = self .key ["label" ].tolist ()
@@ -221,9 +234,9 @@ def draw(self):
221234
222235 # colorbar
223236 if self .display == "rectangles" :
224- add_segmented_colorbar (auxbox , colors , elements )
237+ add_segmented_colorbar (auxbox , colors , alpha , elements )
225238 else :
226- add_gradient_colorbar (auxbox , colors , elements , raster )
239+ add_gradient_colorbar (auxbox , colors , alpha , elements , raster )
227240
228241 # ticks
229242 visible = slice (
@@ -266,6 +279,7 @@ def draw(self):
266279def add_gradient_colorbar (
267280 auxbox : AuxTransformBox ,
268281 colors : Sequence [str ],
282+ alpha : float | None ,
269283 elements : GuideElementsColorbar ,
270284 raster : bool = False ,
271285):
@@ -321,6 +335,7 @@ def add_gradient_colorbar(
321335 shading = "gouraud" ,
322336 cmap = cmap ,
323337 array = Z .ravel (),
338+ alpha = alpha ,
324339 rasterized = raster ,
325340 )
326341 auxbox .add_artist (coll )
@@ -329,6 +344,7 @@ def add_gradient_colorbar(
329344def add_segmented_colorbar (
330345 auxbox : AuxTransformBox ,
331346 colors : Sequence [str ],
347+ alpha : float | None ,
332348 elements : GuideElementsColorbar ,
333349):
334350 """
@@ -337,6 +353,21 @@ def add_segmented_colorbar(
337353 from matplotlib .collections import PolyCollection
338354
339355 nbreak = len (colors )
356+ # Problem:
357+ # 1. Webbrowsers do not properly render SVG with QuadMesh
358+ # colorbars. Also when the QuadMesh is "rasterized",
359+ # the colorbar is misplaced within the SVG (and pdfs!).
360+ # So SVGs cannot use `add_gradient_colobar` at all.
361+ # 2. Webbrowsers do not properly render SVG with PolyCollection
362+ # colorbars when the adjacent rectangles that make up the
363+ # colorbar touch each other precisely. The "bars" appear to
364+ # be separated by lines.
365+ #
366+ # For a wayout, we overlap the bars. Overlapping creates artefacts
367+ # when alpha < 1, but having a gradient + alpha is rare. And, we can
368+ # minimise apparent artefacts by using a large overlap_factor.
369+ # A value of 2 gives the best results in the rare case should alpha < 1.
370+ overlap_factor = 2
340371 if elements .is_vertical :
341372 colorbar_height = elements .key_height
342373 colorbar_width = elements .key_width
@@ -347,6 +378,8 @@ def add_segmented_colorbar(
347378 for i in range (nbreak ):
348379 y1 = i * linewidth
349380 y2 = y1 + linewidth
381+ if i > 1 :
382+ y1 -= linewidth * overlap_factor
350383 verts .append (((x1 , y1 ), (x1 , y2 ), (x2 , y2 ), (x2 , y1 )))
351384 else :
352385 colorbar_width = elements .key_height
@@ -358,12 +391,15 @@ def add_segmented_colorbar(
358391 for i in range (nbreak ):
359392 x1 = i * linewidth
360393 x2 = x1 + linewidth
394+ if i > 1 :
395+ x1 -= linewidth * overlap_factor
361396 verts .append (((x1 , y1 ), (x1 , y2 ), (x2 , y2 ), (x2 , y1 )))
362397
363398 coll = PolyCollection (
364399 verts ,
365400 facecolors = colors ,
366401 linewidth = 0 ,
402+ alpha = alpha ,
367403 antialiased = False ,
368404 )
369405 auxbox .add_artist (coll )
0 commit comments