Hide keyboard shortcuts

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`. 

5 

6All possible markers are defined here: 

7 

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============================== ====== ========================================= 

65 

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'. 

69 

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`. 

75 

76Integer numbers from ``0`` to ``11`` create lines and triangles. Those are 

77equally accessible via capitalized variables, like ``CARETDOWNBASE``. 

78Hence the following are equivalent:: 

79 

80 plt.plot([1, 2, 3], marker=11) 

81 plt.plot([1, 2, 3], marker=matplotlib.markers.CARETDOWNBASE) 

82 

83Examples showing the use of markers: 

84 

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` 

88 

89 

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""" 

129 

130from collections.abc import Sized 

131from numbers import Number 

132 

133import numpy as np 

134 

135from . import cbook, rcParams 

136from .path import Path 

137from .transforms import IdentityTransform, Affine2D 

138 

139# special-purpose marker identifiers: 

140(TICKLEFT, TICKRIGHT, TICKUP, TICKDOWN, 

141 CARETLEFT, CARETRIGHT, CARETUP, CARETDOWN, 

142 CARETLEFTBASE, CARETRIGHTBASE, CARETUPBASE, CARETDOWNBASE) = range(12) 

143 

144_empty_path = Path(np.empty((0, 2))) 

145 

146 

147class MarkerStyle: 

148 

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 } 

192 

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') 

198 

199 fillstyles = ('full', 'left', 'right', 'bottom', 'top', 'none') 

200 _half_fillstyles = ('left', 'right', 'bottom', 'top') 

201 

202 # TODO: Is this ever used as a non-constant? 

203 _point_size_reduction = 0.5 

204 

205 def __init__(self, marker=None, fillstyle=None): 

206 """ 

207 Attributes 

208 ---------- 

209 markers : list of known marks 

210 

211 fillstyles : list of known fillstyles 

212 

213 filled_markers : list of known filled markers. 

214 

215 Parameters 

216 ---------- 

217 marker : str or array-like, optional, default: None 

218 See the descriptions of possible markers in the module docstring. 

219 

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) 

226 

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() 

239 

240 def __bool__(self): 

241 return bool(len(self._path.vertices)) 

242 

243 def is_filled(self): 

244 return self._filled 

245 

246 def get_fillstyle(self): 

247 return self._fillstyle 

248 

249 def set_fillstyle(self, fillstyle): 

250 """ 

251 Sets fillstyle 

252 

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() 

262 

263 def get_joinstyle(self): 

264 return self._joinstyle 

265 

266 def get_capstyle(self): 

267 return self._capstyle 

268 

269 def get_marker(self): 

270 return self._marker 

271 

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)) 

294 

295 self._marker = marker 

296 self._recache() 

297 

298 def get_path(self): 

299 return self._path 

300 

301 def get_transform(self): 

302 return self._transform.frozen() 

303 

304 def get_alt_path(self): 

305 return self._alt_path 

306 

307 def get_alt_transform(self): 

308 return self._alt_transform.frozen() 

309 

310 def get_snap_threshold(self): 

311 return self._snap_threshold 

312 

313 def _set_nothing(self): 

314 self._filled = False 

315 

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 

322 

323 def _set_path_marker(self): 

324 self._set_custom_marker(self._marker) 

325 

326 def _set_vertices(self): 

327 verts = self._marker 

328 marker = Path(verts) 

329 self._set_custom_marker(marker) 

330 

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) 

351 

352 def _set_mathtext_path(self): 

353 """ 

354 Draws mathtext markers '$...$' using TextPath object. 

355 

356 Submitted by tcb 

357 """ 

358 from matplotlib.text import TextPath 

359 from matplotlib.font_manager import FontProperties 

360 

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 

367 

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 

378 

379 def _half_fill(self): 

380 return self.get_fillstyle() in self._half_fillstyles 

381 

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. 

398 

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.) 

402 

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 

416 

417 def _set_point(self): 

418 self._set_circle(reduction=self._point_size_reduction) 

419 

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]) 

437 

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() 

442 

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] 

450 

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] 

463 

464 self._alt_transform = self._transform 

465 

466 self._joinstyle = 'miter' 

467 

468 def _set_triangle_up(self): 

469 return self._set_triangle(0.0, 0) 

470 

471 def _set_triangle_down(self): 

472 return self._set_triangle(180.0, 2) 

473 

474 def _set_triangle_left(self): 

475 return self._set_triangle(90.0, 3) 

476 

477 def _set_triangle_right(self): 

478 return self._set_triangle(270.0, 1) 

479 

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. 

498 

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 

505 

506 self._joinstyle = 'miter' 

507 

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]]) 

518 

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. 

527 

528 self._transform.rotate_deg(rotate) 

529 self._alt_transform = self._transform 

530 

531 self._joinstyle = 'miter' 

532 

533 def _set_thin_diamond(self): 

534 self._set_diamond() 

535 self._transform.scale(0.6, 1.0) 

536 

537 def _set_pentagon(self): 

538 self._transform = Affine2D().scale(0.5) 

539 self._snap_threshold = 5.0 

540 

541 polypath = Path.unit_regular_polygon(5) 

542 fs = self.get_fillstyle() 

543 

544 if not self._half_fill(): 

545 self._path = polypath 

546 else: 

547 verts = polypath.vertices 

548 

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]]) 

554 

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 

566 

567 self._joinstyle = 'miter' 

568 

569 def _set_star(self): 

570 self._transform = Affine2D().scale(0.5) 

571 self._snap_threshold = 5.0 

572 

573 fs = self.get_fillstyle() 

574 polypath = Path.unit_regular_star(5, innerCircle=0.381966) 

575 

576 if not self._half_fill(): 

577 self._path = polypath 

578 else: 

579 verts = polypath.vertices 

580 

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]))) 

585 

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 

597 

598 self._joinstyle = 'bevel' 

599 

600 def _set_hexagon1(self): 

601 self._transform = Affine2D().scale(0.5) 

602 self._snap_threshold = None 

603 

604 fs = self.get_fillstyle() 

605 polypath = Path.unit_regular_polygon(6) 

606 

607 if not self._half_fill(): 

608 self._path = polypath 

609 else: 

610 verts = polypath.vertices 

611 

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), :]) 

618 

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 

627 

628 self._path = mpath 

629 self._alt_path = mpath_alt 

630 self._alt_transform = self._transform 

631 

632 self._joinstyle = 'miter' 

633 

634 def _set_hexagon2(self): 

635 self._transform = Affine2D().scale(0.5).rotate_deg(30) 

636 self._snap_threshold = None 

637 

638 fs = self.get_fillstyle() 

639 polypath = Path.unit_regular_polygon(6) 

640 

641 if not self._half_fill(): 

642 self._path = polypath 

643 else: 

644 verts = polypath.vertices 

645 

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]))) 

653 

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 

662 

663 self._path = mpath 

664 self._alt_path = mpath_alt 

665 self._alt_transform = self._transform 

666 

667 self._joinstyle = 'miter' 

668 

669 def _set_octagon(self): 

670 self._transform = Affine2D().scale(0.5) 

671 self._snap_threshold = 5.0 

672 

673 fs = self.get_fillstyle() 

674 polypath = Path.unit_regular_polygon(8) 

675 

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]]) 

683 

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. 

692 

693 self._transform.rotate_deg(rotate) 

694 self._path = self._alt_path = half 

695 self._alt_transform = self._transform.frozen().rotate_deg(180.0) 

696 

697 self._joinstyle = 'miter' 

698 

699 _line_marker_path = Path([[0.0, -1.0], [0.0, 1.0]]) 

700 

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 

706 

707 def _set_hline(self): 

708 self._set_vline() 

709 self._transform = self._transform.rotate_deg(90) 

710 

711 _tickhoriz_path = Path([[0.0, 0.0], [1.0, 0.0]]) 

712 

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 

718 

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 

724 

725 _tickvert_path = Path([[-0.0, 0.0], [-0.0, 1.0]]) 

726 

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 

732 

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 

738 

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]) 

745 

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 

751 

752 def _set_tri_up(self): 

753 self._set_tri_down() 

754 self._transform = self._transform.rotate_deg(180) 

755 

756 def _set_tri_left(self): 

757 self._set_tri_down() 

758 self._transform = self._transform.rotate_deg(270) 

759 

760 def _set_tri_right(self): 

761 self._set_tri_down() 

762 self._transform = self._transform.rotate_deg(90) 

763 

764 _caret_path = Path([[-1.0, 1.5], [0.0, 0.0], [1.0, 1.5]]) 

765 

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' 

772 

773 def _set_caretup(self): 

774 self._set_caretdown() 

775 self._transform = self._transform.rotate_deg(180) 

776 

777 def _set_caretleft(self): 

778 self._set_caretdown() 

779 self._transform = self._transform.rotate_deg(270) 

780 

781 def _set_caretright(self): 

782 self._set_caretdown() 

783 self._transform = self._transform.rotate_deg(90) 

784 

785 _caret_path_base = Path([[-1.0, 0.0], [0.0, -1.5], [1.0, 0]]) 

786 

787 def _set_caretdownbase(self): 

788 self._set_caretdown() 

789 self._path = self._caret_path_base 

790 

791 def _set_caretupbase(self): 

792 self._set_caretdownbase() 

793 self._transform = self._transform.rotate_deg(180) 

794 

795 def _set_caretleftbase(self): 

796 self._set_caretdownbase() 

797 self._transform = self._transform.rotate_deg(270) 

798 

799 def _set_caretrightbase(self): 

800 self._set_caretdownbase() 

801 self._transform = self._transform.rotate_deg(90) 

802 

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]) 

807 

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 

813 

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]) 

818 

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 

824 

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]) 

835 

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]) 

843 

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 

861 

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) 

867 

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]) 

877 

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]) 

884 

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 

902 

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)