Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/matplotlib/markers.py : 20%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1r"""
2This module contains functions to handle markers. Used by both the
3marker functionality of `~matplotlib.axes.Axes.plot` and
4`~matplotlib.axes.Axes.scatter`.
6All possible markers are defined here:
8============================== ====== =========================================
9marker symbol description
10============================== ====== =========================================
11``"."`` |m00| point
12``","`` |m01| pixel
13``"o"`` |m02| circle
14``"v"`` |m03| triangle_down
15``"^"`` |m04| triangle_up
16``"<"`` |m05| triangle_left
17``">"`` |m06| triangle_right
18``"1"`` |m07| tri_down
19``"2"`` |m08| tri_up
20``"3"`` |m09| tri_left
21``"4"`` |m10| tri_right
22``"8"`` |m11| octagon
23``"s"`` |m12| square
24``"p"`` |m13| pentagon
25``"P"`` |m23| plus (filled)
26``"*"`` |m14| star
27``"h"`` |m15| hexagon1
28``"H"`` |m16| hexagon2
29``"+"`` |m17| plus
30``"x"`` |m18| x
31``"X"`` |m24| x (filled)
32``"D"`` |m19| diamond
33``"d"`` |m20| thin_diamond
34``"|"`` |m21| vline
35``"_"`` |m22| hline
36``0`` (``TICKLEFT``) |m25| tickleft
37``1`` (``TICKRIGHT``) |m26| tickright
38``2`` (``TICKUP``) |m27| tickup
39``3`` (``TICKDOWN``) |m28| tickdown
40``4`` (``CARETLEFT``) |m29| caretleft
41``5`` (``CARETRIGHT``) |m30| caretright
42``6`` (``CARETUP``) |m31| caretup
43``7`` (``CARETDOWN``) |m32| caretdown
44``8`` (``CARETLEFTBASE``) |m33| caretleft (centered at base)
45``9`` (``CARETRIGHTBASE``) |m34| caretright (centered at base)
46``10`` (``CARETUPBASE``) |m35| caretup (centered at base)
47``11`` (``CARETDOWNBASE``) |m36| caretdown (centered at base)
48``"None"``, ``" "`` or ``""`` nothing
49``'$...$'`` |m37| Render the string using mathtext.
50 E.g ``"$f$"`` for marker showing the
51 letter ``f``.
52``verts`` A list of (x, y) pairs used for Path
53 vertices. The center of the marker is
54 located at (0, 0) and the size is
55 normalized, such that the created path
56 is encapsulated inside the unit cell.
57path A `~matplotlib.path.Path` instance.
58``(numsides, 0, angle)`` A regular polygon with ``numsides``
59 sides, rotated by ``angle``.
60``(numsides, 1, angle)`` A star-like symbol with ``numsides``
61 sides, rotated by ``angle``.
62``(numsides, 2, angle)`` An asterisk with ``numsides`` sides,
63 rotated by ``angle``.
64============================== ====== =========================================
66``None`` is the default which means 'nothing', however this table is
67referred to from other docs for the valid inputs from marker inputs and in
68those cases ``None`` still means 'default'.
70Note that special symbols can be defined via the
71:doc:`STIX math font </tutorials/text/mathtext>`,
72e.g. ``"$\u266B$"``. For an overview over the STIX font symbols refer to the
73`STIX font table <http://www.stixfonts.org/allGlyphs.html>`_.
74Also see the :doc:`/gallery/text_labels_and_annotations/stix_fonts_demo`.
76Integer numbers from ``0`` to ``11`` create lines and triangles. Those are
77equally accessible via capitalized variables, like ``CARETDOWNBASE``.
78Hence the following are equivalent::
80 plt.plot([1, 2, 3], marker=11)
81 plt.plot([1, 2, 3], marker=matplotlib.markers.CARETDOWNBASE)
83Examples showing the use of markers:
85* :doc:`/gallery/lines_bars_and_markers/marker_reference`
86* :doc:`/gallery/lines_bars_and_markers/marker_fillstyle_reference`
87* :doc:`/gallery/shapes_and_collections/marker_path`
90.. |m00| image:: /_static/markers/m00.png
91.. |m01| image:: /_static/markers/m01.png
92.. |m02| image:: /_static/markers/m02.png
93.. |m03| image:: /_static/markers/m03.png
94.. |m04| image:: /_static/markers/m04.png
95.. |m05| image:: /_static/markers/m05.png
96.. |m06| image:: /_static/markers/m06.png
97.. |m07| image:: /_static/markers/m07.png
98.. |m08| image:: /_static/markers/m08.png
99.. |m09| image:: /_static/markers/m09.png
100.. |m10| image:: /_static/markers/m10.png
101.. |m11| image:: /_static/markers/m11.png
102.. |m12| image:: /_static/markers/m12.png
103.. |m13| image:: /_static/markers/m13.png
104.. |m14| image:: /_static/markers/m14.png
105.. |m15| image:: /_static/markers/m15.png
106.. |m16| image:: /_static/markers/m16.png
107.. |m17| image:: /_static/markers/m17.png
108.. |m18| image:: /_static/markers/m18.png
109.. |m19| image:: /_static/markers/m19.png
110.. |m20| image:: /_static/markers/m20.png
111.. |m21| image:: /_static/markers/m21.png
112.. |m22| image:: /_static/markers/m22.png
113.. |m23| image:: /_static/markers/m23.png
114.. |m24| image:: /_static/markers/m24.png
115.. |m25| image:: /_static/markers/m25.png
116.. |m26| image:: /_static/markers/m26.png
117.. |m27| image:: /_static/markers/m27.png
118.. |m28| image:: /_static/markers/m28.png
119.. |m29| image:: /_static/markers/m29.png
120.. |m30| image:: /_static/markers/m30.png
121.. |m31| image:: /_static/markers/m31.png
122.. |m32| image:: /_static/markers/m32.png
123.. |m33| image:: /_static/markers/m33.png
124.. |m34| image:: /_static/markers/m34.png
125.. |m35| image:: /_static/markers/m35.png
126.. |m36| image:: /_static/markers/m36.png
127.. |m37| image:: /_static/markers/m37.png
128"""
130from collections.abc import Sized
131from numbers import Number
133import numpy as np
135from . import cbook, rcParams
136from .path import Path
137from .transforms import IdentityTransform, Affine2D
139# special-purpose marker identifiers:
140(TICKLEFT, TICKRIGHT, TICKUP, TICKDOWN,
141 CARETLEFT, CARETRIGHT, CARETUP, CARETDOWN,
142 CARETLEFTBASE, CARETRIGHTBASE, CARETUPBASE, CARETDOWNBASE) = range(12)
144_empty_path = Path(np.empty((0, 2)))
147class MarkerStyle:
149 markers = {
150 '.': 'point',
151 ',': 'pixel',
152 'o': 'circle',
153 'v': 'triangle_down',
154 '^': 'triangle_up',
155 '<': 'triangle_left',
156 '>': 'triangle_right',
157 '1': 'tri_down',
158 '2': 'tri_up',
159 '3': 'tri_left',
160 '4': 'tri_right',
161 '8': 'octagon',
162 's': 'square',
163 'p': 'pentagon',
164 '*': 'star',
165 'h': 'hexagon1',
166 'H': 'hexagon2',
167 '+': 'plus',
168 'x': 'x',
169 'D': 'diamond',
170 'd': 'thin_diamond',
171 '|': 'vline',
172 '_': 'hline',
173 'P': 'plus_filled',
174 'X': 'x_filled',
175 TICKLEFT: 'tickleft',
176 TICKRIGHT: 'tickright',
177 TICKUP: 'tickup',
178 TICKDOWN: 'tickdown',
179 CARETLEFT: 'caretleft',
180 CARETRIGHT: 'caretright',
181 CARETUP: 'caretup',
182 CARETDOWN: 'caretdown',
183 CARETLEFTBASE: 'caretleftbase',
184 CARETRIGHTBASE: 'caretrightbase',
185 CARETUPBASE: 'caretupbase',
186 CARETDOWNBASE: 'caretdownbase',
187 "None": 'nothing',
188 None: 'nothing',
189 ' ': 'nothing',
190 '': 'nothing'
191 }
193 # Just used for informational purposes. is_filled()
194 # is calculated in the _set_* functions.
195 filled_markers = (
196 'o', 'v', '^', '<', '>', '8', 's', 'p', '*', 'h', 'H', 'D', 'd',
197 'P', 'X')
199 fillstyles = ('full', 'left', 'right', 'bottom', 'top', 'none')
200 _half_fillstyles = ('left', 'right', 'bottom', 'top')
202 # TODO: Is this ever used as a non-constant?
203 _point_size_reduction = 0.5
205 def __init__(self, marker=None, fillstyle=None):
206 """
207 Attributes
208 ----------
209 markers : list of known marks
211 fillstyles : list of known fillstyles
213 filled_markers : list of known filled markers.
215 Parameters
216 ----------
217 marker : str or array-like, optional, default: None
218 See the descriptions of possible markers in the module docstring.
220 fillstyle : str, optional, default: 'full'
221 'full', 'left", 'right', 'bottom', 'top', 'none'
222 """
223 self._marker_function = None
224 self.set_fillstyle(fillstyle)
225 self.set_marker(marker)
227 def _recache(self):
228 if self._marker_function is None:
229 return
230 self._path = _empty_path
231 self._transform = IdentityTransform()
232 self._alt_path = None
233 self._alt_transform = None
234 self._snap_threshold = None
235 self._joinstyle = 'round'
236 self._capstyle = 'butt'
237 self._filled = True
238 self._marker_function()
240 def __bool__(self):
241 return bool(len(self._path.vertices))
243 def is_filled(self):
244 return self._filled
246 def get_fillstyle(self):
247 return self._fillstyle
249 def set_fillstyle(self, fillstyle):
250 """
251 Sets fillstyle
253 Parameters
254 ----------
255 fillstyle : string amongst known fillstyles
256 """
257 if fillstyle is None:
258 fillstyle = rcParams['markers.fillstyle']
259 cbook._check_in_list(self.fillstyles, fillstyle=fillstyle)
260 self._fillstyle = fillstyle
261 self._recache()
263 def get_joinstyle(self):
264 return self._joinstyle
266 def get_capstyle(self):
267 return self._capstyle
269 def get_marker(self):
270 return self._marker
272 def set_marker(self, marker):
273 if (isinstance(marker, np.ndarray) and marker.ndim == 2 and
274 marker.shape[1] == 2):
275 self._marker_function = self._set_vertices
276 elif isinstance(marker, str) and cbook.is_math_text(marker):
277 self._marker_function = self._set_mathtext_path
278 elif isinstance(marker, Path):
279 self._marker_function = self._set_path_marker
280 elif (isinstance(marker, Sized) and len(marker) in (2, 3) and
281 marker[1] in (0, 1, 2)):
282 self._marker_function = self._set_tuple_marker
283 elif (not isinstance(marker, (np.ndarray, list)) and
284 marker in self.markers):
285 self._marker_function = getattr(
286 self, '_set_' + self.markers[marker])
287 else:
288 try:
289 Path(marker)
290 self._marker_function = self._set_vertices
291 except ValueError:
292 raise ValueError('Unrecognized marker style {!r}'
293 .format(marker))
295 self._marker = marker
296 self._recache()
298 def get_path(self):
299 return self._path
301 def get_transform(self):
302 return self._transform.frozen()
304 def get_alt_path(self):
305 return self._alt_path
307 def get_alt_transform(self):
308 return self._alt_transform.frozen()
310 def get_snap_threshold(self):
311 return self._snap_threshold
313 def _set_nothing(self):
314 self._filled = False
316 def _set_custom_marker(self, path):
317 verts = path.vertices
318 rescale = max(np.max(np.abs(verts[:, 0])),
319 np.max(np.abs(verts[:, 1])))
320 self._transform = Affine2D().scale(0.5 / rescale)
321 self._path = path
323 def _set_path_marker(self):
324 self._set_custom_marker(self._marker)
326 def _set_vertices(self):
327 verts = self._marker
328 marker = Path(verts)
329 self._set_custom_marker(marker)
331 def _set_tuple_marker(self):
332 marker = self._marker
333 if len(marker) == 2:
334 numsides, rotation = marker[0], 0.0
335 elif len(marker) == 3:
336 numsides, rotation = marker[0], marker[2]
337 symstyle = marker[1]
338 if symstyle == 0:
339 self._path = Path.unit_regular_polygon(numsides)
340 self._joinstyle = 'miter'
341 elif symstyle == 1:
342 self._path = Path.unit_regular_star(numsides)
343 self._joinstyle = 'bevel'
344 elif symstyle == 2:
345 self._path = Path.unit_regular_asterisk(numsides)
346 self._filled = False
347 self._joinstyle = 'bevel'
348 else:
349 raise ValueError(f"Unexpected tuple marker: {marker}")
350 self._transform = Affine2D().scale(0.5).rotate_deg(rotation)
352 def _set_mathtext_path(self):
353 """
354 Draws mathtext markers '$...$' using TextPath object.
356 Submitted by tcb
357 """
358 from matplotlib.text import TextPath
359 from matplotlib.font_manager import FontProperties
361 # again, the properties could be initialised just once outside
362 # this function
363 text = TextPath(xy=(0, 0), s=self.get_marker(),
364 usetex=rcParams['text.usetex'])
365 if len(text.vertices) == 0:
366 return
368 xmin, ymin = text.vertices.min(axis=0)
369 xmax, ymax = text.vertices.max(axis=0)
370 width = xmax - xmin
371 height = ymax - ymin
372 max_dim = max(width, height)
373 self._transform = Affine2D() \
374 .translate(-xmin + 0.5 * -width, -ymin + 0.5 * -height) \
375 .scale(1.0 / max_dim)
376 self._path = text
377 self._snap = False
379 def _half_fill(self):
380 return self.get_fillstyle() in self._half_fillstyles
382 def _set_circle(self, reduction=1.0):
383 self._transform = Affine2D().scale(0.5 * reduction)
384 self._snap_threshold = np.inf
385 fs = self.get_fillstyle()
386 if not self._half_fill():
387 self._path = Path.unit_circle()
388 else:
389 # build a right-half circle
390 if fs == 'bottom':
391 rotate = 270.
392 elif fs == 'top':
393 rotate = 90.
394 elif fs == 'left':
395 rotate = 180.
396 else:
397 rotate = 0.
399 self._path = self._alt_path = Path.unit_circle_righthalf()
400 self._transform.rotate_deg(rotate)
401 self._alt_transform = self._transform.frozen().rotate_deg(180.)
403 def _set_pixel(self):
404 self._path = Path.unit_rectangle()
405 # Ideally, you'd want -0.5, -0.5 here, but then the snapping
406 # algorithm in the Agg backend will round this to a 2x2
407 # rectangle from (-1, -1) to (1, 1). By offsetting it
408 # slightly, we can force it to be (0, 0) to (1, 1), which both
409 # makes it only be a single pixel and places it correctly
410 # aligned to 1-width stroking (i.e. the ticks). This hack is
411 # the best of a number of bad alternatives, mainly because the
412 # backends are not aware of what marker is actually being used
413 # beyond just its path data.
414 self._transform = Affine2D().translate(-0.49999, -0.49999)
415 self._snap_threshold = None
417 def _set_point(self):
418 self._set_circle(reduction=self._point_size_reduction)
420 _triangle_path = Path(
421 [[0.0, 1.0], [-1.0, -1.0], [1.0, -1.0], [0.0, 1.0]],
422 [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])
423 # Going down halfway looks to small. Golden ratio is too far.
424 _triangle_path_u = Path(
425 [[0.0, 1.0], [-3 / 5., -1 / 5.], [3 / 5., -1 / 5.], [0.0, 1.0]],
426 [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])
427 _triangle_path_d = Path(
428 [[-3 / 5., -1 / 5.], [3 / 5., -1 / 5.], [1.0, -1.0], [-1.0, -1.0],
429 [-3 / 5., -1 / 5.]],
430 [Path.MOVETO, Path.LINETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])
431 _triangle_path_l = Path(
432 [[0.0, 1.0], [0.0, -1.0], [-1.0, -1.0], [0.0, 1.0]],
433 [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])
434 _triangle_path_r = Path(
435 [[0.0, 1.0], [0.0, -1.0], [1.0, -1.0], [0.0, 1.0]],
436 [Path.MOVETO, Path.LINETO, Path.LINETO, Path.CLOSEPOLY])
438 def _set_triangle(self, rot, skip):
439 self._transform = Affine2D().scale(0.5).rotate_deg(rot)
440 self._snap_threshold = 5.0
441 fs = self.get_fillstyle()
443 if not self._half_fill():
444 self._path = self._triangle_path
445 else:
446 mpaths = [self._triangle_path_u,
447 self._triangle_path_l,
448 self._triangle_path_d,
449 self._triangle_path_r]
451 if fs == 'top':
452 self._path = mpaths[(0 + skip) % 4]
453 self._alt_path = mpaths[(2 + skip) % 4]
454 elif fs == 'bottom':
455 self._path = mpaths[(2 + skip) % 4]
456 self._alt_path = mpaths[(0 + skip) % 4]
457 elif fs == 'left':
458 self._path = mpaths[(1 + skip) % 4]
459 self._alt_path = mpaths[(3 + skip) % 4]
460 else:
461 self._path = mpaths[(3 + skip) % 4]
462 self._alt_path = mpaths[(1 + skip) % 4]
464 self._alt_transform = self._transform
466 self._joinstyle = 'miter'
468 def _set_triangle_up(self):
469 return self._set_triangle(0.0, 0)
471 def _set_triangle_down(self):
472 return self._set_triangle(180.0, 2)
474 def _set_triangle_left(self):
475 return self._set_triangle(90.0, 3)
477 def _set_triangle_right(self):
478 return self._set_triangle(270.0, 1)
480 def _set_square(self):
481 self._transform = Affine2D().translate(-0.5, -0.5)
482 self._snap_threshold = 2.0
483 fs = self.get_fillstyle()
484 if not self._half_fill():
485 self._path = Path.unit_rectangle()
486 else:
487 # build a bottom filled square out of two rectangles, one
488 # filled. Use the rotation to support left, right, bottom
489 # or top
490 if fs == 'bottom':
491 rotate = 0.
492 elif fs == 'top':
493 rotate = 180.
494 elif fs == 'left':
495 rotate = 270.
496 else:
497 rotate = 90.
499 self._path = Path([[0.0, 0.0], [1.0, 0.0], [1.0, 0.5],
500 [0.0, 0.5], [0.0, 0.0]])
501 self._alt_path = Path([[0.0, 0.5], [1.0, 0.5], [1.0, 1.0],
502 [0.0, 1.0], [0.0, 0.5]])
503 self._transform.rotate_deg(rotate)
504 self._alt_transform = self._transform
506 self._joinstyle = 'miter'
508 def _set_diamond(self):
509 self._transform = Affine2D().translate(-0.5, -0.5).rotate_deg(45)
510 self._snap_threshold = 5.0
511 fs = self.get_fillstyle()
512 if not self._half_fill():
513 self._path = Path.unit_rectangle()
514 else:
515 self._path = Path([[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 0.0]])
516 self._alt_path = Path([[0.0, 0.0], [0.0, 1.0],
517 [1.0, 1.0], [0.0, 0.0]])
519 if fs == 'bottom':
520 rotate = 270.
521 elif fs == 'top':
522 rotate = 90.
523 elif fs == 'left':
524 rotate = 180.
525 else:
526 rotate = 0.
528 self._transform.rotate_deg(rotate)
529 self._alt_transform = self._transform
531 self._joinstyle = 'miter'
533 def _set_thin_diamond(self):
534 self._set_diamond()
535 self._transform.scale(0.6, 1.0)
537 def _set_pentagon(self):
538 self._transform = Affine2D().scale(0.5)
539 self._snap_threshold = 5.0
541 polypath = Path.unit_regular_polygon(5)
542 fs = self.get_fillstyle()
544 if not self._half_fill():
545 self._path = polypath
546 else:
547 verts = polypath.vertices
549 y = (1 + np.sqrt(5)) / 4.
550 top = Path([verts[0], verts[1], verts[4], verts[0]])
551 bottom = Path([verts[1], verts[2], verts[3], verts[4], verts[1]])
552 left = Path([verts[0], verts[1], verts[2], [0, -y], verts[0]])
553 right = Path([verts[0], verts[4], verts[3], [0, -y], verts[0]])
555 if fs == 'top':
556 mpath, mpath_alt = top, bottom
557 elif fs == 'bottom':
558 mpath, mpath_alt = bottom, top
559 elif fs == 'left':
560 mpath, mpath_alt = left, right
561 else:
562 mpath, mpath_alt = right, left
563 self._path = mpath
564 self._alt_path = mpath_alt
565 self._alt_transform = self._transform
567 self._joinstyle = 'miter'
569 def _set_star(self):
570 self._transform = Affine2D().scale(0.5)
571 self._snap_threshold = 5.0
573 fs = self.get_fillstyle()
574 polypath = Path.unit_regular_star(5, innerCircle=0.381966)
576 if not self._half_fill():
577 self._path = polypath
578 else:
579 verts = polypath.vertices
581 top = Path(np.vstack((verts[0:4, :], verts[7:10, :], verts[0])))
582 bottom = Path(np.vstack((verts[3:8, :], verts[3])))
583 left = Path(np.vstack((verts[0:6, :], verts[0])))
584 right = Path(np.vstack((verts[0], verts[5:10, :], verts[0])))
586 if fs == 'top':
587 mpath, mpath_alt = top, bottom
588 elif fs == 'bottom':
589 mpath, mpath_alt = bottom, top
590 elif fs == 'left':
591 mpath, mpath_alt = left, right
592 else:
593 mpath, mpath_alt = right, left
594 self._path = mpath
595 self._alt_path = mpath_alt
596 self._alt_transform = self._transform
598 self._joinstyle = 'bevel'
600 def _set_hexagon1(self):
601 self._transform = Affine2D().scale(0.5)
602 self._snap_threshold = None
604 fs = self.get_fillstyle()
605 polypath = Path.unit_regular_polygon(6)
607 if not self._half_fill():
608 self._path = polypath
609 else:
610 verts = polypath.vertices
612 # not drawing inside lines
613 x = np.abs(np.cos(5 * np.pi / 6.))
614 top = Path(np.vstack(([-x, 0], verts[(1, 0, 5), :], [x, 0])))
615 bottom = Path(np.vstack(([-x, 0], verts[2:5, :], [x, 0])))
616 left = Path(verts[(0, 1, 2, 3), :])
617 right = Path(verts[(0, 5, 4, 3), :])
619 if fs == 'top':
620 mpath, mpath_alt = top, bottom
621 elif fs == 'bottom':
622 mpath, mpath_alt = bottom, top
623 elif fs == 'left':
624 mpath, mpath_alt = left, right
625 else:
626 mpath, mpath_alt = right, left
628 self._path = mpath
629 self._alt_path = mpath_alt
630 self._alt_transform = self._transform
632 self._joinstyle = 'miter'
634 def _set_hexagon2(self):
635 self._transform = Affine2D().scale(0.5).rotate_deg(30)
636 self._snap_threshold = None
638 fs = self.get_fillstyle()
639 polypath = Path.unit_regular_polygon(6)
641 if not self._half_fill():
642 self._path = polypath
643 else:
644 verts = polypath.vertices
646 # not drawing inside lines
647 x, y = np.sqrt(3) / 4, 3 / 4.
648 top = Path(verts[(1, 0, 5, 4, 1), :])
649 bottom = Path(verts[(1, 2, 3, 4), :])
650 left = Path(np.vstack(([x, y], verts[(0, 1, 2), :],
651 [-x, -y], [x, y])))
652 right = Path(np.vstack(([x, y], verts[(5, 4, 3), :], [-x, -y])))
654 if fs == 'top':
655 mpath, mpath_alt = top, bottom
656 elif fs == 'bottom':
657 mpath, mpath_alt = bottom, top
658 elif fs == 'left':
659 mpath, mpath_alt = left, right
660 else:
661 mpath, mpath_alt = right, left
663 self._path = mpath
664 self._alt_path = mpath_alt
665 self._alt_transform = self._transform
667 self._joinstyle = 'miter'
669 def _set_octagon(self):
670 self._transform = Affine2D().scale(0.5)
671 self._snap_threshold = 5.0
673 fs = self.get_fillstyle()
674 polypath = Path.unit_regular_polygon(8)
676 if not self._half_fill():
677 self._transform.rotate_deg(22.5)
678 self._path = polypath
679 else:
680 x = np.sqrt(2.) / 4.
681 half = Path([[0, -1], [0, 1], [-x, 1], [-1, x],
682 [-1, -x], [-x, -1], [0, -1]])
684 if fs == 'bottom':
685 rotate = 90.
686 elif fs == 'top':
687 rotate = 270.
688 elif fs == 'right':
689 rotate = 180.
690 else:
691 rotate = 0.
693 self._transform.rotate_deg(rotate)
694 self._path = self._alt_path = half
695 self._alt_transform = self._transform.frozen().rotate_deg(180.0)
697 self._joinstyle = 'miter'
699 _line_marker_path = Path([[0.0, -1.0], [0.0, 1.0]])
701 def _set_vline(self):
702 self._transform = Affine2D().scale(0.5)
703 self._snap_threshold = 1.0
704 self._filled = False
705 self._path = self._line_marker_path
707 def _set_hline(self):
708 self._set_vline()
709 self._transform = self._transform.rotate_deg(90)
711 _tickhoriz_path = Path([[0.0, 0.0], [1.0, 0.0]])
713 def _set_tickleft(self):
714 self._transform = Affine2D().scale(-1.0, 1.0)
715 self._snap_threshold = 1.0
716 self._filled = False
717 self._path = self._tickhoriz_path
719 def _set_tickright(self):
720 self._transform = Affine2D().scale(1.0, 1.0)
721 self._snap_threshold = 1.0
722 self._filled = False
723 self._path = self._tickhoriz_path
725 _tickvert_path = Path([[-0.0, 0.0], [-0.0, 1.0]])
727 def _set_tickup(self):
728 self._transform = Affine2D().scale(1.0, 1.0)
729 self._snap_threshold = 1.0
730 self._filled = False
731 self._path = self._tickvert_path
733 def _set_tickdown(self):
734 self._transform = Affine2D().scale(1.0, -1.0)
735 self._snap_threshold = 1.0
736 self._filled = False
737 self._path = self._tickvert_path
739 _tri_path = Path([[0.0, 0.0], [0.0, -1.0],
740 [0.0, 0.0], [0.8, 0.5],
741 [0.0, 0.0], [-0.8, 0.5]],
742 [Path.MOVETO, Path.LINETO,
743 Path.MOVETO, Path.LINETO,
744 Path.MOVETO, Path.LINETO])
746 def _set_tri_down(self):
747 self._transform = Affine2D().scale(0.5)
748 self._snap_threshold = 5.0
749 self._filled = False
750 self._path = self._tri_path
752 def _set_tri_up(self):
753 self._set_tri_down()
754 self._transform = self._transform.rotate_deg(180)
756 def _set_tri_left(self):
757 self._set_tri_down()
758 self._transform = self._transform.rotate_deg(270)
760 def _set_tri_right(self):
761 self._set_tri_down()
762 self._transform = self._transform.rotate_deg(90)
764 _caret_path = Path([[-1.0, 1.5], [0.0, 0.0], [1.0, 1.5]])
766 def _set_caretdown(self):
767 self._transform = Affine2D().scale(0.5)
768 self._snap_threshold = 3.0
769 self._filled = False
770 self._path = self._caret_path
771 self._joinstyle = 'miter'
773 def _set_caretup(self):
774 self._set_caretdown()
775 self._transform = self._transform.rotate_deg(180)
777 def _set_caretleft(self):
778 self._set_caretdown()
779 self._transform = self._transform.rotate_deg(270)
781 def _set_caretright(self):
782 self._set_caretdown()
783 self._transform = self._transform.rotate_deg(90)
785 _caret_path_base = Path([[-1.0, 0.0], [0.0, -1.5], [1.0, 0]])
787 def _set_caretdownbase(self):
788 self._set_caretdown()
789 self._path = self._caret_path_base
791 def _set_caretupbase(self):
792 self._set_caretdownbase()
793 self._transform = self._transform.rotate_deg(180)
795 def _set_caretleftbase(self):
796 self._set_caretdownbase()
797 self._transform = self._transform.rotate_deg(270)
799 def _set_caretrightbase(self):
800 self._set_caretdownbase()
801 self._transform = self._transform.rotate_deg(90)
803 _plus_path = Path([[-1.0, 0.0], [1.0, 0.0],
804 [0.0, -1.0], [0.0, 1.0]],
805 [Path.MOVETO, Path.LINETO,
806 Path.MOVETO, Path.LINETO])
808 def _set_plus(self):
809 self._transform = Affine2D().scale(0.5)
810 self._snap_threshold = 1.0
811 self._filled = False
812 self._path = self._plus_path
814 _x_path = Path([[-1.0, -1.0], [1.0, 1.0],
815 [-1.0, 1.0], [1.0, -1.0]],
816 [Path.MOVETO, Path.LINETO,
817 Path.MOVETO, Path.LINETO])
819 def _set_x(self):
820 self._transform = Affine2D().scale(0.5)
821 self._snap_threshold = 3.0
822 self._filled = False
823 self._path = self._x_path
825 _plus_filled_path = Path([(1/3, 0), (2/3, 0), (2/3, 1/3),
826 (1, 1/3), (1, 2/3), (2/3, 2/3),
827 (2/3, 1), (1/3, 1), (1/3, 2/3),
828 (0, 2/3), (0, 1/3), (1/3, 1/3),
829 (1/3, 0)],
830 [Path.MOVETO, Path.LINETO, Path.LINETO,
831 Path.LINETO, Path.LINETO, Path.LINETO,
832 Path.LINETO, Path.LINETO, Path.LINETO,
833 Path.LINETO, Path.LINETO, Path.LINETO,
834 Path.CLOSEPOLY])
836 _plus_filled_path_t = Path([(1, 1/2), (1, 2/3), (2/3, 2/3),
837 (2/3, 1), (1/3, 1), (1/3, 2/3),
838 (0, 2/3), (0, 1/2), (1, 1/2)],
839 [Path.MOVETO, Path.LINETO, Path.LINETO,
840 Path.LINETO, Path.LINETO, Path.LINETO,
841 Path.LINETO, Path.LINETO,
842 Path.CLOSEPOLY])
844 def _set_plus_filled(self):
845 self._transform = Affine2D().translate(-0.5, -0.5)
846 self._snap_threshold = 5.0
847 self._joinstyle = 'miter'
848 fs = self.get_fillstyle()
849 if not self._half_fill():
850 self._path = self._plus_filled_path
851 else:
852 # Rotate top half path to support all partitions
853 if fs == 'top':
854 rotate, rotate_alt = 0, 180
855 elif fs == 'bottom':
856 rotate, rotate_alt = 180, 0
857 elif fs == 'left':
858 rotate, rotate_alt = 90, 270
859 else:
860 rotate, rotate_alt = 270, 90
862 self._path = self._plus_filled_path_t
863 self._alt_path = self._plus_filled_path_t
864 self._alt_transform = Affine2D().translate(-0.5, -0.5)
865 self._transform.rotate_deg(rotate)
866 self._alt_transform.rotate_deg(rotate_alt)
868 _x_filled_path = Path([(0.25, 0), (0.5, 0.25), (0.75, 0), (1, 0.25),
869 (0.75, 0.5), (1, 0.75), (0.75, 1), (0.5, 0.75),
870 (0.25, 1), (0, 0.75), (0.25, 0.5), (0, 0.25),
871 (0.25, 0)],
872 [Path.MOVETO, Path.LINETO, Path.LINETO,
873 Path.LINETO, Path.LINETO, Path.LINETO,
874 Path.LINETO, Path.LINETO, Path.LINETO,
875 Path.LINETO, Path.LINETO, Path.LINETO,
876 Path.CLOSEPOLY])
878 _x_filled_path_t = Path([(0.75, 0.5), (1, 0.75), (0.75, 1),
879 (0.5, 0.75), (0.25, 1), (0, 0.75),
880 (0.25, 0.5), (0.75, 0.5)],
881 [Path.MOVETO, Path.LINETO, Path.LINETO,
882 Path.LINETO, Path.LINETO, Path.LINETO,
883 Path.LINETO, Path.CLOSEPOLY])
885 def _set_x_filled(self):
886 self._transform = Affine2D().translate(-0.5, -0.5)
887 self._snap_threshold = 5.0
888 self._joinstyle = 'miter'
889 fs = self.get_fillstyle()
890 if not self._half_fill():
891 self._path = self._x_filled_path
892 else:
893 # Rotate top half path to support all partitions
894 if fs == 'top':
895 rotate, rotate_alt = 0, 180
896 elif fs == 'bottom':
897 rotate, rotate_alt = 180, 0
898 elif fs == 'left':
899 rotate, rotate_alt = 90, 270
900 else:
901 rotate, rotate_alt = 270, 90
903 self._path = self._x_filled_path_t
904 self._alt_path = self._x_filled_path_t
905 self._alt_transform = Affine2D().translate(-0.5, -0.5)
906 self._transform.rotate_deg(rotate)
907 self._alt_transform.rotate_deg(rotate_alt)