]> git.donarmstrong.com Git - lilypond.git/commitdiff
Improves horizontal spacing of axis groups that SpanBars traverse (issue 1846).
authorMike Solomon <mike@apollinemike.com>
Fri, 30 Sep 2011 06:16:07 +0000 (08:16 +0200)
committerMike Solomon <mike@apollinemike.com>
Fri, 30 Sep 2011 06:16:07 +0000 (08:16 +0200)
Also fixes issue 910, where lyrics force notes to take up too much
horizontal space because of incorrect SpanBar pure heights.

12 files changed:
input/regression/span-bar-allow-span-bar.ly [new file with mode: 0644]
lily/grob.cc
lily/include/grob.hh
lily/include/pure-from-neighbor-interface.hh [new file with mode: 0644]
lily/pure-from-neighbor-engraver.cc [new file with mode: 0644]
lily/pure-from-neighbor-interface.cc [new file with mode: 0644]
lily/span-bar-engraver.cc
lily/span-bar-stub-engraver.cc [new file with mode: 0644]
ly/engraver-init.ly
scm/define-grob-properties.scm
scm/define-grobs.scm
scm/output-lib.scm

diff --git a/input/regression/span-bar-allow-span-bar.ly b/input/regression/span-bar-allow-span-bar.ly
new file mode 100644 (file)
index 0000000..5bb765d
--- /dev/null
@@ -0,0 +1,59 @@
+
+\version "2.15.10"
+
+\header {
+  texidoc = "The @code{SpanBarStub} grob takes care of horizontal spacing
+for @code{SpanBar} grobs.  When the @code{SpanBar} is disallowed, objects
+in contexts that the span bar would have otherwise crossed align as if the
+span bar were not there.
+"
+}
+
+<<
+  \new Staff {
+    \repeat unfold 64 { c''8 }
+  }
+  \new GrandStaff <<
+    \new Staff
+      \new Voice = "upper"
+        \relative c'' {
+          c2 c c c
+          \once \override Staff . BarLine #'allow-span-bar = ##f
+          c2 c c c
+          c2 c c c
+          \once \override Staff . BarLine #'allow-span-bar = ##f
+          c2 c c c
+        }
+    \new Lyrics \lyricsto "upper" \lyricmode {
+      long-syllable a b c long-syllable a b c
+      long-syllable a b c long-syllable a b c
+    }
+
+    \new Staff
+      \new Voice = "middle"
+        \relative c'' {
+          c2 c c c
+          c2 c c c
+          c2 c c c
+          \once \override Staff . BarLine #'allow-span-bar = ##f
+          c2 c c c
+        }
+    \new Lyrics \lyricsto "middle" \lyricmode {
+      syllable a b c syllable a b c
+      syllable a b c syllable a b c
+    }
+
+    \new Staff
+      \new Voice = "lower"
+        \relative c'' {
+          c2 c c c
+          c2 c c c
+          c2 c c c
+          c2 c c c
+        }
+    \new Lyrics \lyricsto "lower" \lyricmode {
+      word a b c word a b c
+      word a b c word a b c
+    }
+  >>
+>>
index 36aadb4e938b549df05260d98cf7dc13030e6455..fa8e1f684307453ec9d90b1d4998c9747b2e212a 100644 (file)
@@ -572,6 +572,82 @@ Grob::fixup_refpoint ()
     }
 }
 
