]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/axis-group-interface.cc
use PaperColumns instead of SeparationItems
[lilypond.git] / lily / axis-group-interface.cc
index a1a1353c1f9c731a759d66abe6250dd2da893b93..b9641d41c3ec735ebacdb447b5bcbc5beab3fc7c 100644 (file)
@@ -3,7 +3,7 @@
 
   source file of the GNU LilyPond music typesetter
 
 
   source file of the GNU LilyPond music typesetter
 
-  (c) 2000--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
+  (c) 2000--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
 */
 
 #include "axis-group-interface.hh"
 */
 
 #include "axis-group-interface.hh"
 #include "grob-array.hh"
 #include "hara-kiri-group-spanner.hh"
 #include "international.hh"
 #include "grob-array.hh"
 #include "hara-kiri-group-spanner.hh"
 #include "international.hh"
+#include "lookup.hh"
 #include "paper-column.hh"
 #include "paper-score.hh"
 #include "separation-item.hh"
 #include "paper-column.hh"
 #include "paper-score.hh"
 #include "separation-item.hh"
+#include "stencil.hh"
 #include "system.hh"
 #include "warn.hh"
 
 #include "system.hh"
 #include "warn.hh"
 
@@ -61,71 +63,108 @@ Axis_group_interface::relative_group_extent (vector<Grob*> const &elts,
   for (vsize i = 0; i < elts.size (); i++)
     {
       Grob *se = elts[i];
   for (vsize i = 0; i < elts.size (); i++)
     {
       Grob *se = elts[i];
-      Interval dims = se->extent (common, a);
-      if (!dims.is_empty ())
-       r.unite (dims);
+      if (!to_boolean (se->get_property ("cross-staff")))
+       {
+         Interval dims = se->extent (common, a);
+         if (!dims.is_empty ())
+           r.unite (dims);
+       }
     }
   return r;
 }
 
     }
   return r;
 }
 
+
+/*
+  FIXME: pure extent handling has a lot of ad-hoc caching.
+  This should be done with grob property callbacks.
+
+  --hwn
+*/
+
 Interval
 Interval
