--- /dev/null
+\version "2.15.22"
+
+\header {
+ texidoc = "@code{Hairpin} grobs do not collide with @code{SpanBar} grobs.
+@code{Hairpin} grobs should, however, go to the end of a line when the
+@code{SpanBar} is not present.
+"
+}
+
+\score {
+ <<
+ \new GrandStaff <<
+ \new Staff \relative c'' { a\< a a a \break a a a a \break a a a a\! }
+ \new Staff \relative c'' { a4 a a a s1 a4 a a a }
+ >>
+ \new GrandStaff <<
+ \new Staff \relative c'' { a^\< a a a a a a a a a a a\! }
+ \new Staff \relative c'' { \repeat unfold 12 a4 }
+ >>
+ \new GrandStaff <<
+ \new Staff \relative c'' { a4 a a a s1 a4 a a a }
+ \new Staff \relative c'' { a^\< a a a a a a a a a a a\! }
+ >>
+ >>
+ \layout {
+ \context {
+ \RemoveEmptyStaffContext
+ }
+ }
+}
\ No newline at end of file
--- /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 "engraver.hh"
+
+#include "international.hh"
+#include "pointer-group-interface.hh"
+#include "spanner.hh"
+#include "stream-event.hh"
+#include "warn.hh"
+#include "item.hh"
+
+#include "translator.icc"
+
+class Concurrent_hairpin_engraver : public Engraver
+{
+public:
+ TRANSLATOR_DECLARATIONS (Concurrent_hairpin_engraver);
+
+protected:
+ DECLARE_ACKNOWLEDGER (hairpin);
+ DECLARE_END_ACKNOWLEDGER (hairpin);
+
+ void stop_translation_timestep ();
+ void finalize ();
+
+private:
+ vector<Grob *> arriving_hairpins_;
+ vector<Grob *> departing_hairpins_;
+ vector<Grob *> hairpins_hanging_out_;
+};
+
+Concurrent_hairpin_engraver::Concurrent_hairpin_engraver ()
+{
+}
+
+void
+Concurrent_hairpin_engraver::acknowledge_hairpin (Grob_info info)
+{
+ arriving_hairpins_.push_back (info.grob ());
+}
+
+void
+Concurrent_hairpin_engraver::acknowledge_end_hairpin (Grob_info info)
+{
+ departing_hairpins_.push_back (info.grob ());
+}
+
+void
+Concurrent_hairpin_engraver::stop_translation_timestep ()
+{
+ for (vsize i = 0; i < departing_hairpins_.size (); i++)
+ for (vsize j = 0; j < hairpins_hanging_out_.size (); j++)
+ if (departing_hairpins_[i] == hairpins_hanging_out_[j])
+ {
+ hairpins_hanging_out_.erase (hairpins_hanging_out_.begin () + j);
+ break;
+ }
+ if (arriving_hairpins_.size ())
+ {
+ if (arriving_hairpins_.size () > 1)
+ for (vsize i = 0; i < arriving_hairpins_.size (); i++)
+ for (vsize j = i; j < arriving_hairpins_.size (); j++)
+ {
+ Pointer_group_interface::add_grob (arriving_hairpins_[i], ly_symbol2scm ("concurrent-hairpins"), arriving_hairpins_[j]);
+ Pointer_group_interface::add_grob (arriving_hairpins_[j], ly_symbol2scm ("concurrent-hairpins"), arriving_hairpins_[i]);
+ }
+
+ for (vsize i = 0; i < arriving_hairpins_.size (); i++)
+ for (vsize j = 0; j < hairpins_hanging_out_.size (); j++)
+ {
+ Pointer_group_interface::add_grob (arriving_hairpins_[i], ly_symbol2scm ("concurrent-hairpins"), hairpins_hanging_out_[j]);
+ Pointer_group_interface::add_grob (hairpins_hanging_out_[j], ly_symbol2scm ("concurrent-hairpins"), arriving_hairpins_[i]);
+ }
+ }
+ hairpins_hanging_out_.insert (hairpins_hanging_out_.end (), arriving_hairpins_.begin (), arriving_hairpins_.end ());
+ arriving_hairpins_.resize (0);
+ departing_hairpins_.resize (0);
+}
+
+void
+Concurrent_hairpin_engraver::finalize ()
+{
+ hairpins_hanging_out_.resize (0);
+}
+
+ADD_ACKNOWLEDGER (Concurrent_hairpin_engraver, hairpin);
+ADD_END_ACKNOWLEDGER (Concurrent_hairpin_engraver, hairpin);
+
+ADD_TRANSLATOR (Concurrent_hairpin_engraver,
+ /* doc */
+ "Collect concurrent hairpins.",
+
+ /* create */
+ "",
+
+ /* read */
+ "",
+
+ /* write */
+ ""
+ );
#include "hairpin.hh"
#include "axis-group-interface.hh"
+#include "bar-line.hh"
#include "dimensions.hh"
+#include "directional-element-interface.hh"
#include "international.hh"
#include "line-interface.hh"
#include "output-def.hh"
#include "staff-symbol-referencer.hh"
#include "text-interface.hh"
#include "note-column.hh"
+#include "system.hh"
#include "warn.hh"
MAKE_SCHEME_CALLBACK (Hairpin, pure_height, 3);
return ly_interval2scm (Interval (-height, height));
}
+MAKE_SCHEME_CALLBACK (Hairpin, broken_bound_padding, 1);
+SCM
+Hairpin::broken_bound_padding (SCM smob)
+{
+ Spanner *me = unsmob_spanner (smob);
+ Item *r_bound = me->get_bound (RIGHT);
+ if (r_bound->break_status_dir () != -1)
+ {
+ me->warning ("Asking for broken bound padding at a non-broken bound.");
+ return scm_from_double (0.0);
+ }
+
+ System *sys = dynamic_cast<System *> (me->get_system ());
+ Direction dir = get_grob_direction (me->get_parent (Y_AXIS));
+ if (!dir)
+ return scm_from_double (0.0);
+
+ Grob *my_vertical_axis_group = Grob::get_vertical_axis_group (me);
+ Drul_array<Grob *> vertical_axis_groups;
+ Direction d = DOWN;
+ do
+ vertical_axis_groups[d] = d == dir
+ ? sys->get_neighboring_staff (d, my_vertical_axis_group, Interval_t<int> (me->spanned_rank_interval ()))
+ : my_vertical_axis_group;
+ while (flip (&d) != DOWN);
+
+ if (!vertical_axis_groups[dir])
+ return scm_from_double (0.0);
+
+ Drul_array<Grob *> span_bars (0, 0);
+ d = DOWN;
+ do
+ {
+ extract_grob_set (vertical_axis_groups[d], "elements", elts);
+ for (vsize i = elts.size (); i--;)
+ if (Bar_line::has_interface (elts[i])
+ && dynamic_cast<Item *> (elts[i])->break_status_dir () == -1)
+ {
+ SCM hsb = elts[i]->get_property ("has-span-bar");
+ if (!scm_is_pair (hsb))
+ break;
+
+ span_bars[d] = unsmob_grob ((d == UP ? scm_car : scm_cdr) (hsb));
+ break;
+ }
+ if (!span_bars[d])
+ return scm_from_double (0.0);
+ }
+ while (flip (&d) != DOWN);
+
+ if (span_bars[DOWN] != span_bars[UP])
+ return scm_from_double (0.0);
+
+ return scm_from_double (0.6);
+}
+
MAKE_SCHEME_CALLBACK (Hairpin, print, 1);
SCM
Hairpin::print (SCM smob)
x_points[d] = e[-d];
else
x_points[d] = e[d];
-
- Item *bound = me->get_bound (d);
- if (bound->is_non_musical (bound))
- x_points[d] -= d * padding;
}
}
}
}
while (flip (&d) != LEFT);
+ // here, add padding for barlines that are not at the end of a staff
+ if (Item::is_non_musical (bounds[RIGHT]) && bounds[RIGHT]->break_status_dir () == 0)
+ x_points[RIGHT] -= padding;
+
+ // here, add padding for barlines that are at the end of a staff
+ Real broken_bound_padding = 0.0;
+ if (bounds[RIGHT]->break_status_dir () == -1)
+ {
+ extract_grob_set (me, "concurrent-hairpins", chp);
+ for (vsize i = 0; i < chp.size (); i++)
+ {
+ Spanner *span_elt = dynamic_cast<Spanner *> (chp[i]);
+ if (span_elt->get_bound (RIGHT)->break_status_dir () == -1)
+ broken_bound_padding = max (broken_bound_padding,
+ robust_scm2double (span_elt->get_property ("broken-bound-padding"), 0.0));
+ }
+ }
+ x_points[RIGHT] -= broken_bound_padding;
+
Real width = x_points[RIGHT] - x_points[LEFT];
+
if (width < 0)
{
me->warning (_ ((grow_dir < 0) ? "decrescendo too small"
/* properties */
"adjacent-spanners "
"circled-tip "
+ "concurrent-hairpins "
+ "broken-bound-padding "
"bound-padding "
"grow-direction "
"height "
struct Hairpin
{
public:
+ static Real span_bar_correction (Spanner *me);
DECLARE_SCHEME_CALLBACK (print, (SCM));
+ DECLARE_SCHEME_CALLBACK (broken_bound_padding, (SCM));
DECLARE_SCHEME_CALLBACK (pure_height, (SCM, SCM, SCM));
DECLARE_GROB_INTERFACE ();
};
Paper_score *paper_score () const;
Grob *get_vertical_alignment ();
Grob *get_extremal_staff (Direction dir, Interval const &);
+ Grob * get_neighboring_staff (Direction dir, Grob *vertical_axis_group, Interval_t<int> bounds);
Grob *get_pure_bound (Direction dir, int start, int end);
Grob *get_maybe_pure_bound (Direction dir, bool pure, int start, int end);
int get_rank () const;
vector_sort (sortable, Grob::vertical_less);
for (vsize i = 0; i < sortable.size (); i++)
sortable[i]->set_property ("has-span-bar",
- scm_cons (scm_from_bool (i != 0),
- scm_from_bool (i != sortable.size () - 1)));
+ scm_cons (i != sortable.size () - 1 ? me->self_scm () : scm_from_bool (false),
+ i != 0 ? me->self_scm () : scm_from_bool (false)));
}
Interval
return 0;
}
+// Finds the neighboring staff in the given direction over bounds
+Grob *
+System::get_neighboring_staff (Direction dir, Grob *vertical_axis_group, Interval_t<int> bounds)
+{
+ Grob *align = get_vertical_alignment ();
+ if (!align)
+ return 0;
+
+ extract_grob_set (align, "elements", elts);
+ vsize start = (dir == UP) ? 0 : elts.size () - 1;
+ vsize end = (dir == UP) ? elts.size () : VPOS;
+
+ Grob *out = 0;
+
+ for (vsize i = start; i != end; i += dir)
+ {
+ if (elts[i] == vertical_axis_group)
+ return out;
+
+ if (Hara_kiri_group_spanner::has_interface (elts[i]))
+ Hara_kiri_group_spanner::consider_suicide (elts[i]);
+
+ bounds.intersect (elts[i]->spanned_rank_interval ());
+ if (elts[i]->is_live () && !bounds.is_empty ())
+ out = elts[i];
+ }
+
+ return 0;
+}
+
vector<Simple_spacer>
System::get_simple_spacers (Real line_len, Real indent, bool ragged)
{
\consists "Stanza_number_align_engraver"
\consists "Bar_number_engraver"
\consists "Parenthesis_engraver"
+ \consists "Concurrent_hairpin_engraver"
\defaultchild "Staff"
@code{#(@var{end-of-line} @var{unbroken} @var{begin-of-line})}.
@code{#t} means visible, @code{#f} means killed.")
(breakable ,boolean? "Allow breaks here.")
+ (broken-bound-padding ,number? "The amount of padding to insert
+around spanner bounds at a line break.")
;;
;; c
(columns ,ly:grob-array? "An array of grobs, typically containing
@code{PaperColumn} or @code{NoteColumn} objects.")
+ (concurrent-hairpins ,ly:grob-array? "All concurrent hairpins.")
(conditional-elements ,ly:grob-array? "Internal use only.")
(covered-grobs ,ly:grob-array? "Grobs that could potentially collide
with a beam.")
column.")
(grace-spacing ,ly:grob? "A run of grace notes.")
- (has-span-bar ,pair? "A pair of booleans indicating whether a a span bar
-is drawn above, or respectively below, this staff.")
+ (has-span-bar ,pair? "A pair of grobs containing the span bars to
+be drawn below and above the staff. If no span bar is in a position,
+the respective element is set to @code{#f}.")
(heads ,ly:grob-array? "An array of note heads.")
(items-worth-living ,ly:grob-array? "An array of interesting items. If
(quantized-positions ,number-pair? "The beam positions after
quanting.")
+
(script-stencil ,pair? "A pair @code{(@var{type} . @var{arg})} which
acts as an index for looking up a @code{Stencil} object.")
(shorten ,ly:dimension? "The amount of space that a stem is shortened.
. (
(after-line-breaking . ,ly:spanner::kill-zero-spanned-time)
(bound-padding . 1.0)
+ (broken-bound-padding . ,ly:hairpin::broken-bound-padding)
(circled-tip . #f)
(grow-direction . ,hairpin::calc-grow-direction)
(height . 0.6666)
(hsb (ly:grob-property grob 'has-span-bar))
(ii (interval-intersection esh (cons -1.01 1.01))))
(if (pair? hsb)
- (cons (car (if (and (cdr hsb)
+ (cons (car (if (and (car hsb)
(ly:grob-property grob 'allow-span-bar))
esh ii))
- (cdr (if (car hsb) esh ii)))
+ (cdr (if (cdr hsb) esh ii)))
ii)))
(define-public (pure-from-neighbor-interface::extra-spacing-height-including-staff grob)