+/****************************************************************
+  VERTICAL ORDERING
+****************************************************************/
+
+Grob*
+get_maybe_root_vertical_alignment (Grob *g, Grob *maybe)
+{
+  if (!g)
+    return maybe;
+  if (Align_interface::has_interface (g))
+    return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), g);
+  return get_maybe_root_vertical_alignment (g->get_parent (Y_AXIS), maybe);
+
+}
+
+Grob*
+Grob::get_root_vertical_alignment (Grob *g)
+{
+  return get_maybe_root_vertical_alignment (g, 0);
+}
+
+Grob*
+Grob::get_vertical_axis_group (Grob *g)
+{
+  if (!g)
+    return 0;
+  if (Axis_group_interface::has_interface (g)
+      && Align_interface::has_interface (g->get_parent (Y_AXIS)))
+    return g;
+  return get_vertical_axis_group (g->get_parent (Y_AXIS));
+
+}
+
+int
+Grob::get_vertical_axis_group_index (Grob *g)
+{
+  Grob *val = get_root_vertical_alignment (g);
+  if (!val)
+    return -1;
+  Grob *vax = get_vertical_axis_group (g);
+  extract_grob_set (val, "elements", elts);
+  for (vsize i = 0; i < elts.size (); i++)
+    if (elts[i] == vax)
+      return (int) i;
+  g->programming_error ("could not find this grob's vertical axis group in the vertical alignment");
+  return -1;
+}
+
+bool
+Grob::vertical_less (Grob *g1, Grob *g2)
+{
+  Grob *vag = get_root_vertical_alignment (g1);
+  if (!vag)
+    return false;
+  if (!vag)
+    {
+      g1->programming_error ("grob does not belong to a VerticalAlignment?");
+      return false;
+    }
+  Grob *ag1 = get_vertical_axis_group (g1);
+  Grob *ag2 = get_vertical_axis_group (g2);
+
+  extract_grob_set (vag, "elements", elts);
+
+  for (vsize i = 0; i < elts.size (); i++)
+    {
+      if (elts[i] == ag1)
+        return true;
+      if (elts[i] == ag2)
+        return false;
+    }
+
+  g1->programming_error ("could not place this grob in its axis group");
+  return false;
+}
+
 /****************************************************************
   MESSAGES
 ****************************************************************/
index 5ca85402e0468d8cb21aee93488e206f835d2f03..36f912a2894629a4b789366bd359d486ee60a238 100644 (file)
@@ -140,6 +140,12 @@ public:
   Grob *get_parent (Axis a) const;
   void fixup_refpoint ();
 
+  /* vertical ordering */
+  static Grob *get_root_vertical_alignment (Grob *g);
+  static Grob *get_vertical_axis_group (Grob *g);
+  static bool vertical_less (Grob *g1, Grob *g2);
+  static int get_vertical_axis_group_index (Grob *g);
+
   virtual Interval_t<int> spanned_rank_interval () const;
   virtual bool pure_is_visible (int start, int end) const;
   bool check_cross_staff (Grob *common);