-Axis_group_interface::cached_pure_height (Grob *me,
-                                         vector<Grob*> const &elts,
-                                         Grob *common,
-                                         int start, int end)
+Axis_group_interface::cached_pure_height (Grob *me, 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 ();
 {
   Paper_score *ps = get_root_system (me)->paper_score ();
   vector<vsize> breaks = ps->get_break_indices ();
   vector<Grob*> cols = ps->get_columns ();
-  vsize start_index = VPOS;
-  vsize end_index = VPOS;
 
 
-  for (vsize i = 0; i < breaks.size (); i++)
+  SCM extents = me->get_property ("adjacent-pure-heights");
+
+  Interval ext;
+  for (vsize i = 0; i + 1 < breaks.size (); i++)
     {
       int r = Paper_column::get_rank (cols[breaks[i]]);
     {
       int r = Paper_column::get_rank (cols[breaks[i]]);
-      if (start == r)
-       start_index = i;
-      if (end == r)
-       end_index = i;
-    }
-  if (end == INT_MAX)
-    end_index = breaks.size () - 1;
+      if (r > end)
+       break;
 
 
-  if (start_index == VPOS || end_index == VPOS)
-    {
-      programming_error (_ ("tried to calculate pure-height at a non-breakpoint"));
-      return Interval (0, 0);
+      if (r >= start)
+       ext.unite (ly_scm2interval (scm_c_vector_ref (extents, i)));
     }
 
     }
 
-  SCM extents = me->get_property ("cached-pure-extents");
-  if (!scm_is_vector (extents))
+  return ext;
+}
+
+MAKE_SCHEME_CALLBACK (Axis_group_interface, adjacent_pure_heights, 1)
+SCM
+Axis_group_interface::adjacent_pure_heights (SCM smob)
+{
+  Grob *me = unsmob_grob (smob);
+
+  Grob *common = calc_pure_elts_and_common (me);
+  extract_grob_set (me, "pure-relevant-items", items);
+  extract_grob_set (me, "pure-relevant-spanners", spanners);
+
+  Paper_score *ps = get_root_system (me)->paper_score ();
+  vector<vsize> breaks = ps->get_break_indices ();
+  vector<Grob*> cols = ps->get_columns ();
+
+  SCM ret = scm_c_make_vector (breaks.size () - 1, SCM_EOL);
+  vsize it_index = 0;
+  for (vsize i = 0; i + 1 < breaks.size (); i++)
     {
     {
-      extents = scm_c_make_vector (breaks.size () - 1, SCM_EOL);
-      for (vsize i = 0; i < breaks.size () - 1; i++)
+      int start = Paper_column::get_rank (cols[breaks[i]]);
+      int end = Paper_column::get_rank (cols[breaks[i+1]]);
+      Interval iv;
+
+      for (vsize j = it_index; j < items.size (); j++)
+       {
+         Item *it = dynamic_cast<Item*> (items[j]);
+         int rank = it->get_column ()->get_rank ();
+
+         if (rank <= end && it->pure_is_visible (start, end))
+           {
+             Interval dims = items[j]->pure_height (common, start, end);
+             if (!dims.is_empty ())
+               iv.unite (dims);
+           }
+
+         if (rank < end)
+           it_index++;
+         else if (rank > end)
+           break;
+       }
+
+      for (vsize j = 0; j < spanners.size (); j++)
        {
        {
-         int st = Paper_column::get_rank (cols[breaks[i]]);
-         int ed = Paper_column::get_rank (cols[breaks[i+1]]);
-         Interval iv = relative_pure_height (me, elts, common, st, ed, false);
-         scm_vector_set_x (extents, scm_from_int (i), ly_interval2scm (iv));
+         Interval_t<int> rank_span = spanners[j]->spanned_rank_interval ();
+         if (rank_span[LEFT] <= end && rank_span[RIGHT] >= start)
+           {
+             Interval dims = spanners[j]->pure_height (common, start, end);
+             if (!dims.is_empty ())
+               iv.unite (dims);
+           }
        }
        }
-      me->set_property ("cached-pure-extents", extents);
-    }
 
 
-  Interval ext (0, 0);
-  for (vsize i = start_index; i < end_index; i++)
-    ext.unite (ly_scm2interval (scm_c_vector_ref (extents, i)));
-  return ext;
+      scm_vector_set_x (ret, scm_from_int (i), ly_interval2scm (iv));
+    }
+  return ret;
 }
 
 Interval
 }
 
 Interval
