§
    ƒØj|¶  ã                  ót  — d Z ddlmZ ddlZddlZddlZddlZddlZddl	m
Z
mZ ddlmZ ddlmZ ddlmZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZ ddlmZ ej        rddlmZ dZ G d„ dej        ¦  «        Z G d„ de¦  «        Z G d„ de¦  «        Z G d„ de¦  «        Z G d„ de¦  «        Z  G d„ de¦  «        Z! G d„ de!¦  «        Z" G d„ de!¦  «        Z# G d „ d!e"¦  «        Z$ G d"„ d#e¦  «        Z% G d$„ d%e¦  «        Z& G d&„ d'ej'        ¦  «        Z(e)d(k    rddlZ ej*        e(¦  «         dS dS ))z„
Definitions for extracting data from a Stream to place on one axis of a
:class:`~music21.graph.plot.PlotStream` or similar object.
é    )ÚannotationsN)ÚaccidentalLabelToUnicodeÚGraphException)Úbar)Úcommon)Úduration)Údynamics)Úpitch)Úprebase)Ústream)Úelements)ÚpitchAnalysis)Únoteéÿÿÿÿc                  óø   — e Zd ZU dZddddddœZded	<   d
ZddddœZdZded<   d%d„Z	d„ Z
ed„ ¦   «         Zej        d„ ¦   «         Zed„ ¦   «         Zej        d„ ¦   «         Zed„ ¦   «         Zd&d!„Zd"„ Zd#„ Zd'd$„ZdS )(ÚAxisz¸
    An Axis is an easier way of specifying what to plot on any given axis.

    Client should be a .plot.PlotStream or None.  Eventually a Stream may be allowed,
    but not yet.
    z=the name of the axis.  One of "x" or "y" or for 3D Plots, "z"zV
            None or number representing the axis minimum.  Default None.
            zV
            None or number representing the axis maximum.  Default None.
            a{  
            a dict of {'x': 0, 'y': 1, 'z': 2} mapping where an axis's data can
            be found in self.client.data after extract data is run:

            >>> b = corpus.parse('bwv66.6')
            >>> plot = graph.plot.ScatterPitchClassOffset(b)
            >>> pcAxis = plot.axisY
            >>> pcAxis.axisName
            'y'
            >>> pcAxisDataIndex = pcAxis.axisDataMap[pcAxis.axisName]
            >>> pcAxisDataIndex
            1
            >>> plot.extractData()
            >>> pcValues = [dataTuple[pcAxisDataIndex] for dataTuple in plot.data]
            >>> pcValues[0:2]
            [1, 11]
            a  
            a tuple of strings representing the quantities the axis can plot.
            The first element of the tuple is the authoritative name.

            >>> ax = graph.axis.DynamicsAxis()
            >>> ax.quantities
            ('dynamic', 'dynamics', 'volume')
            )ÚaxisNameÚminValueÚmaxValueÚaxisDataMapÚ
quantitiesúdict[str, str]Ú	_DOC_ATTRzan axisr   é   é   )ÚxÚyÚz)ÚgenericÚoneÚnothingÚblankútuple[str, ...]r   Nr   c                ó¢   — t          |t          ¦  «        rt          d¦  «        ‚d | _        d | _        || _        || _        d | _        d | _        d S )Nz,Client must be a PlotStream, Stream, or None)	Ú
isinstanceÚstrr   Ú_clientÚ_labelÚclientr   r   r   )Úselfr)   r   s      ú‡/Users/tedpeters/Documents/Projects/Claude Projects/Misc Tools/notename-sheets/.venv/lib/python3.11/site-packages/music21/graph/axis.pyÚ__init__zAxis.__init__[   sR   € Ý�f�cÑ"Ô"ð 	QÝ Ð!OÑPÔPÐPØˆŒØˆŒàˆŒØ ˆŒàˆŒØˆŒˆˆó    c                óL   — | j         }|�|j        j        }nd}d| j        › d|› �S )a  
        The representation of the Axis shows the client and the axisName
        in addition to the class name.

        >>> s = stream.Stream()
        >>> plot = graph.plot.ScatterPitchClassQuarterLength(s)
        >>> plot.axisX
        <music21.graph.axis.QuarterLengthAxis: x axis for ScatterPitchClassQuarterLength>

        >>> plot.axisY
        <music21.graph.axis.PitchClassAxis: y axis for ScatterPitchClassQuarterLength>

        >>> axIsolated = graph.axis.DynamicsAxis(axisName='z')
        >>> axIsolated
        <music21.graph.axis.DynamicsAxis: z axis for (no client)>

        >>> s = stream.Part()
        >>> axStream = graph.axis.DynamicsAxis(s, axisName='y')
        >>> axStream
        <music21.graph.axis.DynamicsAxis: y axis for Part>

        Nz(no client)z: z
 axis for )r)   Ú	__class__Ú__name__r   )r*   ÚcÚ
clientNames      r+   Ú_reprInternalzAxis._reprInternalg   s9   € ð. ŒKˆØˆ=ØœÔ-ˆJˆJà&ˆJà9�D”MÐ9Ð9¨ZÐ9Ð9Ð9r-   c                ó,   — | j         �| j         S | j        S )zç
        Returns self.label or class.labelDefault if not set:

        >>> ax = graph.axis.Axis(axisName='y')
        >>> ax.label
        'an axis'
        >>> ax.label = 'velocity'
        >>> ax.label
        'velocity'
        )r(   ÚlabelDefault©r*   s    r+   Úlabelz
Axis.label†   s   € ð Œ;Ð"Ø”;ÐàÔ$Ð$r-   c                ó   — || _         d S ©N©r(   ©r*   Úvalues     r+   r7   z
Axis.label—   ó   € àˆŒˆˆr-   c                ó4   — t          j        | j        ¦  «        S )zä
        The client stores a reference to the Plot that
        makes reference to this axis.

        (Like all music21 clients, It is normally stored internally as a weakref,
        so no need for garbage collecting)
        )r   ÚunwrapWeakrefr'   r6   s    r+   r)   zAxis.client›   s   € õ Ô# D¤LÑ1Ô1Ð1r-   c                ó8   — t          j        |¦  «        | _        d S r9   )r   ÚwrapWeakrefr'   )r*   Úreferents     r+   r)   zAxis.client¦   s   € åÔ)¨(Ñ3Ô3ˆŒˆˆr-   c                ó^   — | j         }|€dS t          |t          j        ¦  «        r|S |j        S )z¦
        Returns a reference to the client's .streamObj  (or None if client is None)

        If the client is itself a stream, return it.

        Read-only
        N)r)   r%   r   ÚStreamÚ	streamObj)r*   r1   s     r+   r   zAxis.streamª   s6   € ð ŒKˆØˆ9Ø�4Ý˜�6œ=Ñ)Ô)ð 	ØˆHà”;Ðr-   Únúnote.GeneralNoteÚ
formatDictúdict[str, t.Any]Úreturnút.Anyc                ó   — dS )z(
        Override in subclasses
        r   © ©r*   rF   rH   s      r+   ÚextractOneElementzAxis.extractOneElement»   s	   € ð ˆqr-   c                ó‚   — | j         €|rt          |¦  «        | _         | j        €|rt          |¦  «        | _        dS dS dS )a{  
        If self.minValue is not set,
        then set self.minValue to be the minimum of these values.

        Same with maxValue

        >>> ax = graph.axis.Axis()
        >>> print(ax.minValue)
        None

        >>> values = [10, 0, 3, 5]
        >>> ax.setBoundariesFromData(values)
        >>> ax.minValue
        0
        >>> ax.maxValue
        10

        If a boundary is given or .setXXXFromData is False then no changes are made

        >>> ax = graph.axis.Axis()
        >>> ax.minValue = -1
        >>> ax.setBoundariesFromData(values)
        >>> ax.minValue
        -1
        >>> ax.maxValue
        10
        N)r   Úminr   Úmax©r*   Úvaluess     r+   ÚsetBoundariesFromDatazAxis.setBoundariesFromDataÁ   sJ   € ð: Œ=Ð  VÐ Ý ™KœKˆDŒMØŒ=Ð  VÐ Ý ™KœKˆDŒMˆMˆMð !Ð Ð Ð r-   c                óÖ  — | j         }| j        }|€d}|€d}||z
  }|dk    rd}n$t          t          j        ||z
  ¦  «        ¦  «        }d|z  }|dk    r||z  dk    rt          |dz  ¦  «        }t          ||z  ¦  «        dz
  |z  }|dk     r|dk    rd}t          ||z  ¦  «        dz   |z  }t          |||¦  «        }g }	|D ]&}
