]> git.donarmstrong.com Git - lilypond.git/commitdiff
Better approximations for cross-staff slurs
authorMike Solomon <mike@apollinemike.com>
Mon, 10 Sep 2012 07:17:28 +0000 (09:17 +0200)
committerMike Solomon <mike@apollinemike.com>
Mon, 10 Sep 2012 07:17:28 +0000 (09:17 +0200)
SlurStub grobs are used to approximate the vertical skylines of
cross staff slurs by using control points generated by pure heights
instead of heights. Depending on how far off the actual translations are
from the minimal translations, these control points will be more or less
viable. A SlurStub is generated for every VerticalAxisGroup on which
a slur has encompass objects and only those residing on the extremal
UP and DOWN vertical axis groups get vertical skylines.

22 files changed:
input/regression/cross-staff-slur-vertical-spacing.ly [new file with mode: 0644]
lily/align-interface.cc
lily/axis-group-interface.cc
lily/figured-bass-position-engraver.cc
lily/grob.cc
lily/include/axis-group-interface.hh
lily/include/grob.hh
lily/include/slur-scoring.hh
lily/include/slur.hh
lily/melody-engraver.cc
lily/phrasing-slur-engraver.cc
lily/script-column.cc
lily/slur-configuration.cc
lily/slur-engraver.cc
lily/slur-scoring.cc
lily/slur.cc
lily/tab-tie-follow-engraver.cc
ly/property-init.ly
scm/define-grob-interfaces.scm
scm/define-grob-properties.scm
scm/define-grobs.scm
scm/output-lib.scm

diff --git a/input/regression/cross-staff-slur-vertical-spacing.ly b/input/regression/cross-staff-slur-vertical-spacing.ly
new file mode 100644 (file)
index 0000000..4daa69f
--- /dev/null
@@ -0,0 +1,73 @@
+\version "2.17.0"
+
+\header {
+  texidoc = "Cross-staff slurs are accounted for in vertical spacing.
+"
+}
+
+%#(ly:set-option 'debug-skylines #t)
+
+\new PianoStaff <<
+  \new Staff = "up" {
+    <e' c'>8
+    \change Staff = "down"
+    \slurDown
+    g,,8 ( fis g
+    \change Staff = "up"
+    <g'' c''>8 )
+    \change Staff = "down"
+    e8 dis e
+    \change Staff = "up"
+    \break
+    a'8 a'8 a'8^\markup \column { "f" "o" "o" } a'8 a'8 a'8 a'8 a'8
+  }
+  \new Staff = "down" {
+    \clef bass
+    % keep staff alive
+    s1 s1
+  }
+>>
+
+\new PianoStaff <<
+  \new Staff = "up" {
+    <e' c'>8
+    \change Staff = "down"
+    \slurDown
+    g,,8 (
+    \change Staff = "up"
+    fis'' g <g c''>8 )
+    \change Staff = "down"
+    e8 dis e
+    \change Staff = "up"
+    \break
+    a'8 a'8 a'8^\markup \column { "f" "o" "o" } a'8 a'8 a'8 a'8 a'8
+  }
+  \new Staff = "down" {
+    \clef bass
+    % keep staff alive
+    s1 s1
+  }
+>>
+
+\new PianoStaff <<
+  \new Staff = "up" {
+    R1
+    <e' c'>8
+    \change Staff = "down"
+    \slurUp
+    g,,8 (
+    \change Staff = "up"
+    fis'' g \override Stem #'direction = #UP
+    <g'' c''>8 )
+    \change Staff = "down"
+    e8 dis e
+    \change Staff = "up"
+  }
+  \new Staff = "down" {
+    \clef bass
+    % keep staff alive
+    a8 a8 a8_\markup \column { "f" "o" "o" }
+    a8 a8 a8 a8 a8
+    \break s1
+  }
+>>
index 8a2a8d9b819a4469f2b60c73f75f1d0899963d91..7f91eca59763e29171654c9b911bf1fa2a340448 100644 (file)
@@ -83,9 +83,10 @@ get_skylines (Grob *me,
 
       if (!pure)
         {
-          Skyline_pair *skys = Skyline_pair::unsmob (g->get_property (a == Y_AXIS
-                                                                      ? "vertical-skylines"
-                                                                      : "horizontal-skylines"));
+          Skyline_pair *skys = a == Y_AXIS
+                               ? Skyline_pair::unsmob (g->get_property ("vertical-skylines"))
+                               : Skyline_pair::unsmob (g->get_property ("horizontal-skylines"));
+
           if (skys)
             skylines = *skys;
 
index 707e045c9b1a13b3127997d8c1ec05fed81467b7..71b2b7cfa8d8b609a97be2a97d7ec253bd8135d8 100644 (file)
@@ -613,28 +613,6 @@ pure_staff_priority_less (Grob *const &g1, Grob *const &g2)
   return priority_1 < priority_2;
 }
 
-static void
-add_interior_skylines (Grob *me, Grob *x_common, Grob *y_common, vector<Skyline_pair> *skylines)
-{
-  if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements")))
-    {
-      for (vsize i = 0; i < elements->size (); i++)
-        add_interior_skylines (elements->grob (i), x_common, y_common, skylines);
-    }
-  else if (!scm_is_number (me->get_property ("outside-staff-priority"))
-           && !to_boolean (me->get_property ("cross-staff")))
-    {
-      Skyline_pair *maybe_pair = Skyline_pair::unsmob (me->get_property ("vertical-skylines"));
-      if (!maybe_pair)
-        return;
-      if (maybe_pair->is_empty ())
-        return;
-      skylines->push_back (Skyline_pair (*maybe_pair));
-      skylines->back ().shift (me->relative_coordinate (x_common, X_AXIS));
-      skylines->back ().raise (me->relative_coordinate (y_common, Y_AXIS));
-    }
-}
-
 // Raises the grob elt (whose skylines are given by h_skyline
 // and v_skyline) so that it doesn't intersect with staff_skyline,
 // or with anything in other_h_skylines and other_v_skylines.
@@ -824,6 +802,30 @@ Axis_group_interface::outside_staff_ancestor (Grob *me)
   return outside_staff_ancestor (parent);
 }
 
+void
+Axis_group_interface::add_interior_skylines
+(Grob *me, Grob *x_common, Grob *y_common, vector<Skyline_pair> *skylines, bool pure)
+{
+  if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements")))
+    {
+      for (vsize i = 0; i < elements->size (); i++)
+        add_interior_skylines (elements->grob (i), x_common, y_common, skylines, pure);
+    }
+  else if (pure ||
+           (!scm_is_number (me->get_property ("outside-staff-priority"))
+            && !to_boolean (me->get_property ("cross-staff"))))
+    {
+      Skyline_pair *maybe_pair = Skyline_pair::unsmob (me->get_property ("vertical-skylines"));
+      if (!maybe_pair)
+        return;
+      if (maybe_pair->is_empty ())
+        return;
+      skylines->push_back (Skyline_pair (*maybe_pair));
+      skylines->back ().shift (me->relative_coordinate (x_common, X_AXIS));
+      skylines->back ().raise (me->maybe_pure_coordinate (y_common, Y_AXIS, pure, 0, INT_MAX));
+    }
+}
+
 // It is tricky to correctly handle skyline placement of cross-staff grobs.
 // For example, cross-staff beams cannot be formatted until the distance between
 // staves is known and therefore any grobs that depend on the beam cannot be placed
