From e4e0be528632168d8f769615cd75593554095c93 Mon Sep 17 00:00:00 2001
From: Joe Neeman <joeneeman@gmail.com>
Date: Thu, 14 May 2009 11:37:59 -0700
Subject: [PATCH] Improve the accuracy of pure-heights for systems with many
 staves.

---
 lily/align-interface.cc              | 27 +++++++++++-
 lily/axis-group-interface.cc         | 61 ++++++++++++++++++++--------
 lily/include/axis-group-interface.hh |  2 +
 scm/define-grob-properties.scm       |  2 +-
 4 files changed, 72 insertions(+), 20 deletions(-)

diff --git a/lily/align-interface.cc b/lily/align-interface.cc
index 27839aca92..d69930c3aa 100644
--- a/lily/align-interface.cc
+++ b/lily/align-interface.cc
@@ -84,6 +84,8 @@ Align_interface::stretch_after_break (SCM grob)
    an empty extent, delete it from the list instead. If the extent is
    non-empty but there is no skyline available (or pure is true), just
    create a flat skyline from the bounding box */
+// TODO(jneem): the pure and non-pure parts seem to share very little
+// code. Split them into 2 functions, perhaps?
 static void
 get_skylines (Grob *me,
 	      vector<Grob*> *const elements,
@@ -137,9 +139,32 @@ get_skylines (Grob *me,
 	    {
 	      Box b;
 	      b[a] = extent;
-	      b[other_axis (a)] = Interval (-infinity_f, infinity_f);
+	      b[other_axis (a)] = Interval (0, infinity_f);
 	      skylines.insert (b, 0, other_axis (a));
 	    }
+
+	  // This is a hack to get better accuracy on the pure-height of VerticalAlignment.
+	  // It's quite common for a treble clef to be the highest element of one system
+	  // and for a low note (or lyrics) to be the lowest note on another. The two will
+	  // never collide, but the pure-height stuff only works with bounding boxes, so it
+	  // doesn't know that. The result is a significant over-estimation of the pure-height,
+	  // especially on systems with many staves. To correct for this, we build a skyline
+	  // in two parts: the part we did above contains most of the grobs (note-heads, etc.)
+	  // while the bit we're about to do only contains the breakable grobs at the beginning
+	  // of the system. This way, the tall treble clefs are only compared with the treble
+	  // clefs of the other staff and they will be ignored if the staff above is, for example,
+	  // lyrics.
+	  if (Axis_group_interface::has_interface (g))
+	    {
+	      Interval begin_of_line_extent = Axis_group_interface::begin_of_line_pure_height (g, start);
+	      if (!begin_of_line_extent.is_empty ())
+		{
+		  Box b;
+		  b[a] = begin_of_line_extent;
+		  b[other_axis (a)] = Interval (-infinity_f, -1);
+		  skylines.insert (b, 0, other_axis (a));
+		}
+	    }
 	}
 
       if (skylines.is_empty ())
diff --git a/lily/axis-group-interface.cc b/lily/axis-group-interface.cc
index 639c8b6267..d14a56c084 100644
--- a/lily/axis-group-interface.cc
+++ b/lily/axis-group-interface.cc
@@ -74,26 +74,37 @@ Axis_group_interface::relative_group_extent (vector<Grob*> const &elts,
   return r;
 }
 
+Interval
+Axis_group_interface::cached_pure_height (Grob *me, int start, int end)
+{
+  SCM adjacent_pure_heights = me->get_property ("adjacent-pure-heights");
 
-/*
-  FIXME: pure extent handling has a lot of ad-hoc caching.
-  This should be done with grob property callbacks.
+  if (!scm_is_pair (adjacent_pure_heights)
+      || !scm_is_vector (scm_cdr (adjacent_pure_heights)))
+    return Interval (0, 0);
 
-  --hwn
-*/
+  return combine_pure_heights (me, scm_cdr (adjacent_pure_heights), start, end);
+}
 
 Interval
-Axis_group_interface::cached_pure_height (Grob *me, int start, int end)
+Axis_group_interface::begin_of_line_pure_height (Grob *me, int start)
+{
+  SCM adjacent_pure_heights = me->get_property ("adjacent-pure-heights");
+
+  if (!scm_is_pair (adjacent_pure_heights)
+      || !scm_is_vector (scm_car (adjacent_pure_heights)))
+    return Interval (0, 0);
+
+  return combine_pure_heights (me, scm_car (adjacent_pure_heights), start, start+1);
+}
+
+Interval
+Axis_group_interface::combine_pure_heights (Grob *me, SCM measure_extents, int start, int end)
 {
   Paper_score *ps = get_root_system (me)->paper_score ();
   vector<vsize> breaks = ps->get_break_indices ();
   vector<Grob*> cols = ps->get_columns ();
 
-  SCM extents = me->get_property ("adjacent-pure-heights");
-
-  if (!scm_is_vector (extents))
-    return Interval (0, 0);
-
   Interval ext;
   for (vsize i = 0; i + 1 < breaks.size (); i++)
     {
@@ -102,12 +113,19 @@ Axis_group_interface::cached_pure_height (Grob *me, int start, int end)
 	break;
 
       if (r >= start)
-	ext.unite (ly_scm2interval (scm_c_vector_ref (extents, i)));
+	ext.unite (ly_scm2interval (scm_c_vector_ref (measure_extents, i)));
     }
 
   return ext;
 }
 
+// adjacent-pure-heights is a pair of vectors, each of which has one element
+// for every measure in the score. The first vector stores, for each measure,
+// the combined height of the elements that are present only when the bar
+// is at the beginning of a line. The second vector stores, for each measure,
+// the combined height of the elements that are present only when the bar
+// is not at the beginning of a line.
+
 MAKE_SCHEME_CALLBACK (Axis_group_interface, adjacent_pure_heights, 1)
 SCM
 Axis_group_interface::adjacent_pure_heights (SCM smob)
@@ -122,13 +140,16 @@ Axis_group_interface::adjacent_pure_heights (SCM smob)
   vector<vsize> breaks = ps->get_break_indices ();
   vector<Grob*> cols = ps->get_columns ();
 
-  SCM ret = scm_c_make_vector (breaks.size () - 1, SCM_EOL);
+  SCM begin_line_heights = scm_c_make_vector (breaks.size () - 1, SCM_EOL);
+  SCM mid_line_heights = scm_c_make_vector (breaks.size () - 1, SCM_EOL);
+
   vsize it_index = 0;
   for (vsize i = 0; i + 1 < breaks.size (); i++)
     {
       int start = Paper_column::get_rank (cols[breaks[i]]);
       int end = Paper_column::get_rank (cols[breaks[i+1]]);
-      Interval iv;
+      Interval begin_line_iv;
+      Interval mid_line_iv;
 
       for (vsize j = it_index; j < items.size (); j++)
 	{
@@ -139,8 +160,10 @@ Axis_group_interface::adjacent_pure_heights (SCM smob)
 	      && !to_boolean (it->get_property ("cross-staff")))
 	    {
 	      Interval dims = items[j]->pure_height (common, start, end);
+	      Interval &target_iv = it->pure_is_visible (start-1, end) ? mid_line_iv : begin_line_iv;
+
 	      if (!dims.is_empty ())
-		iv.unite (dims);
+		target_iv.unite (dims);
 	    }
 
 	  if (rank < end)
@@ -156,14 +179,16 @@ Axis_group_interface::adjacent_pure_heights (SCM smob)
 	      && !to_boolean (spanners[j]->get_property ("cross-staff")))
 	    {
 	      Interval dims = spanners[j]->pure_height (common, start, end);
+
 	      if (!dims.is_empty ())
-		iv.unite (dims);
+		mid_line_iv.unite (dims);
 	    }
 	}
 
