
    rh<                       % S r SSKJr  SSKrSSKrSSKrSSKrSSKJ	r	  SSKJ
r
  SSKJr  \
R                  \
R                  \
R                  \
R                  \
R                  S.rS\S	'   / S
QrS\S'   \ V s/ s H  o S   PM	     sn rSS jrSS jrSS jrS rSS jrSS jr    SS jr    SSS jjr " S S\R8                  5      r\S:X  a  SSKr\R@                  " \5        ggs  sn f ) 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                    / n [        [        R                  5       H  n[        [        U5      n[	        U5      (       d  M%  [        U[        R                  5      (       a  MF  [        US5      (       d  MY  [        R                  UR                  ;   d  My  [        R                  UR                  ;   d  M  U R                  [        R                  " [         [        R                     U5      5        M     U $ )aW  
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      P/home/james-whalen/.local/lib/python3.13/site-packages/music21/graph/findPlot.pygetPlotClassesr7   3   s     13GDMM"tQTNN"4););<<D),,((DLL8$$4NN166$t';';"<dCD # N    c                 4   / n [        [        R                  5       Hx  n[        [        U5      n[	        U5      (       d  M%  [        U[        R                  5      (       a  MF  [        U[        R                  5      (       d  Mg  U R                  U5        Mz     U $ )a  
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=   L   sh     &(GDMM"tQTNN"4););<<tTYY//NN4  # Nr8   c                    Uc
  [        5       n/ nU HE  nU (       a  UR                  UR                  5        M'  UR                  UR                  S   5        MG     U$ )aq  
>>> 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   a   sY    & $&M  !3!34  !3!3A!67	 
 r8   c                |    U R                  5       n U R                  SS5      n [         H  nX;   d  M
  US   s  $    U $ )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      sE    $ !!#J##C,Jq6M  r8   c                    [        U 5      R                  5       n Uc
  [        5       n/ nU H4  nUR                  R                  5       U :X  d  M#  UR	                  U5        M6     U$ )aP  
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                N    [        5        H  n[        X5      (       d  M  Us  $    g)a7  
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&    " #$H00O % r8   c                v    UR                  5       nU R                   H  nUR                  5       U:X  d  M    g   g)aA  
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    2 !I!!779	! " r8   c                  ^^^ UUU4S jnU TTT/S/S-  :X  a  Sn U [         ;   a  [         U    /nO[        U 5      nU(       d  U (       a  U TTsmmmSn [        5       n/ nST4ST4ST44 Hg  u  pxU H\  n	Uc  M  XyR                  ;  a  UR	                  U	5        M*  U	R                  U   n
[        X5      (       a  MK  UR	                  U	5        M^     Mi     U Vs/ s H  oU;  d  M
  UPM     nnU(       a   U (       d  [        U5      S:X  a  U$ U" U5      $ / nST4ST4ST44 Hf  u  pxU H[  n	Uc  M  S	nU	R                  R                  5        H  u  p[        X5      (       d  M  S
n  O   U(       a  MJ  UR	                  U	5        M]     Mh     U Vs/ s H  oU;  d  M
  UPM     nnU(       a   U (       d  [        U5      S:X  a  U$ U" U5      $ [        R                  " 5       nST4ST4ST44 H  u  pxUc  M
  [        U5      nUc  M  UX'   M      [        U5      S:X  a  US   U4/$ U" U5      nU(       a  US   U4/$ US   U4/$ s  snf s  snf )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TT4 Vs/ s H	  oc  M  SPM     sn5      nSnUS:X  a  SnOUS:X  a  SnOUS:X  a  Sn[        X05      nU(       a  U$ U $ s  snf )N   rH      r      r   r   )lenrT   )graphClassesToChooseFromvalnumAxesbestGraphTypeinnerFilteredClassesxValueyValuezValues        r6   _bestPlotType%getPlotsToMake.<locals>._bestPlotType=  sn    VVV$<P$<Sq$<PQa<-M\%M\'M7`''++ 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axisObjClassrn   graphClassesFilteredfoundunused_axisLetteraxisDictr[   filteredClassess    ```              r6   getPlotsToMaker      s   Z," 	VVV,
:! ))+K89/< K!,ff%' K#&-#vf!N
B /""2&>>*5L#L<<""2&  "O (4L|!7KA|L#23q8'' !566 K#&-#vf!N
B E35>>3G3G3I/!#L<< E 4J
 5""2&  "O (4L|!7KA|L#23q8'' !566 &&(H#&-#vf!N
))4	( "O <Aa(+,,'5$Q'233!!_h/00a M, Ms   	I&I	I	I	c                       \ rS rSrS rS rSrg)Testi  c                   [        S5      nU R                  U[        R                  /5        [        S5      nU R                  U[        R                  /5        [        5       nU R                  U[        R
                  /5        [        S5      nU R                  U[        R                  /5        [        S5      nU R                  U[        R                  /5        [        S5      nU R                  U[        R                  /5        [        S5      nU R                  U[        R                  /5        [        S5      nU R                  U[        R                  /5        [        S5      nU R                  U[        R                  /5        [        S	SS
5      nU R                  U[        R                  /5        [        S	SS5      nU R                  U[        R                  /5        g )Nr   r
   durationquarterLengthpspitch
pitchspace
pitchClassr   qlpcoffset)r   assertEqualr   WindowedAmbitusWindowedKeyHorizontalBarPitchSpaceOffsetHistogramQuarterLengthHistogramPitchSpaceHistogramPitchClassScatterPitchSpaceQuarterLengthScatterPitchClassOffsetselfposts     r6   testGetPlotsToMakeA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                    [        S5      nU R                  U[        R                  /5        [        S5      nU R                  U[        R                  /5        g )Nr   r	   )r   r   r   Dolanr   s     r6   testGetPlotsToMakeBTest.testGetPlotsToMakeB  sB    g&

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

|,r8    N)__name__
__module____qualname____firstlineno__r   r   __static_attributes__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   rs   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> : *&  -
-_cq6_
-2*<<8. #(,@ ,0j1Z#-8 #-L zT g .s   8C,