@@ -875,8 +877,9 @@ Axis_group_interface::skyline_spacing (Grob *me, vector<Grob *> elements)
     {
       Grob *elt = elements[i];
       Grob *ancestor = outside_staff_ancestor (elt);
-      if (!(to_boolean (elt->get_property ("cross-staff")) || ancestor))
-        add_interior_skylines (elt, x_common, y_common, &inside_staff_skylines);
+      if (!(to_boolean (elt->get_property ("cross-staff")) || ancestor)
+          || elt->internal_has_interface (ly_symbol2scm ("cross-staff-stub-interface")))
+        add_interior_skylines (elt, x_common, y_common, &inside_staff_skylines, elt->internal_has_interface (ly_symbol2scm ("cross-staff-stub-interface")));
       if (ancestor)
         riders.insert (pair<Grob *, Grob *> (ancestor, elt));
     }
index 9b55e04f49cd83360dfbff36c6f8ed6530a4467f..a9d0a89dc04a932d672f8ba02742db860447640d 100644 (file)
@@ -102,7 +102,8 @@ Figured_bass_position_engraver::acknowledge_end_slur (Grob_info info)
 void
 Figured_bass_position_engraver::acknowledge_slur (Grob_info info)
 {
-  span_support_.push_back (info.grob ());
+  if (!info.grob ()->internal_has_interface (ly_symbol2scm ("cross-staff-stub-interface")))
+    span_support_.push_back (info.grob ());
 }
 
 void
index c515c186169e16402ea38a5476e93f7e1ae5e7a5..4e661ebf4ee5d3cc3d531845d03febde41937d61 100644 (file)
@@ -923,6 +923,25 @@ common_refpoint_of_array (set<Grob *> const &arr, Grob *common, Axis a)
   return common;
 }
 