|	                     |
t          |
¦  «        f¦  «         Œ'|	S )az  
        Get a set of ticks for this data.  Used by several numeric axes
        to make a reasonable number of ticks.

        >>> cax = graph.axis.Axis()
        >>> cax.minValue = 1
        >>> cax.maxValue = 9
        >>> cax.ticks()
        [(0, '0'), (1, '1'), (2, '2'), (3, '3'), (4, '4'),
         (5, '5'), (6, '6'), (7, '7'), (8, '8'), (9, '9'), (10, '10')]

        For larger data, the ticks are farther apart.

        >>> cax.minValue = 7
        >>> cax.maxValue = 80
        >>> cax.ticks()
        [(0, '0'), (10, '10'), (20, '20'), (30, '30'), (40, '40'),
         (50, '50'), (60, '60'), (70, '70'), (80, '80'), (90, '90')]

        >>> cax.minValue = 712
        >>> cax.maxValue = 2213
        >>> cax.ticks()
        [(600, '600'), (700, '700'), (800, '800'), (900, '900'), (1000, '1000'),
         ...
         (2100, '2100'), (2200, '2200'), (2300, '2300')]
        Nr   é
   r   r   )r   r   ÚintÚmathÚlog10ÚrangeÚappendr&   )r*   ÚminVÚmaxVÚ
differenceÚlog10distanceÚ	closest10Ú
startValueÚ	stopValueÚstepsÚticksÚtickNums              r+   re   z
Axis.ticksã   s%  € ð6 Œ}ˆØŒ}ˆàˆ<ØˆDØˆ<ØˆDà˜D‘[ˆ
Ø˜Š?ˆ?ØˆMˆMå¥¤
¨4°$©;Ñ 7Ô 7Ñ8Ô8ˆMà˜-Ñ'ˆ	Ø�qŠ=ˆ=˜j¨9Ñ4¸Ò:Ð:Ý˜I¨™NÑ+Ô+ˆIå˜$ Ñ*Ñ+Ô+¨aÑ/°9Ñ<ˆ
Ø˜Š>ˆ>˜d aši˜iØˆJå˜ 	Ñ)Ñ*Ô*¨QÑ.°)Ñ;ˆ	Ý�j )¨YÑ7Ô7ˆàˆØð 	2ð 	2ˆGØ�LŠL˜'¥3 w¡<¤<Ð0Ñ1Ô1Ð1Ð1Øˆr-   c                ó   — dS )z¿
        Routine to be called after data has been extracted to
        do any cleanup, etc.  Defaults to doing nothing, but
        see CountingAxis for an example of how this works.
        NrM   )r*   ÚdataLists     r+   ÚpostProcessDatazAxis.postProcessData  s	   € ð 	ˆr-   ©Nr   )rF   rG   rH   rI   rJ   rK   r9   )r0   Ú
__module__Ú__qualname__Ú__doc__r   Ú__annotations__r5   r   r   r,   r3   Úpropertyr7   Úsetterr)   r   rO   rU   re   ri   rM   r-   r+   r   r   -   sx  € € € € € € ðð ð Tðððð"ð3!!ð !!€Ið !ð !ð !ñ !ðF €LØ ¨Ð*Ð*€KØ"H€JÐHÐHÐHÑHð
ð 
ð 
ð 
ð:ð :ð :ð> ð%ð %ñ „Xð%ð  „\ðð ñ „\ðð ð2ð 2ñ „Xð2ð „]ð4ð 4ñ „]ð4ð ðð ñ „Xðð ð ð ð ð (ð  (ð  (ðD7ð 7ð 7ðrð ð ð ð ð r-   r   c                  ón   ‡ — e Zd ZU dZdddddœZded<   d	Zd
Zded<   dˆ fd„	Ze	dd„¦   «         Z
d„ Zˆ xZS )Ú	PitchAxisz0
    Axis subclass for dealing with Pitches
    za
            bool on whether to show both common enharmonics in labels, default True
            zZ
            bool on whether to hide labels for unused pitches, default True.
            zw
            bool on whether not to even show a tick when a pitch doesn't exist.
            default True.
            z¥
            bool or 'few' about whether to show octave numbers.  If 'few' then
            only the first pitch in each octave is shown.  Default 'few'
            )ÚshowEnharmonicÚblankLabelUnusedÚ
hideUnusedÚshowOctavesr   r   ÚPitch)ÚpitchGenericr#   r   Nr   c                ó„   •— t          ¦   «                              ||¦  «         d| _        d| _        d| _        d| _        d S )NÚfewT)Úsuperr,   rv   rs   rt   ru   ©r*   r)   r   r/   s      €r+   r,   zPitchAxis.__init__>  s@   ø€ Ý‰Œ×Ò˜ Ñ*Ô*Ð*Ø ˆÔØ"ˆÔØ $ˆÔØˆŒˆˆr-   re   úlist[tuple[t.Any, str]]rJ   c                óf   — g }| D ]+\  }}t          |¦  «        }|                     ||f¦  «         Œ,|S )uî  
        Given a list of ticks, replace all labels with alternative/unicode symbols where necessary.

        >>> ticks = [(60, 'C4'), (61, 'C#4'), (62, 'D4'), (63, 'E-4')]
        >>> t2 = graph.axis.PitchAxis.makePitchLabelsUnicode(ticks)
        >>> len(t2)
        4
        >>> [num for num, label in t2]
        [60, 61, 62, 63]
        >>> t2[0]
        (60, 'C4')
        >>> for num, label in t2:
        ...     label
        'C4'
        'Câ™¯4'
        'D4'
        'Eâ™­4'
        )r   r\   )re   Úpostr<   r7   s       r+   ÚmakePitchLabelsUnicodez PitchAxis.makePitchLabelsUnicodeE  sG   € ð0 ˆØ!ð 	(ð 	(‰LˆE�5Ý,¨UÑ3Ô3ˆEØ�KŠK˜ ˜Ñ'Ô'Ð'Ð'Øˆr-   c                ó´  — | j         }|€i }nt          j        ||¦  «        }g }i }t          ¦   «         }d„ }d„ }	t	          t          | j        ¦  «        t          | j        ¦  «        dz   ¦  «        D �]Ð}
t          j	        ¦   «         }t          |||
¦  «         g }|D ]T}||vr%t          t          j	        |¦  «        |¦  «        ||<   ||         |
k    r|                     ||         |f¦  «         ŒU| j        r|                     |	¬¦  «         n|                     |¬¦  «         d}|s#| j        rŒÀ| j        st          ||¦  «        }nZd}nW| j        s|d         d         }nAg }|D ]'\  }}|                     t#          |¦  «        ¦  «         Œ(d                     |¦  «        }| j        d	u rt)          j        d
d|¦  «        }ng| j        dk    r\t)          j        d
|¦  «        }|rE|                     d¦  «        }||v rt)          j        d
d|¦  «        }n|                     |¦  «         |                     |
|f¦  «         �ŒÒ|                      |¦  «        }|S )zµ
        Helper method that can apply all the showEnharmonic, etc. values consistently

        see the `ticks` methods below.

        Returns a list of two-element tuples
        Nc                ó^   — | \  }}|                      d¦  «        rd|dd…         z   }d|z  |fS )a  
            ensure that higher weighed weights come first, but
            then alphabetical by name, except that G comes before
            A.  That's the only "out of order" item we need to be
            concerned with since we are only comparing enharmonics.
            ÚAÚHr   Nr   ©Ú
startswith©r   ÚweightÚ	sort_names      r+   ÚweightedSortHelperz6PitchAxis._pitchTickHelper.<locals>.weightedSortHelperw  sE   € ð !"ÑˆF�IØ×#Ò# CÑ(Ô(ð 0Ø )¨A¨B¨B¤-Ñ/�	Ø˜‘K Ð+Ð+r-   c                óX   — | \  }}|                      d¦  «        rd|dd …         z   }||fS )Nrƒ   r„   r   r…   r‡   s      r+   ÚunweightedSortHelperz8PitchAxis._pitchTickHelper.<locals>.unweightedSortHelperƒ  s?   € Ø !ÑˆF�IØ×#Ò# CÑ(Ô(ð 0Ø )¨A¨B¨B¤-Ñ/�	Ø˜IÐ&Ð&r-   r   )ÚkeyÚ r   ú/Fz\drz   )r   r   ÚpitchAttributeCountÚsetr[   rX   r   r   r
   rw   ÚsetattrÚgetattrr\   rs   Úsortru   rt   r   Újoinrv   ÚreÚsubÚsearchÚgroupÚaddr€   )r*   ÚattributeCounterÚattributeCompareÚsÚ	nameCountre   Ú
