--- /dev/null
+\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
+ }
+>>
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;
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.
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
{
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));
}
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
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)
{
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);
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);
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. */
{
Spanner *slur_;
Grob *common_[NO_AXES];
+ bool stub_;
bool valid_;
bool edge_has_beams_;
bool is_broken_;
#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));
}
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
#include "translator.icc"
+#include <algorithm>
+
/*
NOTE NOTE NOTE
(on principle, engravers don't use inheritance for code sharing)
+ For info on SlurStubs, check out slur-engraver.cc.
+
*/
/*
{
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:
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
Phrasing_slur_engraver::acknowledge_script (Grob_info info)
{
if (!info.grob ()->internal_has_interface (ly_symbol2scm ("dynamic-interface")))
+
acknowledge_extra_object (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
// 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)
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];
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)
{
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;
}
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;
}
slur->set_property ("spanner-id", ly_string2scm (id));
if (updown)
set_grob_direction (slur, updown);
- slurs_.push_back (slur);
+ slur_infos_.push_back (slur);
}
}
}
{
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 ();
}
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.")
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");
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);
#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);
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
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
// 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)
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];
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)
{
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;
}
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;
}
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")))
{
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
{
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 ();
}
{
musical_dy_ = 0.0;
valid_ = false;
+ stub_ = false;
edge_has_beams_ = false;
has_same_beam_ = false;
is_broken_ = false;
{
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);
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);
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 ())
{
/*
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;
}
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"));
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_
{
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);
{
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);
}
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
|| 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);
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_
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
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);
{
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 ())
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++)
{
{
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);
Slur::print (SCM smob)
{
Grob *me = unsmob_grob (smob);
+
extract_grob_set (me, "note-columns", encompasses);
if (encompasses.empty ())
{
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)
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 ());
}
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
%% 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 =
"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."
(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.")
(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)
(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
(define-public spanbar-begin-of-line-invisible #(#t #f #f))
-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; neighbor-interface routines