Coverage for /usr/lib/python3/dist-packages/matplotlib/markers.py: 39%

427 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1r""" 

2Functions to handle markers; used by the marker functionality of 

3`~matplotlib.axes.Axes.plot`, `~matplotlib.axes.Axes.scatter`, and 

4`~matplotlib.axes.Axes.errorbar`. 

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 ``"None"`` nothing 

49``" "`` or ``""`` nothing 

50``'$...$'`` |m37| Render the string using mathtext. 

51 E.g ``"$f$"`` for marker showing the 

52 letter ``f``. 

53``verts`` A list of (x, y) pairs used for Path 

54 vertices. The center of the marker is 

55 located at (0, 0) and the size is 

56 normalized, such that the created path 

57 is encapsulated inside the unit cell. 

58path A `~matplotlib.path.Path` instance. 

59``(numsides, 0, angle)`` A regular polygon with ``numsides`` 

60 sides, rotated by ``angle``. 

61``(numsides, 1, angle)`` A star-like symbol with ``numsides`` 

62 sides, rotated by ``angle``. 

63``(numsides, 2, angle)`` An asterisk with ``numsides`` sides, 

64 rotated by ``angle``. 

65============================== ====== ========================================= 

66 

67As a deprecated feature, ``None`` also means 'nothing' when directly 

68constructing a `.MarkerStyle`, but note that there are other contexts where 

69``marker=None`` instead means "the default marker" (e.g. :rc:`scatter.marker` 

70for `.Axes.scatter`). 

71 

72Note that special symbols can be defined via the 

73:doc:`STIX math font </tutorials/text/mathtext>`, 

74e.g. ``"$\u266B$"``. For an overview over the STIX font symbols refer to the 

75`STIX font table <http://www.stixfonts.org/allGlyphs.html>`_. 

76Also see the :doc:`/gallery/text_labels_and_annotations/stix_fonts_demo`. 

77 

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

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

80Hence the following are equivalent:: 

81 

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

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

84 

85Markers join and cap styles can be customized by creating a new instance of 

86MarkerStyle. 

87A MarkerStyle can also have a custom `~matplotlib.transforms.Transform` 

88allowing it to be arbitrarily rotated or offset. 

89 

90Examples showing the use of markers: 

91 

92* :doc:`/gallery/lines_bars_and_markers/marker_reference` 

93* :doc:`/gallery/lines_bars_and_markers/scatter_star_poly` 

94* :doc:`/gallery/lines_bars_and_markers/multivariate_marker_plot` 

95 

96.. |m00| image:: /_static/markers/m00.png 

97.. |m01| image:: /_static/markers/m01.png 

98.. |m02| image:: /_static/markers/m02.png 

99.. |m03| image:: /_static/markers/m03.png 

100.. |m04| image:: /_static/markers/m04.png 

101.. |m05| image:: /_static/markers/m05.png 

102.. |m06| image:: /_static/markers/m06.png 

103.. |m07| image:: /_static/markers/m07.png 

104.. |m08| image:: /_static/markers/m08.png 

105.. |m09| image:: /_static/markers/m09.png 

106.. |m10| image:: /_static/markers/m10.png 

107.. |m11| image:: /_static/markers/m11.png 

108.. |m12| image:: /_static/markers/m12.png 

109.. |m13| image:: /_static/markers/m13.png 

110.. |m14| image:: /_static/markers/m14.png 

111.. |m15| image:: /_static/markers/m15.png 

112.. |m16| image:: /_static/markers/m16.png 

113.. |m17| image:: /_static/markers/m17.png 

114.. |m18| image:: /_static/markers/m18.png 

115.. |m19| image:: /_static/markers/m19.png 

116.. |m20| image:: /_static/markers/m20.png 

117.. |m21| image:: /_static/markers/m21.png 

118.. |m22| image:: /_static/markers/m22.png 

119.. |m23| image:: /_static/markers/m23.png 

120.. |m24| image:: /_static/markers/m24.png 

121.. |m25| image:: /_static/markers/m25.png 

122.. |m26| image:: /_static/markers/m26.png 

123.. |m27| image:: /_static/markers/m27.png 

124.. |m28| image:: /_static/markers/m28.png 

125.. |m29| image:: /_static/markers/m29.png 

126.. |m30| image:: /_static/markers/m30.png 

127.. |m31| image:: /_static/markers/m31.png 

128.. |m32| image:: /_static/markers/m32.png 

129.. |m33| image:: /_static/markers/m33.png 

130.. |m34| image:: /_static/markers/m34.png 

131.. |m35| image:: /_static/markers/m35.png 

132.. |m36| image:: /_static/markers/m36.png 

133.. |m37| image:: /_static/markers/m37.png 

134""" 