-Axis_group_interface::relative_pure_height (Grob *me,
-                                           vector<Grob*> const &elts,
-                                           Grob *common,
-                                           int start, int end,
-                                           bool use_cache)
+Axis_group_interface::relative_pure_height (Grob *me, int start, int end)
 {
   /* It saves a _lot_ of time if we assume a VerticalAxisGroup is additive
 {
   /* It saves a _lot_ of time if we assume a VerticalAxisGroup is additive
-     (ie. height (i, k) = height (i, j) + height (j, k) for all i <= j <= k).
+     (ie. height (i, k) = max (height (i, j) height (j, k)) for all i <= j <= k).
      Unfortunately, it isn't always true, particularly if there is a
      VerticalAlignment somewhere in the descendants.
 
      Unfortunately, it isn't always true, particularly if there is a
      VerticalAlignment somewhere in the descendants.
 
@@ -134,18 +173,36 @@ Axis_group_interface::relative_pure_height (Grob *me,
      reasonably safe to assume that if our parent is a VerticalAlignment,
      we can assume additivity and cache things nicely. */
   Grob *p = me->get_parent (Y_AXIS);
      reasonably safe to assume that if our parent is a VerticalAlignment,
      we can assume additivity and cache things nicely. */
   Grob *p = me->get_parent (Y_AXIS);
-  if (use_cache && p && Align_interface::has_interface (p))
-    return Axis_group_interface::cached_pure_height (me, elts, common, start, end);
+  if (p && Align_interface::has_interface (p))
+    return Axis_group_interface::cached_pure_height (me, start, end);
+
+  Grob *common = calc_pure_elts_and_common (me);
+  extract_grob_set (me, "pure-relevant-items", items);
+  extract_grob_set (me, "pure-relevant-spanners", spanners);
 
   Interval r;
 
 
   Interval r;
 
-  for (vsize i = 0; i < elts.size (); i++)
+  for (vsize i = 0; i < items.size (); i++)
     {
     {
-      Interval_t<int> rank_span = elts[i]->spanned_rank_iv ();
-      Item *it = dynamic_cast<Item*> (elts[i]);
-      if (rank_span[LEFT] <= end && rank_span[RIGHT] >= start && (!it || it->pure_is_visible (start, end)))
+      Item *it = dynamic_cast<Item*> (items[i]);
+      int rank = it->get_column ()->get_rank ();
+
+      if (rank > end)
+       break;
+      else if (rank >= start && it->pure_is_visible (start, end))
+       {
+         Interval dims = it->pure_height (common, start, end);
+         if (!dims.is_empty ())
+           r.unite (dims);
+       }
+    }
+
+  for (vsize i = 0; i < spanners.size (); i++)
+    {
+      Interval_t<int> rank_span = spanners[i]->spanned_rank_interval ();
+      if (rank_span[LEFT] <= end && rank_span[RIGHT] >= start)
        {
        {
-         Interval dims = elts[i]->pure_height (common, start, end);
+         Interval dims = spanners[i]->pure_height (common, start, end);
          if (!dims.is_empty ())
            r.unite (dims);
        }
          if (!dims.is_empty ())
            r.unite (dims);
        }
@@ -177,7 +234,18 @@ Axis_group_interface::pure_height (SCM smob, SCM start_scm, SCM end_scm)
   int end = robust_scm2int (end_scm, INT_MAX);
   Grob *me = unsmob_grob (smob);
 
   int end = robust_scm2int (end_scm, INT_MAX);
   Grob *me = unsmob_grob (smob);
 
-  return pure_group_height (me, start, end);
+  /* Maybe we are in the second pass of a two-pass spacing run. In that
+     case, the Y-extent of a system is already given to us */
+  System *system = dynamic_cast<System*> (me);
+  if (system)
+    {
+      SCM line_break_details = system->column (start)->get_property ("line-break-system-details");
+      SCM system_y_extent = scm_assq (ly_symbol2scm ("system-Y-extent"), line_break_details);
+      if (scm_is_pair (system_y_extent))
+       return scm_cdr (system_y_extent);
+    }
+
+  return ly_interval2scm (pure_group_height (me, start, end));
 }
 
 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_skylines, 1);
 }
 
 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_skylines, 1);