-      scm_vector_set_x (ret, scm_from_int (i), ly_interval2scm (iv));
+      scm_vector_set_x (begin_line_heights, scm_from_int (i), ly_interval2scm (begin_line_iv));
+      scm_vector_set_x (mid_line_heights, scm_from_int (i), ly_interval2scm (mid_line_iv));
     }
-  return ret;
+  return scm_cons (begin_line_heights, mid_line_heights);
 }
 
 Interval
diff --git a/lily/include/axis-group-interface.hh b/lily/include/axis-group-interface.hh
index 6dd2e7b95c..f852c92a34 100644
--- a/lily/include/axis-group-interface.hh
+++ b/lily/include/axis-group-interface.hh
@@ -31,7 +31,9 @@ struct Axis_group_interface
   static Interval relative_group_extent (vector<Grob*> const &list,
 					 Grob *common, Axis);
   static Interval relative_pure_height (Grob *me, int start, int end);
+  static Interval combine_pure_heights (Grob *me, SCM, int, int);
   static Interval cached_pure_height (Grob *me, int, int);
+  static Interval begin_of_line_pure_height (Grob *me, int);
 
   static Grob *calc_pure_elts_and_common (Grob*);
   static Skyline_pair skyline_spacing (Grob *me, vector<Grob*> elements);
diff --git a/scm/define-grob-properties.scm b/scm/define-grob-properties.scm
index 8d166a4d23..af9aa948d0 100644
--- a/scm/define-grob-properties.scm
+++ b/scm/define-grob-properties.scm
@@ -841,7 +841,7 @@ constructed from a whole number of squiggles.")
      (accidental-grob ,ly:grob? "The accidental for this note.")
      (accidental-grobs ,list? "An alist with @code{(@var{notename} .
 @var{groblist})} entries.")
-     (adjacent-pure-heights ,vector? "Used by a @code{VerticalAxisGroup} to
+     (adjacent-pure-heights ,pair? "A pair of vectors. Used by a @code{VerticalAxisGroup} to
 cache the @code{Y-extent}s of different column ranges.")
      (adjacent-hairpins ,ly:grob-array? "A list of directly neighboring
 hairpins.")
-- 
2.39.5