Skip to content

Commit 91becf3

Browse files
committed
Shorten/make more consistent the half-filled marker definitions.
- Use dict lookups for fillstyle. - When possible, prefer defining `path` and `alt_path` to the same path and just make `alt_transform` the 180°-rotated version of `transform`. (This is consistent with what's already being done for "circle" and "octagon".) Doing so requires redefining the half-X and half-plus marker paths to not require a (-.5, -.5) translation but that actually makes the path symmetries more apparent. - Don't let the miter joins protrude by manually cropping them. This is currently only implemented for diamond, but others should work "similarly". However, this reveals a bug(?) in Agg's handling of miter with close corners, which does not handle that case correctly. (pdf, ps, svg, and cairo all handle it fine).
1 parent 1eef019 commit 91becf3

File tree

1 file changed

+58
-142
lines changed

1 file changed

+58
-142
lines changed

lib/matplotlib/markers.py

Lines changed: 58 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -428,18 +428,9 @@ def _set_circle(self, reduction=1.0):
428428
if not self._half_fill():
429429
self._path = Path.unit_circle()
430430
else:
431-
# build a right-half circle
432-
if fs == 'bottom':
433-
rotate = 270.
434-
elif fs == 'top':
435-
rotate = 90.
436-
elif fs == 'left':
437-
rotate = 180.
438-
else:
439-
rotate = 0.
440-
441431
self._path = self._alt_path = Path.unit_circle_righthalf()
442-
self._transform.rotate_deg(rotate)
432+
self._transform.rotate_deg(
433+
{'right': 0, 'top': 90, 'left': 180, 'bottom': 270}[fs])
443434
self._alt_transform = self._transform.frozen().rotate_deg(180.)
444435

445436
def _set_pixel(self):
@@ -518,24 +509,12 @@ def _set_square(self):
518509
if not self._half_fill():
519510
self._path = Path.unit_rectangle()
520511
else:
521-
# build a bottom filled square out of two rectangles, one
522-
# filled. Use the rotation to support left, right, bottom
523-
# or top
524-
if fs == 'bottom':
525-
rotate = 0.
526-
elif fs == 'top':
527-
rotate = 180.
528-
elif fs == 'left':
529-
rotate = 270.
530-
else:
531-
rotate = 90.
532-
533-
self._path = Path([[0.0, 0.0], [1.0, 0.0], [1.0, 0.5],
534-
[0.0, 0.5], [0.0, 0.0]])
535-
self._alt_path = Path([[0.0, 0.5], [1.0, 0.5], [1.0, 1.0],
536-
[0.0, 1.0], [0.0, 0.5]])
537-
self._transform.rotate_deg(rotate)
538-
self._alt_transform = self._transform
512+
self._path = self._alt_path = Path( # Half-square.
513+
[[0.0, 0.0], [1.0, 0.0], [1.0, 0.5], [0.0, 0.5], [0.0, 0.0]],
514+
closed=True)
515+
self._transform.rotate_deg(
516+
{'bottom': 0, 'right': 90, 'top': 180, 'left': 270}[fs])
517+
self._alt_transform = self._transform.frozen().rotate_deg(180)
539518

540519
self._joinstyle = 'miter'
541520

@@ -546,23 +525,20 @@ def _set_diamond(self):
546525
if not self._half_fill():
547526
self._path = Path.unit_rectangle()
548527
else:
549-
self._path = Path([[0, 0], [1, 0], [1, 1], [0, 0]])
550-
self._alt_path = Path([[0, 0], [0, 1], [1, 1], [0, 0]])
551-
if fs == 'bottom':
552-
rotate = 270.
553-
elif fs == 'top':
554-
rotate = 90.
555-
elif fs == 'left':
556-
rotate = 180.
557-
else:
558-
rotate = 0.
559-
self._transform.rotate_deg(rotate)
560-
self._alt_transform = self._transform
528+
eps = 1e-3 # Don't let the miter joins protrude.
529+
self._path = self._alt_path = Path(
530+
[[eps, eps], [eps, 0], [1, 0], [1, 1-eps], [1-eps, 1-eps],
531+
[eps, eps]], closed=True)
532+
self._transform.rotate_deg(
533+
{'right': 0, 'top': 90, 'left': 180, 'bottom': 270}[fs])
534+
self._alt_transform = self._transform.frozen().rotate_deg(180)
561535
self._joinstyle = 'miter'
562536

563537
def _set_thin_diamond(self):
564538
self._set_diamond()
565539
self._transform.scale(0.6, 1.0)
540+
if self._alt_transform:
541+
self._alt_transform.scale(0.6, 1.0)
566542

567543
def _set_pentagon(self):
568544
self._transform = Affine2D().scale(0.5)
@@ -575,23 +551,15 @@ def _set_pentagon(self):
575551
self._path = polypath
576552
else:
577553
verts = polypath.vertices
578-
579554
y = (1 + np.sqrt(5)) / 4.
580555
top = Path([verts[0], verts[1], verts[4], verts[0]])
581556
bottom = Path([verts[1], verts[2], verts[3], verts[4], verts[1]])
582557
left = Path([verts[0], verts[1], verts[2], [0, -y], verts[0]])
583558
right = Path([verts[0], verts[4], verts[3], [0, -y], verts[0]])
584-
585-
if fs == 'top':
586-
mpath, mpath_alt = top, bottom
587-
elif fs == 'bottom':
588-
mpath, mpath_alt = bottom, top
589-
elif fs == 'left':
590-
mpath, mpath_alt = left, right
591-
else:
592-
mpath, mpath_alt = right, left
593-
self._path = mpath
594-
self._alt_path = mpath_alt
559+
self._path, self._alt_path = {
560+
'top': (top, bottom), 'bottom': (bottom, top),
561+
'left': (left, right), 'right': (right, left),
562+
}[fs]
595563
self._alt_transform = self._transform
596564

597565
self._joinstyle = 'miter'
@@ -607,22 +575,14 @@ def _set_star(self):
607575
self._path = polypath
608576
else:
609577
verts = polypath.vertices
610-
611578
top = Path(np.concatenate([verts[0:4], verts[7:10], verts[0:1]]))
612579
bottom = Path(np.concatenate([verts[3:8], verts[3:4]]))
613580
left = Path(np.concatenate([verts[0:6], verts[0:1]]))
614581
right = Path(np.concatenate([verts[0:1], verts[5:10], verts[0:1]]))
615-
616-
if fs == 'top':
617-
mpath, mpath_alt = top, bottom
618-
elif fs == 'bottom':
619-
mpath, mpath_alt = bottom, top
620-
elif fs == 'left':
621-
mpath, mpath_alt = left, right
622-
else:
623-
mpath, mpath_alt = right, left
624-
self._path = mpath
625-
self._alt_path = mpath_alt
582+
self._path, self._alt_path = {
583+
'top': (top, bottom), 'bottom': (bottom, top),
584+
'left': (left, right), 'right': (right, left),
585+
}[fs]
626586
self._alt_transform = self._transform
627587

628588
self._joinstyle = 'bevel'
@@ -638,25 +598,16 @@ def _set_hexagon1(self):
638598
self._path = polypath
639599
else:
640600
verts = polypath.vertices
641-
642601
# not drawing inside lines
643602
x = np.abs(np.cos(5 * np.pi / 6.))
644603
top = Path(np.concatenate([[(-x, 0)], verts[[1, 0, 5]], [(x, 0)]]))
645604
bottom = Path(np.concatenate([[(-x, 0)], verts[2:5], [(x, 0)]]))
646605
left = Path(verts[0:4])
647606
right = Path(verts[[0, 5, 4, 3]])
648-
649-
if fs == 'top':
650-
mpath, mpath_alt = top, bottom
651-
elif fs == 'bottom':
652-
mpath, mpath_alt = bottom, top
653-
elif fs == 'left':
654-
mpath, mpath_alt = left, right
655-
else:
656-
mpath, mpath_alt = right, left
657-
658-
self._path = mpath
659-
self._alt_path = mpath_alt
607+
self._path, self._alt_path = {
608+
'top': (top, bottom), 'bottom': (bottom, top),
609+
'left': (left, right), 'right': (right, left),
610+
}[fs]
660611
self._alt_transform = self._transform
661612

662613
self._joinstyle = 'miter'
@@ -672,7 +623,6 @@ def _set_hexagon2(self):
672623
self._path = polypath
673624
else:
674625
verts = polypath.vertices
675-
676626
# not drawing inside lines
677627
x, y = np.sqrt(3) / 4, 3 / 4.
678628
top = Path(verts[[1, 0, 5, 4, 1]])
@@ -681,18 +631,10 @@ def _set_hexagon2(self):
681631
[(x, y)], verts[:3], [(-x, -y), (x, y)]]))
682632
right = Path(np.concatenate([
683633
[(x, y)], verts[5:2:-1], [(-x, -y)]]))
684-
685-
if fs == 'top':
686-
mpath, mpath_alt = top, bottom
687-
elif fs == 'bottom':
688-
mpath, mpath_alt = bottom, top
689-
elif fs == 'left':
690-
mpath, mpath_alt = left, right
691-
else:
692-
mpath, mpath_alt = right, left
693-
694-
self._path = mpath
695-
self._alt_path = mpath_alt
634+
self._path, self._alt_path = {
635+
'top': (top, bottom), 'bottom': (bottom, top),
636+
'left': (left, right), 'right': (right, left),
637+
}[fs]
696638
self._alt_transform = self._transform
697639