@@ -186,7 +254,16 @@ Axis_group_interface::calc_skylines (SCM smob)
 {
   Grob *me = unsmob_grob (smob);
   extract_grob_set (me, "elements", elts);
 {
   Grob *me = unsmob_grob (smob);
   extract_grob_set (me, "elements", elts);
-  return skyline_spacing (me, elts).smobbed_copy ();
+  Skyline_pair skylines = skyline_spacing (me, elts);
+
+  /* add a minimum-Y-extent-sized box to the skyline */
+  SCM min_y_extent = me->get_property ("minimum-Y-extent");
+  if (is_number_pair (min_y_extent))
+    {
+      Box b (me->extent (me, X_AXIS), ly_scm2interval (min_y_extent));
+      skylines.insert (b, 0, X_AXIS);
+    }
+  return skylines.smobbed_copy ();
 }
 
 /* whereas calc_skylines calculates skylines for axis-groups with a lot of
 }
 
 /* whereas calc_skylines calculates skylines for axis-groups with a lot of
@@ -208,7 +285,7 @@ Axis_group_interface::combine_skylines (SCM smob)
   Skyline_pair ret;
   for (vsize i = 0; i < elements.size (); i++)
     {
   Skyline_pair ret;
   for (vsize i = 0; i < elements.size (); i++)
     {
-      SCM skyline_scm = elements[i]->get_property ("skylines");
+      SCM skyline_scm = elements[i]->get_property ("vertical-skylines");
       if (Skyline_pair::unsmob (skyline_scm))
        {
          Real offset = elements[i]->relative_coordinate (y_common, Y_AXIS);
       if (Skyline_pair::unsmob (skyline_scm))
        {
          Real offset = elements[i]->relative_coordinate (y_common, Y_AXIS);
@@ -224,7 +301,8 @@ SCM
 Axis_group_interface::generic_group_extent (Grob *me, Axis a)
 {
   /* trigger the callback to do skyline-spacing on the children */
 Axis_group_interface::generic_group_extent (Grob *me, Axis a)
 {
   /* trigger the callback to do skyline-spacing on the children */
-  (void) me->get_property ("skylines");
+  if (a == Y_AXIS)
+    (void) me->get_property ("vertical-skylines");
 
   extract_grob_set (me, "elements", elts);
   Grob *common = common_refpoint_of_array (elts, me, a);
 
   extract_grob_set (me, "elements", elts);
   Grob *common = common_refpoint_of_array (elts, me, a);
@@ -235,49 +313,90 @@ Axis_group_interface::generic_group_extent (Grob *me, Axis a)
   return ly_interval2scm (r - my_coord);
 }
 
   return ly_interval2scm (r - my_coord);
 }
 