helperDictÚoctavesSeenrŠ   rŒ   ÚiÚpÚweightsr�   r7   r—   Úunused_weightÚnameÚmatchOctaveÚoctaveMatchs                       r+   Ú_pitchTickHelperzPitchAxis._pitchTickHelperc  s­  € ð ŒKˆØˆ9àˆIˆIå%Ô9¸!Ð=MÑNÔNˆIàˆàˆ
Ý‘e”eˆð
	,ð 
	,ð 
	,ð	'ð 	'ð 	'õ •s˜4œ=Ñ)Ô)­3¨t¬}Ñ+=Ô+=ÀÑ+AÑBÔBð .	%ñ .	%ˆAÝ”‘”ˆAÝ�AÐ'¨Ñ+Ô+Ð+ØˆGØ ð :ð :�Ø˜jÐ(Ð(õ '.­e¬k¸#Ñ.>Ô.>Ð@PÑ&QÔ&Q�J˜s‘Oà˜c”? aÒ'Ð'Ø—N’N I¨c¤N°CÐ#8Ñ9Ô9Ð9øàÔ"ð 5Ø—’Ð!5�Ñ6Ô6Ð6Ð6à—’Ð!3�Ñ4Ô4Ð4àˆEØð &Ø”?ð ØØÔ,ð Ý# AÐ'7Ñ8Ô8�E�Eà�E�EØÔ(ð &à œ
 1œ��à�Ø+2ð ?ð ?Ñ'�M 4Ø—J’JÕ7¸Ñ=Ô=Ñ>Ô>Ð>Ð>ØŸš ™œ�àÔ 5Ð(Ð(Ýœ˜u b¨%Ñ0Ô0��ØÔ! UÒ*Ð*Ý œi¨¨uÑ5Ô5�Øð 5Ø"-×"3Ò"3°AÑ"6Ô"6�KØ" kÐ1Ð1Ý "¤ u¨b°%Ñ 8Ô 8˜˜à#Ÿš¨Ñ4Ô4Ð4à�LŠL˜!˜U˜Ñ$Ô$Ð$Ñ$Ø×+Ò+¨EÑ2Ô2ˆØˆr-   rj   )re   r}   rJ   r}   )r0   rk   rl   rm   r   rn   r5   r   r,   Ústaticmethodr€   r¨   Ú__classcell__©r/   s   @r+   rr   rr   '  sË   ø€ € € € € € ðð ððððð!ð !€Ið ð ð ñ ð  €LØ"4€JÐ4Ð4Ð4Ñ4ðð ð ð ð ð ð ðð ð ñ „\ðð:Vð Vð Vð Vð Vð Vð Vr-   rr   c                  óF   ‡ — e Zd ZU dZdZdZded<   dˆ fd„	Zdd„Zd„ Z	ˆ xZ
S )ÚPitchClassAxiszr
    Axis subclass for dealing with PitchClasses

    By default, axis is not set from data, but set to 0, 11
    zPitch Class)Ú
pitchClassÚ
pitchclassÚpcr#   r   Nr   c                óv   •— d| _         t          ¦   «                              ||¦  «         d| _        d| _        d S )NFr   é   )rv   r{   r,   r   r   r|   s      €r+   r,   zPitchClassAxis.__init__Å  s7   ø€ Ø ˆÔÝ‰Œ×Ò˜ Ñ*Ô*Ð*ØˆŒØˆŒˆˆr-   rJ   ú
int | Nonec                ó>   — t          |d¦  «        r|j        j        S d S ©Nr
   )Úhasattrr
   r®   rN   s      r+   rO   z PitchClassAxis.extractOneElementË  s$   € Ý�1�gÑÔð 	&Ø”7Ô%Ð%Øˆtr-   c                ó.   — |                       dd¦  «        S )u'
  
        Get ticks and labels for pitch classes.

        If `showEnharmonic` is `True` (default) then
        when choosing whether to display as sharp or flat use
        the most commonly used enharmonic.

        >>> s = corpus.parse('bach/bwv324.xml')
        >>> s.analyze('key')
        <music21.key.Key of G major>

        >>> plotS = graph.plot.PlotStream(s)
        >>> ax = graph.axis.PitchClassAxis(plotS)
        >>> ax.hideUnused = True

        Ticks returns a list of two-element tuples:

        >>> ax.ticks()
        [(0, 'C'), (2, 'D'), ..., (11, 'B')]

        >>> for position, noteName in ax.ticks():
        ...            print(str(position) + ' ' + noteName)
        0 C
        2 D
        3 Dâ™¯
        4 E
        6 Fâ™¯
        7 G
        9 A
        11 B

        >>> s = corpus.parse('bach/bwv281.xml')
        >>> plotS = graph.plot.PlotStream(s)
        >>> ax = graph.axis.PitchClassAxis(plotS)
        >>> ax.hideUnused = True
        >>> ax.showEnharmonic = True

        >>> for position, noteName in ax.ticks():
        ...            print(str(position) + ' ' + noteName)
        0 C
        2 D
        3 Eâ™­
        4 E
        5 F
        7 G
        9 A
        10 Bâ™­
        11 B

        >>> ax.blankLabelUnused = True
        >>> ax.hideUnused = False
        >>> for position, noteName in ax.ticks():
        ...            print(str(position) + ' ' + noteName)
        0 C
        1
        2 D
        3 Eâ™­
        4 E
        5 F
        6
        7 G
        8
        9 A
        10 Bâ™­
        11 B

        `.showEnharmonic` will change here:

        >>> s.append(note.Note('A#4'))
        >>> s.append(note.Note('G#4'))
        >>> s.append(note.Note('A-4'))
        >>> s.append(note.Note('A-4'))
        >>> for position, noteName in ax.ticks():
        ...            print(str(position) + ' ' + noteName)
        0 C
        1
        2 D
        3 Eâ™­
        4 E
        5 F
        6
        7 G
        8 Gâ™¯/Aâ™­
        9 A
        10 Aâ™¯/Bâ™­
        11 B

        Make sure that Ab shows since there are two of them and only one G#

        >>> ax.showEnharmonic = False
        >>> for position, noteName in ax.ticks():
        ...            print(str(position) + ' ' + noteName)
        0 C
        1
        2 D
        3 Eâ™­
        4 E
        5 F
        6
        7 G
        8 Aâ™­
        9 A
        10 Bâ™­
        11 B

        OMIT_FROM_DOCS

        TODO: this ultimately needs to look at key signature/key to determine
            defaults for undefined notes where blankLabelUnused is False.
        r¥   r®   ©r¨   r6   s    r+   re   zPitchClassAxis.ticksÐ  s   € ðb ×$Ò$ V¨\Ñ:Ô:Ð:r-   rj   )rJ   r³   )r0   rk   rl   rm   r5   r   rn   r,   rO   re   rª   r«   s   @r+   r­   r­   ¼  s‹   ø€ € € € € € ðð ð
 !€LØ"D€JÐDÐDÐDÑDðð ð ð ð ð ðð ð ð ð
q;ð q;ð q;ð q;ð q;ð q;ð q;r-   r­   c                  ó4   — e Zd ZU dZdZdZded<   d„ Zdd	„Zd
S )ÚPitchSpaceAxiszB
    Axis subclass for dealing with PitchSpace (MIDI numbers)
    rw   )Ú
pitchSpacer
   Ú
