From f12dff06a034c765190ed51e49a0040a7ad74ca7 Mon Sep 17 00:00:00 2001 From: Kevin Dalley Date: Mon, 5 Mar 2007 23:43:20 -0800 Subject: [PATCH] Added chromatic ledger and internal ledger lines. Sample test file is input/test/chromatic-scales.ly. --- Documentation/topdocs/AUTHORS.texi | 2 + THANKS | 2 +- input/test/chromatic-scales.ly | 35 ++++ lily/ledger-line-spanner.cc | 305 +++++++++++++++++++++++++++++++----- lily/note-heads-engraver.cc | 55 ++++++- lily/staff-symbol.cc | 1 + scm/define-context-properties.scm | 1 + scm/define-grob-properties.scm | 1 + 8 files changed, 357 insertions(+), 45 deletions(-) diff --git a/Documentation/topdocs/AUTHORS.texi b/Documentation/topdocs/AUTHORS.texi index db20189..905abf4 100644 --- a/Documentation/topdocs/AUTHORS.texi +++ b/Documentation/topdocs/AUTHORS.texi @@ -29,6 +29,8 @@ al-niente hairpins. @item @email{benkop@@freestart.hu,Pal Benko}, Ancient notation. +@item @email{kevin@@kelphead.org,Kevin Dalley}, + Chromatic staff, staff-line-layout, and internal ledger lines. @item @email{david.feuer@@gmail.com, David Feuer}, PS output code refactoring. @item @email{bernard@@fong-hurley.org.uk, Bernard Hurley}, diff --git a/THANKS b/THANKS index 3862361..f750419 100644 --- a/THANKS +++ b/THANKS @@ -19,7 +19,7 @@ Erlend Aasland Guido Amoruso Heikki Junes Joe Neeman - +Kevin Dalley SPONSORS diff --git a/input/test/chromatic-scales.ly b/input/test/chromatic-scales.ly new file mode 100644 index 0000000..dd75c70 --- /dev/null +++ b/input/test/chromatic-scales.ly @@ -0,0 +1,35 @@ +\version "2.10" +\include "english.ly" +% This shows how to use internal-ledger-lines and staff-line-layout +% to produce alternative chromatic notation. +% This notation is 6-6 Tetragram by Richard Parncutt +% For more information, +% see http://web.syr.edu/~pwmorris/mnma/gallery/4LineNotations.html + +scales = \relative { + a, as b c cs d ds e f fs g gs + a as b c cs d ds e f fs g gs + a as b c cs d ds e f fs g gs + a +} + +\new Staff \with { + \remove "Accidental_engraver" + staff-line-layout = #'semitone + middleCPosition = #-2 + clefGlyph = #"clefs.G" + clefPosition = #(+ -2 7) +} +{ + \override Staff.StaffSymbol #'line-count = #8 + \override Staff.StaffSymbol #'line-positions = #'(-9 -7 -5 -3 3 5 7 9) + \override Staff.StaffSymbol #'internal-ledger-lines = #'((-1 1)) + \time 4/4 + << + \scales + \context NoteNames { + \set printOctaveNames= ##f + \scales + } + >> +} diff --git a/lily/ledger-line-spanner.cc b/lily/ledger-line-spanner.cc index bf7444d..8401d2d 100644 --- a/lily/ledger-line-spanner.cc +++ b/lily/ledger-line-spanner.cc @@ -7,6 +7,7 @@ */ #include +#include using namespace std; #include "item.hh" @@ -18,6 +19,104 @@ using namespace std; #include "pointer-group-interface.hh" #include "paper-column.hh" + + +struct Ledger_request +{ + Interval ledger_extent_; + Interval head_extent_; + /* non-negative position, direction is removed */ + int position_; + bool excentric_; + Ledger_request () + { + ledger_extent_.set_empty (); + head_extent_.set_empty (); + position_ = 0; + } +}; + +typedef map < int, Drul_array > Ledger_requests; +typedef map Ledger_requests_internal; + +/* This consists of a group of ledger lines internal to the staff. + There should be no staff lines between these ledger lines + */ +class Internal_ledgers +{ +public: + Internal_ledgers(): + halfspace_(1) + { + } + void set_halfspace(const Real& halfspace) + { + halfspace_ = halfspace; + } + /** + * add new ledger line at \a new_ledger + * + * @param newLedger new ledger position + * + */ + void add_ledger(int new_ledger) + { + ledgers_.insert(new_ledger); + } + + /** must be performed before using contains */ + void calculate_extent() + { + ledger_extent_ = Interval(*min_element(ledgers_.begin(), + ledgers_.end())/halfspace_ - 1, + *max_element(ledgers_.begin(), + ledgers_.end())/halfspace_ + 1); + } + /** + * + * + * @param pos + * + * @return true if \a pos is contain in ledger + */ + bool contains(int pos) const + { + return ledger_extent_.contains(pos/halfspace_); + } + const set& ledger_set() const + { + return ledgers_; + } +private: + Interval ledger_extent_; + set ledgers_; + /** set halfspace for rounding ledger extent */ + Real halfspace_; +}; + + +/** + * + * + * @param pos + * @param internal_ledgers_container + * + * @return true if \a pos is contained in at least one item in Internal_ledgers + */ +bool contains(int pos, const vector& internal_ledgers_container) +{ + for (vector::const_iterator internal_ledgers = internal_ledgers_container.begin(); + internal_ledgers != internal_ledgers_container.end(); + ++internal_ledgers) + { + if ((*internal_ledgers).contains(pos)) + { + return true; + } + } + return false; +} + struct Ledger_line_spanner { DECLARE_SCHEME_CALLBACK (print, (SCM)); @@ -28,8 +127,21 @@ struct Ledger_line_spanner Real, Real, Interval x_extent, Real left_shorten); - + static Stencil brew_internal_ledger_lines (const Internal_ledgers& internal_ledgers, + Real, Real, + Interval x_extent, + Real left_shorten); static bool has_interface (Grob *); + /* returns ledger_size and left_shorten, given other values + * helper method for brew_ledger_lines + */ + static void find_ledger_size(const Item *head, Grob *common[], + Real length_fraction, + Ledger_request& req, + Interval& ledger_size, + Real& left_shorten + ); + }; Stencil @@ -44,6 +156,7 @@ Ledger_line_spanner::brew_ledger_lines (Grob *staff, int line_count = (staff_extent.contains (pos) ? 0 : sign (pos) * int (rint(pos - staff_extent[Direction (sign (pos))])) / 2); + Stencil stencil; if (line_count) { @@ -73,7 +186,40 @@ Ledger_line_spanner::brew_ledger_lines (Grob *staff, stencil.add_stencil (ledger_line); } } - + + return stencil; +} +/** extra lines for internal ledger lines */ +Stencil +Ledger_line_spanner::brew_internal_ledger_lines (const Internal_ledgers& internal_ledgers, + Real halfspace, + Real ledgerlinethickness, + Interval x_extent, + Real left_shorten) +{ + Stencil stencil; + /* halfspace is 1/2 of staff-space */ + Real blotdiameter = ledgerlinethickness; + Interval y_extent + = Interval (-0.5 * (ledgerlinethickness), + +0.5 * (ledgerlinethickness)); + Stencil proto_ledger_line + = Lookup::round_filled_box (Box (x_extent, y_extent), blotdiameter); + x_extent[LEFT] += left_shorten; + Stencil proto_first_line + = Lookup::round_filled_box (Box (x_extent, y_extent), blotdiameter); + const set& ledgers = internal_ledgers.ledger_set(); + for (set::const_iterator currentLedger = ledgers.begin(); + currentLedger != ledgers.end(); + ++currentLedger) + { + int pos = *currentLedger; + Real offs = pos * halfspace; + Stencil ledger_line(proto_ledger_line); + // Stencil ledger_line(proto_first_line); + ledger_line.translate_axis(offs, Y_AXIS); + stencil.add_stencil (ledger_line); + } return stencil; } @@ -181,26 +327,50 @@ Ledger_line_spanner::set_spacing_rods (SCM smob) return SCM_UNSPECIFIED; } -struct Ledger_request +/* + * Calculate + * ledger_size + * left_shorten + */ +void +Ledger_line_spanner::find_ledger_size(const Item *head, Grob *common[], + Real length_fraction, + Ledger_request& req, + Interval& ledger_size, + Real& left_shorten + ) { - Interval ledger_extent_; - Interval head_extent_; - int position_; - bool excentric_; - Ledger_request () - { - ledger_extent_.set_empty (); - head_extent_.set_empty (); - position_ = 0; - } -}; + + Interval head_size = head->extent (common[X_AXIS], X_AXIS); + ledger_size = head_size; + ledger_size.widen (ledger_size.length () * length_fraction); -typedef map < int, Drul_array > Ledger_requests; + Interval max_size = req.ledger_extent_; + ledger_size.intersect (max_size); + left_shorten = 0.0; + if (Grob *g = unsmob_grob (head->get_object ("accidental-grob"))) + { + Interval accidental_size = g->extent (common[X_AXIS], X_AXIS); + Real d + = linear_combination (Drul_array (accidental_size[RIGHT], + head_size[LEFT]), + 0.0); + + left_shorten = max (-ledger_size[LEFT] + d, 0.0); + + /* + TODO: shorten 2 ledger lines for the case natural + + downstem. + */ + } +} + /* TODO: ledger share a lot of info. Lots of room to optimize away common use of objects/variables. */ + MAKE_SCHEME_CALLBACK (Ledger_line_spanner, print, 1); SCM Ledger_line_spanner::print (SCM smob) @@ -240,12 +410,42 @@ Ledger_line_spanner::print (SCM smob) } Ledger_requests reqs; + Ledger_requests_internal reqs_internal; + + // add internal ledger lines to list + vector internal_ledgers_container; + SCM internal_ledgers = staff->get_property("internal-ledger-lines"); + set ledger_set; + if (scm_is_pair(internal_ledgers)) + { + for (SCM s = internal_ledgers; scm_is_pair (s); + s = scm_cdr (s)) + { + SCM car = scm_car(s); + Internal_ledgers internal_ledgers; + internal_ledgers.set_halfspace(halfspace); + // ignore if s2 is not car + for (SCM s2 = car; scm_is_pair (s2); + s2 = scm_cdr (s2)) + { + int pos = scm_to_int (scm_car(s2)); + // ledger_set.insert(pos); + internal_ledgers.add_ledger(pos); + } + internal_ledgers.calculate_extent(); + internal_ledgers_container.push_back(internal_ledgers); + } + + } + + + for (vsize i = heads.size (); i--;) { Item *h = dynamic_cast (heads[i]); int pos = Staff_symbol_referencer::get_rounded_position (h); - if (pos && !staff_extent.contains (pos)) + if (pos != 0 && !staff_extent.contains (pos)) { Interval head_extent = h->extent (common[X_AXIS], X_AXIS); Interval ledger_extent = head_extent; @@ -259,6 +459,22 @@ Ledger_line_spanner::print (SCM smob) reqs[rank][vdir].position_ = vdir * max (vdir * reqs[rank][vdir].position_, vdir * pos); } + + if (contains(pos, internal_ledgers_container)) + { + Interval head_extent = h->extent (common[X_AXIS], X_AXIS); + Interval ledger_extent = head_extent; + ledger_extent.widen (length_fraction * head_extent.length ()); + + Direction vdir = Direction (sign (pos)); + int rank = h->get_column ()->get_rank (); + + reqs_internal[rank].ledger_extent_.unite (ledger_extent); + reqs_internal[rank].head_extent_.unite (head_extent); + reqs_internal[rank].position_ + = vdir * max (vdir * reqs_internal[rank].position_, vdir * pos); + } + } // determine maximum size for non-colliding ledger. @@ -312,30 +528,14 @@ Ledger_line_spanner::print (SCM smob) int pos = Staff_symbol_referencer::get_rounded_position (h); if (!staff_extent.contains (pos - sign (pos))) { - Interval head_size = h->extent (common[X_AXIS], X_AXIS); - Interval ledger_size = head_size; - ledger_size.widen (ledger_size.length () * length_fraction); - - Interval max_size = reqs[h->get_column ()->get_rank ()] - [Direction (sign (pos))].ledger_extent_; + Interval ledger_size; + Real left_shorten; - ledger_size.intersect (max_size); - Real left_shorten = 0.0; - if (Grob *g = unsmob_grob (h->get_object ("accidental-grob"))) - { - Interval accidental_size = g->extent (common[X_AXIS], X_AXIS); - Real d - = linear_combination (Drul_array (accidental_size[RIGHT], - head_size[LEFT]), - 0.0); - - left_shorten = max (-ledger_size[LEFT] + d, 0.0); - - /* - TODO: shorten 2 ledger lines for the case natural + - downstem. - */ - } + find_ledger_size(h, common, length_fraction, + reqs[h->get_column ()->get_rank ()] + [Direction (sign (pos))], + ledger_size, + left_shorten); ledgers.add_stencil (brew_ledger_lines (staff, pos, staff_extent, halfspace, @@ -343,6 +543,30 @@ Ledger_line_spanner::print (SCM smob) ledger_size, left_shorten)); } + + /* handle internal ledger lines */ + for (vector::const_iterator internal_ledgers = internal_ledgers_container.begin(); + internal_ledgers != internal_ledgers_container.end(); + ++internal_ledgers) + { + if ((*internal_ledgers).contains(pos)) + { + Interval ledger_size; + Real left_shorten; + + find_ledger_size(h, common, length_fraction, + reqs_internal[h->get_column ()->get_rank ()], + ledger_size, + left_shorten); + + ledgers.add_stencil (brew_internal_ledger_lines (*internal_ledgers, + halfspace, + ledgerlinethickness, + ledger_size, + left_shorten)); + } + } + } ledgers.translate_axis (-me->relative_coordinate (common[X_AXIS], X_AXIS), @@ -363,7 +587,8 @@ ADD_INTERFACE (Ledger_line_spanner, "thickness " "minimum-length-fraction " "length-fraction " - "gap"); + "gap " + "internal-ledger-lines"); struct Ledgered_interface { diff --git a/lily/note-heads-engraver.cc b/lily/note-heads-engraver.cc index 6c065af..4e9b0fe 100644 --- a/lily/note-heads-engraver.cc +++ b/lily/note-heads-engraver.cc @@ -22,6 +22,35 @@ using namespace std; #include "translator.icc" +class Layout_pos +{ +public: + virtual int pos(Pitch* pit) = 0; +}; + +class Layout_pos_traditional : public Layout_pos +{ +public: + virtual int pos(Pitch* pit) + { + return pit ? pit->steps () : 0; + } +}; + +static Layout_pos_traditional layout_pos_traditional; + +class Layout_pos_semitone : public Layout_pos +{ +public: + virtual int pos(Pitch* pit) + { + return pit ? pit->semitone_pitch() : 0; + } +}; + +static Layout_pos_semitone layout_pos_semitone; + + class Note_heads_engraver : public Engraver { vector notes_; @@ -50,6 +79,18 @@ Note_heads_engraver::listen_note (Stream_event *ev) void Note_heads_engraver::process_music () { + Layout_pos *layout_pos = &layout_pos_traditional; + SCM layout_property = get_property("staff-line-layout"); + if (layout_property) + { + if (ly_is_equal(layout_property, ly_symbol2scm("traditional"))){ + layout_pos = &layout_pos_traditional; + } + else if (ly_is_equal(layout_property, ly_symbol2scm("semitone"))){ + layout_pos = &layout_pos_semitone; + } + } + for (vsize i = 0; i < note_evs_.size (); i++) { Stream_event *ev = note_evs_[i]; @@ -63,11 +104,16 @@ Note_heads_engraver::process_music () ev->origin ()->warning (_ ("NoteEvent without pitch")); #endif - int pos = pit ? pit->steps () : 0; SCM c0 = get_property ("middleCPosition"); + int middleC; if (scm_is_number (c0)) - pos += scm_to_int (c0); - + middleC = scm_to_int(c0); + else + middleC = 0; + int pos = layout_pos->pos(pit); + + pos += middleC; + note->set_property ("staff-position", scm_from_int (pos)); /* @@ -106,5 +152,6 @@ ADD_TRANSLATOR (Note_heads_engraver, /* doc */ "Generate noteheads.", /* create */ "NoteHead ", - /* read */ "middleCPosition", + /* read */ "middleCPosition " + "staff-line-layout", /* write */ ""); diff --git a/lily/staff-symbol.cc b/lily/staff-symbol.cc index 6edb1f9..554b2e2 100644 --- a/lily/staff-symbol.cc +++ b/lily/staff-symbol.cc @@ -186,4 +186,5 @@ ADD_INTERFACE (Staff_symbol, "staff-symbol-interface", "staff-space " "thickness " "width " + "internal-ledger-lines " ); diff --git a/scm/define-context-properties.scm b/scm/define-context-properties.scm index fe32b4e..bf781bb 100644 --- a/scm/define-context-properties.scm +++ b/scm/define-context-properties.scm @@ -372,6 +372,7 @@ up the interpretation phase. This speeds up debugging large scores.") (squashedPosition ,integer? " Vertical position of squashing for @internalsref{Pitch_squash_engraver}.") + (staff-line-layout ,symbol? "Layout of staff lines, 'traditional, or 'semitone.") (stringNumberOrientations ,list? "See @code{fingeringOrientations}") (strokeFingerOrientations ,list? "See @code{fingeringOrientations}") (stringOneTopmost ,boolean? "Whether the 1st string is printed on the diff --git a/scm/define-grob-properties.scm b/scm/define-grob-properties.scm index dddcef9..4c79a53 100644 --- a/scm/define-grob-properties.scm +++ b/scm/define-grob-properties.scm @@ -204,6 +204,7 @@ and slur ignore eachother.") set beam/slur quant to this position, and print the respective scores.") (inspect-index ,integer? "If debugging is set, set beam/slur configuration to this index, and print the respective scores.") + (internal-ledger-lines ,list? "Ledger lines placed between first and last lines of staff.") (implicit ,boolean? "Is this an implicit bass figure?") (keep-inside-line ,boolean? "If set, this column cannot have things sticking into the margin.") -- 1.4.4.4