Fixes issue 1846.
This patch introduces the idea of a pure-from-neighbor-interface.
The interface provides support for grobs whose pure height is difficult
to estimate (any grob that crosses staffs likely suffers from this)
by allowing for the creation of spacing "stubs" in the crossed
vertical axis groups. These stubs take their pure heights from their
neighbors. So long as one knows that a grob fully crosses a vertical
axis group and the grob should take up the full vertical space of this
axis group for its rank, using approximations of pure height's from the
grob's neighbors ensures that it will be as tall as the rest of the
axis group without triggering a vertical alignment.
--- /dev/null
+
+\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
+ }
+ >>
+>>
}
}
+/****************************************************************
+ 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
****************************************************************/
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);
--- /dev/null
+/*
+ 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
--- /dev/null
+/*
+ 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 */
+ ""
+ );
--- /dev/null
+/*
+ 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 "
+ );
class Span_bar_engraver : public Engraver
{
Item *spanbar_;
+ bool make_spanbar_;
vector<Item *> bars_;
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
bars_.push_back (it);
if (bars_.size () >= 2 && !spanbar_)
- {
- spanbar_ = make_item ("SpanBar", SCM_EOL);
-
- spanbar_->set_parent (bars_[0], X_AXIS);
- }
+ make_spanbar_ = true;
}
}
void
-Span_bar_engraver::stop_translation_timestep ()
+Span_bar_engraver::process_acknowledged ()
{
- if (spanbar_)
+ 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);
for (vsize i = 0; i < bars_.size (); i++)
Span_bar::add_bar (spanbar_, bars_[i]);
+ make_spanbar_ = false;
+ }
+}
+void
+Span_bar_engraver::stop_translation_timestep ()
+{
+ if (spanbar_)
+ {
SCM vissym = ly_symbol2scm ("break-visibility");
SCM vis = bars_[0]->internal_get_property (vissym);
if (ly_is_equal (spanbar_->internal_get_property (vissym), vis))
--- /dev/null
+/*
+ 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 */
+ ""
+ );
if (!model_bar)
model_bar = me;
- vector_sort (extents, Interval::left_less);
-
Stencil span_bar;
for (vsize i = 1; i < extents.size (); i++)
{
\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"
\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 = #'()
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
(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)
(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)
(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