pitchspaceÚpsr#   r   c                ó>   — t          |d¦  «        r|j        j        S d S rµ   )r¶   r
   r½   rN   s      r+   rO   z PitchSpaceAxis.extractOneElementK  s(   € Ý�1�gÑÔð 	Ø”7”:Ðð	ð 	r-   é$   éd   c                ó.   — |                       dd¦  «        S )u  
        >>> ax = graph.axis.PitchSpaceAxis()
        >>> ax.hideUnused = False
        >>> ax.blankLabelUnused = False
        >>> ax.minValue = 20
        >>> ax.maxValue = 24
        >>> for ps, label in ax.ticks():
        ...     print(str(ps) + ' ' + label)
        20 Gâ™¯0
        21 A
        22 Bâ™­
        23 B
        24 C1

        >>> ax.showOctaves = False
        >>> for ps, label in ax.ticks():
        ...     print(str(ps) + ' ' + label)
        20 Gâ™¯
        21 A
        22 Bâ™­
        23 B
        24 C

        >>> ax.showOctaves = True
        >>> for ps, label in ax.ticks():
        ...     print(str(ps) + ' ' + label)
        20 Gâ™¯0
        21 A0
        22 Bâ™­0
        23 B0
        24 C1

        >>> ax.minValue = 60
        >>> ax.maxValue = 72
        >>> [x for x, y in ax.ticks()]
        [60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72]

        >>> bach = corpus.parse('bwv66.6')
        >>> plotS = graph.plot.PlotStream(bach.parts[-1])
        >>> ax = graph.axis.PitchSpaceAxis(plotS)
        >>> ax.hideUnused = False
        >>> ax.minValue = 36
        >>> ax.maxValue = 100
        >>> ticks = ax.ticks()
        >>> ticks[0]  # blank because no note 36 in data
        (36, '')
        >>> ticks[21]
        (57, 'A')
        ÚnameWithOctaver½   r¸   )r*   ÚdataMinÚdataMaxs      r+   re   zPitchSpaceAxis.ticksO  s   € ðd ×$Ò$Ð%5°tÑ<Ô<Ð<r-   N)r¿   rÀ   )	r0   rk   rl   rm   r5   r   rn   rO   re   rM   r-   r+   rº   rº   D  s[   € € € € € € ðð ð €LØ"M€JÐMÐMÐMÑMðð ð ð2=ð 2=ð 2=ð 2=ð 2=ð 2=r-   rº   c                  ó>   ‡ — e Zd ZU dZdZdZded<   d
ˆ fd„	Zd	„ Zˆ xZ	S )ÚPitchSpaceOctaveAxiszF
    An axis similar to pitch classes, but just shows the octaves
    ÚOctave)ÚoctaveÚoctavesr#   r   Nr   c                óZ   •— t          ¦   «                              ||¦  «         d| _        d S )NÚC2)r{   r,   ÚstartNameWithOctaver|   s      €r+   r,   zPitchSpaceOctaveAxis.__init__‹  s+   ø€ Ý‰Œ×Ò˜ Ñ*Ô*Ð*Ø#'ˆÔ Ð Ð r-   c                óB  — g }t          j        | j        ¦  «        }|j        | j        k    r^|j        | j        k    r.|                     t          |j        ¦  «        |j        f¦  «         |xj	        dz  c_	        |j        | j        k    °^|  
                    |¦  «        }|S )aß  
        This class does not currently take into account whether the octaves themselves
        are found in the Stream.

        >>> ax = graph.axis.PitchSpaceOctaveAxis()
        >>> ax.minValue = 36
        >>> ax.maxValue = 100
        >>> ax.ticks()
        [(36, 'C2'), (48, 'C3'), (60, 'C4'), (72, 'C5'), (84, 'C6'), (96, 'C7')]

        >>> ax.startNameWithOctave = 'A2'
        >>> ax.ticks()
        [(45, 'A2'), (57, 'A3'), (69, 'A4'), (81, 'A5'), (93, 'A6')]
        r   )r
   rw   rÌ   r½   r   r   r\   rX   rÂ   rÈ   r€   )r*   re   ÚcurrentPitchs      r+   re   zPitchSpaceOctaveAxis.ticks�  sœ   € ð ˆÝ”{ 4Ô#;Ñ<Ô<ˆØŒo ¤Ò.Ð.ØŒ $¤-Ò/Ð/Ø—’�c ,¤/Ñ2Ô2°LÔ4OÐPÑQÔQÐQØÐÔ 1Ñ$ÐÔð Œo ¤Ò.Ð.ð ×+Ò+¨EÑ2Ô2ˆØˆr-   rj   )
r0   rk   rl   rm   r5   r   rn   r,   re   rª   r«   s   @r+   rÆ   rÆ   „  sp   ø€ € € € € € ðð ð €LØ"7€JÐ7Ð7Ð7Ñ7ð(ð (ð (ð (ð (ð (ðð ð ð ð ð ð r-   rÆ   c                  óJ   ‡ — e Zd ZU dZddiZded<   dZdZded	<   dˆ fd„	Zˆ xZ	S )ÚPositionAxisz2
    Axis subclass for dealing with Positions
    ÚgraceNoteQLz™
            length to substitute a grace note or other Zero-length element for.
            Default is the length of a 64th note (1/16 of a QL)
        r   r   ÚPosition)ÚpositionÚ	positionsr#   r   Nr   c                óZ   •— t          ¦   «                              ||¦  «         d| _        d S )Ng      °?)r{   r,   rÑ   r|   s      €r+   r,   zPositionAxis.__init__ò  s+   ø€ Ý‰Œ×Ò˜ Ñ*Ô*Ð*Ø ˆÔÐÐr-   rj   )
r0   rk   rl   rm   r   rn   r5   r   r,   rª   r«   s   @r+   rÐ   rÐ   ä  s†   ø€ € € € € € ðð ð 	ð ð!€Ið ð ð ñ ð €LØ";€JÐ;Ð;Ð;Ñ;ð!ð !ð !ð !ð !ð !ð !ð !ð !ð !r-   rÐ   c                  ó²   ‡ — e Zd ZU dZdddddddœZd	ed
<   dZdZded<   dˆ fd„	Zd„ Z	e
d„ ¦   «         Zej        d„ ¦   «         Zdd„Zd„ Zd„ Zd„ Zdd„Zˆ xZS )Ú
OffsetAxisz0
    Axis subclass for dealing with Offsets
    zì
            bool or None for whether offsets (False) or measure numbers (True) should be used
            in the case of an offset access.  Default, None, meaning to check whether
            the stream has measures first.
            zü
            If measures are not used then this number is used to create the number
            of steps between an axis tick.  Currently the default is 10, but it
            might become a function of the length of the stream eventually.
            zŒ
            If True then only the first and last values will be used to
            create ticks for measures.  Default False.
            zHThe lowest starting position (as an offset).  Will be set automatically.zGThe highest ending position (as an offset).  Will be set automatically.a  
            When plotting measures, will limit the number of ticks given to at most
            this number.  Note that since all double/final/heavy bars are show, this number
            may be exceeded if there are more that this number of double bars.  Default: 20.
            )ÚuseMeasuresÚoffsetStepSizeÚminMaxMeasureOnlyr   r   ÚmostMeasureTicksToShowr   r   ÚOffset)ÚoffsetÚmeasureÚoffsetsÚmeasuresÚtimer#   r   Nr   c                ó„   •— t          ¦   «                              ||¦  «         d | _        d| _        d| _        d| _        d S )NrW   é   F)r{   r,   rØ   rÙ   rÛ   rÚ   r|   s      €r+   r,   zOffsetAxis.__init__  sC   ø€ Ý‰Œ×Ò˜ Ñ*Ô*Ð*ØˆÔà ˆÔØ&(ˆÔ#Ø!&ˆÔÐÐr-   c                ó6   — |                      | j        ¦  «        S r9   )ÚgetOffsetInHierarchyr   rN   s      r+   rO   zOffsetAxis.extractOneElement  s   € Ø×%Ò% d¤kÑ2Ô2Ð2r-   c                ód   — | j         �| j         S | j        }|€|                      ¦   «         }|rdS dS )a  
        Return an axis label for measure or offset, depending on if measures are available.

        >>> a = graph.axis.OffsetAxis()
        >>> a.label
        'Offset'
        >>> a.useMeasures = True
        >>> a.label
        'Measure Number'
        NzMeasure NumberrÜ   )r(   rØ   ÚsetUseMeasuresFromOffsetMap)r*   rØ   s     r+   r7   zOffsetAxis.label!  sF   € ð Œ;Ð"Ø”;ÐàÔ&ˆØÐØ×:Ò:Ñ<Ô<ˆKàð 	Ø#Ð#à�8r-   c                ó   — || _         d S r9   r:   r;   s     r+   r7   zOffsetAxis.label9  r=   r-   c                ó¼   — 	 | j         j        | _        | j         j        | _        d S # t
          $ r+ d| _        |rt          |¦  «        | _        Y d S d| _        Y d S w xY w)Nr   rW   )r   ÚlowestOffsetr   ÚhighestTimer   ÚAttributeErrorrR   rS   s     r+   rU   z OffsetAxis.setBoundariesFromData=  sm   € ð	#Ø œKÔ4ˆDŒMØ œKÔ3ˆDŒMˆMˆMøÝð 	#ð 	#ð 	#ØˆDŒMØð #Ý # F¡¤�”���à "�”���ð	#øøøs   ‚"& ¦'AÁAÁAc                óÄ  — |                       ¦   «         }|                      |¦  «         | j        r!|                      | j        | j        |¦  «        S g }t          t          j        | j        ¦  «        ¦  «        }t          t          j	        | j        ¦  «        ¦  «        }t          ||dz   | j        ¦  «        D ]&}|                     |t          |¦  «        f¦  «         Œ'|S )aÎ
  
        Get offset or measure ticks

        >>> bach = corpus.parse('bach/bwv281.xml')
        >>> plotS = graph.plot.PlotStream(bach)
        >>> ax = graph.axis.OffsetAxis(plotS)
        >>> ax.setBoundariesFromData()
        >>> ax.ticks()  # on whole score, showing anacrusis spacing
        [(0.0, '0'), (1.0, '1'), (5.0, '2'), (9.0, '3'), (13.0, '4'), (17.0, '5'),
         (21.0, '6'), (25.0, '7'), (29.0, '8')]

        We can reduce the number of ticks shown:

        >>> ax.mostMeasureTicksToShow = 4
        >>> ax.ticks()
        [(0.0, '0'), (9.0, '3'), (21.0, '6'), (29.0, '8')]

        We can also plot on a part:

        >>> soprano = bach.parts.first()
        >>> plotSoprano = graph.plot.PlotStream(soprano)
        >>> ax = graph.axis.OffsetAxis(plotSoprano)
        >>> ax.setBoundariesFromData()
        >>> ax.ticks()  # on whole score, showing anacrusis spacing
        [(0.0, '0'), (1.0, '1'), (5.0, '2'), (9.0, '3'), (13.0, '4'), (17.0, '5'),
         (21.0, '6'), (25.0, '7'), (29.0, '8')]

        Now we will show just the first and last measure:

        >>> ax.minMaxMeasureOnly = True
        >>> ax.ticks()
        [(0.0, '0'), (29.0, '8')]

        Only show ticks between minValue and maxValue (in offsets):

        >>> ax.minMaxMeasureOnly = False
        >>> ax.minValue = 8
        >>> ax.maxValue = 12
        >>> ax.ticks()
        [(9.0, '3')]

        Double bars and other heavy bars always show up.
        (Let's get a new axis object to see.)

        >>> ax = graph.axis.OffsetAxis(plotSoprano)
        >>> ax.setBoundariesFromData()
        >>> ax.mostMeasureTicksToShow = 4
        >>> ax.ticks()
        [(0.0, '0'), (9.0, '3'), (21.0, '6'), (29.0, '8')]
        >>> m5 = soprano.getElementsByClass(stream.Measure)[5]
        >>> m5.number
        5
        >>> m5.rightBarline = bar.Barline('double')
        >>> ax.ticks()
        [(0.0, '0'), (13.0, '4'), (17.0, '5'), (29.0, '8')]

        Future improvements might make the spacing around the double bars
        a bit better.  It'd be nice to see measure 2 or 3 ticked rather
        than measure 4.

        On a raw collection of notes with no measures, offsets are used:

        >>> n = note.Note('a')
        >>> s = stream.Stream()
        >>> s.repeatAppend(n, 20)
        >>> plotS = graph.plot.PlotStream(s)
        >>> ax = graph.axis.OffsetAxis(plotS)
        >>> ax.setBoundariesFromData()
        >>> ax.ticks()
        [(0, '0'), (10, '10'), (20, '20')]

        The space between offsets is configured by `.offsetStepSize`.  At
        present mostMeasureTicksToShow to does affect streams without measures.

        >>> ax.offsetStepSize = 5
        >>> ax.ticks()
        [(0, '0'), (5, '5'), (10, '10'), (15, '15'), (20, '20')]
        r   )ÚgetOffsetMaprç   rØ   Ú_measureTicksr   r   rX   rY   ÚfloorÚceilr[   rÙ   r\   r&   )r*   Ú	offsetMapre   ÚoMinÚoMaxr¡   s         r+   re   zOffsetAxis.ticksH  sÐ   € ð^ ×%Ò%Ñ'Ô'ˆ	Ø×(Ò(¨Ñ3Ô3Ð3àÔð 	Ø×%Ò% d¤m°T´]ÀIÑNÔNÐNð ˆEÝ•t”z $¤-Ñ0Ô0Ñ1Ô1ˆDÝ•t”y ¤Ñ/Ô/Ñ0Ô0ˆDÝ˜4 ¨¡¨4Ô+>Ñ?Ô?ð *ð *�Ø—’˜a¥ Q¡¤˜[Ñ)Ô)Ð)Ð)àˆLr-   c                óÞ  ‡‡‡‡— g Šg Št          ‰                     ¦   «         ¦  «        }|D ]'}||cxk    r|k    rn Œ‰                     |¦  «         Œ(| j        rLdD ]G}‰|         }‰|         d         j        }|t          |¦  «        f}	|	‰vr‰                     |	¦  «         ŒH�nFt          ¦   «         Šˆˆˆˆfd„}
 |
