diff options
author | Mike Solomon <mike@apollinemike.com> | 2011-03-16 15:07:34 -0400 |
---|---|---|
committer | Mike Solomon <mike@apollinemike.com> | 2011-03-16 15:07:34 -0400 |
commit | a339f8b9865b0f02febd351ba494129d414fb568 (patch) | |
tree | 99a12240e9af206155e671356f104e3205966038 | |
parent | 84bdcba4e72ed9161fa6091441a41a02d81069aa (diff) |
Fixes Issue 1504, allowing feather beam line breaking.
Makes it such that the degree of feathering at the end of a system
is preserved at the beginning of the next system.
Adds a normalized-endpoints property to Spanner, which calculates
the portion of a spanner (normalized from 0 to 1) taken up by any
broken child.
-rw-r--r-- | input/regression/beam-feather-breaking.ly | 137 | ||||
-rw-r--r-- | lily/beam.cc | 66 | ||||
-rw-r--r-- | lily/include/spanner.hh | 1 | ||||
-rw-r--r-- | lily/spanner.cc | 48 | ||||
-rw-r--r-- | scm/define-grob-properties.scm | 3 | ||||
-rw-r--r-- | scm/define-grobs.scm | 2 |
6 files changed, 247 insertions, 10 deletions
diff --git a/input/regression/beam-feather-breaking.ly b/input/regression/beam-feather-breaking.ly new file mode 100644 index 0000000000..1b3aae08bf --- /dev/null +++ b/input/regression/beam-feather-breaking.ly @@ -0,0 +1,137 @@ +\version "2.13.55" +\header { + texidoc = "Feathered beams should have the same progress of their feathering +at the end of a line break as they do at the beginning of the next line." +} + +\paper { + left-margin = 2\cm + line-width = 10\cm + ragged-right = ##t + indent = 0\cm +} + +\new Staff << + \relative c' { + \cadenzaOn + \override Staff . TimeSignature #'stencil = ##f + \override Voice . Beam #'breakable = ##t + \once \override Voice . Beam #'grow-direction = #RIGHT + a32[ b c d e f g a ] + \once \override Voice . Beam #'grow-direction = #LEFT + a[ g f e d c b a] \bar "|" +} >> + +\new Staff << + \relative c' { + \cadenzaOn + \override Staff . TimeSignature #'stencil = ##f + \override Voice . Beam #'breakable = ##t + \once \override Voice . Beam #'grow-direction = #RIGHT + a32[ b c d \bar "" \break e f g a b c d e f g a ] \bar "|" +} >> + +\new Staff << + \relative c' { + \cadenzaOn + \override Staff . TimeSignature #'stencil = ##f + \override Voice . Beam #'breakable = ##t + \once \override Voice . Beam #'grow-direction = #RIGHT + a32[ b c d e f g a \bar "" \break b c d e f g a ] \bar "|" +} >> + +\new Staff << + \relative c' { + \cadenzaOn + \override Staff . TimeSignature #'stencil = ##f + \override Voice . Beam #'breakable = ##t + \once \override Voice . Beam #'grow-direction = #RIGHT + a32[ b c d e f g a b c d \bar "" \break e f g a ] \bar "|" +} >> + +\new Staff << + \relative c' { + \cadenzaOn + \override Staff . TimeSignature #'stencil = ##f + \override Voice . Beam #'breakable = ##t + \once \override Voice . Beam #'grow-direction = #LEFT + a32[ b c d \bar "" \break e f g a b c d e f g a ] \bar "|" +} >> + +\new Staff << + \relative c' { + \cadenzaOn + \override Staff . TimeSignature #'stencil = ##f + \override Voice . Beam #'breakable = ##t + \once \override Voice . Beam #'grow-direction = #LEFT + a32[ b c d e f g a \bar "" \break b c d e f g a ] \bar "|" +} >> + +\new Staff << + \relative c' { + \cadenzaOn + \override Staff . TimeSignature #'stencil = ##f + \override Voice . Beam #'breakable = ##t + \once \override Voice . Beam #'grow-direction = #LEFT + a32[ b c d e f g a b c d \bar "" \break e f g a ] \bar "|" +} >> + +\new Staff << + \relative c' { + \cadenzaOn + \override Staff . TimeSignature #'stencil = ##f + \override Voice . Stem #'direction = #DOWN + \override Voice . Beam #'breakable = ##t + \once \override Voice . Beam #'grow-direction = #RIGHT + a32[ b c d \bar "" \break e f g a b c d e f g a ] \bar "|" +} >> + +\new Staff << + \relative c' { + \cadenzaOn + \override Staff . TimeSignature #'stencil = ##f + \override Voice . Stem #'direction = #DOWN + \override Voice . Beam #'breakable = ##t + \once \override Voice . Beam #'grow-direction = #RIGHT + a32[ b c d e f g a \bar "" \break b c d e f g a ] \bar "|" +} >> + +\new Staff << + \relative c' { + \cadenzaOn + \override Staff . TimeSignature #'stencil = ##f + \override Voice . Stem #'direction = #DOWN + \override Voice . Beam #'breakable = ##t + \once \override Voice . Beam #'grow-direction = #RIGHT + a32[ b c d e f g a b c d \bar "" \break e f g a ] \bar "|" +} >> + +\new Staff << + \relative c' { + \cadenzaOn + \override Staff . TimeSignature #'stencil = ##f + \override Voice . Stem #'direction = #DOWN + \override Voice . Beam #'breakable = ##t + \once \override Voice . Beam #'grow-direction = #LEFT + a32[ b c d \bar "" \break e f g a b c d e f g a ] \bar "|" +} >> + +\new Staff << + \relative c' { + \cadenzaOn + \override Staff . TimeSignature #'stencil = ##f + \override Voice . Stem #'direction = #DOWN + \override Voice . Beam #'breakable = ##t + \once \override Voice . Beam #'grow-direction = #LEFT + a32[ b c d e f g a \bar "" \break b c d e f g a ] \bar "|" +} >> + +\new Staff << + \relative c' { + \cadenzaOn + \override Staff . TimeSignature #'stencil = ##f + \override Voice . Stem #'direction = #DOWN + \override Voice . Beam #'breakable = ##t + \once \override Voice . Beam #'grow-direction = #LEFT + a32[ b c d e f g a b c d \bar "" \break e f g a ] \bar "|" +} >> diff --git a/lily/beam.cc b/lily/beam.cc index 53fda81743..e3135dadfa 100644 --- a/lily/beam.cc +++ b/lily/beam.cc @@ -590,24 +590,72 @@ Beam::print (SCM grob) Direction feather_dir = to_dir (me->get_property ("grow-direction")); + Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0)); + Stencil the_beam; + + int extreme = (segments[0].vertical_count_ == 0 + ? segments[0].vertical_count_ + : segments.back ().vertical_count_); + for (vsize i = 0; i < segments.size (); i ++) { Real local_slope = slope; + /* + Makes local slope proportional to the ratio of the length of this beam + to the total length. + */ if (feather_dir) - { - local_slope += feather_dir * segments[i].vertical_count_ * beam_dy / span.length (); - } + local_slope += (feather_dir * segments[i].vertical_count_ + * beam_dy + * placements.length () + / span.length ()); Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot); b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS); + Real multiplier = feather_dir ? placements[LEFT] : 1.0; + + Interval weights (1 - multiplier, multiplier); + + if (feather_dir != LEFT) + weights.swap (); + + // we need two translations: the normal one and + // the one of the lowest segment + int idx[] = {i, extreme}; + Real translations[2]; + + for (int j = 0; j < 2; j++) + translations[j] = slope + * (segments[idx[j]].horizontal_[LEFT] - span.linear_combination (CENTER)) + + pos.linear_combination (CENTER) + + beam_dy * segments[idx[j]].vertical_count_; + + Real weighted_average = translations[0] * weights[LEFT] + translations[1] * weights[RIGHT]; + + /* + Tricky. The manipulation of the variable `weighted_average' below ensures + that beams with a RIGHT grow direction will start from the position of the + lowest segment at 0, and this error will decrease and decrease over the + course of the beam. Something with a LEFT grow direction, on the other + hand, will always start in the correct place but progressively accrue + error at broken places. This code shifts beams up given where they are + in the total span length (controlled by the variable `multiplier'). To + better understand what it does, try commenting it out: you'll see that + all of the RIGHT growing beams immediately start too low and get better + over line breaks, whereas all of the LEFT growing beams start just right + and get worse over line breaks. + */ + Real factor = Interval (multiplier, 1 - multiplier).linear_combination (feather_dir); + + if (segments[0].vertical_count_ < 0 && feather_dir) + weighted_average += beam_dy * (segments.size () - 1) * factor; + + b.translate_axis (weighted_average, Y_AXIS); - b.translate_axis (local_slope - * (segments[i].horizontal_[LEFT] - span.linear_combination (feather_dir)) - + pos.linear_combination (feather_dir) - + beam_dy * segments[i].vertical_count_, Y_AXIS); the_beam.add_stencil (b); + } #if (DEBUG_BEAM_SCORING) @@ -626,7 +674,7 @@ Beam::print (SCM grob) properties = scm_cons(scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL), properties); - + Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP; Stencil score = *unsmob_stencil (Text_interface::interpret_markup @@ -1088,7 +1136,7 @@ Beam::shift_region_to_valid (SCM grob, SCM posns) Real x = s->relative_coordinate (common[X_AXIS], X_AXIS) - x_span[LEFT]; x_posns.push_back (x); } - + Grob *lvs = last_normal_stem (me); x_span[RIGHT] = lvs->relative_coordinate (common[X_AXIS], X_AXIS); diff --git a/lily/include/spanner.hh b/lily/include/spanner.hh index e0f461a7db..7bda060c86 100644 --- a/lily/include/spanner.hh +++ b/lily/include/spanner.hh @@ -48,6 +48,7 @@ class Spanner : public Grob public: DECLARE_SCHEME_CALLBACK (set_spacing_rods, (SCM)); + DECLARE_SCHEME_CALLBACK (calc_normalized_endpoints, (SCM)); DECLARE_SCHEME_CALLBACK (bounds_width, (SCM)); DECLARE_SCHEME_CALLBACK (kill_zero_spanned_time, (SCM)); diff --git a/lily/spanner.cc b/lily/spanner.cc index 6083c67461..d3fd5a0955 100644 --- a/lily/spanner.cc +++ b/lily/spanner.cc @@ -398,6 +398,53 @@ Spanner::set_spacing_rods (SCM smob) return SCM_UNSPECIFIED; } +MAKE_SCHEME_CALLBACK (Spanner, calc_normalized_endpoints, 1); +SCM +Spanner::calc_normalized_endpoints (SCM smob) +{ + Spanner *me = unsmob_spanner (smob); + SCM result = SCM_EOL; + + Spanner *orig = dynamic_cast<Spanner *> (me->original ()); + + orig = orig ? orig : me; + + if (orig->is_broken ()) + { + Real total_width = 0.0; + vector<Real> span_data; + + if (!orig->is_broken ()) + span_data.push_back (orig->spanner_length ()); + else + for (vsize i = 0; i < orig->broken_intos_.size (); i++) + span_data.push_back (orig->broken_intos_[i]->spanner_length ()); + + vector<Interval> unnormalized_endpoints; + + for (vsize i = 0; i < span_data.size (); i++) + { + unnormalized_endpoints.push_back (Interval (total_width, total_width + span_data[i])); + total_width += span_data[i]; + } + + for (vsize i = 0; i < unnormalized_endpoints.size (); i++) + { + SCM t = ly_interval2scm (1 / total_width * unnormalized_endpoints[i]); + orig->broken_intos_[i]->set_property ("normalized-endpoints", t); + if (me->get_break_index () == i) + result = t; + } + } + else + { + result = scm_cons (scm_from_double (0.0), scm_from_double (1.0)); + orig->set_property ("normalized-endpoints", result); + } + + return result; +} + Spanner * unsmob_spanner (SCM s) { @@ -479,6 +526,7 @@ ADD_INTERFACE (Spanner, " point of the spanner.", /* properties */ + "normalized-endpoints " "minimum-length " "to-barline " ); diff --git a/scm/define-grob-properties.scm b/scm/define-grob-properties.scm index 879a1c780d..83b327413d 100644 --- a/scm/define-grob-properties.scm +++ b/scm/define-grob-properties.scm @@ -616,6 +616,9 @@ the nearest staff in the opposite direction from between the two, and @code{staff-affinity} is either @code{UP} or @code{DOWN}. See @code{staff-staff-spacing} for a description of the alist structure.") + (normalized-endpoints ,pair? "Represents left and right placement +over the total spanner, where the width of the spanner is normalized +between 0 and 1.") (note-names ,vector? "Vector of strings containing names for easy-notation note heads.") diff --git a/scm/define-grobs.scm b/scm/define-grobs.scm index 6e4faedf40..8b9564f41e 100644 --- a/scm/define-grobs.scm +++ b/scm/define-grobs.scm @@ -326,7 +326,6 @@ . ( ;; todo: clean this up a bit: the list is getting ;; rather long. - (auto-knee-gap . 5.5) (beam-thickness . 0.48) ; in staff-space @@ -367,6 +366,7 @@ (round-to-zero-slope . 0.02))) (direction . ,ly:beam::calc-direction) + (normalized-endpoints . ,ly:spanner::calc-normalized-endpoints) ;; only for debugging. (font-family . roman) |