
    3j<                       U 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m	Z	 ddlm
Z
 ddlmZ e
j                  e
j                  e
j                  e
j                  e
j                  dZded	<   g d
Zded<   eD  cg c]  } | d   	 c} ZddZddZddZd ZddZddZ	 	 	 	 ddZ	 	 	 	 dddZ G d dej8                        Zedk(  rddlZ ej@                  e       yyc c} w ) z7
Functions that find appropriate plots for graph.plot.
    )annotationsN)axis)plot)
primitives)ambitusdolaninstrumentskey	pianorollz%dict[str, type[plot.PlotStreamMixin]]PLOTCLASS_SHORTCUTS))horizontalbarbar
horizontalr   piano)	histogramhistocount)scatterpoint)scatterweightedweightedscatterweighted)3dbars3d)	colorgridgridwindowwindowed)horizontalbarweightedbarweightedweightedbarzlist[tuple[str, ...]]FORMAT_SYNONYMSc                    g } t        t        j                        D ]  }t        t        |      }t	        |      st        |t        j                        r:t        |d      sGt        j                  |j                  v sdt        j                  |j                  v s| j                  t        j                  t         t        j                     |              | S )a{  
    return a list of all PlotStreamMixin subclasses. Returns a list sorted by name

    >>> graph.findPlot.getPlotClasses()
    [<class 'music21.graph.plot.Dolan'>,
     <class 'music21.graph.plot.Features'>,
     <class 'music21.graph.plot.Histogram'>,
     <class 'music21.graph.plot.HistogramPitchClass'>,
     <class 'music21.graph.plot.HistogramPitchSpace'>,
     ...]
    __mro__)sortedr   __dict__getattrcallable
isinstancetypesFunctionTypehasattrPlotStreamMixinr$   r   Graphappendtcasttype)allPlotinames      C/DATA/.local/lib/python3.12/site-packages/music21/graph/findPlot.pygetPlotClassesr7   2   s     13GDMM"tQTN"4););<D),((DLL8$$4NN166$t';';"<dCD # N    c                    g } t        t        j                        D ]e  }t        t        |      }t	        |      st        |t        j                        r:t        |t        j                        sU| j                  |       g | S )a7  
    return a list of all Axis subclasses.  Returns a list sorted by name

    >>> graph.findPlot.getAxisClasses()
    [<class 'music21.graph.axis.Axis'>,
     <class 'music21.graph.axis.CountingAxis'>,
     <class 'music21.graph.axis.DynamicsAxis'>,
     <class 'music21.graph.axis.OffsetAxis'>,
     ...]
    )r%   r   r&   r'   r(   r)   r*   r+   
issubclassAxisr/   )allAxisr4   r5   s      r6   getAxisClassesr=   K   s`     &(GDMM"tQTN"4););<tTYY/NN4  # Nr8   c                    |
t               }g }|D ]>  }| r|j                  |j                         !|j                  |j                  d          @ |S )a  
    >>> graph.findPlot.getAxisQuantities()
    ['generic', 'count', 'dynamic', 'offset', 'offsetEnd',
     'pitchGeneric', 'pitchClass', 'pitchSpace', 'octave', 'position', 'quarterLength']

    >>> graph.findPlot.getAxisQuantities(synonyms=True)
    ['generic', 'one', 'nothing', 'blank', 'count', 'quantity', 'frequency', ...]

    >>> theseAxes = [graph.axis.CountingAxis, graph.axis.OffsetAxis]
    >>> graph.findPlot.getAxisQuantities(axesToCheck=theseAxes)
    ['count', 'offset']

    >>> graph.findPlot.getAxisQuantities(True, axesToCheck=theseAxes)
    ['count', 'quantity', 'frequency', 'counting',
     'offset', 'measure', 'offsets', 'measures', 'time']

    r   )r=   extend
quantitiesr/   )synonymsaxesToCheckallQuantitiesaxClasss       r6   getAxisQuantitiesrE   `   sY    $ $&M  !3!34  !3!3A!67	 
 r8   c                v    | j                         } | j                  dd      } t        D ]  }| |v s|d   c S  | S )a  
    Replace possible user format strings with defined format names as used herein.
    Returns string unaltered if no match.

    >>> graph.findPlot.userFormatsToFormat('horizontal')
    'horizontalbar'
    >>> graph.findPlot.userFormatsToFormat('Weighted Scatter')
    'scatterweighted'
    >>> graph.findPlot.userFormatsToFormat('3D')
    '3dbars'

    Unknown formats pass through unaltered.

    >>> graph.findPlot.userFormatsToFormat('4D super chart')
    '4dsuperchart'
      r   )lowerreplacer"   )
