|
3 | 3 | """
|
4 | 4 |
|
5 | 5 | fromenumimportEnum
|
6 |
| -frommatplotlibimportcbook |
7 | 6 |
|
| 7 | +from .importcbook |
8 | 8 |
|
9 | 9 | def_deprecate_case_insensitive_join_cap(s):
|
10 | 10 | s_low=s.lower()
|
@@ -163,3 +163,236 @@ def demo():
|
163 | 163 |
|
164 | 164 | ax.set_ylim(-.5,1.5)
|
165 | 165 | ax.set_axis_off()
|
| 166 | + |
| 167 | + |
| 168 | +def_get_dash_pattern(style): |
| 169 | +"""Convert linestyle to dash pattern.""" |
| 170 | +# import must be local for validator code to live here |
| 171 | +from .importrcParams |
| 172 | +# un-dashed styles |
| 173 | +ifstylein ['solid','None']: |
| 174 | +offset=0 |
| 175 | +dashes=None |
| 176 | +# dashed styles |
| 177 | +elifstylein ['dashed','dashdot','dotted']: |
| 178 | +offset=0 |
| 179 | +dashes=tuple(rcParams['lines.{}_pattern'.format(style)]) |
| 180 | +returnoffset,dashes |
| 181 | + |
| 182 | + |
| 183 | +classLineStyle(Enum): |
| 184 | +""" |
| 185 | + Describe if the line is solid or dashed, and the dash pattern, if any. |
| 186 | +
|
| 187 | + All lines in Matplotlib are considered either solid or "dashed". Some |
| 188 | + common dashing patterns are built-in, and are sufficient for a majority of |
| 189 | + uses: |
| 190 | +
|
| 191 | + =============================== ================= |
| 192 | + Linestyle Description |
| 193 | + =============================== ================= |
| 194 | + ``'-'`` or ``'solid'`` solid line |
| 195 | + ``'--'`` or ``'dashed'`` dashed line |
| 196 | + ``'-.'`` or ``'dashdot'`` dash-dotted line |
| 197 | + ``':'`` or ``'dotted'`` dotted line |
| 198 | + ``'None'`` or ``' '`` or ``''`` draw nothing |
| 199 | + =============================== ================= |
| 200 | +
|
| 201 | + However, for more fine-grained control, one can directly specify the |
| 202 | + dashing pattern by specifying:: |
| 203 | +
|
| 204 | + (offset, onoffseq) |
| 205 | +
|
| 206 | + where ``onoffseq`` is an even length tuple specifying the lengths of each |
| 207 | + subsequent dash and space, and ``offset`` controls at which point in this |
| 208 | + pattern the start of the line will begin (to allow you to e.g. prevent |
| 209 | + corners from happening to land in between dashes). |
| 210 | +
|
| 211 | + For example, (5, 2, 1, 2) describes a sequence of 5 point and 1 point |
| 212 | + dashes separated by 2 point spaces. |
| 213 | +
|
| 214 | + Setting ``onoffseq`` to ``None`` results in a solid *LineStyle*. |
| 215 | +
|
| 216 | + The default dashing patterns described in the table above are themselves |
| 217 | + all described in this notation, and can therefore be customized by editing |
| 218 | + the appropriate ``lines.*_pattern`` *rc* parameter, as described in |
| 219 | + :doc:`/tutorials/introductory/customizing`. |
| 220 | +
|
| 221 | + .. plot:: |
| 222 | + :alt: Demo of possible LineStyle's. |
| 223 | +
|
| 224 | + from matplotlib._types import LineStyle |
| 225 | + LineStyle.demo() |
| 226 | +
|
| 227 | + .. note:: |
| 228 | +
|
| 229 | + In addition to directly taking a ``linestyle`` argument, |
| 230 | + `~.lines.Line2D` exposes a ``~.lines.Line2D.set_dashes`` method that |
| 231 | + can be used to create a new *LineStyle* by providing just the |
| 232 | + ``onoffseq``, but does not let you customize the offset. This method is |
| 233 | + called when using the keyword *dashes* to the cycler , as shown in |
| 234 | + :doc:`property_cycle </tutorials/intermediate/color_cycle>`. |
| 235 | + """ |
| 236 | +solid='-' |
| 237 | +dashed='--' |
| 238 | +dotted=':' |
| 239 | +dashdot='-.' |
| 240 | +none='None' |
| 241 | +_custom='custom' |
| 242 | + |
| 243 | +_deprecated_lineStyles= {# hidden names deprecated |
| 244 | +'-':'_draw_solid', |
| 245 | +'--':'_draw_dashed', |
| 246 | +'-.':'_draw_dash_dot', |
| 247 | +':':'_draw_dotted', |
| 248 | +'None':'_draw_nothing', |
| 249 | +' ':'_draw_nothing', |
| 250 | +'':'_draw_nothing', |
| 251 | + } |
| 252 | + |
| 253 | +#: Maps short codes for line style to their full name used by backends. |
| 254 | +ls_mapper= {'-':'solid','--':'dashed','-.':'dashdot',':':'dotted'} |
| 255 | + |
| 256 | +def__init__(self,ls,scale=1): |
| 257 | +""" |
| 258 | + Parameters |
| 259 | + ---------- |
| 260 | + ls : str or dash tuple |
| 261 | + A description of the dashing pattern of the line. Allowed string |
| 262 | + inputs are {'-', 'solid', '--', 'dashed', '-.', 'dashdot', ':', |
| 263 | + 'dotted', '', ' ', 'None', 'none'}. Alternatively, the dash tuple |
| 264 | + (``offset``, ``onoffseq``) can be specified directly in points. |
| 265 | + scale : float |
| 266 | + Uniformly scale the internal dash sequence length by a constant |
| 267 | + factor. |
| 268 | + """ |
| 269 | + |
| 270 | +ifisinstance(ls,str): |
| 271 | +iflsin [' ','','none']: |
| 272 | +ls='None' |
| 273 | +iflsinls_mapper: |
| 274 | +ls=ls_mapper[ls] |
| 275 | +Enum.__init__(self) |
| 276 | +offset,onoffseq=_get_dash_pattern(ls) |
| 277 | +else: |
| 278 | +try: |
| 279 | +offset,onoffseq=ls |
| 280 | +exceptValueError:# not enough/too many values to unpack |
| 281 | +raiseValueError('LineStyle should be a string or a 2-tuple, ' |
| 282 | +'instead received: '+str(style)) |
| 283 | +ifoffsetisNone: |
| 284 | +cbook.warn_deprecated( |
| 285 | +"3.3",message="Passing the dash offset as None is deprecated " |
| 286 | +"since %(since)s and support for it will be removed " |
| 287 | +"%(removal)s; pass it as zero instead.") |
| 288 | +offset=0 |
| 289 | + |
| 290 | +ifonoffseqisnotNone: |
| 291 | +# normalize offset to be positive and shorter than the dash cycle |
| 292 | +dsum=sum(onoffseq) |
| 293 | +ifdsum: |
| 294 | +offset%=dsum |
| 295 | +iflen(onoffseq)%2!=0: |
| 296 | +raiseValueError('LineStyle onoffseq must be of even length.') |
| 297 | +ifnotall(isinstance(elem,Number)foreleminonoffseq): |
| 298 | +raiseValueError('LineStyle onoffseq must be list of floats.') |
| 299 | +self._us_offset=offset |
| 300 | +self._us_onoffseq=dashes |
| 301 | +self.scale(scale) |
| 302 | + |
| 303 | +@property |
| 304 | +defscale(self): |
| 305 | +returnself._scale |
| 306 | + |
| 307 | +@scale.setter |
| 308 | +defscale(self,s): |
| 309 | +ifs<0: |
| 310 | +raiseValueError('LineStyle cannot be scaled by a negative value.') |
| 311 | +self.offset=self._us_offset*s |
| 312 | +self.onoffseq= ( |
| 313 | + [x*sifxisnotNoneelseNoneforxinself._us_onoffseq] |
| 314 | +ifself._us_onoffseqisnotNoneelseNone |
| 315 | + ) |
| 316 | + |
| 317 | +@staticmethod |
| 318 | +deffrom_dashes(seq): |
| 319 | +""" |
| 320 | + Create a `.LineStyle` from a dash sequence (i.e. the ``onoffseq``). |
| 321 | +
|
| 322 | + The dash sequence is a sequence of floats of even length describing |
| 323 | + the length of dashes and spaces in points. |
| 324 | +
|
| 325 | + Parameters |
| 326 | + ---------- |
| 327 | + seq : sequence of floats (on/off ink in points) or (None, None) |
| 328 | + If *seq* is empty or ``(None, None)``, the `.LineStyle` will be |
| 329 | + solid. |
| 330 | + """ |
| 331 | +ifseq== (None,None)orlen(seq)==0: |
| 332 | +returnLineStyle('-') |
| 333 | +else: |
| 334 | +returnLineStyle((0,seq)) |
| 335 | + |
| 336 | +@staticmethod |
| 337 | +defdemo(): |
| 338 | +importnumpyasnp |
| 339 | +importmatplotlib.pyplotasplt |
| 340 | + |
| 341 | +linestyle_str= [ |
| 342 | + ('solid','solid'),# Same as (0, ()) or '-' |
| 343 | + ('dotted','dotted'),# Same as (0, (1, 1)) or '.' |
| 344 | + ('dashed','dashed'),# Same as '--' |
| 345 | + ('dashdot','dashdot')]# Same as '-.' |
| 346 | + |
| 347 | +linestyle_tuple= [ |
| 348 | + ('loosely dotted', (0, (1,10))), |
| 349 | + ('dotted', (0, (1,1))), |
| 350 | + ('densely dotted', (0, (1,1))), |
| 351 | + |
| 352 | + ('loosely dashed', (0, (5,10))), |
| 353 | + ('dashed', (0, (5,5))), |
| 354 | + ('densely dashed', (0, (5,1))), |
| 355 | + |
| 356 | + ('loosely dashdotted', (0, (3,10,1,10))), |
| 357 | + ('dashdotted', (0, (3,5,1,5))), |
| 358 | + ('densely dashdotted', (0, (3,1,1,1))), |
| 359 | + |
| 360 | + ('dashdotdotted', (0, (3,5,1,5,1,5))), |
| 361 | + ('loosely dashdotdotted', (0, (3,10,1,10,1,10))), |
| 362 | + ('densely dashdotdotted', (0, (3,1,1,1,1,1)))] |
| 363 | + |
| 364 | + |
| 365 | +defplot_linestyles(ax,linestyles,title): |
| 366 | +X,Y=np.linspace(0,100,10),np.zeros(10) |
| 367 | +yticklabels= [] |
| 368 | + |
| 369 | +fori, (name,linestyle)inenumerate(linestyles): |
| 370 | +ax.plot(X,Y+i,linestyle=linestyle,linewidth=1.5,color='black') |
| 371 | +yticklabels.append(name) |
| 372 | + |
| 373 | +ax.set_title(title) |
| 374 | +ax.set(ylim=(-0.5,len(linestyles)-0.5), |
| 375 | +yticks=np.arange(len(linestyles)), |
| 376 | +yticklabels=yticklabels) |
| 377 | +ax.tick_params(left=False,bottom=False,labelbottom=False) |
| 378 | +forspineinax.spines.values(): |
| 379 | +spine.set_visible(False) |
| 380 | + |
| 381 | +# For each line style, add a text annotation with a small offset from |
| 382 | +# the reference point (0 in Axes coords, y tick value in Data coords). |
| 383 | +fori, (name,linestyle)inenumerate(linestyles): |
| 384 | +ax.annotate(repr(linestyle), |
| 385 | +xy=(0.0,i),xycoords=ax.get_yaxis_transform(), |
| 386 | +xytext=(-6,-12),textcoords='offset points', |
| 387 | +color="blue",fontsize=8,ha="right",family="monospace") |
| 388 | + |
| 389 | + |
| 390 | +ax0,ax1= (plt.figure(figsize=(10,8)) |
| 391 | + .add_gridspec(2,1,height_ratios=[1,3]) |
| 392 | + .subplots()) |
| 393 | + |
| 394 | +plot_linestyles(ax0,linestyle_str[::-1],title='Named linestyles') |
| 395 | +plot_linestyles(ax1,linestyle_tuple[::-1],title='Parametrized linestyles') |
| 396 | + |
| 397 | +plt.tight_layout() |
| 398 | +plt.show() |