+Interval
+maybe_pure_robust_relative_extent (Grob *me, Grob *refp, Axis a, bool pure, int start, int end)
+{
+  if (pure && a != Y_AXIS)
+    programming_error ("tried to get pure X-offset");
+  return (pure && a == Y_AXIS) ? pure_robust_relative_extent (me, refp, start, end)
+         : robust_relative_extent (me, refp, a);
+}
+
+Interval
+pure_robust_relative_extent (Grob *me, Grob *refpoint, int start, int end)
+{
+  Interval ext = me->pure_height (refpoint, start, end);
+  if (ext.is_empty ())
+    ext.add_point (me->pure_relative_y_coordinate (refpoint, start, end));
+
+  return ext;
+}
+
 Interval
 robust_relative_extent (Grob *me, Grob *refpoint, Axis a)
 {
index 4098a19514b6efe4f7c04f660d7cd6d06f6e0cfa..8567736954db701f2f0029469500a202a1ef0527 100644 (file)
@@ -28,8 +28,8 @@
 class Axis_group_interface
 {
   static Real default_outside_staff_padding_;
-  public
-:
+  public :
+
   static SCM generic_group_extent (Grob *me, Axis a);
   static Real get_default_outside_staff_padding ();
   static Interval generic_bound_extent (Grob *me, Grob *common, Axis a);
@@ -63,6 +63,7 @@ class Axis_group_interface
 
   static Grob *outside_staff_ancestor (Grob *me);
   static Skyline_pair skyline_spacing (Grob *me, vector<Grob *> elements);
+  static void add_interior_skylines (Grob *me, Grob *x_common, Grob *y_common, vector<Skyline_pair> *skylines, bool pure = false);
   static void add_element (Grob *me, Grob *);
   static void set_axes (Grob *, Axis, Axis);
   static bool has_axis (Grob *, Axis);
index 09cd566ad335b39708990d05ab74894ff2099af1..05b8f278ba3ff2856666508856986d076c734168 100644 (file)
@@ -177,6 +177,8 @@ Grob *common_refpoint_of_array (set<Grob *> const &, Grob *, Axis a);
 System *get_root_system (Grob *me);
 
 /* extents */
+Interval maybe_pure_robust_relative_extent (Grob *, Grob *, Axis, bool, int, int);
+Interval pure_robust_relative_extent (Grob *, Grob *, int, int);
 Interval robust_relative_extent (Grob *, Grob *, Axis);
 
 /* offset/extent callbacks. */
index e5ff38d6112c73750aab1d89e0baa8d1cbff4303..392253b49ccecfd8dc7fa85e254ade2b17926a43 100644 (file)
@@ -84,6 +84,7 @@ struct Slur_score_state
 {
   Spanner *slur_;
   Grob *common_[NO_AXES];
+  bool stub_;
   bool valid_;
   bool edge_has_beams_;
   bool is_broken_;
index b20311785d2d1870c23075cc28e4645f4ea70c4d..61a32348c2fa8d85d5c79245f0aef1794d55aca8 100644 (file)
 #include "std-vector.hh"
 #include "grob-interface.hh"
 
+struct Slur_info
+{
+  Slur_info (Grob *slur);
+  Grob *slur_;
+  vector<Grob *> stubs_;
+};
+
 class Slur
 {
 public:
   static void add_column (Grob *me, Grob *col);
   static void add_extra_encompass (Grob *me, Grob *col);
+  static void main_to_stub (Grob *main, Grob *stub);
   static void replace_breakable_encompass_objects (Grob *me);
-  static void auxiliary_acknowledge_extra_object (Grob_info const &, vector<Grob *> &, vector<Grob *> &);
+  static void auxiliary_acknowledge_extra_object (Grob_info const &, vector<Slur_info> &, vector<Slur_info> &);
   DECLARE_SCHEME_CALLBACK (print, (SCM));
   DECLARE_SCHEME_CALLBACK (calc_control_points, (SCM));
   DECLARE_SCHEME_CALLBACK (calc_direction, (SCM));
   DECLARE_SCHEME_CALLBACK (pure_height, (SCM, SCM, SCM));
   DECLARE_SCHEME_CALLBACK (height, (SCM));
+  DECLARE_SCHEME_CALLBACK (extremal_stub_vertical_skylines, (SCM));
   DECLARE_SCHEME_CALLBACK (vertical_skylines, (SCM));
   DECLARE_SCHEME_CALLBACK (outside_slur_callback, (SCM, SCM));
   DECLARE_SCHEME_CALLBACK (pure_outside_slur_callback, (SCM, SCM, SCM, SCM));
index 0ebd1324506ebf7b404a3ab640ba57e746cf4f91..0c9644e3843f9eb340951cc76a01b8cea28fe287 100644 (file)
@@ -70,9 +70,10 @@ Melody_engraver::stop_translation_timestep ()
 }
 
 void
-Melody_engraver::acknowledge_slur (Grob_info /* info */)
+Melody_engraver::acknowledge_slur (Grob_info info)
 {
-  melody_item_ = 0;
+  if (!info.grob ()->internal_has_interface (ly_symbol2scm ("cross-staff-stub-interface")))
+    melody_item_ = 0;
 }
 
 void
index 7e6c3c63fe3d8b0e026022e2ad555d767ca4dfda..233446bd4d44e73b68afb5d9a1d532612c936729 100644 (file)
@@ -30,6 +30,8 @@
 
 #include "translator.icc"
 
+#include <algorithm>
+
 /*
   NOTE NOTE NOTE
 
@@ -38,6 +40,8 @@
 
   (on principle, engravers don't use inheritance for code sharing)
 
+  For info on SlurStubs, check out slur-engraver.cc.
+
  */
 
 /*
@@ -49,8 +53,8 @@ class Phrasing_slur_engraver : public Engraver
 {
   vector<Stream_event *> start_events_;
   vector<Stream_event *> stop_events_;
-  vector<Grob *> slurs_;
-  vector<Grob *> end_slurs_;
+  vector<Slur_info> slur_infos_;
+  vector<Slur_info> end_slur_infos_;
   vector<Grob_info> objects_to_acknowledge_;
 
 protected:
@@ -106,10 +110,18 @@ void
 Phrasing_slur_engraver::acknowledge_note_column (Grob_info info)
 {
   Grob *e = info.grob ();
-  for (vsize i = slurs_.size (); i--;)
-    Slur::add_column (slurs_[i], e);
-  for (vsize i = end_slurs_.size (); i--;)
-    Slur::add_column (end_slurs_[i], e);
+  for (vsize i = slur_infos_.size (); i--;)
+    {
+      Slur::add_column (slur_infos_[i].slur_, e);
+      Grob *stub = make_spanner ("SlurStub", info.grob ()->self_scm ());
+      slur_infos_[i].stubs_.push_back (stub);
+    }
+  for (vsize i = end_slur_infos_.size (); i--;)
+    {
+      Slur::add_column (end_slur_infos_[i].slur_, e);
+      Grob *stub = make_spanner ("SlurStub", info.grob ()->self_scm ());
+      end_slur_infos_[i].stubs_.push_back (stub);
+    }
 }
 
 void
@@ -146,6 +158,7 @@ void
 Phrasing_slur_engraver::acknowledge_script (Grob_info info)
 {
   if (!info.grob ()->internal_has_interface (ly_symbol2scm ("dynamic-interface")))
+
     acknowledge_extra_object (info);
 }
 
@@ -164,18 +177,21 @@ Phrasing_slur_engraver::acknowledge_end_tie (Grob_info info)
 void
 Phrasing_slur_engraver::acknowledge_slur (Grob_info info)
 {
-  acknowledge_extra_object (info);
+  if (!info.grob ()->internal_has_interface (ly_symbol2scm ("cross-staff-stub-interface")))
+    acknowledge_extra_object (info);
 }
 
 void
 Phrasing_slur_engraver::finalize ()
 {
-  for (vsize i = 0; i < slurs_.size (); i++)
+  for (vsize i = 0; i < slur_infos_.size (); i++)
     {
-      slurs_[i]->warning (_ ("unterminated phrasing slur"));
-      slurs_[i]->suicide ();
+      slur_infos_[i].slur_->warning (_ ("unterminated phrasing slur"));
+      slur_infos_[i].slur_->suicide ();
+      for (vsize j = 0; j < slur_infos_[i].stubs_.size (); j++)
+        slur_infos_[i].stubs_[j]->suicide ();
     }
-  slurs_.clear ();
+  slur_infos_.clear ();
 }
 
 void
@@ -188,13 +204,13 @@ Phrasing_slur_engraver::process_music ()
 
       // Find the slurs that are ended with this event (by checking the spanner-id)
       bool ended = false;
-      for (vsize j = slurs_.size (); j--;)
+      for (vsize j = slur_infos_.size (); j--;)
         {
-          if (id == robust_scm2string (slurs_[j]->get_property ("spanner-id"), ""))
+          if (id == robust_scm2string (slur_infos_[j].slur_->get_property ("spanner-id"), ""))
             {
               ended = true;
-              end_slurs_.push_back (slurs_[j]);
-              slurs_.erase (slurs_.begin () + j);
+              end_slur_infos_.push_back (slur_infos_[j].slur_);
+              slur_infos_.erase (slur_infos_.begin () + j);
             }
         }
       if (ended)
@@ -210,7 +226,7 @@ Phrasing_slur_engraver::process_music ()
         ev->origin ()->warning (_ ("cannot end phrasing slur"));
     }
 
-  vsize old_slurs = slurs_.size ();
+  vsize old_slurs = slur_infos_.size ();
   for (vsize i = start_events_.size (); i--;)
     {
       Stream_event *ev = start_events_[i];
@@ -218,10 +234,10 @@ Phrasing_slur_engraver::process_music ()
       Direction updown = to_dir (ev->get_property ("direction"));
 
       bool completed;
-      for (vsize j = slurs_.size (); !(completed = (j-- == 0));)
+      for (vsize j = slur_infos_.size (); !(completed = (j-- == 0));)
         {
           // Check if we already have a slur with the same spanner-id.
-          if (id == robust_scm2string (slurs_[j]->get_property ("spanner-id"), ""))
+          if (id == robust_scm2string (slur_infos_[j].slur_->get_property ("spanner-id"), ""))
             {
               if (j < old_slurs)
                 {
@@ -239,11 +255,11 @@ Phrasing_slur_engraver::process_music ()
               if (!updown)
                 break;
 
-              Stream_event *c = unsmob_stream_event (slurs_[j]->get_property ("cause"));
+              Stream_event *c = unsmob_stream_event (slur_infos_[j].slur_->get_property ("cause"));
 
               if (!c)
                 {
-                  slurs_[j]->programming_error ("phrasing slur without a cause");
+                  slur_infos_[j].slur_->programming_error ("phrasing slur without a cause");
                   continue;
                 }
 
@@ -254,8 +270,10 @@ Phrasing_slur_engraver::process_music ()
 
               if (!slur_dir)
                 {
-                  slurs_[j]->suicide ();
-                  slurs_.erase (slurs_.begin () + j);
+                  slur_infos_[j].slur_->suicide ();
+                  for (vsize k = 0; k < slur_infos_[i].stubs_.size (); k++)
+                    slur_infos_[j].stubs_[k]->suicide ();
+                  slur_infos_.erase (slur_infos_.begin () + j);
                   continue;
                 }
 
@@ -272,7 +290,7 @@ Phrasing_slur_engraver::process_music ()
           slur->set_property ("spanner-id", ly_string2scm (id));
           if (updown)
             set_grob_direction (slur, updown);
-          slurs_.push_back (slur);
+          slur_infos_.push_back (slur);
         }
     }
 }
@@ -282,27 +300,58 @@ Phrasing_slur_engraver::stop_translation_timestep ()
 {
   if (Grob *g = unsmob_grob (get_property ("currentCommandColumn")))
     {
-      for (vsize i = 0; i < end_slurs_.size (); i++)
-        Slur::add_extra_encompass (end_slurs_[i], g);
+      for (vsize i = 0; i < end_slur_infos_.size (); i++)
+        Slur::add_extra_encompass (end_slur_infos_[i].slur_, g);
 
       if (!start_events_.size ())
-        for (vsize i = 0; i < slurs_.size (); i++)
-          Slur::add_extra_encompass (slurs_[i], g);
+        for (vsize i = 0; i < slur_infos_.size (); i++)
+          Slur::add_extra_encompass (slur_infos_[i].slur_, g);
     }
 
-  for (vsize i = 0; i < end_slurs_.size (); i++)
+  for (vsize i = 0; i < end_slur_infos_.size (); i++)
     {
-      Spanner *s = dynamic_cast<Spanner *> (end_slurs_[i]);
+      Spanner *s = dynamic_cast<Spanner *> (end_slur_infos_[i].slur_);
       if (!s->get_bound (RIGHT))
         s->set_bound (RIGHT, unsmob_grob (get_property ("currentMusicalColumn")));
       announce_end_grob (s, SCM_EOL);
     }
 
   for (vsize i = 0; i < objects_to_acknowledge_.size (); i++)
-    Slur::auxiliary_acknowledge_extra_object (objects_to_acknowledge_[i], slurs_, end_slurs_);
+    Slur::auxiliary_acknowledge_extra_object (objects_to_acknowledge_[i], slur_infos_, end_slur_infos_);
+
+  for (vsize i = 0; i < end_slur_infos_.size (); i++)
+    {
+      // There are likely SlurStubs we don't need. Get rid of them.
+      vector<Grob *> vags;
+      vector<Grob *> stubs;
+      for (vsize j = 0; j < end_slur_infos_[i].stubs_.size (); j++)
+        {
+          Grob *stub = end_slur_infos_[i].stubs_[j];
+          Grob *vag = Grob::get_vertical_axis_group (stub);
+          if (vag)
+            {
+              vector<Grob *>::const_iterator it =
+                find (vags.begin (), vags.end (), vag);
+              if (it != vags.end ())
+                stub->suicide ();
+              else
+                {
+                  vags.push_back (vag);
+                  stubs.push_back (stub);
+                }
+            }
+          else
+            {
+              end_slur_infos_[i].slur_->programming_error ("Cannot find vertical axis group for NoteColumn.");
+              stub->suicide ();
+            }
+        }
+      for (vsize j = 0; j < stubs.size (); j++)
+        Slur::main_to_stub (end_slur_infos_[i].slur_, stubs[j]);
+    }
 
   objects_to_acknowledge_.clear ();
-  end_slurs_.clear ();
+  end_slur_infos_.clear ();
   start_events_.clear ();
   stop_events_.clear ();
 }
index 0a014a38008bb3e57aafb92a3671aa90b0c8ac95..149e666c38b2c8ff1a9a4f0cb4711dbf2e3c2a18 100644 (file)
@@ -41,6 +41,13 @@ Script_column::add_side_positioned (Grob *me, Grob *script)
   Pointer_group_interface::add_grob (me, ly_symbol2scm ("scripts"), script);
 }
 
+int
+pushed_by_slur (Grob *g)
+{
+  return g->get_property ("avoid-slur") == ly_symbol2scm ("outside")
+         || g->get_property ("avoid-slur") == ly_symbol2scm ("around");
+}
+
 LY_DEFINE (ly_grob_script_priority_less, "ly:grob-script-priority-less",
            2, 0, 0, (SCM a, SCM b),
            "Compare two grobs by script priority.  For internal use.")
@@ -48,6 +55,21 @@ LY_DEFINE (ly_grob_script_priority_less, "ly:grob-script-priority-less",
   Grob *i1 = unsmob_grob (a);
   Grob *i2 = unsmob_grob (b);
 
+  /*
+   * avoid-staff of slur trumps script priority. If one grob is
+   * supposed to be printed outside a slur and another grob inside,
+   * we place the inside grob below the outside even if the inside
+   * grob has a higher script-priority.
+   */
+  if (unsmob_grob (i1->get_object ("slur"))
+      && unsmob_grob (i2->get_object ("slur")))
+    {
+      int push1 = pushed_by_slur (i1);
+      int push2 = pushed_by_slur (i2);
+      if (push1 != push2)
+        return push1 < push2 ? SCM_BOOL_T : SCM_BOOL_F;
+    }
+
   SCM p1 = i1->get_property ("script-priority");
   SCM p2 = i2->get_property ("script-priority");
 
index b144b2656ff927aeb5a51f506e631b98cf441499..02df6fe35ff6ea22ef40430162287fc69945f0d0 100644 (file)
@@ -47,7 +47,7 @@ avoid_staff_line (Slur_score_state const &state,
 
       Grob *staff = state.extremes_[LEFT].staff_;
 
-      Real p = 2 * (y - staff->relative_coordinate (state.common_[Y_AXIS], Y_AXIS))
+      Real p = 2 * (y - staff->maybe_pure_coordinate (state.common_[Y_AXIS], Y_AXIS, state.stub_, 0, INT_MAX))
                / state.staff_space_;
 
       Real const round = my_round (p);
index 49f92ab0ed5a96b04e5bd5331fb1b49cf12f4e35..05249d91769eb3f3644c1cc346246c2168a3a24c 100644 (file)
@@ -30,6 +30,8 @@
 
 #include "translator.icc"
 
+#include <algorithm>
+
 /*
   NOTE NOTE NOTE
 
   least, it is for phrasing slurs: a note can be both beginning and
   ending of a phrase.
 */
+
+Slur_info::Slur_info (Grob *slur)
+{
+  slur_ = slur;
+}
+
 class Slur_engraver : public Engraver
 {
   vector<Stream_event *> start_events_;
   vector<Stream_event *> stop_events_;
-  vector<Grob *> slurs_;
-  vector<Grob *> end_slurs_;
+  vector<Slur_info> slur_infos_;
+  vector<Slur_info> end_slur_infos_;
   vector<Grob_info> objects_to_acknowledge_;
 
   void set_melisma (bool);
@@ -112,11 +120,28 @@ Slur_engraver::set_melisma (bool m)
 void
 Slur_engraver::acknowledge_note_column (Grob_info info)
 {
+  /*
+   * For every active slur, we create a slur stub.
+   * As we do not yet know what vertical axis groups note columns belong to,
+   * we create a stub for each note and then suicide duplicate stubs on
+   * axis groups.
+   * These slurs should be used ONLY to approximate cross-staff slurs
+   * in vertical skylines.
+   */
+
   Grob *e = info.grob ();
-  for (vsize i = slurs_.size (); i--;)
-    Slur::add_column (slurs_[i], e);
-  for (vsize i = end_slurs_.size (); i--;)
-    Slur::add_column (end_slurs_[i], e);
+  for (vsize i = slur_infos_.size (); i--;)
+    {
+      Slur::add_column (slur_infos_[i].slur_, e);
+      Grob *stub = make_spanner ("SlurStub", info.grob ()->self_scm ());
+      slur_infos_[i].stubs_.push_back (stub);
+    }
+  for (vsize i = end_slur_infos_.size (); i--;)
+    {
+      Slur::add_column (end_slur_infos_[i].slur_, e);
+      Grob *stub = make_spanner ("SlurStub", info.grob ()->self_scm ());
+      end_slur_infos_[i].stubs_.push_back (stub);
+    }
 }
 
 void
@@ -171,12 +196,15 @@ Slur_engraver::acknowledge_end_tie (Grob_info info)
 void
 Slur_engraver::finalize ()
 {
-  for (vsize i = 0; i < slurs_.size (); i++)
+  for (vsize i = 0; i < slur_infos_.size (); i++)
     {
-      slurs_[i]->warning (_ ("unterminated slur"));
-      slurs_[i]->suicide ();
+      slur_infos_[i].slur_->warning (_ ("unterminated slur"));
+      slur_infos_[i].slur_->suicide ();
+      for (vsize j = 0; j < slur_infos_[i].stubs_.size (); j++)
+        slur_infos_[i].stubs_[j]->suicide ();
     }
-  slurs_.clear ();
+
+  slur_infos_.clear ();
 }
 
 void
@@ -189,13 +217,13 @@ Slur_engraver::process_music ()
 
       // Find the slurs that are ended with this event (by checking the spanner-id)
       bool ended = false;
-      for (vsize j = slurs_.size (); j--;)
+      for (vsize j = slur_infos_.size (); j--;)
         {
-          if (id == robust_scm2string (slurs_[j]->get_property ("spanner-id"), ""))
+          if (id == robust_scm2string (slur_infos_[j].slur_->get_property ("spanner-id"), ""))
             {
               ended = true;
-              end_slurs_.push_back (slurs_[j]);
-              slurs_.erase (slurs_.begin () + j);
+              end_slur_infos_.push_back (slur_infos_[j]);
+              slur_infos_.erase (slur_infos_.begin () + j);
             }
         }
       if (ended)
@@ -211,7 +239,7 @@ Slur_engraver::process_music ()
         ev->origin ()->warning (_ ("cannot end slur"));
     }
 
-  vsize old_slurs = slurs_.size ();
+  vsize old_slurs = slur_infos_.size ();
   for (vsize i = start_events_.size (); i--;)
     {
       Stream_event *ev = start_events_[i];
@@ -219,10 +247,10 @@ Slur_engraver::process_music ()
       Direction updown = to_dir (ev->get_property ("direction"));
 
       bool completed;
-      for (vsize j = slurs_.size (); !(completed = (j-- == 0));)
+      for (vsize j = slur_infos_.size (); !(completed = (j-- == 0));)
         {
           // Check if we already have a slur with the same spanner-id.
-          if (id == robust_scm2string (slurs_[j]->get_property ("spanner-id"), ""))
+          if (id == robust_scm2string (slur_infos_[j].slur_->get_property ("spanner-id"), ""))
             {
               if (j < old_slurs)
                 {
@@ -240,11 +268,11 @@ Slur_engraver::process_music ()
               if (!updown)
                 break;
 
-              Stream_event *c = unsmob_stream_event (slurs_[j]->get_property ("cause"));
+              Stream_event *c = unsmob_stream_event (slur_infos_[j].slur_->get_property ("cause"));
 
               if (!c)
                 {
-                  slurs_[j]->programming_error ("slur without a cause");
+                  slur_infos_[j].slur_->programming_error ("slur without a cause");
                   continue;
                 }
 
@@ -255,8 +283,10 @@ Slur_engraver::process_music ()
 
               if (!slur_dir)
                 {
-                  slurs_[j]->suicide ();
-                  slurs_.erase (slurs_.begin () + j);
+                  slur_infos_[j].slur_->suicide ();
+                  for (vsize k = 0; k < slur_infos_[j].stubs_.size (); k++)
+                    slur_infos_[j].stubs_[k]->suicide ();
+                  slur_infos_.erase (slur_infos_.begin () + j);
                   continue;
                 }
 
@@ -273,7 +303,7 @@ Slur_engraver::process_music ()
           slur->set_property ("spanner-id", ly_string2scm (id));
           if (updown)
             set_grob_direction (slur, updown);
-          slurs_.push_back (slur);
+          slur_infos_.push_back (Slur_info (slur));
 
           if (to_boolean (get_property ("doubleSlurs")))
             {
@@ -281,11 +311,11 @@ Slur_engraver::process_music ()
               slur = make_spanner ("Slur", ev->self_scm ());
               slur->set_property ("spanner-id", ly_string2scm (id));
               set_grob_direction (slur, UP);
-              slurs_.push_back (slur);
+              slur_infos_.push_back (Slur_info (slur));
             }
         }
     }
-  set_melisma (slurs_.size ());
+  set_melisma (slur_infos_.size ());
 }
 
 void
@@ -293,27 +323,59 @@ Slur_engraver::stop_translation_timestep ()
 {
   if (Grob *g = unsmob_grob (get_property ("currentCommandColumn")))
     {
-      for (vsize i = 0; i < end_slurs_.size (); i++)
-        Slur::add_extra_encompass (end_slurs_[i], g);
+      for (vsize i = 0; i < end_slur_infos_.size (); i++)
+        Slur::add_extra_encompass (end_slur_infos_[i].slur_, g);
 
       if (!start_events_.size ())
-        for (vsize i = 0; i < slurs_.size (); i++)
-          Slur::add_extra_encompass (slurs_[i], g);
+        for (vsize i = 0; i < slur_infos_.size (); i++)
+          Slur::add_extra_encompass (slur_infos_[i].slur_, g);
     }
 
-  for (vsize i = 0; i < end_slurs_.size (); i++)
+  for (vsize i = 0; i < end_slur_infos_.size (); i++)
     {
-      Spanner *s = dynamic_cast<Spanner *> (end_slurs_[i]);
+      Spanner *s = dynamic_cast<Spanner *> (end_slur_infos_[i].slur_);
       if (!s->get_bound (RIGHT))
         s->set_bound (RIGHT, unsmob_grob (get_property ("currentMusicalColumn")));
       announce_end_grob (s, SCM_EOL);
     }
 
   for (vsize i = 0; i < objects_to_acknowledge_.size (); i++)
-    Slur::auxiliary_acknowledge_extra_object (objects_to_acknowledge_[i], slurs_, end_slurs_);
+    Slur::auxiliary_acknowledge_extra_object (objects_to_acknowledge_[i], slur_infos_, end_slur_infos_);
+
+  for (vsize i = 0; i < end_slur_infos_.size (); i++)
+    {
+      // There are likely SlurStubs we don't need. Get rid of them
+      // and only keep one per VerticalAxisGroup.
+      vector<Grob *> vags;
+      vector<Grob *> stubs;
+      for (vsize j = 0; j < end_slur_infos_[i].stubs_.size (); j++)
+        {
+          Grob *stub = end_slur_infos_[i].stubs_[j];
+          Grob *vag = Grob::get_vertical_axis_group (stub);
+          if (vag)
+            {
+              vector<Grob *>::const_iterator it =
+                find (vags.begin (), vags.end (), vag);
+              if (it != vags.end ())
+                stub->suicide ();
+              else
+                {
+                  vags.push_back (vag);
+                  stubs.push_back (stub);
+                }
+            }
+          else
+            {
+              end_slur_infos_[i].slur_->programming_error ("Cannot find vertical axis group for NoteColumn.");
+              stub->suicide ();
+            }
+        }
+      for (vsize j = 0; j < stubs.size (); j++)
+        Slur::main_to_stub (end_slur_infos_[i].slur_, stubs[j]);
+    }
 
   objects_to_acknowledge_.clear ();
-  end_slurs_.clear ();
+  end_slur_infos_.clear ();
   start_events_.clear ();
   stop_events_.clear ();
 }
index e1025fa545c56c1c20ee0a476355b778229cf336..3d929ca750038217ed528e571bc10161ba80afa8 100644 (file)
@@ -69,6 +69,7 @@ Slur_score_state::Slur_score_state ()
 {
   musical_dy_ = 0.0;
   valid_ = false;
+  stub_ = false;
   edge_has_beams_ = false;
   has_same_beam_ = false;
   is_broken_ = false;
@@ -114,8 +115,8 @@ Slur_score_state::get_encompass_info (Grob *col) const
     {
       programming_error ("no stem for note column");
       ei.x_ = col->relative_coordinate (common_[X_AXIS], X_AXIS);
-      ei.head_ = ei.stem_ = col->extent (common_[Y_AXIS],
-                                         Y_AXIS)[dir_];
+      ei.head_ = ei.stem_ = col->maybe_pure_extent (common_[Y_AXIS],
+                                         Y_AXIS, stub_, 0, INT_MAX)[dir_];
       return ei;
     }
   Direction stem_dir = get_grob_direction (stem);
@@ -128,16 +129,16 @@ Slur_score_state::get_encompass_info (Grob *col) const
   Grob *h = Stem::extremal_heads (stem)[Direction (dir_)];
   if (!h)
     {
-      ei.head_ = ei.stem_ = col->extent (common_[Y_AXIS], Y_AXIS)[dir_];
+      ei.head_ = ei.stem_ = col->maybe_pure_extent (common_[Y_AXIS], Y_AXIS, stub_, 0, INT_MAX)[dir_];
       return ei;
     }
 
-  ei.head_ = h->extent (common_[Y_AXIS], Y_AXIS)[dir_];
+  ei.head_ = h->maybe_pure_extent (common_[Y_AXIS], Y_AXIS, stub_, 0, INT_MAX)[dir_];
 
   if ((stem_dir == dir_)
-      && !stem->extent (stem, Y_AXIS).is_empty ())
+      && !stem->maybe_pure_extent (stem, Y_AXIS, stub_, 0, INT_MAX).is_empty ())
     {
-      ei.stem_ = stem->extent (common_[Y_AXIS], Y_AXIS)[dir_];
+      ei.stem_ = stem->maybe_pure_extent (common_[Y_AXIS], Y_AXIS, stub_, 0, INT_MAX)[dir_];
       if (Grob *b = Stem::get_beam (stem))
         ei.stem_ += stem_dir * 0.5 * Beam::get_beam_thickness (b);
 
@@ -175,9 +176,13 @@ Slur_score_state::get_bound_info () const
               for (int a = X_AXIS; a < NO_AXES; a++)
                 {
                   Axis ax = Axis (a);
-                  Interval s = extremes[d].stem_->extent (common_[ax], ax);
+                  Interval s = ax == Y_AXIS
+                               ? extremes[d].stem_->maybe_pure_extent (common_[ax], ax, stub_, 0, INT_MAX)
+                               : extremes[d].stem_->extent (common_[ax], ax);
                   if (extremes[d].flag_)
-                    s.unite (extremes[d].flag_->extent (common_[ax], ax));
+                    s.unite (ax == Y_AXIS
+                             ? extremes[d].flag_->maybe_pure_extent (common_[ax], ax, stub_, 0, INT_MAX)
+                             : extremes[d].flag_->extent (common_[ax], ax));
                   if (s.is_empty ())
                     {
                       /*
@@ -185,7 +190,9 @@ Slur_score_state::get_bound_info () const
                         whole notes.
                       */
                       s = Interval (0, 0)
-                          + extremes[d].stem_->relative_coordinate (common_[ax], ax);
+                          + (ax == Y_AXIS
+                             ? extremes[d].stem_->maybe_pure_coordinate (common_[ax], ax, stub_, 0, INT_MAX)
+                             : extremes[d].stem_->relative_coordinate (common_[ax], ax));
                     }
                   extremes[d].stem_extent_[ax] = s;
                 }
@@ -215,6 +222,8 @@ void
 Slur_score_state::fill (Grob *me)
 {
   slur_ = dynamic_cast<Spanner *> (me);
+  stub_ = slur_->internal_has_interface (ly_symbol2scm ("cross-staff-stub-interface"));
+
   columns_
     = internal_extract_grob_array (me, ly_symbol2scm ("note-columns"));
 
@@ -316,7 +325,7 @@ Slur_score_state::fill (Grob *me)
       if (!is_broken_
           && extremes_[d].slur_head_)
         musical_dy_ += d
-                       * extremes_[d].slur_head_->relative_coordinate (common_[Y_AXIS], Y_AXIS);
+                       * extremes_[d].slur_head_->maybe_pure_coordinate (common_[Y_AXIS], Y_AXIS, stub_, 0, INT_MAX);
     }
 
   edge_has_beams_
@@ -333,6 +342,13 @@ Slur::calc_control_points (SCM smob)
 {
   Spanner *me = unsmob_spanner (smob);
 
+  if (!to_boolean (me->get_property ("cross-staff"))
+      && me->internal_has_interface (ly_symbol2scm ("cross-staff-stub-interface")))
+    {
+      me->suicide ();
+      return SCM_EOL;
+    }
+
   Slur_score_state state;
   state.fill (me);
 
@@ -372,7 +388,9 @@ Slur::calc_control_points (SCM smob)
     {
       Offset o = best->curve_.control_[i]
                  - Offset (me->relative_coordinate (state.common_[X_AXIS], X_AXIS),
-                           me->relative_coordinate (state.common_[Y_AXIS], Y_AXIS));
+                           me->maybe_pure_coordinate (state.common_[Y_AXIS], Y_AXIS,
+                               state.stub_, 0, INT_MAX));
+
       controls = scm_cons (ly_offset2scm (o), controls);
     }
 
@@ -462,7 +480,8 @@ Slur_score_state::get_y_attachment_range () const
           end_ys[d] = dir_
                       * max (max (dir_ * (base_attachments_[d][Y_AXIS]
                                           + parameters_.region_size_ * dir_),
-                                  dir_ * (dir_ + extremes_[d].note_column_->extent (common_[Y_AXIS], Y_AXIS)[dir_])),
+                                  dir_ * (dir_ + extremes_[d].note_column_->maybe_pure_extent
+                                                   (common_[Y_AXIS], Y_AXIS, stub_, 0, INT_MAX)[dir_])),
                              dir_ * base_attachments_[-d][Y_AXIS]);
         }
       else
@@ -512,7 +531,7 @@ Slur_score_state::get_base_attachments () const
                   || has_same_beam_))
             y = extremes_[d].stem_extent_[Y_AXIS][dir_];
           else if (head)
-            y = head->extent (common_[Y_AXIS], Y_AXIS)[dir_];
+            y = head->maybe_pure_extent (common_[Y_AXIS], Y_AXIS, stub_, 0, INT_MAX)[dir_];
           y += dir_ * 0.5 * staff_space_;
 
           y = move_away_from_staffline (y, head);
@@ -544,7 +563,7 @@ Slur_score_state::get_base_attachments () const
 
           if (extremes_[-d].bound_ != col)
             {
-              y = robust_relative_extent (col, common_[Y_AXIS], Y_AXIS)[dir_];
+              y = maybe_pure_robust_relative_extent (col, common_[Y_AXIS], Y_AXIS, stub_, 0, INT_MAX)[dir_];
               y += dir_ * 0.5 * staff_space_;
 
               if (get_grob_direction (col) == dir_
@@ -590,8 +609,8 @@ Slur_score_state::move_away_from_staffline (Real y,
     return y;
 
   Real pos
-    = (y - staff_symbol->relative_coordinate (common_[Y_AXIS],
-                                              Y_AXIS))
+    = (y - staff_symbol->maybe_pure_coordinate (common_[Y_AXIS],
+                                              Y_AXIS, stub_, 0, INT_MAX))
       * 2.0 / staff_space_;
 
   if (fabs (pos - my_round (pos)) < 0.2
@@ -629,7 +648,7 @@ Slur_score_state::generate_avoid_offsets () const
 
           Offset z = b.curve_point (0.5);
           z += Offset (small_slur->relative_coordinate (common_[X_AXIS], X_AXIS),
-                       small_slur->relative_coordinate (common_[Y_AXIS], Y_AXIS));
+                       small_slur->maybe_pure_coordinate (common_[Y_AXIS], Y_AXIS, stub_, 0, INT_MAX));
 
           z[Y_AXIS] += dir_ * parameters_.free_slur_distance_;
           avoid.push_back (z);
@@ -638,7 +657,7 @@ Slur_score_state::generate_avoid_offsets () const
         {
           Grob *g = extra_encompasses [i];
           Interval xe = g->extent (common_[X_AXIS], X_AXIS);
-          Interval ye = g->extent (common_[Y_AXIS], Y_AXIS);
+          Interval ye = g->maybe_pure_extent (common_[Y_AXIS], Y_AXIS, stub_, 0, INT_MAX);
 
           if (!xe.is_empty ()
               && !ye.is_empty ())
@@ -756,7 +775,7 @@ Slur_score_state::get_extra_encompass_infos () const
           Bezier b = Slur::get_curve (small_slur);
 
           Offset relative (small_slur->relative_coordinate (common_[X_AXIS], X_AXIS),
-                           small_slur->relative_coordinate (common_[Y_AXIS], Y_AXIS));
+                           small_slur->maybe_pure_coordinate (common_[Y_AXIS], Y_AXIS, stub_, 0, INT_MAX));
 
           for (int k = 0; k < 3; k++)
             {
@@ -790,7 +809,7 @@ Slur_score_state::get_extra_encompass_infos () const
         {
           Grob *g = encompasses [i];
           Interval xe = g->extent (common_[X_AXIS], X_AXIS);
-          Interval ye = g->extent (common_[Y_AXIS], Y_AXIS);
+          Interval ye = g->maybe_pure_extent (common_[Y_AXIS], Y_AXIS, stub_, 0, INT_MAX);
           if (Dots::has_interface (g))
             ye.widen (0.2);
 
index eb9913dc160b94a6e8821f757d067832fb30c7aa..aec1e04266a49421589f0cb331a65cc10f5f2321 100644 (file)
@@ -145,6 +145,7 @@ SCM
 Slur::print (SCM smob)
 {
   Grob *me = unsmob_grob (smob);
+
   extract_grob_set (me, "note-columns", encompasses);
   if (encompasses.empty ())
     {
@@ -251,6 +252,25 @@ Slur::add_extra_encompass (Grob *me, Grob *n)
   Pointer_group_interface::add_grob (me, ly_symbol2scm ("encompass-objects"), n);
 }
 
+void
+Slur::main_to_stub (Grob *main, Grob *stub)
+{
+  extract_grob_set (main, "note-columns", nc);
+  for (vsize i = 0; i < nc.size (); i++)
+    add_column (stub, nc[i]);
+
+  extract_grob_set (main, "encompass-objects", eo);
+  for (vsize i = 0; i < eo.size (); i++)
+    add_extra_encompass (stub, eo[i]);
+
+  stub->set_object ("surrogate", main->self_scm ());
+
+  dynamic_cast<Spanner *> (stub)->set_bound
+    (LEFT, dynamic_cast<Spanner *> (main)->get_bound (LEFT));
+  dynamic_cast<Spanner *> (stub)->set_bound
+    (RIGHT, dynamic_cast<Spanner *> (main)->get_bound (RIGHT));
+}
+
 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Slur, pure_outside_slur_callback, 4, 1, "");
 SCM
 Slur::pure_outside_slur_callback (SCM grob, SCM start_scm, SCM end_scm, SCM offset_scm)
@@ -387,32 +407,69 @@ Slur::vertical_skylines (SCM smob)
   return Skyline_pair (boxes, X_AXIS).smobbed_copy ();
 }
 
+/*
+ * USE ME ONLY FOR CROSS STAFF SLURS!
+ * We only want to keep the topmost skyline of the topmost axis group(s)
+ * and the bottommost skyline of the bottommost axis group(s). Otherwise,
+ * the VerticalAxisGroups will be spaced very far apart to accommodate the
+ * slur, which we don't want, as it is cross staff.
+ *
+ * TODO: Currently, the code below keeps the topmost and bottommost axis
+ * groups and gets rid of the rest.  This should be more nuanced for
+ * cases like ossias where the topmost staff changes over the course of
+ * the slur.  Ditto for the bottommost staff.
+ */
+
+MAKE_SCHEME_CALLBACK (Slur, extremal_stub_vertical_skylines, 1);
+SCM
+Slur::extremal_stub_vertical_skylines (SCM smob)
+{
+  Grob *me = unsmob_grob (smob);
+  Grob *my_vag = Grob::get_vertical_axis_group (me);
+  extract_grob_set (me, "note-columns", ro_note_columns);
+  vector<Grob *> note_columns (ro_note_columns);
+  vector_sort (note_columns, Grob::vertical_less);
+  bool highest = my_vag == Grob::get_vertical_axis_group (note_columns[0]);
+  bool lowest = my_vag == Grob::get_vertical_axis_group (note_columns.back ());
+  if (!highest && !lowest)
+    return Skyline_pair ().smobbed_copy ();
+
+  Skyline_pair sky = *Skyline_pair::unsmob (vertical_skylines (smob));
+
+  if (highest)
+    sky[DOWN] = Skyline (DOWN);
+  else
+    sky[UP] = Skyline (UP);
+
+  return sky.smobbed_copy ();
+}
+
 /*
  * Used by Slur_engraver:: and Phrasing_slur_engraver::
  */
 void
 Slur::auxiliary_acknowledge_extra_object (Grob_info const &info,
-                                          vector<Grob *> &slurs,
-                                          vector<Grob *> &end_slurs)
+                                          vector<Slur_info> &slur_infos,
+                                          vector<Slur_info> &end_slur_infos)
 {
-  if (slurs.empty () && end_slurs.empty ())
+  if (slur_infos.empty () && end_slur_infos.empty ())
     return;
 
   Grob *e = info.grob ();
   SCM avoid = e->get_property ("avoid-slur");
   Grob *slur;
-  if (end_slurs.size () && !slurs.size ())
-    slur = end_slurs[0];
+  if (end_slur_infos.size () && !slur_infos.size ())
+    slur = end_slur_infos[0].slur_;
   else
-    slur = slurs[0];
+    slur = slur_infos[0].slur_;
 
   if (Tie::has_interface (e)
       || avoid == ly_symbol2scm ("inside"))
     {
-      for (vsize i = slurs.size (); i--;)
-        add_extra_encompass (slurs[i], e);
-      for (vsize i = end_slurs.size (); i--;)
-        add_extra_encompass (end_slurs[i], e);
+      for (vsize i = slur_infos.size (); i--;)
+        add_extra_encompass (slur_infos[i].slur_, e);
+      for (vsize i = end_slur_infos.size (); i--;)
+        add_extra_encompass (end_slur_infos[i].slur_, e);
       if (slur)
         e->set_object ("slur", slur->self_scm ());
     }
index b0365990146a8e352b3a28e3a1aa32200bd2b61e..5b4d2990a79bc6e1cb96d8450a8106449cd65f45 100644 (file)
@@ -70,7 +70,8 @@ Tab_tie_follow_engraver::acknowledge_tab_note_head (Grob_info info)
 void
 Tab_tie_follow_engraver::acknowledge_slur (Grob_info info)
 {
-  slurs_.push_back (info.spanner ());
+  if (!info.grob ()->internal_has_interface (ly_symbol2scm ("cross-staff-stub-interface")))
+    slurs_.push_back (info.spanner ());
 }
 
 void
index e36c3c04650fb83ac0a00bd150bc0d19bde1c694..f2fadc1d396802454bcef1dcce404f18d16c02e1 100644 (file)
@@ -402,9 +402,9 @@ shiftOff  = \revert NoteColumn #'horizontal-shift
 %% slurs
 
 % directions
-slurUp         = \override Slur #'direction = #UP
-slurDown       = \override Slur #'direction = #DOWN
-slurNeutral    = \revert Slur #'direction
+slurUp         = { \override Slur #'direction = #UP \override SlurStub #'direction = #UP }
+slurDown       = { \override Slur #'direction = #DOWN \override SlurStub #'direction = #DOWN }
+slurNeutral    = { \revert Slur #'direction \revert SlurStub #'direction  }
 
 % dash-patterns (make-simple-dash-definition defined at top of file)
 slurDashPattern =
index eb76e96e0b53e76bccc60a281543bb0013700f09..f73f6d95344a9b38d7e6ae0d405fc21a2b926809 100644 (file)
@@ -95,6 +95,11 @@ printed, but a line break is allowed at that spot.
  "A doit or drop."
  '(thickness delta-position))
 
+(ly:add-interface
+ 'cross-staff-stub-interface
+ "Used to approximate vertical skylines in cross-staff grobs."
+ '(surrogate))
+
 (ly:add-interface
  'dynamic-interface
  "Any kind of loudness sign."
index 82102dd42ec68d21904880743802c3e5af990b18..72e9437cad50010024e30b375efce001d2b9cd45 100644 (file)
@@ -1155,6 +1155,7 @@ results, use @code{LEFT} and @code{RIGHT}.")
      (staff-symbol ,ly:grob? "The staff symbol grob that we are in.")
      (stem ,ly:grob? "A pointer to a @code{Stem} object.")
      (stems ,ly:grob-array? "An array of stem objects.")
+     (surrogate ,ly:grob? "The grob that a cross-staff stub is shadowing.")
 
      (tie ,ly:grob? "A pointer to a @code{Tie} object.")
      (ties ,ly:grob-array? "A grob array of @code{Tie} objects.")
index cc8a73556567a3c556ffc568abbc233599ab0dd8..b23ec54e24210baada246c42baeda39cac611d2c 100644 (file)
        (meta . ((class . Spanner)
                 (interfaces . (slur-interface))))))
 
+    ;; Only should be used to approximate vertical skylines
+    ;; of a cross-staff slur.
+    (SlurStub
+     . (
+       (avoid-slur . inside)
+       (control-points . ,ly:slur::calc-control-points)
+       (cross-staff . ,slur::if-not-cross-staff-suicide)
+       (details . ,default-slur-details)
+       (direction . ,ly:slur::calc-direction)
+       (height-limit . 2.0)
+       (line-thickness . 0.8)
+       (minimum-length . 1.5)
+       (ratio . 0.25)
+       (spanner-id . "")
+       (springs-and-rods . ,ly:spanner::set-spacing-rods)
+       (stencil . ,ly:slur::print)
+       (thickness . 1.2)
+       (transparent . #t)
+       (vertical-skylines . ,ly:slur::extremal-stub-vertical-skylines)
+       (Y-extent . ,ly:slur::height)
+       (meta . ((class . Spanner)
+                (interfaces . (cross-staff-stub-interface
+                               slur-interface))))))
+
     (SostenutoPedal
      . (
        (direction . ,RIGHT)
index 3ced3354cd2cfba56e2721d655408066a58222ed..d041e5d17221edc167e8de6bb68ad861f59ea20c 100644 (file)
    (ly:side-position-interface::calc-cross-staff g)))
 
 
+(define-public (slur::if-not-cross-staff-suicide g)
+  (let ((cs (ly:slur::calc-cross-staff g)))
+    (if (not cs)
+        (begin
+          (ly:grob-suicide! g)
+          #f)
+        #t)))
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; note heads
 
@@ -356,7 +364,6 @@ and duration-log @var{log}."
 
 (define-public spanbar-begin-of-line-invisible #(#t #f #f))
 
-
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; neighbor-interface routines