userFormatopts     r6   userFormatsToFormatrM   }   sG    $ !!#J##C,Jq6M  r8   c                    t        |       j                         } |
t               }g }|D ]1  }|j                  j                         | k(  s!|j	                  |       3 |S )a|  
    Given a graphFormat, find a list of plots that match:

    >>> graph.findPlot.getPlotClassesFromFormat('scatterweighted')
    [<class 'music21.graph.plot.ScatterWeighted'>,
     <class 'music21.graph.plot.ScatterWeightedPitchClassQuarterLength'>,
     <class 'music21.graph.plot.ScatterWeightedPitchSpaceDynamicSymbol'>,
     <class 'music21.graph.plot.ScatterWeightedPitchSpaceQuarterLength'>]

    Or give a list of plot classes to check:

    >>> pcs = [graph.plot.ScatterWeighted, graph.plot.Dolan]
    >>> graph.findPlot.getPlotClassesFromFormat('scatterweighted', pcs)
    [<class 'music21.graph.plot.ScatterWeighted'>]

    )rM   rI   r7   	graphTyper/   )graphFormatcheckPlotClassesfilteredPlotsps       r6   getPlotClassesFromFormatrT      s`    " &k288:K)+M;;+-  #  r8   c                B    t               D ]  }t        ||       s|c S  y)a_  
    given an axis value return the single best axis for the value, or None

    uses Axis.quantities

    >>> getAxis = graph.findPlot.getAxisClassFromValue

    >>> getAxis('counting')
    <class 'music21.graph.axis.CountingAxis'>

    >>> getAxis('pc')
    <class 'music21.graph.axis.PitchClassAxis'>

    >>> print(getAxis('boogie'))
    None
    N)r=   axisMatchesValue)	axisValuethisAxiss     r6   getAxisClassFromValuerY      s%    " #$Hi0O % r8   c                p    |j                         }| j                  D ]  }|j                         |k(  s y y)a  
    Returns Bool about whether axisValue.lower() is anywhere in axisClass.quantities

    >>> ax = graph.axis.CountingAxis
    >>> graph.findPlot.axisMatchesValue(ax, 'counting')
    True
    >>> graph.findPlot.axisMatchesValue(ax, 'count')
    True
    >>> graph.findPlot.axisMatchesValue(ax, 'offset')
    False

    Works on an instantiated object as well:

    >>> ax = graph.axis.CountingAxis()
    >>> graph.findPlot.axisMatchesValue(ax, 'counting')
    True
    >>> graph.findPlot.axisMatchesValue(ax, 'flute')
    False

    * Changed in v8: Must send a subclass of axis.Axis or an instance.
        `None` is no longer supported.
    TF)rI   r@   )	axisClassrW   vs      r6   rV   rV      s6    0 !I!!779	! " r8   c                   fd}| gdgdz  k(  rd} | t         v rt         |    g}nt        |       }|s| r| cd} t               }g }dfdfdffD ]\  \  }}|D ]R  }	|||	j                  vr|j	                  |	       &|	j                  |   }
t        |
|      rB|j	                  |	       T ^ |D cg c]	  }||vs| }}|r| st        |      dk(  r|S  ||      S g }dfdfdffD ]W  \  }}|D ]M  }	|d	}|	j                  j                         D ]  \  }}
t        |
|      sd
} n |r=|j	                  |	       O Y |D cg c]	  }||vs| }}|r| st        |      dk(  r|S  ||      S t        j                         }dfdfdffD ]  \  }}|	t        |      }||||<    t        |      dk(  r|d   |fgS  ||      }|r|d   |fgS |d   |fgS c c}w c c}w )a
  
    Returns either a list of plot classes to make if there is a predetermined class

    or a list of tuples where the first element of each tuple is the plot class
    and the second is a dict of {'x': axisXClass, 'y': axisYClass} etc.

    Default is pianoroll

    >>> graph.findPlot.getPlotsToMake()
    [<class 'music21.graph.plot.HorizontalBarPitchSpaceOffset'>]

    >>> graph.findPlot.getPlotsToMake('scatter')
    [<class 'music21.graph.plot.Scatter'>,
     <class 'music21.graph.plot.ScatterPitchClassOffset'>,
     <class 'music21.graph.plot.ScatterPitchClassQuarterLength'>,
     <class 'music21.graph.plot.ScatterPitchSpaceDynamicSymbol'>,
     <class 'music21.graph.plot.ScatterPitchSpaceQuarterLength'>]

    >>> graph.findPlot.getPlotsToMake('scatter', 'offset', 'pitchClass')
    [<class 'music21.graph.plot.ScatterPitchClassOffset'>]

    Try in wrong order:

    >>> graph.findPlot.getPlotsToMake('scatter', 'pitchClass', 'offset')
    [<class 'music21.graph.plot.ScatterPitchClassOffset'>]

    Try giving just one value:

    >>> graph.findPlot.getPlotsToMake('scatter', 'offset')
    [<class 'music21.graph.plot.ScatterPitchClassOffset'>]

    >>> graph.findPlot.getPlotsToMake('scatter', 'ql')  # abbreviation
    [<class 'music21.graph.plot.ScatterPitchClassQuarterLength'>,
     <class 'music21.graph.plot.ScatterPitchSpaceQuarterLength'>]

    Just one value, but it is in the wrong axis:

    >>> graph.findPlot.getPlotsToMake('scatter', 'pitchClass')
    [<class 'music21.graph.plot.ScatterPitchClassOffset'>,
     <class 'music21.graph.plot.ScatterPitchClassQuarterLength'>]

    Create a graph that does not exist:

    >>> graph.findPlot.getPlotsToMake('scatter', 'offset', 'dynamics')
    [(<class 'music21.graph.plot.Scatter'>,
      OrderedDict([('x', <class 'music21.graph.axis.OffsetAxis'>),
                   ('y', <class 'music21.graph.axis.DynamicsAxis'>)]))]

    Just a couple of values:

    >>> graph.findPlot.getPlotsToMake('offset', 'dynamics')
    [(<class 'music21.graph.plot.Scatter'>,
      OrderedDict([('x', <class 'music21.graph.axis.OffsetAxis'>),
                   ('y', <class 'music21.graph.axis.DynamicsAxis'>)]))]

    Just one value:

    >>> graph.findPlot.getPlotsToMake('octave')
    [(<class 'music21.graph.plot.Histogram'>,
      OrderedDict([('x', <class 'music21.graph.axis.PitchSpaceOctaveAxis'>)]))]

    Three values:

    >>> graph.findPlot.getPlotsToMake('offset', 'dynamics', 'count')
    [(<class 'music21.graph.plot.ScatterWeighted'>,
      OrderedDict([('x', <class 'music21.graph.axis.OffsetAxis'>),
                   ('y', <class 'music21.graph.axis.DynamicsAxis'>),
                   ('z', <class 'music21.graph.axis.CountingAxis'>)]))]

    c                    t        fD cg c]  }|d	 c}      }d}|dk(  rd}n|dk(  rd}n|dk(  rd}t        ||       }|r|S | S c c}w )N   rH      r      r   r   )lenrT   )graphClassesToChooseFromvalnumAxesbestGraphTypeinnerFilteredClassesxValueyValuezValues        r6   _bestPlotTypez%getPlotsToMake.<locals>._bestPlotType7  su    VVV$<P$<Sq$<PQa<-M\%M\'M7G_`''++ Qs
   AAN   r   xyzr_   FTr   )r   rT   r7   axesClassesr/   rV   rb   itemscollectionsOrderedDictrY   )rP   rh   ri   rj   rk   graphClassesgraphRemove
