├── midis ├── fuer_elise.mid └── ravel_bolero_piano.mid ├── images ├── Bolero (Ravel).png └── Fuer Elise (Beethoven).png ├── .gitignore ├── arc_diagram.py ├── substring.py └── README.md /midis/fuer_elise.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asigalov61/arc-diagrams/master/midis/fuer_elise.mid -------------------------------------------------------------------------------- /images/Bolero (Ravel).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asigalov61/arc-diagrams/master/images/Bolero (Ravel).png -------------------------------------------------------------------------------- /midis/ravel_bolero_piano.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asigalov61/arc-diagrams/master/midis/ravel_bolero_piano.mid -------------------------------------------------------------------------------- /images/Fuer Elise (Beethoven).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asigalov61/arc-diagrams/master/images/Fuer Elise (Beethoven).png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/settings.json 2 | __pycache__/midi_stuff.cpython-36.pyc 3 | __pycache__/substring.cpython-36.pyc 4 | __pycache__/test_midi.cpython-36.pyc 5 | .vscode/ 6 | __pycache__/ 7 | -------------------------------------------------------------------------------- /arc_diagram.py: -------------------------------------------------------------------------------- 1 | 2 | import matplotlib.pyplot as plt 3 | from matplotlib.patches import Circle 4 | 5 | from substring import matching_substring_pairs 6 | 7 | 8 | def plot_arc_diagram( string, plot_title="" ): 9 | slds = matching_substring_pairs(string) 10 | bews = map( lambda sld: (sld[0], sum(sld)+sld[1], sld[1]), slds ) 11 | plot_arc_diagram_impl(bews, plot_title) 12 | 13 | # begin end 14 | # / / 15 | # ***********-----------O-----------*********** 16 | # |--width--| \ |--width--| 17 | # |-inner rad-| \ 18 | # |-----outer radius----| center 19 | 20 | def plot_ring( ax, begin, end, width ): 21 | cx = 0.5*(begin + end) 22 | center = (cx, 0) 23 | outer_radius = cx - begin 24 | inner_radius = outer_radius - width 25 | 26 | mypie, _ = ax.pie([1], radius=outer_radius, colors=[(0.4,0.4, 1.0, 0.3)], center=center ) 27 | plt.setp( mypie, width=width) 28 | 29 | return outer_radius 30 | 31 | def plot_arc_diagram_impl( bews, plot_title ): 32 | fig, ax = plt.subplots(subplot_kw={'aspect': 'auto'}) 33 | 34 | x_min = 0 35 | x_max = 1920 36 | max_width = 1080 37 | for bew in bews: 38 | x_max = max(x_max, bew[1]) 39 | orad = plot_ring(ax, bew[0], bew[1], bew[2]) 40 | max_width = max(max_width, orad) 41 | 42 | ax.set_xlim(x_min, x_max) 43 | ax.set_ylim( -max_width, max_width) 44 | 45 | plt.axis('off') 46 | 47 | title_obj = plt.title(plot_title, loc='center') 48 | plt.setp(title_obj, color=(0.0, 0.0, 0.0, 1)) 49 | 50 | plt.savefig('output.png', dpi=300) 51 | plt.show() 52 | -------------------------------------------------------------------------------- /substring.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def maximal_matching_pair( s, substring_length, old_index=-1 ): 4 | ''' 5 | find the first pair of matching substrings at least as long as the specified length 6 | ''' 7 | if substring_length > len(s)/2: 8 | return (len(substring_length), -1) # fail- futile to keep searching with this string 9 | 10 | head = s[:substring_length] 11 | tail = s[substring_length:] 12 | index = tail.find(head) 13 | if index == -1: 14 | if substring_length > 2: 15 | return (substring_length-1, old_index) # success 16 | return (substring_length, index) # fail- failed on first 2 character substring attempt 17 | 18 | return maximal_matching_pair(s, substring_length+1, index) # keep looking 19 | 20 | def first_matching_substring_pair( s, start=0 ): 21 | ''' 22 | returns the first matching substring pair of at least length 2 in the given string, 23 | ignoring all characters of the string before the given start index 24 | ''' 25 | if start < 0: 26 | return () # invalid input: start must be non-negative 27 | 28 | if len(s[start:]) < 4: 29 | return () # fail: string too short to find matching substrings of minimal length 2 30 | 31 | minimal_substring_length = 2 32 | (length, distance) = maximal_matching_pair(s[start:], minimal_substring_length) 33 | if distance != -1: 34 | return (start, length, distance) # success 35 | 36 | return first_matching_substring_pair(s, start+1) # keep looking 37 | 38 | def matching_substring_pairs( string ): 39 | ''' 40 | returns a collection of consecutive substring pairs encoded as (start, length, distance) where 41 | * start is the index of the first character of the first substring of the matching substring pair, 42 | * length is the length of the substrings in the matching substring pair, and 43 | * distance is the distance from the end of the first substring to the begining of the second substring 44 | ''' 45 | pairs = [] 46 | pair = first_matching_substring_pair(string, 0) 47 | while pair: 48 | pairs.append(pair) 49 | (start, length, distance) = pair 50 | pair = first_matching_substring_pair(string, start+length) 51 | return pairs 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # arc-diagrams 2 | 3 | This project is an attempt to implement the Arc Diagrams designed by [Martin Wattenberg](http://www.bewitched.com/about.html) and used in his project [The Shape of Song](http://www.bewitched.com/song.html). 4 | 5 | Broadly speaking, the aim of an arc diagram is to visualize patterns in strings by finding matching substrings and connecting them with a transluscent arc. The overlapping arcs of various thickeness and radius help to visualize structure and patterns embedded within the string. Arc diagrams can be applied to any string of characters. The following examples show arc diagrams generated by the code in this repository from strings generated from midi music files of Bolero by Maurice Ravel, and Für Elise by Ludwig van Beethoven. 6 | 7 | ![An arc diagram of the melody of the song Fuer Elise, by Ludwig van Beethoven](https://github.com/j-brent/arc-diagrams/blob/master/images/Fuer%20Elise%20(Beethoven).png) 8 | 9 | ![An arc diagram of the melody of the song Bolero, by Maurice Ravel](https://github.com/j-brent/arc-diagrams/blob/master/images/Bolero%20(Ravel).png) 10 | 11 | 12 | Caveat: The algorithms in this repository are not an implementation of the algorithm developed and described by Martin Wattenberg in his article [Arc Diagrams: Visualizing Structures in Strings](http://hint.fm/papers/arc-diagrams.pdf), but merely a first order approximation that I implemented from seeing his diagrams and reading his [simplified description](http://turbulence.org/Works/song/method/method.html). I thought that the results were visually interesting enough to share here, and I provide links to the original sources for those who want to explore the ideas further, and to give credit to the original creator of these ideas. 13 | 14 | # Requirements 15 | 16 | [matplotlib](https://matplotlib.org/) 17 | 18 | [mido](https://mido.readthedocs.io/en/latest/installing.html) (optional: only required if you want to plot arc diagrams created from midi files) 19 | 20 | # Getting Started 21 | 22 | To generate arc diagrams from your own strings, simply include the files arc_diagram.py and substring.py in your project and call 23 | ```python 24 | plot_arc_diagram(string) 25 | ``` 26 | to generate an arc diagram as a matplotlib figure. 27 | 28 | You can also generate the examples by calling 29 | ```python 30 | python arc-diagram-midi.py 31 | ``` 32 | if you have installed the optional python module [mido](https://mido.readthedocs.io/en/latest/installing.html). 33 | 34 | # References 35 | [Arc Diagrams: Visualizing Structures in Strings](http://hint.fm/papers/arc-diagrams.pdf) 36 | 37 | [The Shape of Song (2001)](http://www.bewitched.com/song.html) 38 | 39 | [The Shape of Song (official website)](http://turbulence.org/Works/song/) 40 | 41 | I discovered these diagrams while perusing the book [Processing: A Programming Handbook for Visual Designers and Artists](https://processing.org/handbook/) --------------------------------------------------------------------------------