-SCM
-Axis_group_interface::pure_group_height (Grob *me, int start, int end)
-{
-  Grob *common = unsmob_grob (me->get_object ("common-refpoint-of-elements"));
 
 
-  if (!common)
-    {
-      extract_grob_set (me, "elements", elts);
+Grob *
+Axis_group_interface::calc_pure_elts_and_common (Grob *me)
+{
+  if (Grob *c = unsmob_grob (me->get_object ("pure-Y-common")))
+    return c;
+  
+  extract_grob_set (me, "elements", elts);
 
 
-      vector<Grob*> relevant_elts;
-      SCM is_relevant = ly_lily_module_constant ("pure-relevant");
+  vector<Grob*> relevant_items;
+  vector<Grob*> relevant_spanners;
+  SCM pure_relevant_p = ly_lily_module_constant ("pure-relevant?");
 
 
-      for (vsize i = 0; i < elts.size (); i++)
+  for (vsize i = 0; i < elts.size (); i++)
+    {
+      if (to_boolean (scm_apply_1 (pure_relevant_p, elts[i]->self_scm (), SCM_EOL)))
        {
        {
-         if (to_boolean (scm_apply_1 (is_relevant, elts[i]->self_scm (), SCM_EOL)))
-           relevant_elts.push_back (elts[i]);
-
-         Item *it = dynamic_cast<Item*> (elts[i]);
-         Direction d = LEFT;
-         if (it)
-           do
-             {
-               Item *piece = it->find_prebroken_piece (d);
-               if (piece && to_boolean (scm_apply_1 (is_relevant, piece->self_scm (), SCM_EOL)))
-                 relevant_elts.push_back (piece);
-             }
-           while (flip (&d) != LEFT);
+         if (dynamic_cast<Item*> (elts[i]))
+           relevant_items.push_back (elts[i]);
+         else if (dynamic_cast<Spanner*> (elts[i]))
+           relevant_spanners.push_back (elts[i]);
        }
        }
+           
 
 
-      common = common_refpoint_of_array (relevant_elts, me, Y_AXIS);
-      me->set_object ("common-refpoint-of-elements", common->self_scm ());
-
-      SCM ga_scm = Grob_array::make_array ();
-      Grob_array *ga = unsmob_grob_array (ga_scm);
-      ga->set_array (relevant_elts);
-      me->set_object ("pure-relevant-elements", ga_scm);
+      Item *it = dynamic_cast<Item*> (elts[i]);
+      Direction d = LEFT;
+      if (it)
+       do
+         {
+           Item *piece = it->find_prebroken_piece (d);
+           if (piece && to_boolean (scm_apply_1 (pure_relevant_p, piece->self_scm (), SCM_EOL)))
+             relevant_items.push_back (piece);
+         }
+       while (flip (&d) != LEFT);
     }
     }
+  vector_sort (relevant_items, Item::less);
+
+  Grob *common = common_refpoint_of_array (relevant_items, me, Y_AXIS);
+  common = common_refpoint_of_array (relevant_spanners, common, Y_AXIS);
+
+  me->set_object ("pure-Y-common", common->self_scm ());
+  
+  SCM items_scm = Grob_array::make_array ();
+  SCM spanners_scm = Grob_array::make_array ();
+
+  unsmob_grob_array (items_scm)->set_array (relevant_items);
+  unsmob_grob_array (spanners_scm)->set_array (relevant_spanners);
+  me->set_object ("pure-relevant-items", items_scm);
+  me->set_object ("pure-relevant-spanners", spanners_scm);
+
+  return common;
+}
+
+MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_x_common, 1);
+SCM
+Axis_group_interface::calc_x_common (SCM grob)
+{
+  Grob *me = unsmob_grob (grob);
+
+  extract_grob_set (me, "elements", elts);
+  Grob *common = common_refpoint_of_array (elts, me, X_AXIS);
+  return common->self_scm ();
+}
 
 
-  extract_grob_set (me, "pure-relevant-elements", elts);
+MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_y_common, 1);
+SCM
+Axis_group_interface::calc_y_common (SCM grob)
+{
+  Grob *me = unsmob_grob (grob);
+
+  extract_grob_set (me, "elements", elts);
+  Grob *common = common_refpoint_of_array (elts, me, Y_AXIS);
+  return common->self_scm ();
+}
+
+Interval
+Axis_group_interface::pure_group_height (Grob *me, int start, int end)
+{
+  Grob *common = calc_pure_elts_and_common (me);
+       
   Real my_coord = me->relative_coordinate (common, Y_AXIS);
   Real my_coord = me->relative_coordinate (common, Y_AXIS);
-  Interval r (relative_pure_height (me, elts, common, start, end, true));
+  Interval r (relative_pure_height (me, start, end));
 
 
-  return ly_interval2scm (r - my_coord);
+  return r - my_coord;
 }
 
 void
 }
 
 void
@@ -299,14 +418,20 @@ Axis_group_interface::get_children (Grob *me, vector<Grob*> *found)
 bool
 staff_priority_less (Grob * const &g1, Grob * const &g2)
 {
 bool
 staff_priority_less (Grob * const &g1, Grob * const &g2)
 {
-  int priority_1 = robust_scm2int (g1->get_property ("outside-staff-priority"), INT_MIN);
-  int priority_2 = robust_scm2int (g2->get_property ("outside-staff-priority"), INT_MIN);
+  Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
+  Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
 
   if (priority_1 < priority_2)
     return true;
   else if (priority_1 > priority_2)
     return false;
 
 
   if (priority_1 < priority_2)
     return true;
   else if (priority_1 > priority_2)
     return false;
 
+  /* if neither grob has an outside-staff priority, the ordering will have no
+     effect -- we just need to choose a consistent ordering. We do this to
+     avoid the side-effect of calculating extents. */
+  if (isinf (priority_1))
+    return g1 < g2;
+
   /* if there is no preference in staff priority, choose the left-most one */
   Grob *common = g1->common_refpoint (g2, X_AXIS);
   Real start_1 = g1->extent (common, X_AXIS)[LEFT];
   /* if there is no preference in staff priority, choose the left-most one */
   Grob *common = g1->common_refpoint (g2, X_AXIS);
   Real start_1 = g1->extent (common, X_AXIS)[LEFT];
@@ -315,17 +440,27 @@ staff_priority_less (Grob * const &g1, Grob * const &g2)
 }
 
 static void
 }
 
 static void