698640
self._joinstyle = 'miter'
@@ -711,17 +653,8 @@ def _set_octagon(self):
711653
x = np.sqrt(2.) / 4.
712654
half = Path([[0, -1], [0, 1], [-x, 1], [-1, x],
713655
[-1, -x], [-x, -1], [0, -1]])
714-
715-
if fs == 'bottom':
716-
rotate = 90.
717-
elif fs == 'top':
718-
rotate = 270.
719-
elif fs == 'right':
720-
rotate = 180.
721-
else:
722-
rotate = 0.
723-
724-
self._transform.rotate_deg(rotate)
656+
self._transform.rotate_deg(
657+
{'left': 0, 'bottom': 90, 'right': 180, 'top': 270}[fs])
725658
self._path = self._alt_path = half
726659
self._alt_transform = self._transform.frozen().rotate_deg(180.0)
727660

@@ -854,65 +787,48 @@ def _set_x(self):
854787
self._path = self._x_path
855788

856789
_plus_filled_path = Path(
857-
[(1/3, 0), (2/3, 0), (2/3, 1/3), (1, 1/3), (1, 2/3), (2/3, 2/3),
858-
(2/3, 1), (1/3, 1), (1/3, 2/3), (0, 2/3), (0, 1/3), (1/3, 1/3),
859-
(1/3, 0)], closed=True)
790+
np.array([(-1, -3), (+1, -3), (+1, -1), (+3, -1), (+3, +1), (+1, +1),
791+
(+1, +3), (-1, +3), (-1, +1), (-3, +1), (-3, -1), (-1, -1),
792+
(0, 0)]) / 6, closed=True)
860793
_plus_filled_path_t = Path(
861-
[(1, 1/2), (1, 2/3), (2/3, 2/3), (2/3, 1), (1/3, 1), (1/3, 2/3),
862-
(0, 2/3), (0, 1/2), (1, 1/2)], closed=True)
794+
np.array([(+3, 0), (+3, +1), (+1, +1), (+1, +3),
795+
(-1, +3), (-1, +1), (-3, +1), (-3, 0),
796+
(0, 0)]) / 6, closed=True)
863797

