Last weekend, I mentioned that I threw away most of the layout and formatting code, which I started rewriting from scratch. Well, this weekend I threw away everything else, and started abstracting away many of the general properties that are shared between different types of notation.
|Testing Note-Head Displacement|
One of the big design flaws in the previous version was the coupling of notes and tabs. I essentially had a single Note class that encapsulated everything about a specific note, including the string/fret information, attached lyrics, attached accidentals, and other modifiers. This class had a big render() method that drew everything (the tabs, the accidentals, etc.) in one go.
This made the code hard to follow, hard to test, and very hard to add features to.
In the new version, I have different classes for different types of staves, notes, accidentals, and other modifiers, which are managed by a hierarchy of contexts. This means that adding support for a new type of instrument, say a Didgeridoo, is a matter of creating a new DidgeridooStave class to render its stave, and maybe a DidgeridooNote class to render any Didgeridoo notational symbols.
Yes, even the tests were rewritten. I wrote about 150 tests. All new, all fresh. It wasn't much fun, but I do have a significant level of confidence in the code.
So what did I gain from all of this? Just the following...
Accidentals are tricky. In the image below, notice how the flat is nicely shifted to make room for the flat.
And this is how it deals with a chord on steroids.
|Accidentals Line Up Correctly|
I tried hard to break it.
|Whoa there, Fella!|
It gets a bit confused when you bring in a second voice, but still quite respectable.
|Accidentals with Multiple Voices|
The formatter doesn't just work with accidentals. It works with all kinds of modifiers, such as bends. Last week, I showed you how the modifier handled multiple bent notes on the same chord. Below, notice how full and half bends share the text annotation area. (This excited me a wee bit.)
|Bends with Text Annotations|
The new layout code allows for justification. Below, the top stave is rendered without justification, while the bottom two are justified to different widths.
The justification works across staves too, so you can have multiple staves playing different parts, all lined up in the notation.
|Justification across Staves|
That's all I have to show for this week, folks! Since this is all new code, there are a few things that I lost support for, in particular: beams and ties. Hopefully it'll all be back next week.