-add_boxes (Grob *me, Grob *x_common, Grob *y_common, vector<Box> *const boxes)
+add_boxes (Grob *me, Grob *x_common, Grob *y_common, vector<Box> *const boxes, Skyline_pair *skylines)
 {
 {
-  /* if we are a parent, consider the children's boxes instead of mine */
-  if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements")))
+  /* if a child has skylines, use them instead of the extent box */
+  if (Skyline_pair *pair = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
+    {
+      Skyline_pair s = *pair;
+      s.shift (me->relative_coordinate (x_common, X_AXIS));
+      s.raise (me->relative_coordinate (y_common, Y_AXIS));
+      skylines->merge (s);
+    }
+  else if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements")))
     {
       for (vsize i = 0; i < elements->size (); i++)
     {
       for (vsize i = 0; i < elements->size (); i++)
-       add_boxes (elements->grob (i), x_common, y_common, boxes);
+       add_boxes (elements->grob (i), x_common, y_common, boxes, skylines);
+    }
+  else if (!scm_is_number (me->get_property ("outside-staff-priority"))
+          && !to_boolean (me->get_property ("cross-staff")))
+    {
+      boxes->push_back (Box (me->extent (x_common, X_AXIS),
+                            me->extent (y_common, Y_AXIS)));
     }
     }
-  else if (!scm_is_number (me->get_property ("outside-staff-priority")))
-    boxes->push_back (Box (me->extent (x_common, X_AXIS),
-                          me->extent (y_common, Y_AXIS)));
 }
 
 /* We want to avoid situations like this:
 }
 
 /* We want to avoid situations like this:
@@ -343,7 +478,7 @@ add_boxes (Grob *me, Grob *x_common, Grob *y_common, vector<Box> *const boxes)
    positions, one for above the staff, one for below).
 
    In each pass, we loop through the unplaced grobs from left to right.
    positions, one for above the staff, one for below).
 
    In each pass, we loop through the unplaced grobs from left to right.
-   If the grob overlaps the right-most affected position, we place it
+   If the grob doesn't overlap the right-most affected position, we place it
    (and then update the right-most affected position to point to the right
    edge of the just-placed grob).  Otherwise, we skip it until the next pass.
 */
    (and then update the right-most affected position to point to the right
    edge of the just-placed grob).  Otherwise, we skip it until the next pass.
 */
@@ -379,9 +514,7 @@ add_grobs_of_one_priority (Skyline_pair *const skylines,
          if (b[X_AXIS][LEFT] - 2*horizon_padding < last_affected_position[dir])
            continue;
 
          if (b[X_AXIS][LEFT] - 2*horizon_padding < last_affected_position[dir])
            continue;
 
-         if (b[X_AXIS].is_empty () || b[Y_AXIS].is_empty ())
-           warning (_f ("outside-staff object %s has an empty extent", elements[i]->name ().c_str ()));
-         else
+         if (!b[X_AXIS].is_empty () && !b[Y_AXIS].is_empty ())
            {
              boxes.clear ();
              boxes.push_back (b);
            {
              boxes.clear ();
              boxes.push_back (b);
@@ -398,6 +531,10 @@ add_grobs_of_one_priority (Skyline_pair *const skylines,
              elements[i]->set_property ("outside-staff-priority", SCM_BOOL_F);
              last_affected_position[dir] = b[X_AXIS][RIGHT];
            }
              elements[i]->set_property ("outside-staff-priority", SCM_BOOL_F);
              last_affected_position[dir] = b[X_AXIS][RIGHT];
            }
+
+         /*
+           Ugh: quadratic. --hwn
+          */
          elements.erase (elements.begin () + i);
        }
     }
          elements.erase (elements.begin () + i);
        }
     }