864798
def _set_plus_filled(self):
865-
self._transform = Affine2D().translate(-0.5, -0.5)
799+
self._transform = Affine2D()
866800
self._snap_threshold = 5.0
867801
self._joinstyle = 'miter'
868802
fs = self.get_fillstyle()
869803
if not self._half_fill():
870804
self._path = self._plus_filled_path
871805
else:
872806
# Rotate top half path to support all partitions
873-
if fs == 'top':
874-
rotate, rotate_alt = 0, 180
875-
elif fs == 'bottom':
876-
rotate, rotate_alt = 180, 0
877-
elif fs == 'left':
878-
rotate, rotate_alt = 90, 270
879-
else:
880-
rotate, rotate_alt = 270, 90
881-
882-
self._path = self._plus_filled_path_t
883-
self._alt_path = self._plus_filled_path_t
884-
self._alt_transform = Affine2D().translate(-0.5, -0.5)
885-
self._transform.rotate_deg(rotate)
886-
self._alt_transform.rotate_deg(rotate_alt)
807+
self._path = self._alt_path = self._plus_filled_path_t
808+
self._transform.rotate_deg({
809+
'top': 0, 'left': 90, 'bottom': 180, 'right': 270}[fs])
810+
self._alt_transform = self._transform.frozen().rotate_deg(180)
887811

