-/* We want to avoid situations like this:
- still more text
- more text
- text
- -------------------
- staff
- -------------------
-
- The point is that "still more text" should be positioned under
- "more text". In order to achieve this, we place the grobs in several
- passes. We keep track of the right-most horizontal position that has been
- affected by the current pass so far (actually we keep track of 2
- positions, one for above the staff, one for below).
-
- In each pass, we loop through the unplaced grobs from left to right.
- If the grob doesn't overlap the right-most affected position, we place it
- (and then update the right-most affected position to point to the right
- edge of the just-placed grob). Otherwise, we skip it until the next pass.
-*/
+// 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.
+void
+avoid_outside_staff_collisions (Grob *elt,
+ Skyline_pair *v_skyline,
+ Real padding,
+ Real horizon_padding,
+ vector<Skyline_pair> const &other_v_skylines,
+ vector<Real> const &other_padding,
+ vector<Real> const &other_horizon_padding,
+ Direction const dir)
+{
+ assert (other_v_skylines.size () == other_padding.size ());
+ assert (other_v_skylines.size () == other_horizon_padding.size ());
+ vector<Interval> forbidden_intervals;
+ for (vsize j = 0; j < other_v_skylines.size (); j++)
+ {
+ Skyline_pair const &v_other = other_v_skylines[j];
+ Real pad = max (padding, other_padding[j]);
+ Real horizon_pad = max (horizon_padding, other_horizon_padding[j]);
+
+ // We need to push elt up by at least this much to be above v_other.
+ Real up = (*v_skyline)[DOWN].distance (v_other[UP], horizon_pad) + pad;
+ // We need to push elt down by at least this much to be below v_other.
+ Real down = (*v_skyline)[UP].distance (v_other[DOWN], horizon_pad) + pad;
+
+ forbidden_intervals.push_back (Interval (-down, up));
+ }
+
+ Interval_set allowed_shifts
+ = Interval_set::interval_union (forbidden_intervals).complement ();
+ Real move = allowed_shifts.nearest_point (0, dir);
+ v_skyline->raise (move);
+ elt->translate_axis (move, Y_AXIS);
+}
+
+SCM
+valid_outside_staff_placement_directive (Grob *me)
+{
+ SCM directive = me->get_property ("outside-staff-placement-directive");
+
+ if (scm_is_eq (directive, ly_symbol2scm ("left-to-right-greedy"))
+ || scm_is_eq (directive, ly_symbol2scm ("left-to-right-polite"))
+ || scm_is_eq (directive, ly_symbol2scm ("right-to-left-greedy"))
+ || scm_is_eq (directive, ly_symbol2scm ("right-to-left-polite")))
+ return directive;
+
+ me->warning (_f ("\"%s\" is not a valid outside-staff-placement-directive",
+ robust_symbol2string (directive, "").c_str ()));
+
+ return ly_symbol2scm ("left-to-right-polite");
+}
+
+// Shifts the grobs in elements to ensure that they (and any
+// connected riders) don't collide with the staff skylines
+// or anything in all_X_skylines. Afterwards, the skylines
+// of the grobs in elements will be added to all_v_skylines.