@@ -415,34 +552,76 @@ Axis_group_interface::skyline_spacing (Grob *me, vector<Grob*> elements)
   vsize i = 0;
   vector<Box> boxes;
 
   vsize i = 0;
   vector<Box> boxes;
 
+  Skyline_pair skylines;
   for (i = 0; i < elements.size ()
         && !scm_is_number (elements[i]->get_property ("outside-staff-priority")); i++)
   for (i = 0; i < elements.size ()
         && !scm_is_number (elements[i]->get_property ("outside-staff-priority")); i++)
-    add_boxes (elements[i], x_common, y_common, &boxes);
+    add_boxes (elements[i], x_common, y_common, &boxes, &skylines);
 
 
-  Skyline_pair skylines (boxes, 0, X_AXIS);
+  SCM padding_scm = me->get_property ("skyline-horizontal-padding");
+  Real padding = robust_scm2double (padding_scm, 0.1);
+  skylines.merge (Skyline_pair (boxes, padding, X_AXIS));
   for (; i < elements.size (); i++)
     {
       SCM priority = elements[i]->get_property ("outside-staff-priority");
       vector<Grob*> current_elts;
       current_elts.push_back (elements[i]);
   for (; i < elements.size (); i++)
     {
       SCM priority = elements[i]->get_property ("outside-staff-priority");
       vector<Grob*> current_elts;
       current_elts.push_back (elements[i]);
-      while (i < elements.size () - 1
+      while (i + 1 < elements.size () 
             && scm_eq_p (elements[i+1]->get_property ("outside-staff-priority"), priority))
        current_elts.push_back (elements[++i]);
 
       add_grobs_of_one_priority (&skylines, current_elts, x_common, y_common);
     }
             && scm_eq_p (elements[i+1]->get_property ("outside-staff-priority"), priority))
        current_elts.push_back (elements[++i]);
 
       add_grobs_of_one_priority (&skylines, current_elts, x_common, y_common);
     }
+  skylines.shift (-me->relative_coordinate (x_common, X_AXIS));
   return skylines;
 }
 
   return skylines;
 }
 
+MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_max_stretch, 1)
+SCM
+Axis_group_interface::calc_max_stretch (SCM smob)
+{
+  Grob *me = unsmob_grob (smob);
+  Real ret = 0;
+  extract_grob_set (me, "elements", elts);
+
+  for (vsize i = 0; i < elts.size (); i++)
+    if (Axis_group_interface::has_interface (elts[i]))
+      ret += robust_scm2double (elts[i]->get_property ("max-stretch"), 0.0);
+
+  return scm_from_double (ret);
+}
+
+extern bool debug_skylines;
+MAKE_SCHEME_CALLBACK (Axis_group_interface, print, 1)
+SCM
+Axis_group_interface::print (SCM smob)
+{
+  if (!debug_skylines)
+    return SCM_BOOL_F;
+
+  Grob *me = unsmob_grob (smob);
+  Stencil ret;
+  if (Skyline_pair *s = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
+    {
+      ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[UP].to_points (X_AXIS)).in_color (255, 0, 255));
+      ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[DOWN].to_points (X_AXIS)).in_color (0, 255, 255));
+    }
+  return ret.smobbed_copy ();
+}
+
 ADD_INTERFACE (Axis_group_interface,
 
               "An object that groups other layout objects.",
 
               /* properties */
 ADD_INTERFACE (Axis_group_interface,
 
               "An object that groups other layout objects.",
 
               /* properties */
+              "X-common "
+              "Y-common "
+              "adjacent-pure-heights "
               "axes "
               "elements "
               "axes "
               "elements "
-              "common-refpoint-of-elements "
-              "pure-relevant-elements "
-              "skylines "
-              "cached-pure-extents "
+              "keep-fixed-while-stretching "
+              "max-stretch "
+              "pure-Y-common "
+              "pure-relevant-items "
+              "pure-relevant-spanners "
+              "vertical-skylines "
               );
               );