d¦  «          |
t          ‰¦  «        dz
  ¦  «         t          dt          ‰¦  «        dz
  ¦  «        D ]B}‰|         }‰|         d         }|j	        �#|j	        j
        t          j        v r |
|¦  «         ŒCt          | j        t          ‰¦  «        z
  dz   t          ‰¦  «        ¦  «        }t          t          ‰¦  «        |z  d¦  «        }|}|t          ‰¦  «        dz
  k     r& |
|¦  «         ||z  }|t          ‰¦  «        dz
  k     °&‰                     ¦   «          ‰S )zB
        helper method for ticks() just to pull out code.
        )r   r   r   c                óÄ   •— | ‰v rd S ‰|          }‰|         d         }|j         }|t          |¦  «        f}‰                     |¦  «         ‰                     | ¦  «         d S ©Nr   )Únumberr&   r\   rš   )	Úindex_in_mNoToUserÝ   ÚfoundMeasureÚmNumberÚ	tickTupleÚmNoToUserò   ÚtickIndexesUsedre   s	        €€€€r+   Úadd_tick_tuplez0OffsetAxis._measureTicks.<locals>.add_tick_tupleÇ  sr   ø€ Ø$¨Ð7Ð7Ø�FØ!Ð"3Ô4�à(¨Ô0°Ô3�Ø&Ô-�Ø#¥S¨¡\¤\Ð2�	Ø—’˜YÑ'Ô'Ð'Ø×#Ò#Ð$5Ñ6Ô6Ð6Ð6Ð6r-   r   )ÚlistÚkeysr\   rÚ   rø   r&   r‘   Úlenr[   ÚrightBarlineÚtyper   ÚstrongBarlineTypesrQ   rÛ   rR   r”   )r*   rÃ   rÄ   rò   Ú