diff --git a/lily/include/pure-from-neighbor-interface.hh b/lily/include/pure-from-neighbor-interface.hh
new file mode 100644 (file)
index 0000000..eb46139
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+  This file is part of LilyPond, the GNU music typesetter.
+
+  Copyright (C) 2011 Mike Solomon <mike@apollinemike.com>
+
+  LilyPond is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  LilyPond is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef PURE_FROM_NEIGHBOR_INTERFACE_HH
+#define PURE_FROM_NEIGHBOR_INTERFACE_HH
+
+#include "lily-proto.hh"
+#include "grob-interface.hh"
+
+class Pure_from_neighbor_interface
+{
+public:
+  DECLARE_SCHEME_CALLBACK (filter_elements, (SCM));
+  DECLARE_GROB_INTERFACE ();
+
+};
+
+#endif
diff --git a/lily/pure-from-neighbor-engraver.cc b/lily/pure-from-neighbor-engraver.cc
new file mode 100644 (file)
index 0000000..14d9473
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+  This file is part of LilyPond, the GNU music typesetter.
+
+  Copyright (C) 2011 Mike Solomon <mike@apollinemike.com>
+
+  LilyPond is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  LilyPond is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <map>
+#include <algorithm>
+
+#include "grob.hh"
+#include "item.hh"
+#include "pointer-group-interface.hh"
+#include "pure-from-neighbor-interface.hh"
+#include "engraver.hh"
+
+class Pure_from_neighbor_engraver : public Engraver
+{
+  vector<Grob *> items_then_;
+  vector<Grob *> items_now_;
+  vector<Grob *> pures_then_;
+  vector<Grob *> pures_now_;
+
+public:
+  TRANSLATOR_DECLARATIONS (Pure_from_neighbor_engraver);
+protected:
+  DECLARE_ACKNOWLEDGER (pure_from_neighbor);
+  DECLARE_ACKNOWLEDGER (item);
+  void stop_translation_timestep ();
+};
+
+Pure_from_neighbor_engraver::Pure_from_neighbor_engraver ()
+{
+}
+
+void
+Pure_from_neighbor_engraver::acknowledge_item (Grob_info i)
+{
+  SCM pure_relevant_p = ly_lily_module_constant ("pure-relevant?");
+  if (!Pure_from_neighbor_interface::has_interface (i.grob ())
+      && to_boolean (scm_call_1 (pure_relevant_p, i.item ()->self_scm ())))
+    items_now_.push_back (i.item ());
+}
+
+// note that this can get out of hand if there are lots of vertical axis groups...
+
+void
+Pure_from_neighbor_engraver::acknowledge_pure_from_neighbor (Grob_info i)
+{
+  pures_now_.push_back (i.item ());
+}
+
+void
+Pure_from_neighbor_engraver::stop_translation_timestep ()
+{
+  if (pures_now_.size ())
+    {
+      for (vsize i = 0; i < pures_now_.size (); i++)
+        for (vsize j = 0; j < items_then_.size (); j++)
+          Pointer_group_interface::add_grob (pures_now_[i], ly_symbol2scm ("elements"), items_then_[j]);
+
+      for (vsize i = 0; i < pures_then_.size (); i++)
+        for (vsize j = 0; j < items_now_.size (); j++)
+          Pointer_group_interface::add_grob (pures_then_[i], ly_symbol2scm ("elements"), items_now_[j]);
+
+      items_then_.clear ();
+      items_then_.insert (items_then_.end (), items_now_.begin (), items_now_.end ());
+      items_now_.clear ();
+
+      pures_then_.clear ();
+      pures_then_.insert (pures_then_.end (), pures_now_.begin (), pures_now_.end ());
+      pures_now_.clear ();
+    }
+}
+
+#include "translator.icc"
+
+ADD_ACKNOWLEDGER (Pure_from_neighbor_engraver, item);
+ADD_ACKNOWLEDGER (Pure_from_neighbor_engraver, pure_from_neighbor);
+ADD_TRANSLATOR (Pure_from_neighbor_engraver,
+                /* doc */
+                "Coordinates items that get their pure heights from their neighbors.",
+
+                /* create */
+                "",
+
+                /* read */
+                "",
+
+                /* write */
+                ""
+               );
diff --git a/lily/pure-from-neighbor-interface.cc b/lily/pure-from-neighbor-interface.cc
new file mode 100644 (file)
index 0000000..8d5e724
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+  This file is part of LilyPond, the GNU music typesetter.
+
+  Copyright (C) 2011 Mike Solomon <mike@apollinemike.com>
+
+  LilyPond is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  LilyPond is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "grob.hh"
+#include "grob-array.hh"
+#include "pointer-group-interface.hh"
+#include "pure-from-neighbor-interface.hh"
+#include "spanner.hh"
+#include "system.hh"
+
+MAKE_SCHEME_CALLBACK (Pure_from_neighbor_interface, filter_elements, 1);
+SCM
+Pure_from_neighbor_interface::filter_elements (SCM smob)
+{
+  Grob *me = unsmob_grob (smob);
+  extract_grob_set (me, "elements", elts);
+  vector<Grob *> new_elts;
+  Interval_t<int> srl = me->get_system ()->spanned_rank_interval ();
+  for (vsize i = 0; i < elts.size (); i++)
+    if (srl.contains (elts[i]->spanned_rank_interval ()[LEFT]))
+      new_elts.push_back (elts[i]);
+
+  SCM elements_scm = me->get_object ("elements");
+  if (Grob_array::unsmob (elements_scm))
+    {
+      vector<Grob *> &arr
+        = unsmob_grob_array (elements_scm)->array_reference ();
+      arr = new_elts;
+    }
+
+  return SCM_BOOL_T;
+}
+
+ADD_INTERFACE (Pure_from_neighbor_interface,
+               "A collection of routines to allow for objects' pure"
+               "heights and heights to be calculated based on the"
+               "heights of the objects' neighbors.",
+
+               /* properties */
+               "elements-filtered "
+              );
index 6d145898f382ee63f21e92bdb81797c1e77d6653..5ebe256346c6cbc4ad98a20a533c765a7a48282b 100644 (file)
@@ -33,6 +33,7 @@ dependencies to the spanbars.
 class Span_bar_engraver : public Engraver
 {
   Item *spanbar_;
+  bool make_spanbar_;
   vector<Item *> bars_;
 
 public:
@@ -40,11 +41,13 @@ public:
 protected:
   DECLARE_ACKNOWLEDGER (bar_line);
   void stop_translation_timestep ();
+  void process_acknowledged ();
 };
 
 Span_bar_engraver::Span_bar_engraver ()
 {
   spanbar_ = 0;
+  make_spanbar_ = false;
 }
 
 void
