]> git.donarmstrong.com Git - lilypond.git/commitdiff
Implements padding at right-broken hairpins that come up against span bars.
authorMike Solomon <mike@apollinemike.com>
Fri, 16 Dec 2011 08:25:29 +0000 (09:25 +0100)
committerMike Solomon <mike@apollinemike.com>
Fri, 16 Dec 2011 08:25:29 +0000 (09:25 +0100)
Uses a concurrent-hairpin-engraver to find all concurrent hairpins in a score
and shortens all end-of-line hairpins for a given system by 0.6 if there are
collisions with a span bar.

input/regression/hairpin-span-bar.ly [new file with mode: 0644]
lily/concurrent-hairpin-engraver.cc [new file with mode: 0644]
lily/hairpin.cc
lily/include/hairpin.hh
lily/include/system.hh
lily/span-bar.cc
lily/system.cc
ly/engraver-init.ly
scm/define-grob-properties.scm
scm/define-grobs.scm
scm/output-lib.scm

diff --git a/input/regression/hairpin-span-bar.ly b/input/regression/hairpin-span-bar.ly
new file mode 100644 (file)
index 0000000..800a7f3
--- /dev/null
@@ -0,0 +1,30 @@
+\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
diff --git a/lily/concurrent-hairpin-engraver.cc b/lily/concurrent-hairpin-engraver.cc
new file mode 100644 (file)
index 0000000..652b9cb
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+  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 */
+                ""
+               );
index 9f5bb1a7e42edc2b620494b7d052d04a69746cd0..403e7bcd083bf4f19f96e41ee9da89f9108adce0 100644 (file)
@@ -20,7 +20,9 @@
 #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"
@@ -30,6 +32,7 @@
 #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);
@@ -47,6 +50,62 @@ Hairpin::pure_height (SCM smob, SCM, SCM)
   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)
@@ -173,17 +232,33 @@ 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"
@@ -262,6 +337,8 @@ ADD_INTERFACE (Hairpin,
                /* properties */
                "adjacent-spanners "
                "circled-tip "
+               "concurrent-hairpins "
+               "broken-bound-padding "
                "bound-padding "
                "grow-direction "
                "height "
index c6cfb7dcd4bf9975a4a08bdb45b40b01119607be..4bbf2886378a38c4038c644c7dd846df18445872 100644 (file)
@@ -26,7 +26,9 @@
 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 ();
 };
index 5ce98171b81647d3cd4d8b9cd9e3f48038a73e10..8dad61bd0b9d58be7a5342c6e2b9c571f1ed28b2 100644 (file)
@@ -42,6 +42,7 @@ public:
   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;
index d64b6ba87c8f227aadb79a332cce0e7c4db7e27b..d9b9aa0cc3ff36ff5b42bc8ad102d6a5d2148616 100644 (file)
@@ -222,8 +222,8 @@ Span_bar::notify_grobs_of_my_existence (Grob *me)
   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
index 4e5fc1bb4ce5a9815b919d39917eaab86d164451..87f139d164ed8d1fbf9919569cb73c54f5d9b195 100644 (file)
@@ -753,6 +753,36 @@ System::get_extremal_staff (Direction dir, Interval const &iv)
   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)
 {
index e5409daa434a4351ddb1c8debd585008b9f9893a..093b88e9b2f833497f71d2e4e334fc70cda71f3b 100644 (file)
@@ -538,6 +538,7 @@ automatically when an output definition (a @code{\score} or
   \consists "Stanza_number_align_engraver"
   \consists "Bar_number_engraver"
   \consists "Parenthesis_engraver"
+  \consists "Concurrent_hairpin_engraver"
 
   \defaultchild "Staff"
 
index f209fe649ac63f42306339cb40ea9658934d770d..2391c4e6f0ea279d206daa464f1f793e3829a586 100644 (file)
@@ -162,6 +162,8 @@ stick out of its bounds?")
 @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
@@ -1018,6 +1020,7 @@ bounds are spaced.")
 
      (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.")
@@ -1047,8 +1050,9 @@ for a full score's worth of grobs.")
 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
@@ -1177,6 +1181,7 @@ to be within staff spaces.")
      (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.
index 806f720697814d4094e08348b7fc2d6fd9f02230..08b3ff5f1416f87fe29f325937f6797d85d25a96 100644 (file)
      . (
        (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)
index 9137d5be170845fd9e0d9cd498dc425c01575982..84f9984b3399eb57b443da13b284d9f7be6b81d1 100644 (file)
@@ -409,10 +409,10 @@ and duration-log @var{log}."
          (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)