axisLetterrW   gcaxisObjClassrm   graphClassesFiltered_axisLetterfoundunused_axisLetteraxisDictr[   filteredClassess    ```               r6   getPlotsToMaker      s   T," 	VVV,
:! ))+K89/< K!,ff%' K#&-#vf!N
IB /""2&>>*5L#L)<""2&  "O (4L|!q7KA|L#23q8'' !566 K$'=3-#v"OYB E35>>3G3G3I/!<#L)< E 4J
 ""2&  #P (4L|!q7KA|L#23q8'' !566 &&(H#&-#vf!N
I))4	( "O <Aa(+,,'5$Q'233!!_h/00a M, Ms   ?	G/	G/	G4 G4c                      e Zd Zd Zd Zy)Testc                   t        d      }| j                  |t        j                  g       t        d      }| j                  |t        j                  g       t               }| j                  |t        j
                  g       t        d      }| j                  |t        j                  g       t        d      }| j                  |t        j                  g       t        d      }| j                  |t        j                  g       t        d      }| j                  |t        j                  g       t        d      }| j                  |t        j                  g       t        d      }| j                  |t        j                  g       t        d	dd
      }| j                  |t        j                  g       t        d	dd      }| j                  |t        j                  g       y )Nr   r
   durationquarterLengthpspitch
pitchspace
pitchClassr   qlpcoffset)r   assertEqualr   WindowedAmbitusWindowedKeyHorizontalBarPitchSpaceOffsetHistogramQuarterLengthHistogramPitchSpaceHistogramPitchClassScatterPitchSpaceQuarterLengthScatterPitchClassOffsetselfposts     r6   testGetPlotsToMakeAzTest.testGetPlotsToMakeA  s   i( 4 456e$ 0 012  B BCD j) ; ;<=o. ; ;<=d# 8 89:g& 8 89:l+ 8 89:l+ 8 89:i$7 C CDEix8 < <=>r8   c                    t        d      }| j                  |t        j                  g       t        d      }| j                  |t        j                  g       y )Nr   r	   )r   r   r   Dolanr   s     r6   testGetPlotsToMakeBzTest.testGetPlotsToMakeB  sB    g&

|,m,

|,r8   N)__name__
__module____qualname__r   r    r8   r6   r   r     s    ?<-r8   r   __main__)returnz list[type[plot.PlotStreamMixin]])r   zlist[type[axis.Axis]])FN)N)rW   strr   ztype[axis.Axis] | None)r[   ztype[axis.Axis] | axis.AxisrW   r   r   bool)NNNN)rP   z
str | None)!__doc__
__future__r   rr   r*   unittesttypingr0   music21.graphr   r   r   r   r   r   r   r   __annotations__r"   FORMATSr7   r=   rE   rM   rT   rY   rV   r   TestCaser   r   music21mainTest)syns   0r6   <module>r      s   #       $ ##ZZ::33> : *&  -
-_c3q6_
-2*:<8. #(,> ,0g1T#-8 #-L zGT ] .s   8C!