@@ -57,11 +60,24 @@ Span_bar_engraver::acknowledge_bar_line (Grob_info i)
       bars_.push_back (it);
 
       if (bars_.size () >= 2 && !spanbar_)
-        {
-          spanbar_ = make_item ("SpanBar", SCM_EOL);
+        make_spanbar_ = true;
+    }
+}
+
+void
+Span_bar_engraver::process_acknowledged ()
+{
+  if (make_spanbar_)
+    {
+      Grob *vag = Grob::get_root_vertical_alignment (bars_[0]);
+      if (vag)
+        vector_sort (bars_, Grob::vertical_less);
+      spanbar_ = make_item ("SpanBar", SCM_EOL);
 
-          spanbar_->set_parent (bars_[0], X_AXIS);
-        }
+      spanbar_->set_parent (bars_[0], X_AXIS);
+      for (vsize i = 0; i < bars_.size (); i++)
+        Span_bar::add_bar (spanbar_, bars_[i]);
+      make_spanbar_ = false;
     }
 }
 
@@ -70,6 +86,7 @@ Span_bar_engraver::stop_translation_timestep ()
 {
   if (spanbar_)
     {
+      vector_sort (bars_, Grob::vertical_less);
       for (vsize i = 0; i < bars_.size (); i++)
         Span_bar::add_bar (spanbar_, bars_[i]);
 
diff --git a/lily/span-bar-stub-engraver.cc b/lily/span-bar-stub-engraver.cc
new file mode 100644 (file)
index 0000000..47b0c1a
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+  This file is part of LilyPond, the GNU music typesetter.
+
+  Copyright (C) 2011 Mike Solomon <mike@apollinemike.com>
+
+  LilyPond is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  LilyPond is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <map>
+#include <algorithm>
+
+#include "align-interface.hh"
+#include "bar-line.hh"
+#include "context.hh"
+#include "grob.hh"
+#include "item.hh"
+#include "pointer-group-interface.hh"
+#include "span-bar.hh"
+#include "engraver.hh"
+
+class Span_bar_stub_engraver : public Engraver
+{
+  vector<Grob *> spanbars_;
+  map<Grob *, Context *> axis_groups_;
+
+public:
+  TRANSLATOR_DECLARATIONS (Span_bar_stub_engraver);
+protected:
+  DECLARE_ACKNOWLEDGER (span_bar);
+  DECLARE_ACKNOWLEDGER (hara_kiri_group_spanner);
+  void process_acknowledged ();
+};
+
+Span_bar_stub_engraver::Span_bar_stub_engraver ()
+{
+}
+
+void
+Span_bar_stub_engraver::acknowledge_span_bar (Grob_info i)
+{
+  spanbars_.push_back (i.grob ());
+}
+
+// note that this can get out of hand if there are lots of vertical axis groups...
+
+void
+Span_bar_stub_engraver::acknowledge_hara_kiri_group_spanner (Grob_info i)
+{
+  axis_groups_[i.grob ()] = i.context ();
+}
+
+void
+Span_bar_stub_engraver::process_acknowledged ()
+{
+  if (!spanbars_.size ())
+    return;
+
+  Grob *vertical_alignment = Grob::get_root_vertical_alignment ((*axis_groups_.begin ()).first);
+  if (!vertical_alignment) // we are at the beginning of a score, so no need for stubs
+    return;
+
+  extract_grob_set (vertical_alignment, "elements", elts);
+
+  for (vsize i = 0; i < spanbars_.size (); i++)
+    {
+      extract_grob_set (spanbars_[i], "elements", bars);
+      vector<vsize> bar_axis_indices;
+      for (vsize j = 0; j < bars.size (); j++)
+        {
+          int i = Grob::get_vertical_axis_group_index (bars[j]);
+          if (i >= 0)
+            bar_axis_indices.push_back ((vsize) i);
+        }
+      vector<Context *> affected_contexts;
+      vector<Grob *> y_parents;
+      vector<bool> keep_extent;
+      for (vsize j = 0; j < elts.size (); j++)
+        {
+          if (j > bar_axis_indices[0]
+              && j < bar_axis_indices.back ()
+              && find (bar_axis_indices.begin (), bar_axis_indices.end (), j) == bar_axis_indices.end ())
+            {
+              vsize k = 0;
+              for (; k < bar_axis_indices.size (); k++)
+                if (bar_axis_indices[k] > j)
+                  break;
+
+              k--;
+              keep_extent.push_back (to_boolean (bars[k]->get_property ("allow-span-bar")));
+
+              Context *c = axis_groups_[elts[j]];
+              if (c && c->get_parent_context ())
+                {
+                  y_parents.push_back (elts[j]);
+                  affected_contexts.push_back (c);
+                }
+            }
+        }
+      reverse (affected_contexts); // from bottom to top
+      reverse (y_parents); // from bottom to top
+      reverse (keep_extent); // from bottom to top
+      for (vsize j = 0; j < affected_contexts.size (); j++)
+        {
+          Item *it = new Item (updated_grob_properties (affected_contexts[j], ly_symbol2scm ("SpanBarStub")));
+          it->set_parent (spanbars_[i], X_AXIS);
+          Grob_info gi = make_grob_info (it, spanbars_[i]->self_scm ());
+          gi.rerouting_daddy_context_ = affected_contexts[j];
+          announce_grob (gi);
+          if (!keep_extent[j])
+            it->set_property ("Y-extent", ly_interval2scm (Interval (infinity_f, -infinity_f)));
+        }
+    }
+  spanbars_.clear ();
+}
+
+#include "translator.icc"
+
+ADD_ACKNOWLEDGER (Span_bar_stub_engraver, span_bar);
+ADD_ACKNOWLEDGER (Span_bar_stub_engraver, hara_kiri_group_spanner);
+ADD_TRANSLATOR (Span_bar_stub_engraver,
+                /* doc */
+                "Make stubs for span bars in all contexts that the span bars cross.",
+
+                /* create */
+                "SpanBarStub ",
+
+                /* read */
+                "",
+
+                /* write */
+                ""
+               );
index d719d4a43ff9e2d0a3d8355c812540adc116a7ec..41569cd4aed702c7837e76e341b03768a8afaffb 100644 (file)
@@ -312,6 +312,7 @@ contained staves are connected vertically."
 
   \consists "Instrument_name_engraver"
   \consists "Span_bar_engraver"
+  \consists "Span_bar_stub_engraver"
   \consists "Span_arpeggio_engraver"
   \consists "System_start_delimiter_engraver"
   \consists "Vertical_align_engraver"
@@ -428,6 +429,7 @@ printing of a single line of lyrics."
   \consists "Instrument_name_engraver"
   \consists "Font_size_engraver"
   \consists "Hara_kiri_engraver"
+  \consists "Pure_from_neighbor_engraver"
   searchForVoice = ##f
   %% explicitly set instrument, so it is not inherited from the parent
   instrumentName = #'()
index eea44eed4df130d569d3f38d8bd8a3f1b6e3f9b6..58a085800f6ed7a2597ff61c44077eb839d2d1f9 100644 (file)
@@ -242,6 +242,7 @@ Positive means move the center to the right.")
 the vertical edges: @code{(@var{left-height} . @var{right-height})}.")
      (edge-text ,pair? "A pair specifying the texts to be set at the
 edges: @code{(@var{left-text} . @var{right-text})}.")
+     (elements-filtered ,boolean? "Callback to filter an element list.")
      (round-up-exceptions ,list? "A list of pairs where car is the numerator
 and cdr the denominator of a moment.  Each pair in this list means that
 the multi-measure rests of the corresponding length will be rounded up to
index 5e551acf8f3cdad946c128bf9b4bde6636009c0c..db39b6889c20a562259c7620a671c8d12ed634dc 100644 (file)
        (non-musical . #t)
        (stencil . ,ly:span-bar::print)
        (X-extent . ,ly:span-bar::width)
-       (Y-extent . ,ly:axis-group-interface::height)
+       (Y-extent . #f)
        (meta . ((class . Item)
-                (object-callbacks . ((pure-Y-common . ,ly:axis-group-interface::calc-pure-y-common)
-                                     (pure-relevant-grobs . ,ly:axis-group-interface::calc-pure-relevant-grobs)))
                 (interfaces . (bar-line-interface
                                font-interface
                                span-bar-interface))))))
 
+    (SpanBarStub
+     . (
+       (elements-filtered . ,ly:pure-from-neighbor-interface::filter-elements)
+        (X-extent . ,grob::x-parent-width)
+       (Y-extent . ,span-bar-stub::height)
+       (meta . ((class . Item)
+                (object-callbacks . ((pure-Y-common . ,ly:axis-group-interface::calc-pure-y-common)
+                                     (pure-relevant-grobs . ,ly:axis-group-interface::calc-pure-relevant-grobs)))
+                (interfaces . (pure-from-neighbor-interface))))))
+
     (StaffGrouper
      . (
        (staff-staff-spacing . ((basic-distance . 9)
     (,ly:side-position-interface::y-aligned-side . ,ly:side-position-interface::pure-y-aligned-side)
     (,ly:slur::height . ,ly:slur::pure-height)
     (,ly:slur::outside-slur-callback . ,ly:slur::pure-outside-slur-callback)
+    (,span-bar-stub::height . ,ly:axis-group-interface::pure-height)
     (,ly:stem::calc-stem-begin-position . ,ly:stem::pure-calc-stem-begin-position)
     (,ly:stem::calc-stem-end-position . ,ly:stem::pure-calc-stem-end-position)
     (,stem::length . ,stem::pure-length)
index 0fbe9ee3c9ac086e682f3cc64c655d3ce5e00b33..5a15551b340998e43b5df97164680359a5044edd 100644 (file)
@@ -26,6 +26,9 @@
 (define-public (grob::is-live? grob)
   (pair? (ly:grob-basic-properties grob)))
 
+(define-public (grob::x-parent-width grob)
+  (ly:grob-property (ly:grob-parent grob X) 'X-extent))
+
 (define-public (make-stencil-boxer thickness padding callback)
   "Return function that adds a box around the grob passed as argument."
   (lambda (grob)
@@ -345,6 +348,9 @@ and duration-log @var{log}."
           (equal? (ly:item-break-dir g) RIGHT))
       (ly:grob-translate-axis! g 3.5 X)))
 
+(define-public (span-bar-stub::height grob)
+  (ly:grob-property grob 'elements-filtered)
+  (ly:axis-group-interface::height grob))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Tuplets