sortedKeysr�   r¡   rÝ   rû   rü   rÿ   Ú	mapOffsetÚ
mapMeasureÚmaxMoreTicksToAddÚmNoStepSizerý   rþ   re   s      `           @@@r+   rï   zOffsetAxis._measureTicks§  sV  øøøø€ ð ˆð ˆÝ˜)Ÿ.š.Ñ*Ô*Ñ+Ô+ˆ
Øð 	%ð 	%ˆCØ˜#Ð(Ð(Ò(Ð( Ò(Ð(Ð(Ð(Ð(ð —’ Ñ$Ô$Ð$øð Ô!ð ,	Øð ,ð ,�Ø! !œ�Ø# FÔ+¨AÔ.Ô5�Ø#¥S¨¡\¤\Ð2�	Ø EÐ)Ð)Ø—L’L Ñ+Ô+Ð+øñ,õ "™eœeˆOð	7ð 	7ð 	7ð 	7ð 	7ð 	7ð 	7ð 	7ð ˆN˜1ÑÔÐàˆN�3˜x™=œ=¨1Ñ,Ñ-Ô-Ð-õ ˜1�c (™mœm¨aÑ/Ñ0Ô0ð &ð &�Ø$ QœK�	Ø& yÔ1°!Ô4�
ØÔ+Ð7Ø&Ô3Ô8½CÔ<RÐRÐRØ"�N 1Ñ%Ô%Ð%øõ !$ DÔ$?Å#ÀoÑBVÔBVÑ$VÐYZÑ$ZÝ$'¨¡M¤Mñ!3ô !3Ðå�c (™mœmÐ/@Ñ@À!ÑDÔDˆKØˆAØ•c˜(‘m”m aÑ'Ò'Ð'Ø�˜qÑ!Ô!Ð!Ø�[Ñ �ð •c˜(‘m”m aÑ'Ò'Ð'ð �JŠJ‰LŒLˆLàˆr-   c                óR  — | j         }|€i S |                     ¦   «         rP|                     t           j        ¦  «                             ¦   «                              t           j        g¦  «        }n7|                     ¦   «         r!|                     t           j        g¦  «        }ni }|S )a»  
        Find the first partlike object and get the measureOffsetMap from it, or an
        empty-dict if not.

        >>> b = corpus.parse('bwv66.6')
        >>> p = graph.plot.PlotStream(b)
        >>> ax = graph.axis.OffsetAxis(p, 'x')
        >>> om = ax.getOffsetMap()
        >>> om
        OrderedDict([(0.0, [<music21.stream.Measure 0 offset=0.0>]),
                     (1.0, [<music21.stream.Measure 1 offset=1.0>]),
                     (5.0, [<music21.stream.Measure 2 offset=5.0>]),
                     ...])

        Same if called on a single part:

        >>> p = graph.plot.PlotStream(b.parts[0])
        >>> ax = graph.axis.OffsetAxis(p, 'x')
        >>> om2 = ax.getOffsetMap()
        >>> om2
        OrderedDict([(0.0, [<music21.stream.Measure 0 offset=0.0>]),
                     (1.0, [<music21.stream.Measure 1 offset=1.0>]),
                     (5.0, [<music21.stream.Measure 2 offset=5.0>]),
                     ...])

        But empty if called on a single Measure ...

        >>> p = graph.plot.PlotStream(b.parts[0].getElementsByClass(stream.Measure)[2])
        >>> ax = graph.axis.OffsetAxis(p, 'x')
        >>> om3 = ax.getOffsetMap()
        >>> om3
        {}

        )r   ÚhasPartLikeStreamsÚgetElementsByClassrD   ÚfirstÚmeasureOffsetMapÚMeasureÚhasMeasures)r*   r�   rò   s      r+   rî   zOffsetAxis.getOffsetMapì  sš   € ðF ŒKˆØˆ9ØˆIà×ÒÑ!Ô!ð 		ð ×-Ò-­f¬mÑ<Ô<×BÒBÑDÔDß*Ò*­F¬NÐ+;Ñ<Ô<ð ˆIà�]Š]‰_Œ_ð 	Ø×*Ò*­F¬NÐ+;Ñ<Ô<ˆIˆIàˆIàÐr-   c                ó€   — | j         �| j         S |€|                      ¦   «         }t          |¦  «        | _         | j         S )ac  
        Given an offsetMap and `.useMeasures=None` return
        True or False based on whether the offsetMap or self.getOffsetMap() is
        non-empty.

        >>> b = corpus.parse('bwv66.6')
        >>> p = graph.plot.PlotStream(b)
        >>> ax = graph.axis.OffsetAxis(p, 'x')
        >>> print(ax.useMeasures)
        None
        >>> ax.setUseMeasuresFromOffsetMap()
        True

        Sets `.useMeasures` as a side effect:

        >>> ax.useMeasures
        True

        same as:

        >>> ax = graph.axis.OffsetAxis(p, 'x')
        >>> om = ax.getOffsetMap()
        >>> ax.setUseMeasuresFromOffsetMap(om)
        True

        If `.useMeasures` is set explicitly, then
        we return that

        >>> ax.useMeasures = False
        >>> ax.setUseMeasuresFromOffsetMap()
        False

        Returns False if the offsetMap is empty

        >>> p = graph.plot.PlotStream(b.parts[0].getElementsByClass(stream.Measure)[2])
        >>> axMeasure = graph.axis.OffsetAxis(p, 'x')
        >>> axMeasure.setUseMeasuresFromOffsetMap()
        False
        >>> axMeasure.useMeasures
        False
        )rØ   rî   Úbool)r*   rò   s     r+   rç   z&OffsetAxis.setUseMeasuresFromOffsetMap   sE   € ðT ÔÐ'ØÔ#Ð#ØÐØ×)Ò)Ñ+Ô+ˆIÝ 	™?œ?ˆÔØÔÐr-   rj   r9   )r0   rk   rl   rm   r   rn   r5   r   r,   rO   ro   r7   rp   rU   re   rï   rî   rç   rª   r«   s   @r+   r×   r×   ÷  sC  ø€ € € € € € ðð ðð
ð
ð _Ø]ð#ð#!ð !€Ið ð ð ñ ð0 €LØ"V€JÐVÐVÐVÑVð'ð 'ð 'ð 'ð 'ð 'ð3ð 3ð 3ð ðð ñ „Xðð. „\ðð ñ „\ðð	#ð 	#ð 	#ð 	#ð]ð ]ð ]ð~Cð Cð CðJ2ð 2ð 2ðh/ ð / ð / ð / ð / ð / ð / ð / r-   r×   c                  ó¨   ‡ — e Zd ZU dZdddœZded<   dZdZd	ed
<   dˆ fd„	Zd„ Z	d„ Z
d„ Zd„ Zeˆ fd„¦   «         Zej        ˆ fd„¦   «         Zd„ Zˆ xZS )ÚQuarterLengthAxisz7
    Axis subclass for dealing with QuarterLengths
    zñ
            bool or int for whether to scale numbers logarithmically.  Adds (log2) to the
            axis label if used.  If True (default) then log2 is assumed.  If an int, then
            log the int (say, 10) is used. instead.
        zž
            If used then duration names replace numbers for ticks.
            If set, probably will want to change tickFontSize in the graph object
        )ÚuseLogScaleÚuseDurationNamesr   r   zQuarter Length)ÚquarterLengthÚqlÚquarterlengthsÚ	durationsr   r#   r   Nr   c                óh   •— t          ¦   «                              ||¦  «         d| _        d| _        d S )NTF)r{   r,   r  r  r|   s      €r+   r,   zQuarterLengthAxis.__init__j  s3   ø€ Ý‰Œ×Ò˜ Ñ*Ô*Ð*ØˆÔØ %ˆÔÐÐr-   c                ó@   — |                       |j        j        ¦  «        S r9   )Ú
dataFromQLr   r  rN   s      r+   rO   z#QuarterLengthAxis.extractOneElemento  s   € Ø�Š˜qœzÔ7Ñ8Ô8Ð8r-   c                óz   — | j         r|                      |¦  «        }n|dk    rt          |¦  «        }n| j        }|S r÷   )r  ÚremapQuarterLengthÚfloatrÑ   )r*   r  r   s      r+   r  zQuarterLengthAxis.dataFromQLr  sE   € ØÔð 	!Ø×'Ò'¨Ñ+Ô+ˆAˆAØ�!ŠVˆVÝ�b‘	”	ˆAˆAàÔ ˆAØˆr-   c                óê  — | j         }|sg S | j        j        r|                     ¦   «         }n|                     ¦   «         }|                     | j        j        ¦  «        }t          j        |d¦  «        }g }t          |¦  «        D ]l}|  	                    |¦  «        }| j
        rt          j        |¦  «        j        }nt          t          |d¦  «        ¦  «        }|                     ||f¦  «         Œm|S )aA  
        Get ticks for quarterLength.

        If `remap` is `True` (the default), the `remapQuarterLength()`
        method will be used to scale displayed quarter lengths
        by log base 2.

        Note that mix and max do nothing, but must be included
        in order to set the tick style.

        >>> s = stream.Stream()
        >>> for t in ['32nd', '16th', 'eighth', 'quarter', 'half']:
        ...     n = note.Note()
        ...     n.duration.type = t
        ...     s.append(n)

        >>> plotS = graph.plot.PlotStream(s)
        >>> ax = graph.axis.QuarterLengthAxis(plotS)
        >>> ax.ticks()
        [(-3.0, '0.12'), (-2.0, '0.25'), (-1.0, '0.5'), (0.0, '1.0'), (1.0, '2.0')]

        >>> ax.useLogScale = False
        >>> ax.ticks()
        [(0.125, '0.12'), (0.25, '0.25'), (0.5, '0.5'), (1.0, '1.0'), (2.0, '2.0')]
        >>> ax.useDurationNames = True
        >>> ax.ticks()
        [(0.125, '32nd'), (0.25, '16th'), (0.5, 'Eighth'), (1.0, 'Quarter'), (2.0, 'Half')]

        >>> nGrace = note.Note()
        >>> nGrace.getGrace(inPlace=True)
        >>> s.append(nGrace)
        >>> plotS = graph.plot.PlotStream(s)
        >>> ax = graph.axis.QuarterLengthAxis(plotS)
        >>> ax.ticks()[0]
        (-4.0, '0.0')

        >>> ax.useLogScale = False
        >>> ax.ticks()[0]
        (0.0625, '0.0')
        r  r   )r   r)   ÚrecurseÚiterr  ÚclassFilterListÚelementAnalysisÚattributeCountÚsortedr  r  r   ÚDurationÚfullNamer&   Úroundr\   )r*   r�   ÚsSrcÚmappingre   r  r   r7   s           r+   re   zQuarterLengthAxis.ticks{  sê   € ðT ŒKˆØð 	ØˆIØŒ[Ô ð 	Ø—9’9‘;”;ˆDˆDà—6’6‘8”8ˆDà×&Ò& t¤{Ô'BÑCÔCˆå!Ô0°°ÑGÔGˆàˆÝ˜‘/”/ð 	%ð 	%ˆBØ—’ Ñ#Ô#ˆAØÔ$ð *Ý Ô)¨"Ñ-Ô-Ô6��å�E " a™LœLÑ)Ô)�Ø�LŠL˜!˜U˜Ñ$Ô$Ð$Ð$Øˆr-   c                óF   — | j         du rdS | j         du rdS d| j         d›d�S )a¸  
        Returns a TeX formatted tag to the axis label depending on whether
        the scale is logarithmic or not.  Checks `.useLogScale`

        >>> a = graph.axis.QuarterLengthAxis()
        >>> a.useLogScale
        True
        >>> a.labelLogTag()
        ' ($log_2$)'

        >>> a.useLogScale = False
        >>> a.labelLogTag()
        ''

        >>> a.useLogScale = 10
        >>> a.labelLogTag()
        ' ($log_10$)'
        FrŽ   Tz
 ($log_2$)z ($log_Údz$))r  r6   s    r+   ÚlabelLogTagzQuarterLengthAxis.labelLogTag»  sA   € ð& Ô˜uÐ$Ð$Ø�2ØÔ Ð%Ð%Ø�<à3˜TÔ-Ð3Ð3Ð3Ð3Ð3r-   c                óT   •— t          ¦   «         j        |                      ¦   «         z   S r9   )r{   r7   r0  )r*   r/   s    €r+   r7   zQuarterLengthAxis.labelÕ  s    ø€ å‰wŒwŒ}˜t×/Ò/Ñ1Ô1Ñ1Ð1r-   c                ó.   •— |t          ¦   «         _        d S r9   )r{   r7   )r*   r<   r/   s     €r+   r7   zQuarterLengthAxis.labelÙ  s   ø€ à�‰ŒŒˆˆr-   c                ó    — |dk    r| j         }	 t          j        t          |¦  «        ¦  «        S # t          $ r t          d|› �¦  «        ‚w xY w)z�
        Remap a quarter length as its log2.  Essentially it's
        just math.log2(x), but x=0 is replaced with self.graceNoteQL.
        r   zcannot take log of x value: )rÑ   rY   Úlog2r!  Ú
ValueErrorr   )r*   r   s     r+   r   z$QuarterLengthAxis.remapQuarterLengthÝ  sg   € ð
 �Š6ˆ6ØÔ ˆAð	EÝ”9�U 1™XœXÑ&Ô&Ð&øÝð 	Eð 	Eð 	EÝ Ð!CÀÐ!CÐ!CÑDÔDÐDð	Eøøøs	   � 0 °Arj   )r0   rk   rl   rm   r   rn   r5   r   r,   rO   r  re   r0  ro   r7   rp   r   rª   r«   s   @r+   r  r  R  s4  ø€ € € € € € ðð ðð
ð
!ð 
!€Ið 
ð 
ð 
ñ 
ð $€Lð#%€Jð %ð %ð %ñ %ð&ð &ð &ð &ð &ð &ð
9ð 9ð 9ðð ð ð>ð >ð >ð@4ð 4ð 4ð4 ð2ð 2ð 2ð 2ñ „Xð2ð „\ðð ð ð ñ „\ððEð Eð Eð Eð Eð Eð Er-   r  c                  óR   ‡ — e Zd ZU dZddiZded<   dZded<   d	d
efˆ fd„	Zd„ Z	ˆ xZ
S )ÚOffsetEndAxiszI
    An Axis that gives beginning and ending values for each element
    ÚnoteSpacingzt
            amount in QL to leave blank between untied notes.
            (default = self.graceNoteQL)
            r   r   )Ú	offsetEndÚ	timespansÚtimespanr#   r   Nr   c                óŒ   •— t          ¦   «                              ||¦  «         || _        |t          k    r| j        | _        d S d S r9   )r{   r,   r8  ÚUSE_GRACE_NOTE_SPACINGrÑ   )r*   r)   r   r8  r/   s       €r+   r,   zOffsetEndAxis.__init__÷  sJ   ø€ Ý‰Œ×Ò˜ Ñ*Ô*Ð*Ø&ˆÔØÕ0Ò0Ð0Ø#Ô/ˆDÔÐÐð 1Ð0r-   c                ó,  — t          |                     | j        ¦  «        ¦  «        }t          |j        j        ¦  «        }|| j        k     r| j        }n>|| j        dz  k    r0t          |d¦  «        r|j        �|j        j        dv rn
|| j        z  }||fS )Nr   Útie)ÚstartÚcontinue)	r!  rå   r   r   r  r8  r¶   r?  r  )r*   rF   rH   ÚoffÚuseQLs        r+   rO   zOffsetEndAxis.extractOneElementý  sž   € Ý�A×*Ò*¨4¬;Ñ7Ô7Ñ8Ô8ˆÝ�a”jÔ.Ñ/Ô/ˆØ�4Ô#Ò#Ð#ØÔ$ˆEˆEØ�TÔ%¨Ñ)Ò)Ð)Ý�q˜%Ñ Ô ð * Q¤UÐ%6¸1¼5¼:ÐI^Ð;^Ð;^Øà˜Ô)Ñ)�à�Uˆ|Ðr-   )r0   rk   rl   rm   r   rn   r   r=  r,   rO   rª   r«   s   @r+   r7  r7  ë  s™   ø€ € € € € € ðð ð 	ð ð!€Ið ð ð ñ ð #I€JÐHÐHÐHÑHà"¨SÐ>Tð 0ð 0ð 0ð 0ð 0ð 0ðð ð ð ð ð ð r-   r7  c                  ó>   ‡ — e Zd ZU dZdZdZded<   d	ˆ fd„	Zd„ Zˆ xZ	S )
ÚDynamicsAxisz1
    Axis subclass for dealing with Dynamics
    ÚDynamic)Údynamicr	   Úvolumer#   r   Nc                ó  •— |€*d| _         t          t          j        ¦  «        dz
  | _        d S t          ¦   «                              |¦  «         t          | j         ¦  «        | _         t          | j        ¦  «        | _        d S )Nr   r   )r   r  r	   Ú
shortNamesr   r{   rU   rX   )r*   rT   r/   s     €r+   rU   z"DynamicsAxis.setBoundariesFromData  sh   ø€ Øˆ>ØˆDŒMÝ¥Ô 3Ñ4Ô4°qÑ8ˆDŒMˆMˆMå‰GŒG×)Ò)¨&Ñ1Ô1Ð1Ý ¤Ñ.Ô.ˆDŒMÝ ¤Ñ.Ô.ˆDŒMˆMˆMr-   c                óÖ   — g }| j         €|                      ¦   «          t          | j         | j        dz   ¦  «        D ]-}|                     |dt
          j        |         › d�f¦  «         Œ.|S )ak  
        Utility method to get ticks in dynamic values:

        >>> ax = graph.axis.DynamicsAxis()
        >>> ax.ticks()
        [(0, '$pppppp$'), (1, '$ppppp$'), (2, '$pppp$'), (3, '$ppp$'), (4, '$pp$'),
         (5, '$p$'), (6, '$mp$'), (7, '$mf$'), (8, '$f$'), (9, '$fp$'), (10, '$sf$'),
         (11, '$ff$'), (12, '$fff$'), (13, '$ffff$'), (14, '$fffff$'), (15, '$ffffff$')]

        A minimum and maximum dynamic index can be specified as minValue and maxValue

        >>> ax.minValue = 3
        >>> ax.maxValue = 6
        >>> ax.ticks()
        [(3, '$ppp$'), (4, '$pp$'), (5, '$p$'), (6, '$mp$')]

        Nr   ú$)r   rU   r[   r   r\   r	   rJ  )r*   re   r¡   s      r+   re   zDynamicsAxis.ticks  sx   € ð$ ˆØŒ=Ð Ø×&Ò&Ñ(Ô(Ð(Ý�t”} d¤m°aÑ&7Ñ8Ô8ð 	=ð 	=ˆAà�LŠL˜!Ð:¥Ô!4°QÔ!7Ð:Ð:Ð:Ð;Ñ<Ô<Ð<Ð<Øˆr-   r9   )
r0   rk   rl   rm   r5   r   rn   rU   re   rª   r«   s   @r+   rE  rE    sp   ø€ € € € € € ðð ð €LØ"C€JÐCÐCÐCÑCð/ð /ð /ð /ð /ð /ðð ð ð ð ð ð r-   rE  c                  óP   ‡ — e Zd ZU dZddiZded<   dZdZded	<   dˆ fd„	Zd„ Z	ˆ xZ
S )ÚCountingAxisaÌ  
    Axis subclass for counting data in another Axis.

    Used for histograms, weighted scatter, etc.

    >>> bach = corpus.parse('bwv66.6')
    >>> plotS = graph.plot.PlotStream(bach)
    >>> plotS.axisX = graph.axis.PitchSpaceAxis(plotS, 'x')
    >>> plotS.axisY = graph.axis.CountingAxis(plotS)
    >>> plotS.doneAction = None
    >>> plotS.run()
    >>> plotS.data
    [(42.0, 1, {}), (45.0, 1, {}), (46.0, 1, {}), (47.0, 5, {}), (49.0, 6, {}), ...]
    Ú	countAxeszf
            a string or tuple of strings representing an axis or axes to use in counting
            r   r   ÚCount)ÚcountÚquantityÚ	frequencyÚcountingr#   r   Nr   c                óZ   •— t          ¦   «                              ||¦  «         d| _        d S rj   )r{   r,   rO  r|   s      €r+   r,   zCountingAxis.__init__R  s(   ø€ Ý‰Œ×Ò˜ Ñ*Ô*Ð*ØˆŒˆˆr-   c                ó&  ‡ ‡— ‰ j         }|€g S ddlm} ‰ j        }t	          j        |¦  «        s|f}t          ˆ fd„|D ¦   «         ¦  «        }‰ j        ‰ j                 } ||Ž Šˆfd„|j	        D ¦   «         }i }|j	        D ]P} ‰|¦  «        }	|d         }
t          |
t          ¦  «        sŒ+|	|v r||	                              |
¦  «         ŒK|
||	<   ŒQt          j        |¦  «        }g }|D ]Ž}dgt          |¦  «        dz   z  }t          |¦  «        dk    r|D ]}||         ||<   Œn|||d         <   ||         ||<   |                     |i ¦  «        }
|                     t          |¦  «        |
fz   ¦  «         Œ�t%          |¦  «        |_	        |j	        S )zS
        Replace client.data with a list that only includes each key once.
        Nr   )Ú
itemgetterc              3  ó2   •K  — | ]}‰j         |         V — Œd S r9   )r   )Ú.0r   r*   s     €r+   ú	<genexpr>z/CountingAxis.postProcessData.<locals>.<genexpr>c  s+   øè è € ÐQÐQ¸8˜DÔ,¨XÔ6ÐQÐQÐQÐQÐQÐQr-   c                ó&   •— g | ]} ‰|¦  «        ‘ŒS rM   rM   )rY  Ú
innerTupleÚselectors     €r+   ú
<listcomp>z0CountingAxis.postProcessData.<locals>.<listcomp>f  s#   ø€ ÐKÐKÐK°˜˜ Ñ,Ô,ÐKÐKÐKr-   r   r   )r)   ÚoperatorrW  rO  r   Ú
isIterableÚtupler   r   Údatar%   ÚdictÚupdateÚcollectionsÚCounterr  Úgetr\   r(  )r*   r)   rW  rO  ÚaxesIndicesÚ	thisIndexÚrelevantDataÚtupleFormatDictÚ	dataPointÚ	dataIndexrH   ÚcounterÚnewClientDataÚ
counterKeyÚ	innerListÚdependentIndexr]  s   `               @r+   ri   zCountingAxis.postProcessDataV  s  øø€ ð ”ˆØˆ>ØˆIà'Ð'Ð'Ð'Ð'Ð'Ø”Nˆ	ÝÔ  Ñ+Ô+ð 	%Ø"˜ˆIåÐQÐQÐQÐQÀyÐQÑQÔQÑQÔQˆØÔ$ T¤]Ô3ˆ	Ø�:˜{Ð+ˆØKÐKÐKÐK¸v¼{ÐKÑKÔKˆð ˆØœð 	8ð 	8ˆIØ ˜ Ñ+Ô+ˆIØ" 2œˆJÝ˜j­$Ñ/Ô/ð ØØ˜OÐ+Ð+Ø 	Ô*×1Ò1°*Ñ=Ô=Ð=Ð=à-7� 	Ñ*Ð*åÔ% lÑ3Ô3ˆàˆØ!ð 		Cð 		CˆJØ˜¥# kÑ"2Ô"2°QÑ"6Ñ7ˆIÝ�;ÑÔ !Ò#Ð#Ø&1ð Kð K�NØ0:¸>Ô0J�I˜nÑ-Ð-ðKð -7�	˜+ aœ.Ñ)Ø#*¨:Ô#6ˆI�iÑ Ø(×,Ò,¨Z¸Ñ<Ô<ˆJØ× Ò ¥ yÑ!1Ô!1°Z°MÑ!AÑBÔBÐBÐBå˜]Ñ+Ô+ˆŒØŒ{Ðr-   )Nr   )r0   rk   rl   rm   r   rn   r5   r   r,   ri   rª   r«   s   @r+   rN  rN  :  s•   ø€ € € € € € ðð ð 	ð ð!€Ið ð ð ñ ð €LØ"P€JÐPÐPÐPÑPðð ð ð ð ð ð-ð -ð -ð -ð -ð -ð -r-   rN  c                  ó   — e Zd Zd„ ZdS )ÚTestc           
     ó:  — d„ }ddl m} ddlm} |                     d¦  «        } ||¦  «        }d |_        t          |d¦  «        |_        ||j        _        | 	                    ¦   «          |  
                    |j        ddi fddd	d
