--- /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);
+ 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;
}
}
{
if (spanbar_)
{
+ vector_sort (bars_, Grob::vertical_less);
for (vsize i = 0; i < bars_.size (); i++)
Span_bar::add_bar (spanbar_, bars_[i]);
--- /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 */
+ ""
+ );
\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