888812
_x_filled_path = Path(
889-
[(0.25, 0), (0.5, 0.25), (0.75, 0), (1, 0.25), (0.75, 0.5), (1, 0.75),
890-
(0.75, 1), (0.5, 0.75), (0.25, 1), (0, 0.75), (0.25, 0.5), (0, 0.25),
891-
(0.25, 0)], closed=True)
813+
np.array([(-1, -2), (0, -1), (+1, -2), (+2, -1), (+1, 0), (+2, +1),
814+
(+1, +2), (0, +1), (-1, +2), (-2, +1), (-1, 0), (-2, -1),
815+
(0, 0)]) / 4,
816+
closed=True)
892817
_x_filled_path_t = Path(
893-
[(0.75, 0.5), (1, 0.75), (0.75, 1), (0.5, 0.75), (0.25, 1), (0, 0.75),
894-
(0.25, 0.5), (0.75, 0.5)], closed=True)
818+
np.array([(+1, 0), (+2, +1), (+1, +2), (0, +1),
819+
(-1, +2), (-2, +1), (-1, 0), (0, 0)]) / 4,
820+
closed=True)
895821

896822
def _set_x_filled(self):
897-
self._transform = Affine2D().translate(-0.5, -0.5)
823+
self._transform = Affine2D()
898824
self._snap_threshold = 5.0
899825
self._joinstyle = 'miter'
900826
fs = self.get_fillstyle()
901827
if not self._half_fill():
902828
self._path = self._x_filled_path
903829
else:
904830
# Rotate top half path to support all partitions
905-
if fs == 'top':
906-
rotate, rotate_alt = 0, 180
907-
elif fs == 'bottom':
908-
rotate, rotate_alt = 180, 0
909-
elif fs == 'left':
910-
rotate, rotate_alt = 90, 270
911-
else:
912-
rotate, rotate_alt = 270, 90
913-
914-
self._path = self._x_filled_path_t
915-
self._alt_path = self._x_filled_path_t
916-
self._alt_transform = Affine2D().translate(-0.5, -0.5)
917-
self._transform.rotate_deg(rotate)
918-
self._alt_transform.rotate_deg(rotate_alt)
831+
self._path = self._alt_path = self._x_filled_path_t
832+
self._transform.rotate_deg({
833+
'top': 0, 'left': 90, 'bottom': 180, 'right': 270}[fs])
834+
self._alt_transform = self._transform.frozen().rotate_deg(180)

0 commit comments

Comments
 (0)