ifddi fddd	d
ifg¦  «         d S )Nc                ó<   — | j         j        �d|d<   | j         j        S )NÚredÚcolor)r
   Ú
accidentalÚdiatonicNoteNum)rF   rH   s     r+   ÚcountingAxisFormatterz:Test.testCountingAxisFormat.<locals>.countingAxisFormatterŠ  s"   € ØŒwÔ!Ð-Ø&+�
˜7Ñ#Ø”7Ô*Ð*r-   r   )Ú	Histogram)Ú	converterz$tinynotation: 4/4 C4 D E F C D# E F#r   r   r   rx  rw  é   é   )Úmusic21.graph.plotr|  Úmusic21r}  ÚparseÚ
doneActionr   ÚaxisXrO   ÚrunÚassertEqualrb  )r*   r{  r|  r}  r�   Úhists         r+   ÚtestCountingAxisFormatzTest.testCountingAxisFormat‰  sâ   € ð	+ð 	+ð 	+ð
 	1Ð0Ð0Ð0Ð0Ð0Ø%Ð%Ð%Ð%Ð%Ð%Ø�OŠOÐBÑCÔCˆØˆy˜‰|Œ|ˆØˆŒÝ˜$ ‘_”_ˆŒ
Ø'<ˆŒ
Ô$Ø�Š‰
Œ
ˆ
Ø×Ò˜œØ˜a ˜* q¨!¨g°uÐ-=Ð&>Ø˜a ˜* q¨!¨g°uÐ-=Ð&>ð@ñ	Aô 	Að 	Að 	Að 	Ar-   N)r0   rk   rl   rˆ  rM   r-   r+   rt  rt  ‡  s(   € € € € € ðAð Að Að Að Ar-   rt  Ú__main__)+rm   Ú
__future__r   re  rY   r–   ÚtypingÚtÚunittestÚmusic21.graph.utilitiesr   r   r�  r   r   r   r	   r
   r   r   Úmusic21.analysisr   r&  r   ÚTYPE_CHECKINGr   r=  ÚProtoM21Objectr   rr   r­   rº   rÆ   rÐ   r×   r  r7  rE  rN  ÚTestCasert  r0   ÚmainTestrM   r-   r+   ú<module>r”     sµ  ððð ð #Ð "Ð "Ð "Ð "Ð "à Ð Ð Ð Ø €€€Ø 	€	€	€	Ø Ð Ð Ð Ø €€€à LÐ LÐ LÐ LÐ LÐ LÐ LÐ Là Ð Ð Ð Ð Ð Ø Ð Ð Ð Ð Ð Ø Ð Ð Ð Ð Ð Ø Ð Ð Ð Ð Ð Ø Ð Ð Ð Ð Ð Ø Ð Ð Ð Ð Ð Ø Ð Ð Ð Ð Ð à 8Ð 8Ð 8Ð 8Ð 8Ð 8Ø *Ð *Ð *Ð *Ð *Ð *ð „?ð ØÐÐÐÐÐð Ð ðuð uð uð uð uˆ7Ô!ñ uô uð uðtRð Rð Rð Rð R�ñ Rô Rð RðjE;ð E;ð E;ð E;ð E;�Yñ E;ô E;ð E;ðP==ð ==ð ==ð ==ð ==�Yñ ==ô ==ð ==ð@!ð !ð !ð !ð !˜>ñ !ô !ð !ð@!ð !ð !ð !ð !�4ñ !ô !ð !ð&X ð X ð X ð X ð X �ñ X ô X ð X ðv
VEð VEð VEð VEð VE˜ñ VEô VEð VEðrð ð ð ð �Jñ ô ð ðD(ð (ð (ð (ð (�4ñ (ô (ð (ðZIð Ið Ið Ið I�4ñ Iô Ið IðZAð Að Að Að Aˆ8Ôñ Aô Að Að, ˆzÒÐØ€N€N€NØ€GÔ�TÑÔÐÐÐð Ðr-   