135import copy 

136 

137from collections.abc import Sized 

138import inspect 

139 

140import numpy as np 

141 

142import matplotlib as mpl 

143from . import _api, cbook 

144from .path import Path 

145from .transforms import IdentityTransform, Affine2D 

146from ._enums import JoinStyle, CapStyle 

147 

148# special-purpose marker identifiers: 

149(TICKLEFT, TICKRIGHT, TICKUP, TICKDOWN, 

150 CARETLEFT, CARETRIGHT, CARETUP, CARETDOWN, 

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

152 

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

154 

155 

156class MarkerStyle: 

157 """ 

158 A class representing marker types. 

159 

160 Instances are immutable. If you need to change anything, create a new 

161 instance. 

162 

163 Attributes 

164 ---------- 

165 markers : list 

166 All known markers. 

167 filled_markers : list 

168 All known filled markers. This is a subset of *markers*. 

169 fillstyles : list 

170 The supported fillstyles. 

171 """ 

172 

173 markers = { 

174 '.': 'point', 

175 ',': 'pixel', 

176 'o': 'circle', 

177 'v': 'triangle_down', 

178 '^': 'triangle_up', 

179 '<': 'triangle_left', 

180 '>': 'triangle_right', 

181 '1': 'tri_down', 

182 '2': 'tri_up', 

183 '3': 'tri_left', 

184 '4': 'tri_right', 

185 '8': 'octagon', 

186 's': 'square', 

187 'p': 'pentagon', 

188 '*': 'star', 

189 'h': 'hexagon1', 

190 'H': 'hexagon2', 

191 '+': 'plus', 

192 'x': 'x', 

193 'D': 'diamond', 

194 'd': 'thin_diamond', 

195 '|': 'vline', 

196 '_': 'hline', 

197 'P': 'plus_filled', 

198 'X': 'x_filled', 

199 TICKLEFT: 'tickleft', 

200 TICKRIGHT: 'tickright', 

201 TICKUP: 'tickup', 

202 TICKDOWN: 'tickdown', 

203 CARETLEFT: 'caretleft', 

204 CARETRIGHT: 'caretright', 

205 CARETUP: 'caretup', 

206 CARETDOWN: 'caretdown', 

207 CARETLEFTBASE: 'caretleftbase', 

208 CARETRIGHTBASE: 'caretrightbase', 

209 CARETUPBASE: 'caretupbase', 

210 CARETDOWNBASE: 'caretdownbase', 

211 "None": 'nothing', 

212 "none": 'nothing', 

213 ' ': 'nothing', 

214 '': 'nothing' 

215 } 

216 

217 # Just used for informational purposes. is_filled() 

218 # is calculated in the _set_* functions. 

219 filled_markers = ( 

220 'o', 'v', '^', '<', '>', '8', 's', 'p', '*', 'h', 'H', 'D', 'd', 

221 'P', 'X') 

222 

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

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

225 

226 _unset = object() # For deprecation of MarkerStyle(<noargs>). 

227 

228 def __init__(self, marker=_unset, fillstyle=None, 

229 transform=None, capstyle=None, joinstyle=None): 

230 """ 

231 Parameters 

232 ---------- 

233 marker : str, array-like, Path, MarkerStyle, or None 

234 - Another instance of *MarkerStyle* copies the details of that 

235 ``marker``. 

236 - *None* means no marker. This is the deprecated default. 

237 - For other possible marker values, see the module docstring 

238 `matplotlib.markers`. 

239 

240 fillstyle : str, default: :rc:`markers.fillstyle` 

241 One of 'full', 'left', 'right', 'bottom', 'top', 'none'. 

242 

243 transform : transforms.Transform, default: None 

244 Transform that will be combined with the native transform of the 

245 marker. 

246 

247 capstyle : CapStyle, default: None 

248 Cap style that will override the default cap style of the marker. 

249 

250 joinstyle : JoinStyle, default: None 

251 Join style that will override the default join style of the marker. 

252 """ 

253 self._marker_function = None 

254 self._user_transform = transform 

255 self._user_capstyle = capstyle 

256 self._user_joinstyle = joinstyle 

257 self._set_fillstyle(fillstyle) 

258 # Remove _unset and signature rewriting after deprecation elapses. 

259 if marker is self._unset: 

260 marker = "" 

261 _api.warn_deprecated( 

262 "3.6", message="Calling MarkerStyle() with no parameters is " 

263 "deprecated since %(since)s; support will be removed " 

264 "%(removal)s. Use MarkerStyle('') to construct an empty " 

265 "MarkerStyle.") 

266 if marker is None: 

267 marker = "" 

268 _api.warn_deprecated( 

269 "3.6", message="MarkerStyle(None) is deprecated since " 

270 "%(since)s; support will be removed %(removal)s. Use " 

271 "MarkerStyle('') to construct an empty MarkerStyle.") 

272 self._set_marker(marker) 

273 

274 __init__.__signature__ = inspect.signature( # Only for deprecation period. 

275 lambda self, marker, fillstyle=None: None) 

276 

277 def _recache(self): 

278 if self._marker_function is None: 

279 return 

280 self._path = _empty_path 

281 self._transform = IdentityTransform() 

282 self._alt_path = None 

283 self._alt_transform = None 

284 self._snap_threshold = None 

285 self._joinstyle = JoinStyle.round 

286 self._capstyle = self._user_capstyle or CapStyle.butt 

287 # Initial guess: Assume the marker is filled unless the fillstyle is 

288 # set to 'none'. The marker function will override this for unfilled 

289 # markers. 

290 self._filled = self._fillstyle != 'none' 

291 self._marker_function() 

292 

293 def __bool__(self): 

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

295 

296 def is_filled(self): 

297 return self._filled 

298 

299 def get_fillstyle(self): 

300 return self._fillstyle 

301 

302 def _set_fillstyle(self, fillstyle): 

303 """ 

304 Set the fillstyle. 

305 

306 Parameters 

307 ---------- 

308 fillstyle : {'full', 'left', 'right', 'bottom', 'top', 'none'} 

309 The part of the marker surface that is colored with 

310 markerfacecolor. 

311 """ 

312 if fillstyle is None: 

313 fillstyle = mpl.rcParams['markers.fillstyle'] 

314 _api.check_in_list(self.fillstyles, fillstyle=fillstyle) 

315 self._fillstyle = fillstyle 

316 self._recache() 

317 

318 def get_joinstyle(self): 

319 return self._joinstyle.name 

320 

321 def get_capstyle(self): 

322 return self._capstyle.name 

323 

324 def get_marker(self): 

325 return self._marker 

326 

327 def _set_marker(self, marker): 

328 """ 

329 Set the marker. 

330 

331 Parameters 

332 ---------- 

333 marker : str, array-like, Path, MarkerStyle, or None, default: None 

334 - Another instance of *MarkerStyle* copies the details of that 

335 ``marker``. 

336 - *None* means no marker. 

337 - For other possible marker values see the module docstring 

338 `matplotlib.markers`. 

339 """ 

340 if (isinstance(marker, np.ndarray) and marker.ndim == 2 and 

341 marker.shape[1] == 2): 

342 self._marker_function = self._set_vertices 

343 elif isinstance(marker, str) and cbook.is_math_text(marker): 

344 self._marker_function = self._set_mathtext_path 

345 elif isinstance(marker, Path): 

346 self._marker_function = self._set_path_marker 

347 elif (isinstance(marker, Sized) and len(marker) in (2, 3) and 

348 marker[1] in (0, 1, 2)): 

349 self._marker_function = self._set_tuple_marker 

350 elif (not isinstance(marker, (np.ndarray, list)) and 

351 marker in self.markers): 

352 self._marker_function = getattr( 

353 self, '_set_' + self.markers[marker]) 

354 elif isinstance(marker, MarkerStyle): 

355 self.__dict__ = copy.deepcopy(marker.__dict__) 

356 

357 else: 

358 try: 

359 Path(marker) 

360 self._marker_function = self._set_vertices 

361 except ValueError as err: 

362 raise ValueError('Unrecognized marker style {!r}' 

363 .format(marker)) from err 

364 

365 if not isinstance(marker, MarkerStyle): 

366 self._marker = marker 

367 self._recache() 

368 

369 def get_path(self): 

370 """ 

371 Return a `.Path` for the primary part of the marker. 

372 

373 For unfilled markers this is the whole marker, for filled markers, 

374 this is the area to be drawn with *markerfacecolor*. 

375 """ 

376 return self._path 

377 

378 def get_transform(self): 

379 """ 

380 Return the transform to be applied to the `.Path` from 

381 `MarkerStyle.get_path()`. 

382 """ 

383 if self._user_transform is None: 

384 return self._transform.frozen() 

385 else: 

386 return (self._transform + self._user_transform).frozen() 

387 

388 def get_alt_path(self): 

389 """ 

390 Return a `.Path` for the alternate part of the marker. 

391 

392 For unfilled markers, this is *None*; for filled markers, this is the 

393 area to be drawn with *markerfacecoloralt*. 

394 """ 

395 return self._alt_path 

396 

397 def get_alt_transform(self): 

398 """ 

399 Return the transform to be applied to the `.Path` from 

400 `MarkerStyle.get_alt_path()`. 

401 """ 

402 if self._user_transform is None: 

403 return self._alt_transform.frozen() 

404 else: 

405 return (self._alt_transform + self._user_transform).frozen() 

406 

407 def get_snap_threshold(self): 

408 return self._snap_threshold 

409 

410 def get_user_transform(self): 

411 """Return user supplied part of marker transform.""" 

412 if self._user_transform is not None: 

413 return self._user_transform.frozen() 

414 

415 def transformed(self, transform: Affine2D): 

416 """ 

417 Return a new version of this marker with the transform applied. 

418 

419 Parameters 

420 ---------- 

421 transform : Affine2D, default: None 

422 Transform will be combined with current user supplied transform. 

423 """ 

424 new_marker = MarkerStyle(self) 

425 if new_marker._user_transform is not None: 

426 new_marker._user_transform += transform 

427 else: 

428 new_marker._user_transform = transform 

429 return new_marker 

430 

431 def rotated(self, *, deg=None, rad=None): 

432 """ 

433 Return a new version of this marker rotated by specified angle. 

434 

435 Parameters 

436 ---------- 

437 deg : float, default: None 

438 Rotation angle in degrees. 

439 

440 rad : float, default: None 

441 Rotation angle in radians. 

442 

443 .. note:: You must specify exactly one of deg or rad. 

444 """ 

445 if deg is None and rad is None: 

446 raise ValueError('One of deg or rad is required') 

447 if deg is not None and rad is not None: 

448 raise ValueError('Only one of deg and rad can be supplied') 

449 new_marker = MarkerStyle(self) 

450 if new_marker._user_transform is None: 

451 new_marker._user_transform = Affine2D() 

452 

453 if deg is not None: 

454 new_marker._user_transform.rotate_deg(deg) 

455 if rad is not None: 

456 new_marker._user_transform.rotate(rad) 

457 

458 return new_marker 

459 

460 def scaled(self, sx, sy=None): 

461 """ 

462 Return new marker scaled by specified scale factors. 

463 

464 If *sy* is None, the same scale is applied in both the *x*- and 

465 *y*-directions. 

466 

467 Parameters 

468 ---------- 

469 sx : float 

470 *X*-direction scaling factor. 

471 sy : float, default: None 

472 *Y*-direction scaling factor. 

473 """ 

474 if sy is None: 

475 sy = sx 

476 

477 new_marker = MarkerStyle(self) 

478 _transform = new_marker._user_transform or Affine2D() 

479 new_marker._user_transform = _transform.scale(sx, sy) 

480 return new_marker 

481 

482 def _set_nothing(self): 

483 self._filled = False 

484 

485 def _set_custom_marker(self, path): 

486 rescale = np.max(np.abs(path.vertices)) # max of x's and y's. 

487 self._transform = Affine2D().scale(0.5 / rescale) 

488 self._path = path 

489 

490 def _set_path_marker(self): 

491 self._set_custom_marker(self._marker) 

492 

493 def _set_vertices(self): 

494 self._set_custom_marker(Path(self._marker)) 

495 

496 def _set_tuple_marker(self): 

497 marker = self._marker 

498 if len(marker) == 2: 

499 numsides, rotation = marker[0], 0.0 

500 elif len(marker) == 3: 

501 numsides, rotation = marker[0], marker[2] 

502 symstyle = marker[1] 

503 if symstyle == 0: 

504 self._path = Path.unit_regular_polygon(numsides) 

505 self._joinstyle = self._user_joinstyle or JoinStyle.miter 

506 elif symstyle == 1: 

507 self._path = Path.unit_regular_star(numsides) 

508 self._joinstyle = self._user_joinstyle or JoinStyle.bevel 

509 elif symstyle == 2: 

510 self._path = Path.unit_regular_asterisk(numsides) 

511 self._filled = False 

512 self._joinstyle = self._user_joinstyle or JoinStyle.bevel 

513 else: 

514 raise ValueError(f"Unexpected tuple marker: {marker}") 

515 self._transform = Affine2D().scale(0.5).rotate_deg(rotation) 

516 

517 def _set_mathtext_path(self): 

518 """ 

519 Draw mathtext markers '$...$' using TextPath object. 

520 

521 Submitted by tcb 

522 """ 

523 from matplotlib.textpath import TextPath 

524 

525 # again, the properties could be initialised just once outside 

526 # this function 

527 text = TextPath(xy=(0, 0), s=self.get_marker(), 

528 usetex=mpl.rcParams['text.usetex']) 

529 if len(text.vertices) == 0: 

530 return 

531 

532 xmin, ymin = text.vertices.min(axis=0) 

533 xmax, ymax = text.vertices.max(axis=0) 

534 width = xmax - xmin 

535 height = ymax - ymin 

536 max_dim = max(width, height) 

537 self._transform = Affine2D() \ 

538 .translate(-xmin + 0.5 * -width, -ymin + 0.5 * -height) \ 

539 .scale(1.0 / max_dim) 

540 self._path = text 

541 self._snap = False 

542 

543 def _half_fill(self): 

544 return self.get_fillstyle() in self._half_fillstyles 

545 

546 def _set_circle(self, size=1.0): 

547 self._transform = Affine2D().scale(0.5 * size) 

548 self._snap_threshold = np.inf 

549 if not self._half_fill(): 

550 self._path = Path.unit_circle() 

551 else: 

552 self._path = self._alt_path = Path.unit_circle_righthalf() 

553 fs = self.get_fillstyle() 

554 self._transform.rotate_deg( 

555 {'right': 0, 'top': 90, 'left': 180, 'bottom': 270}[fs]) 

556 self._alt_transform = self._transform.frozen().rotate_deg(180.) 

557 

558 def _set_point(self): 

559 self._set_circle(size=0.5) 

560 

561 def _set_pixel(self): 

562 self._path = Path.unit_rectangle() 

563 # Ideally, you'd want -0.5, -0.5 here, but then the snapping 

564 # algorithm in the Agg backend will round this to a 2x2 

565 # rectangle from (-1, -1) to (1, 1). By offsetting it 

566 # slightly, we can force it to be (0, 0) to (1, 1), which both 

567 # makes it only be a single pixel and places it correctly 

568 # aligned to 1-width stroking (i.e. the ticks). This hack is 

569 # the best of a number of bad alternatives, mainly because the 

570 # backends are not aware of what marker is actually being used 

571 # beyond just its path data. 

572 self._transform = Affine2D().translate(-0.49999, -0.49999) 

573 self._snap_threshold = None 

574 

575 _triangle_path = Path._create_closed([[0, 1], [-1, -1], [1, -1]]) 

576 # Going down halfway looks to small. Golden ratio is too far. 

577 _triangle_path_u = Path._create_closed([[0, 1], [-3/5, -1/5], [3/5, -1/5]]) 

578 _triangle_path_d = Path._create_closed( 

579 [[-3/5, -1/5], [3/5, -1/5], [1, -1], [-1, -1]]) 

580 _triangle_path_l = Path._create_closed([[0, 1], [0, -1], [-1, -1]]) 

581 _triangle_path_r = Path._create_closed([[0, 1], [0, -1], [1, -1]]) 

582 

583 def _set_triangle(self, rot, skip): 

584 self._transform = Affine2D().scale(0.5).rotate_deg(rot) 

585 self._snap_threshold = 5.0 

586 

587 if not self._half_fill(): 

588 self._path = self._triangle_path 

589 else: 

590 mpaths = [self._triangle_path_u, 

591 self._triangle_path_l, 

592 self._triangle_path_d, 

593 self._triangle_path_r] 

594 

595 fs = self.get_fillstyle() 

596 if fs == 'top': 

597 self._path = mpaths[(0 + skip) % 4] 

598 self._alt_path = mpaths[(2 + skip) % 4] 

599 elif fs == 'bottom': 

600 self._path = mpaths[(2 + skip) % 4] 

601 self._alt_path = mpaths[(0 + skip) % 4] 

602 elif fs == 'left': 

603 self._path = mpaths[(1 + skip) % 4] 

604 self._alt_path = mpaths[(3 + skip) % 4] 

605 else: 

606 self._path = mpaths[(3 + skip) % 4] 

607 self._alt_path = mpaths[(1 + skip) % 4] 

608 

609 self._alt_transform = self._transform 

610 

611 self._joinstyle = self._user_joinstyle or JoinStyle.miter 

612 

613 def _set_triangle_up(self): 

614 return self._set_triangle(0.0, 0) 

615 

616 def _set_triangle_down(self): 

617 return self._set_triangle(180.0, 2) 

618 

619 def _set_triangle_left(self): 

620 return self._set_triangle(90.0, 3) 

621 

622 def _set_triangle_right(self): 

623 return self._set_triangle(270.0, 1) 

624 

625 def _set_square(self): 

626 self._transform = Affine2D().translate(-0.5, -0.5) 

627 self._snap_threshold = 2.0 

628 if not self._half_fill(): 

629 self._path = Path.unit_rectangle() 

630 else: 

631 # Build a bottom filled square out of two rectangles, one filled. 

632 self._path = Path([[0.0, 0.0], [1.0, 0.0], [1.0, 0.5], 

633 [0.0, 0.5], [0.0, 0.0]]) 

634 self._alt_path = Path([[0.0, 0.5], [1.0, 0.5], [1.0, 1.0], 

635 [0.0, 1.0], [0.0, 0.5]]) 

636 fs = self.get_fillstyle() 

637 rotate = {'bottom': 0, 'right': 90, 'top': 180, 'left': 270}[fs] 

638 self._transform.rotate_deg(rotate) 

639 self._alt_transform = self._transform 

640 

641 self._joinstyle = self._user_joinstyle or JoinStyle.miter 

642 

643 def _set_diamond(self): 

644 self._transform = Affine2D().translate(-0.5, -0.5).rotate_deg(45) 

645 self._snap_threshold = 5.0 

646 if not self._half_fill(): 

647 self._path = Path.unit_rectangle() 

648 else: 

649 self._path = Path([[0, 0], [1, 0], [1, 1], [0, 0]]) 

650 self._alt_path = Path([[0, 0], [0, 1], [1, 1], [0, 0]]) 

651 fs = self.get_fillstyle() 

652 rotate = {'right': 0, 'top': 90, 'left': 180, 'bottom': 270}[fs] 

653 self._transform.rotate_deg(rotate) 

654 self._alt_transform = self._transform 

655 self._joinstyle = self._user_joinstyle or JoinStyle.miter 

656 

657 def _set_thin_diamond(self): 

658 self._set_diamond() 

659 self._transform.scale(0.6, 1.0) 

660 

661 def _set_pentagon(self): 

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

663 self._snap_threshold = 5.0 

664 

665 polypath = Path.unit_regular_polygon(5) 

666 

667 if not self._half_fill(): 

668 self._path = polypath 

669 else: 

670 verts = polypath.vertices 

671 y = (1 + np.sqrt(5)) / 4. 

672 top = Path(verts[[0, 1, 4, 0]]) 

673 bottom = Path(verts[[1, 2, 3, 4, 1]]) 

674 left = Path([verts[0], verts[1], verts[2], [0, -y], verts[0]]) 

675 right = Path([verts[0], verts[4], verts[3], [0, -y], verts[0]]) 

676 self._path, self._alt_path = { 

677 'top': (top, bottom), 'bottom': (bottom, top), 

678 'left': (left, right), 'right': (right, left), 

679 }[self.get_fillstyle()] 

680 self._alt_transform = self._transform 

681 

682 self._joinstyle = self._user_joinstyle or JoinStyle.miter 

683 

684 def _set_star(self): 

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

686 self._snap_threshold = 5.0 

687 

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

689 

690 if not self._half_fill(): 

691 self._path = polypath 

692 else: 

693 verts = polypath.vertices 

694 top = Path(np.concatenate([verts[0:4], verts[7:10], verts[0:1]])) 

695 bottom = Path(np.concatenate([verts[3:8], verts[3:4]])) 

696 left = Path(np.concatenate([verts[0:6], verts[0:1]])) 

697 right = Path(np.concatenate([verts[0:1], verts[5:10], verts[0:1]])) 

698 self._path, self._alt_path = { 

699 'top': (top, bottom), 'bottom': (bottom, top), 

700 'left': (left, right), 'right': (right, left), 

701 }[self.get_fillstyle()] 

702 self._alt_transform = self._transform 

703 

704 self._joinstyle = self._user_joinstyle or JoinStyle.bevel 

705 

706 def _set_hexagon1(self): 

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

708 self._snap_threshold = None 

709 

710 polypath = Path.unit_regular_polygon(6) 

711 

712 if not self._half_fill(): 

713 self._path = polypath 

714 else: 

715 verts = polypath.vertices 

716 # not drawing inside lines 

717 x = np.abs(np.cos(5 * np.pi / 6.)) 

718 top = Path(np.concatenate([[(-x, 0)], verts[[1, 0, 5]], [(x, 0)]])) 

719 bottom = Path(np.concatenate([[(-x, 0)], verts[2:5], [(x, 0)]])) 

720 left = Path(verts[0:4]) 

721 right = Path(verts[[0, 5, 4, 3]]) 

722 self._path, self._alt_path = { 

723 'top': (top, bottom), 'bottom': (bottom, top), 

724 'left': (left, right), 'right': (right, left), 

725 }[self.get_fillstyle()] 

726 self._alt_transform = self._transform 

727 

728 self._joinstyle = self._user_joinstyle or JoinStyle.miter 

729 

730 def _set_hexagon2(self): 

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

732 self._snap_threshold = None 

733 

734 polypath = Path.unit_regular_polygon(6) 

735 

736 if not self._half_fill(): 

737 self._path = polypath 

738 else: 

739 verts = polypath.vertices 

740 # not drawing inside lines 

741 x, y = np.sqrt(3) / 4, 3 / 4. 

742 top = Path(verts[[1, 0, 5, 4, 1]]) 

743 bottom = Path(verts[1:5]) 

744 left = Path(np.concatenate([ 

745 [(x, y)], verts[:3], [(-x, -y), (x, y)]])) 

746 right = Path(np.concatenate([ 

747 [(x, y)], verts[5:2:-1], [(-x, -y)]])) 

748 self._path, self._alt_path = { 

749 'top': (top, bottom), 'bottom': (bottom, top), 

750 'left': (left, right), 'right': (right, left), 

751 }[self.get_fillstyle()] 

752 self._alt_transform = self._transform 

753 

754 self._joinstyle = self._user_joinstyle or JoinStyle.miter 

755 

756 def _set_octagon(self): 

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

758 self._snap_threshold = 5.0 

759 

760 polypath = Path.unit_regular_polygon(8) 

761 

762 if not self._half_fill(): 

763 self._transform.rotate_deg(22.5) 

764 self._path = polypath 

765 else: 

766 x = np.sqrt(2.) / 4. 

767 self._path = self._alt_path = Path( 

768 [[0, -1], [0, 1], [-x, 1], [-1, x], 

769 [-1, -x], [-x, -1], [0, -1]]) 

770 fs = self.get_fillstyle() 

771 self._transform.rotate_deg( 

772 {'left': 0, 'bottom': 90, 'right': 180, 'top': 270}[fs]) 

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

774 

775 self._joinstyle = self._user_joinstyle or JoinStyle.miter 

776 

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

778 

779 def _set_vline(self): 

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

781 self._snap_threshold = 1.0 

782 self._filled = False 

783 self._path = self._line_marker_path 

784 

785 def _set_hline(self): 

786 self._set_vline() 

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

788 

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

790 

791 def _set_tickleft(self): 

792 self._transform = Affine2D().scale(-1.0, 1.0) 

793 self._snap_threshold = 1.0 

794 self._filled = False 

795 self._path = self._tickhoriz_path 

796 

797 def _set_tickright(self): 

798 self._transform = Affine2D().scale(1.0, 1.0) 

799 self._snap_threshold = 1.0 

800 self._filled = False 

801 self._path = self._tickhoriz_path 

802 

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

804 

805 def _set_tickup(self): 

806 self._transform = Affine2D().scale(1.0, 1.0) 

807 self._snap_threshold = 1.0 

808 self._filled = False 

809 self._path = self._tickvert_path 

810 

811 def _set_tickdown(self): 

812 self._transform = Affine2D().scale(1.0, -1.0) 

813 self._snap_threshold = 1.0 

814 self._filled = False 

815 self._path = self._tickvert_path 

816 

817 _tri_path = Path([[0.0, 0.0], [0.0, -1.0], 

818 [0.0, 0.0], [0.8, 0.5], 

819 [0.0, 0.0], [-0.8, 0.5]], 

820 [Path.MOVETO, Path.LINETO, 

821 Path.MOVETO, Path.LINETO, 

822 Path.MOVETO, Path.LINETO]) 

823 

824 def _set_tri_down(self): 

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

826 self._snap_threshold = 5.0 

827 self._filled = False 

828 self._path = self._tri_path 

829 

830 def _set_tri_up(self): 

831 self._set_tri_down() 

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

833 

834 def _set_tri_left(self): 

835 self._set_tri_down() 

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

837 

838 def _set_tri_right(self): 

839 self._set_tri_down() 

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

841 

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

843 

844 def _set_caretdown(self): 

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

846 self._snap_threshold = 3.0 

847 self._filled = False 

848 self._path = self._caret_path 

849 self._joinstyle = self._user_joinstyle or JoinStyle.miter 

850 

851 def _set_caretup(self): 

852 self._set_caretdown() 

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

854 

855 def _set_caretleft(self): 

856 self._set_caretdown() 

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

858 

859 def _set_caretright(self): 

860 self._set_caretdown() 

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

862 

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

864 

865 def _set_caretdownbase(self): 

866 self._set_caretdown() 

867 self._path = self._caret_path_base 

868 

869 def _set_caretupbase(self): 

870 self._set_caretdownbase() 

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

872 

873 def _set_caretleftbase(self): 

874 self._set_caretdownbase() 

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

876 

877 def _set_caretrightbase(self): 

878 self._set_caretdownbase() 

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

880 

881 _plus_path = Path([[-1.0, 0.0], [1.0, 0.0], 

882 [0.0, -1.0], [0.0, 1.0]], 

883 [Path.MOVETO, Path.LINETO, 

884 Path.MOVETO, Path.LINETO]) 

885 

886 def _set_plus(self): 

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

888 self._snap_threshold = 1.0 

889 self._filled = False 

890 self._path = self._plus_path 

891 

892 _x_path = Path([[-1.0, -1.0], [1.0, 1.0], 

893 [-1.0, 1.0], [1.0, -1.0]], 

894 [Path.MOVETO, Path.LINETO, 

895 Path.MOVETO, Path.LINETO]) 

896 

897 def _set_x(self): 

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

899 self._snap_threshold = 3.0 

900 self._filled = False 

901 self._path = self._x_path 

902 

903 _plus_filled_path = Path._create_closed(np.array([ 

904 (-1, -3), (+1, -3), (+1, -1), (+3, -1), (+3, +1), (+1, +1), 

905 (+1, +3), (-1, +3), (-1, +1), (-3, +1), (-3, -1), (-1, -1)]) / 6) 

906 _plus_filled_path_t = Path._create_closed(np.array([ 

907 (+3, 0), (+3, +1), (+1, +1), (+1, +3), 

908 (-1, +3), (-1, +1), (-3, +1), (-3, 0)]) / 6) 

909 

910 def _set_plus_filled(self): 

911 self._transform = Affine2D() 

912 self._snap_threshold = 5.0 

913 self._joinstyle = self._user_joinstyle or JoinStyle.miter 

914 if not self._half_fill(): 

915 self._path = self._plus_filled_path 

916 else: 

917 # Rotate top half path to support all partitions 

918 self._path = self._alt_path = self._plus_filled_path_t 

919 fs = self.get_fillstyle() 

920 self._transform.rotate_deg( 

921 {'top': 0, 'left': 90, 'bottom': 180, 'right': 270}[fs]) 

922 self._alt_transform = self._transform.frozen().rotate_deg(180) 

923 

924 _x_filled_path = Path._create_closed(np.array([ 

925 (-1, -2), (0, -1), (+1, -2), (+2, -1), (+1, 0), (+2, +1), 

926 (+1, +2), (0, +1), (-1, +2), (-2, +1), (-1, 0), (-2, -1)]) / 4) 

927 _x_filled_path_t = Path._create_closed(np.array([ 

928 (+1, 0), (+2, +1), (+1, +2), (0, +1), 

929 (-1, +2), (-2, +1), (-1, 0)]) / 4) 

930 

931 def _set_x_filled(self): 

932 self._transform = Affine2D() 

933 self._snap_threshold = 5.0 

934 self._joinstyle = self._user_joinstyle or JoinStyle.miter 

935 if not self._half_fill(): 

936 self._path = self._x_filled_path 

937 else: 

938 # Rotate top half path to support all partitions 

939 self._path = self._alt_path = self._x_filled_path_t 

940 fs = self.get_fillstyle() 

941 self._transform.rotate_deg( 

942 {'top': 0, 'left': 90, 'bottom': 180, 'right': 270}[fs]) 

943 self._alt_transform = self._transform.frozen().rotate_deg(180)