From: Mike Solomon <mike@apollinemike.com>
Date: Wed, 16 Mar 2011 19:07:34 +0000 (-0400)
Subject: Fixes Issue 1504, allowing feather beam line breaking.
X-Git-Tag: release/2.13.55-1~11^2
X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=a339f8b9865b0f02febd351ba494129d414fb568;p=lilypond.git

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.
---

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)