2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2000--2012 Han-Wen Nienhuys <hanwen@xs4all.nl>
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
20 #include "axis-group-interface.hh"
24 #include "align-interface.hh"
25 #include "directional-element-interface.hh"
26 #include "grob-array.hh"
27 #include "hara-kiri-group-spanner.hh"
28 #include "international.hh"
29 #include "interval-set.hh"
31 #include "paper-column.hh"
32 #include "paper-score.hh"
33 #include "pointer-group-interface.hh"
34 #include "separation-item.hh"
35 #include "skyline-pair.hh"
36 #include "staff-grouper-interface.hh"
41 #include "unpure-pure-container.hh"
44 pure_staff_priority_less (Grob *const &g1, Grob *const &g2);
46 Real Axis_group_interface::default_outside_staff_padding_ = 0.46;
49 Axis_group_interface::get_default_outside_staff_padding ()
51 return default_outside_staff_padding_;
54 MAKE_SCHEME_CALLBACK (Axis_group_interface, cross_staff, 1);
56 Axis_group_interface::cross_staff (SCM smob)
58 Grob *me = unsmob_grob (smob);
59 extract_grob_set (me, "elements", elts);
60 for (vsize i = 0; i < elts.size (); i++)
61 if (to_boolean (elts[i]->get_property ("cross-staff")))
68 Axis_group_interface::add_element (Grob *me, Grob *e)
70 SCM axes = me->get_property ("axes");
71 if (!scm_is_pair (axes))
72 programming_error ("axes should be nonempty");
74 for (SCM ax = axes; scm_is_pair (ax); ax = scm_cdr (ax))
76 Axis a = (Axis) scm_to_int (scm_car (ax));
78 if (!e->get_parent (a))
79 e->set_parent (me, a);
81 e->set_object ((a == X_AXIS)
82 ? ly_symbol2scm ("axis-group-parent-X")
83 : ly_symbol2scm ("axis-group-parent-Y"),
87 /* must be ordered, because Align_interface also uses
88 Axis_group_interface */
89 Pointer_group_interface::add_grob (me, ly_symbol2scm ("elements"), e);
93 Axis_group_interface::has_axis (Grob *me, Axis a)
95 SCM axes = me->get_property ("axes");
97 return (SCM_BOOL_F != scm_memq (scm_from_int (a), axes));
101 Axis_group_interface::relative_group_extent (vector<Grob *> const &elts,
102 Grob *common, Axis a)
104 return relative_maybe_bound_group_extent (elts, common, a, false);
108 Axis_group_interface::relative_maybe_bound_group_extent (vector<Grob *> const &elts,
109 Grob *common, Axis a, bool bound)
112 for (vsize i = 0; i < elts.size (); i++)
115 if (has_interface (se)
116 || !to_boolean (se->get_property ("cross-staff")))
118 Interval dims = (bound && has_interface (se)
119 ? generic_bound_extent (se, common, a)
120 : se->extent (common, a));
121 if (!dims.is_empty ())
129 Axis_group_interface::generic_bound_extent (Grob *me, Grob *common, Axis a)
131 /* trigger the callback to do skyline-spacing on the children */
133 (void) me->get_property ("vertical-skylines");
135 extract_grob_set (me, "elements", elts);
136 vector<Grob *> new_elts;
138 SCM interfaces = me->get_property ("bound-alignment-interfaces");
140 for (vsize i = 0; i < elts.size (); i++)
141 for (SCM l = interfaces; scm_is_pair (l); l = scm_cdr (l))
142 if (elts[i]->internal_has_interface (scm_car (l)))
143 new_elts.push_back (elts[i]);
145 if (!new_elts.size ())
146 return robust_relative_extent (me, common, a);
149 common = common_refpoint_of_array (new_elts, me, a);
151 return relative_maybe_bound_group_extent (new_elts, common, a, true);
155 Axis_group_interface::sum_partial_pure_heights (Grob *me, int start, int end)
157 Interval iv = begin_of_line_pure_height (me, start);
158 iv.unite (rest_of_line_pure_height (me, start, end));
164 Axis_group_interface::part_of_line_pure_height (Grob *me, bool begin, int start, int end)
166 Spanner *sp = dynamic_cast<Spanner *> (me);
167 SCM cache_symbol = begin
168 ? ly_symbol2scm ("begin-of-line-pure-height")
169 : ly_symbol2scm ("rest-of-line-pure-height");
170 SCM cached = sp->get_cached_pure_property (cache_symbol, start, end);
171 if (scm_is_pair (cached))
172 return robust_scm2interval (cached, Interval (0, 0));
174 SCM adjacent_pure_heights = me->get_property ("adjacent-pure-heights");
177 if (!scm_is_pair (adjacent_pure_heights))
178 ret = Interval (0, 0);
181 SCM these_pure_heights = begin
182 ? scm_car (adjacent_pure_heights)
183 : scm_cdr (adjacent_pure_heights);
185 if (scm_is_vector (these_pure_heights))
186 ret = combine_pure_heights (me, these_pure_heights, start, end);
188 ret = Interval (0, 0);
191 sp->cache_pure_property (cache_symbol, start, end, ly_interval2scm (ret));
196 Axis_group_interface::begin_of_line_pure_height (Grob *me, int start)
198 return part_of_line_pure_height (me, true, start, start + 1);
202 Axis_group_interface::rest_of_line_pure_height (Grob *me, int start, int end)
204 return part_of_line_pure_height (me, false, start, end);
208 Axis_group_interface::combine_pure_heights (Grob *me, SCM measure_extents, int start, int end)
210 Paper_score *ps = get_root_system (me)->paper_score ();
211 vector<vsize> breaks = ps->get_break_indices ();
212 vector<Grob *> cols = ps->get_columns ();
215 for (vsize i = 0; i + 1 < breaks.size (); i++)
217 int r = Paper_column::get_rank (cols[breaks[i]]);
222 ext.unite (ly_scm2interval (scm_c_vector_ref (measure_extents, i)));
228 // adjacent-pure-heights is a pair of vectors, each of which has one element
229 // for every measure in the score. The first vector stores, for each measure,
230 // the combined height of the elements that are present only when the bar
231 // is at the beginning of a line. The second vector stores, for each measure,
232 // the combined height of the elements that are present only when the bar
233 // is not at the beginning of a line.
234 MAKE_SCHEME_CALLBACK (Axis_group_interface, adjacent_pure_heights, 1)
236 Axis_group_interface::adjacent_pure_heights (SCM smob)
238 Grob *me = unsmob_grob (smob);
240 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
241 extract_grob_set (me, "pure-relevant-grobs", elts);
243 Paper_score *ps = get_root_system (me)->paper_score ();
244 vector<vsize> ranks = ps->get_break_ranks ();
246 vector<Interval> begin_line_heights;
247 vector<Interval> mid_line_heights;
248 vector<Interval> begin_line_staff_heights;
249 vector<Interval> mid_line_staff_heights;
250 begin_line_heights.resize (ranks.size () - 1);
251 mid_line_heights.resize (ranks.size () - 1);
253 for (vsize i = 0; i < elts.size (); ++i)
257 if (to_boolean (g->get_property ("cross-staff"))
258 && !has_interface (g))
264 bool outside_staff = scm_is_number (g->get_property ("outside-staff-priority"));
265 Real padding = robust_scm2double (g->get_property ("outside-staff-padding"), get_default_outside_staff_padding ());
267 // When we encounter the first outside-staff grob, make a copy
268 // of the current heights to use as an estimate for the staff heights.
269 // Note that the outside-staff approximation that we use here doesn't
270 // consider any collisions that might occur between outside-staff grobs,
271 // but only the fact that outside-staff grobs may need to be raised above
273 if (outside_staff && begin_line_staff_heights.empty ())
275 begin_line_staff_heights = begin_line_heights;
276 mid_line_staff_heights = mid_line_heights;
279 // TODO: consider a pure version of get_grob_direction?
280 Direction d = to_dir (g->get_property_data ("direction"));
281 d = (d == CENTER) ? UP : d;
283 Interval_t<int> rank_span = g->spanned_rank_interval ();
284 vsize first_break = lower_bound (ranks, (vsize)rank_span[LEFT], less<vsize> ());
285 if (first_break > 0 && ranks[first_break] >= (vsize)rank_span[LEFT])
288 for (vsize j = first_break; j + 1 < ranks.size () && (int)ranks[j] <= rank_span[RIGHT]; ++j)
290 int start = ranks[j];
291 int end = ranks[j + 1];
293 // Take grobs that are visible with respect to a slightly longer line.
294 // Otherwise, we will never include grobs at breakpoints which aren't
295 // end-of-line-visible.
296 int visibility_end = j + 2 < ranks.size () ? ranks[j + 2] : end;
298 if (g->pure_is_visible (start, visibility_end))
300 Interval dims = g->pure_height (common, start, end);
301 if (!dims.is_empty ())
303 if (rank_span[LEFT] <= start)
306 begin_line_heights[j].unite (begin_line_staff_heights[j].union_disjoint (dims, padding, d));
308 begin_line_heights[j].unite (dims);
310 if (rank_span[RIGHT] > start)
313 mid_line_heights[j].unite (mid_line_staff_heights[j].union_disjoint (dims, padding, d));
315 mid_line_heights[j].unite (dims);
322 // Convert begin_line_heights and min_line_heights to SCM.
323 SCM begin_scm = scm_c_make_vector (ranks.size () - 1, SCM_EOL);
324 SCM mid_scm = scm_c_make_vector (ranks.size () - 1, SCM_EOL);
325 for (vsize i = 0; i < begin_line_heights.size (); ++i)
327 scm_vector_set_x (begin_scm, scm_from_int (i), ly_interval2scm (begin_line_heights[i]));
328 scm_vector_set_x (mid_scm, scm_from_int (i), ly_interval2scm (mid_line_heights[i]));
331 return scm_cons (begin_scm, mid_scm);
335 Axis_group_interface::relative_pure_height (Grob *me, int start, int end)
337 /* It saves a _lot_ of time if we assume a VerticalAxisGroup is additive
338 (ie. height (i, k) = max (height (i, j) height (j, k)) for all i <= j <= k).
339 Unfortunately, it isn't always true, particularly if there is a
340 VerticalAlignment somewhere in the descendants.
342 Usually, the only VerticalAlignment comes from Score. This makes it
343 reasonably safe to assume that if our parent is a VerticalAlignment,
344 we can assume additivity and cache things nicely. */
345 Grob *p = me->get_parent (Y_AXIS);
346 if (p && Align_interface::has_interface (p))
347 return Axis_group_interface::sum_partial_pure_heights (me, start, end);
349 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
350 extract_grob_set (me, "pure-relevant-grobs", elts);
353 for (vsize i = 0; i < elts.size (); i++)
356 Interval_t<int> rank_span = g->spanned_rank_interval ();
357 if (rank_span[LEFT] <= end && rank_span[RIGHT] >= start
358 && g->pure_is_visible (start, end)
359 && !(to_boolean (g->get_property ("cross-staff"))
360 && Stem::has_interface (g)))
362 Interval dims = g->pure_height (common, start, end);
363 if (!dims.is_empty ())
370 MAKE_SCHEME_CALLBACK (Axis_group_interface, width, 1);
372 Axis_group_interface::width (SCM smob)
374 Grob *me = unsmob_grob (smob);
375 return generic_group_extent (me, X_AXIS);
378 MAKE_SCHEME_CALLBACK (Axis_group_interface, height, 1);
380 Axis_group_interface::height (SCM smob)
382 Grob *me = unsmob_grob (smob);
383 return generic_group_extent (me, Y_AXIS);
386 MAKE_SCHEME_CALLBACK (Axis_group_interface, pure_height, 3);
388 Axis_group_interface::pure_height (SCM smob, SCM start_scm, SCM end_scm)
390 int start = robust_scm2int (start_scm, 0);
391 int end = robust_scm2int (end_scm, INT_MAX);
392 Grob *me = unsmob_grob (smob);
394 /* Maybe we are in the second pass of a two-pass spacing run. In that
395 case, the Y-extent of a system is already given to us */
396 System *system = dynamic_cast<System *> (me);
399 SCM line_break_details = system->column (start)->get_property ("line-break-system-details");
400 SCM system_y_extent = scm_assq (ly_symbol2scm ("system-Y-extent"), line_break_details);
401 if (scm_is_pair (system_y_extent))
402 return scm_cdr (system_y_extent);
405 return ly_interval2scm (pure_group_height (me, start, end));
408 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_skylines, 1);
410 Axis_group_interface::calc_skylines (SCM smob)
412 Grob *me = unsmob_grob (smob);
413 Skyline_pair skylines = skyline_spacing (me);
414 return skylines.smobbed_copy ();
417 /* whereas calc_skylines calculates skylines for axis-groups with a lot of
418 visible children, combine_skylines is designed for axis-groups whose only
419 children are other axis-groups (ie. VerticalAlignment). Rather than
420 calculating all the skylines from scratch, we just merge the skylines
423 MAKE_SCHEME_CALLBACK (Axis_group_interface, combine_skylines, 1);
425 Axis_group_interface::combine_skylines (SCM smob)
427 Grob *me = unsmob_grob (smob);
428 extract_grob_set (me, "elements", elements);
429 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
430 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
433 programming_error ("combining skylines that don't belong to me");
436 for (vsize i = 0; i < elements.size (); i++)
438 SCM skyline_scm = elements[i]->get_property ("vertical-skylines");
439 if (Skyline_pair::unsmob (skyline_scm))
441 Real offset = elements[i]->relative_coordinate (y_common, Y_AXIS);
442 Skyline_pair other = *Skyline_pair::unsmob (skyline_scm);
443 other.raise (offset);
444 other.shift (elements[i]->relative_coordinate (x_common, X_AXIS));
448 return ret.smobbed_copy ();
452 Axis_group_interface::generic_group_extent (Grob *me, Axis a)
454 extract_grob_set (me, "elements", elts);
456 /* trigger the callback to do skyline-spacing on the children */
458 for (vsize i = 0; i < elts.size (); i++)
459 if (!(Stem::has_interface (elts[i])
460 && to_boolean (elts[i]->get_property ("cross-staff"))))
461 (void) elts[i]->get_property ("vertical-skylines");
463 Grob *common = common_refpoint_of_array (elts, me, a);
465 Real my_coord = me->relative_coordinate (common, a);
466 Interval r (relative_group_extent (elts, common, a));
468 return ly_interval2scm (r - my_coord);
471 /* This is like generic_group_extent, but it only counts the grobs that
472 are children of some other axis-group. This is uncached; if it becomes
473 commonly used, it may be necessary to cache it somehow. */
475 Axis_group_interface::staff_extent (Grob *me, Grob *refp, Axis ext_a, Grob *staff, Axis parent_a)
477 extract_grob_set (me, "elements", elts);
478 vector<Grob *> new_elts;
480 for (vsize i = 0; i < elts.size (); i++)
481 if (elts[i]->common_refpoint (staff, parent_a) == staff)
482 new_elts.push_back (elts[i]);
484 return relative_group_extent (new_elts, refp, ext_a);
487 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_relevant_grobs, 1);
489 Axis_group_interface::calc_pure_relevant_grobs (SCM smob)
491 Grob *me = unsmob_grob (smob);
492 return internal_calc_pure_relevant_grobs (me, "elements");
496 Axis_group_interface::internal_calc_pure_relevant_grobs (Grob *me, string grob_set_name)
498 extract_grob_set (me, grob_set_name.c_str (), elts);
500 vector<Grob *> relevant_grobs;
502 for (vsize i = 0; i < elts.size (); i++)
504 if (elts[i] && elts[i]->is_live ())
506 relevant_grobs.push_back (elts[i]);
507 if (Item *it = dynamic_cast<Item *> (elts[i]))
509 for (LEFT_and_RIGHT (d))
511 Item *piece = it->find_prebroken_piece (d);
512 if (piece && piece->is_live ())
513 relevant_grobs.push_back (piece);
519 vector_sort (relevant_grobs, pure_staff_priority_less);
520 SCM grobs_scm = Grob_array::make_array ();
521 unsmob_grob_array (grobs_scm)->set_array (relevant_grobs);
526 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_y_common, 1);
528 Axis_group_interface::calc_pure_y_common (SCM smob)
530 Grob *me = unsmob_grob (smob);
532 extract_grob_set (me, "pure-relevant-grobs", elts);
533 Grob *common = common_refpoint_of_array (elts, me, Y_AXIS);
536 me->programming_error ("No common parent found in calc_pure_y_common.");
540 return common->self_scm ();
544 Axis_group_interface::calc_common (Grob *me, Axis axis)
546 extract_grob_set (me, "elements", elts);
547 Grob *common = common_refpoint_of_array (elts, me, axis);
550 me->programming_error ("No common parent found in calc_common axis.");
554 return common->self_scm ();
557 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_x_common, 1);
559 Axis_group_interface::calc_x_common (SCM grob)
561 return calc_common (unsmob_grob (grob), X_AXIS);
564 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_y_common, 1);
566 Axis_group_interface::calc_y_common (SCM grob)
568 return calc_common (unsmob_grob (grob), Y_AXIS);
572 Axis_group_interface::pure_group_height (Grob *me, int start, int end)
574 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
578 programming_error ("no pure Y common refpoint");
581 Real my_coord = me->pure_relative_y_coordinate (common, start, end);
582 Interval r (relative_pure_height (me, start, end));
588 Axis_group_interface::get_children (Grob *me, vector<Grob *> *found)
590 found->push_back (me);
592 if (!has_interface (me))
595 extract_grob_set (me, "elements", elements);
596 for (vsize i = 0; i < elements.size (); i++)
598 Grob *e = elements[i];
599 Axis_group_interface::get_children (e, found);
604 staff_priority_less (Grob *const &g1, Grob *const &g2)
606 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
607 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
609 if (priority_1 < priority_2)
611 else if (priority_1 > priority_2)
614 /* if neither grob has an outside-staff priority, the ordering will have no
615 effect -- we just need to choose a consistent ordering. We do this to
616 avoid the side-effect of calculating extents. */
617 if (isinf (priority_1))
620 /* if there is no preference in staff priority, choose the left-most one */
621 Grob *common = g1->common_refpoint (g2, X_AXIS);
622 Real start_1 = g1->extent (common, X_AXIS)[LEFT];
623 Real start_2 = g2->extent (common, X_AXIS)[LEFT];
624 return start_1 < start_2;
628 pure_staff_priority_less (Grob *const &g1, Grob *const &g2)
630 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
631 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
633 return priority_1 < priority_2;
637 add_interior_skylines (Grob *me, Grob *x_common, Grob *y_common, vector<Skyline_pair> *skylines)
639 if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements")))
641 for (vsize i = 0; i < elements->size (); i++)
642 add_interior_skylines (elements->grob (i), x_common, y_common, skylines);
644 else if (!scm_is_number (me->get_property ("outside-staff-priority"))
645 && !to_boolean (me->get_property ("cross-staff")))
647 Skyline_pair *maybe_pair = Skyline_pair::unsmob (me->get_property ("vertical-skylines"));
650 if (maybe_pair->is_empty ())
652 skylines->push_back (Skyline_pair (*maybe_pair));
653 skylines->back ().shift (me->relative_coordinate (x_common, X_AXIS));
654 skylines->back ().raise (me->relative_coordinate (y_common, Y_AXIS));
658 // Raises the grob elt (whose skylines are given by h_skyline
659 // and v_skyline) so that it doesn't intersect with staff_skyline,
660 // or with anything in other_h_skylines and other_v_skylines.
662 avoid_outside_staff_collisions (Grob *elt,
663 Skyline_pair *v_skyline,
665 Real horizon_padding,
666 vector<Skyline_pair> const &other_v_skylines,
667 vector<Real> const &other_padding,
668 vector<Real> const &other_horizon_padding,
671 assert (other_v_skylines.size () == other_padding.size ());
672 assert (other_v_skylines.size () == other_horizon_padding.size ());
673 vector<Interval> forbidden_intervals;
674 for (vsize j = 0; j < other_v_skylines.size (); j++)
676 Skyline_pair const &v_other = other_v_skylines[j];
677 Real pad = (padding + other_padding[j]);
678 Real horizon_pad = (horizon_padding + other_horizon_padding[j]);
680 // We need to push elt up by at least this much to be above v_other.
681 Real up = (*v_skyline)[DOWN].distance (v_other[UP], horizon_pad) + pad;
682 // We need to push elt down by at least this much to be below v_other.
683 Real down = (*v_skyline)[UP].distance (v_other[DOWN], horizon_pad) + pad;
685 forbidden_intervals.push_back (Interval (-down, up));
688 Interval_set allowed_shifts
689 = Interval_set::interval_union (forbidden_intervals).complement ();
690 Real move = allowed_shifts.nearest_point (0, dir);
691 v_skyline->raise (move);
692 elt->translate_axis (move, Y_AXIS);
696 valid_outside_staff_placement_directive (Grob *me)
698 SCM directive = me->get_property ("outside-staff-placement-directive");
700 if ((directive == ly_symbol2scm ("left-to-right-greedy"))
701 || (directive == ly_symbol2scm ("left-to-right-polite"))
702 || (directive == ly_symbol2scm ("right-to-left-greedy"))
703 || (directive == ly_symbol2scm ("right-to-left-polite")))
706 me->warning (_f ("\"%s\" is not a valid outside-staff-placement-directive",
707 robust_symbol2string (directive, "").c_str ()));
709 return ly_symbol2scm ("left-to-right-polite");
712 // Shifts the grobs in elements to ensure that they (and any
713 // connected riders) don't collide with the staff skylines
714 // or anything in all_X_skylines. Afterwards, the skylines
715 // of the grobs in elements will be added to all_v_skylines.
717 add_grobs_of_one_priority (Grob *me,
718 Drul_array<vector<Skyline_pair> > *all_v_skylines,
719 Drul_array<vector<Real> > *all_paddings,
720 Drul_array<vector<Real> > *all_horizon_paddings,
721 vector<Grob *> elements,
724 multimap<Grob *, Grob *> const &riders)
728 = valid_outside_staff_placement_directive (me);
730 bool l2r = ((directive == ly_symbol2scm ("left-to-right-greedy"))
731 || (directive == ly_symbol2scm ("left-to-right-polite")));
733 bool polite = ((directive == ly_symbol2scm ("left-to-right-polite"))
734 || (directive == ly_symbol2scm ("right-to-left-polite")));
737 vector<Skyline_pair> skylines_to_merge;
739 // We want to avoid situations like this:
743 // -------------------
745 // -------------------
747 // The point is that "still more text" should be positioned under
748 // "more text". In order to achieve this, we place the grobs in several
749 // passes. We keep track of the right-most horizontal position that has been
750 // affected by the current pass so far (actually we keep track of 2
751 // positions, one for above the staff, one for below).
753 // In each pass, we loop through the unplaced grobs from left to right.
754 // If the grob doesn't overlap the right-most affected position, we place it
755 // (and then update the right-most affected position to point to the right
756 // edge of the just-placed grob). Otherwise, we skip it until the next pass.
757 while (!elements.empty ())
759 Drul_array<Real> last_end (-infinity_f, -infinity_f);
760 vector<Grob *> skipped_elements;
761 for (vsize i = l2r ? 0 : elements.size ();
762 l2r ? i < elements.size () : i--;
765 Grob *elt = elements[i];
767 = robust_scm2double (elt->get_property ("outside-staff-padding"), 0.25);
769 = robust_scm2double (elt->get_property ("outside-staff-horizontal-padding"), 0.0);
770 Interval x_extent = elt->extent (x_common, X_AXIS);
771 x_extent.widen (horizon_padding);
773 Direction dir = get_grob_direction (elt);
776 warning (_ ("an outside-staff object should have a direction, defaulting to up"));
780 if (x_extent[LEFT] <= last_end[dir] && polite)
782 skipped_elements.push_back (elt);
785 last_end[dir] = x_extent[RIGHT];
787 Skyline_pair *v_orig = Skyline_pair::unsmob (elt->get_property ("vertical-skylines"));
788 if (v_orig->is_empty ())
791 // Find the riders associated with this grob, and merge their
792 // skylines with elt's skyline.
793 typedef multimap<Grob *, Grob *>::const_iterator GrobMapIterator;
794 pair<GrobMapIterator, GrobMapIterator> range = riders.equal_range (elt);
795 vector<Skyline_pair> rider_v_skylines;
796 for (GrobMapIterator j = range.first; j != range.second; j++)
798 Grob *rider = j->second;
799 Skyline_pair *v_rider = Skyline_pair::unsmob (rider->get_property ("vertical-skylines"));
802 Skyline_pair copy (*v_rider);
803 copy.shift (rider->relative_coordinate (x_common, X_AXIS));
804 copy.raise (rider->relative_coordinate (y_common, Y_AXIS));
805 rider_v_skylines.push_back (copy);
808 Skyline_pair v_skylines (*v_orig);
809 v_skylines.shift (elt->relative_coordinate (x_common, X_AXIS));
810 v_skylines.raise (elt->relative_coordinate (y_common, Y_AXIS));
811 v_skylines.merge (Skyline_pair (rider_v_skylines));
813 avoid_outside_staff_collisions (elt,
817 (*all_v_skylines)[dir],
818 (*all_paddings)[dir],
819 (*all_horizon_paddings)[dir],
822 elt->set_property ("outside-staff-priority", SCM_BOOL_F);
823 (*all_v_skylines)[dir].push_back (v_skylines);
824 (*all_paddings)[dir].push_back (padding);
825 (*all_horizon_paddings)[dir].push_back (horizon_padding);
827 swap (elements, skipped_elements);
828 skipped_elements.clear ();
832 // If the Grob has a Y-ancestor with outside-staff-priority, return it.
833 // Otherwise, return 0.
835 Axis_group_interface::outside_staff_ancestor (Grob *me)
837 Grob *parent = me->get_parent (Y_AXIS);
841 if (scm_is_number (parent->get_property ("outside-staff-priority")))
844 return outside_staff_ancestor (parent);
847 // It is tricky to correctly handle skyline placement of cross-staff grobs.
848 // For example, cross-staff beams cannot be formatted until the distance between
849 // staves is known and therefore any grobs that depend on the beam cannot be placed
850 // until the skylines are known. On the other hand, the distance between staves should
851 // really depend on position of the cross-staff grobs that lie between them.
852 // Currently, we just leave cross-staff grobs out of the
853 // skyline altogether, but this could mean that staves are placed so close together
854 // that there is no room for the cross-staff grob. It also means, of course, that
855 // we don't get the benefits of skyline placement for cross-staff grobs.
857 Axis_group_interface::skyline_spacing (Grob *me)
859 extract_grob_set (me, Grob_array::unsmob (me->get_object ("vertical-skyline-elements")) ? "vertical-skyline-elements" : "elements", fakeelements);
860 vector<Grob *> elements (fakeelements);
861 for (vsize i = 0; i < elements.size (); i++)
863 As a sanity check, we make sure that no grob with an outside staff priority
864 has a Y-parent that also has an outside staff priority, which would result
867 if (scm_is_number (elements[i]->get_property ("outside-staff-priority"))
868 && outside_staff_ancestor (elements[i]))
870 elements[i]->warning ("Cannot set outside-staff-priority for element and elements' Y parent.");
871 elements[i]->set_property ("outside-staff-priority", SCM_BOOL_F);
874 /* For grobs with an outside-staff-priority, the sorting function might
875 call extent and cause suicide. This breaks the contract that is required
876 for the STL sort function. To avoid this, we make sure that any suicides
877 are triggered beforehand.
879 for (vsize i = 0; i < elements.size (); i++)
880 if (scm_is_number (elements[i]->get_property ("outside-staff-priority")))
881 elements[i]->extent (elements[i], X_AXIS);
883 vector_sort (elements, staff_priority_less);
884 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
885 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
887 assert (y_common == me);
889 // A rider is a grob that is not outside-staff, but has an outside-staff
890 // ancestor. In that case, the rider gets moved along with its ancestor.
891 multimap<Grob *, Grob *> riders;
894 vector<Skyline_pair> inside_staff_skylines;
896 for (i = 0; i < elements.size ()
897 && !scm_is_number (elements[i]->get_property ("outside-staff-priority")); i++)
899 Grob *elt = elements[i];
900 Grob *ancestor = outside_staff_ancestor (elt);
902 add_interior_skylines (elt, x_common, y_common, &inside_staff_skylines);
904 riders.insert (pair<Grob *, Grob *> (ancestor, elt));
907 Skyline_pair skylines (inside_staff_skylines);
909 // These are the skylines of all outside-staff grobs
910 // that have already been processed. We keep them around in order to
911 // check them for collisions with the currently active outside-staff grob.
912 Drul_array<vector<Skyline_pair> > all_v_skylines;
913 Drul_array<vector<Real> > all_paddings;
914 Drul_array<vector<Real> > all_horizon_paddings;
915 for (UP_and_DOWN (d))
917 all_v_skylines[d].push_back (skylines);
918 all_paddings[d].push_back (0);
919 all_horizon_paddings[d].push_back (0);
922 for (; i < elements.size (); i++)
924 if (to_boolean (elements[i]->get_property ("cross-staff")))
927 // Collect all the outside-staff grobs that have a particular priority.
928 SCM priority = elements[i]->get_property ("outside-staff-priority");
929 vector<Grob *> current_elts;
930 current_elts.push_back (elements[i]);
931 while (i + 1 < elements.size ()
932 && scm_is_eq (elements[i + 1]->get_property ("outside-staff-priority"), priority))
934 if (!to_boolean (elements[i + 1]->get_property ("cross-staff")))
935 current_elts.push_back (elements[i + 1]);
939 add_grobs_of_one_priority (me,
942 &all_horizon_paddings,
949 // Now everything in all_v_skylines has been shifted appropriately; merge
950 // them all into skylines to get the complete outline.
951 Skyline_pair other_skylines (all_v_skylines[UP]);
952 other_skylines.merge (Skyline_pair (all_v_skylines[DOWN]));
953 skylines.merge (other_skylines);
955 // We began by shifting my skyline to be relative to the common refpoint; now
957 skylines.shift (-me->relative_coordinate (x_common, X_AXIS));
962 MAKE_SCHEME_CALLBACK (Axis_group_interface, print, 1)
964 Axis_group_interface::print (SCM smob)
969 Grob *me = unsmob_grob (smob);
971 if (Skyline_pair *s = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
973 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[UP].to_points (X_AXIS))
974 .in_color (255, 0, 255));
975 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[DOWN].to_points (X_AXIS))
976 .in_color (0, 255, 255));
978 return ret.smobbed_copy ();
981 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_staff_staff_spacing, 3)
983 Axis_group_interface::calc_pure_staff_staff_spacing (SCM smob, SCM start, SCM end)
985 return calc_maybe_pure_staff_staff_spacing (unsmob_grob (smob),
991 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_staff_staff_spacing, 1)
993 Axis_group_interface::calc_staff_staff_spacing (SCM smob)
995 return calc_maybe_pure_staff_staff_spacing (unsmob_grob (smob),
1002 Axis_group_interface::calc_maybe_pure_staff_staff_spacing (Grob *me, bool pure, int start, int end)
1004 Grob *grouper = unsmob_grob (me->get_object ("staff-grouper"));
1008 bool within_group = Staff_grouper_interface::maybe_pure_within_group (grouper, me, pure, start, end);
1010 return grouper->get_maybe_pure_property ("staff-staff-spacing", pure, start, end);
1012 return grouper->get_maybe_pure_property ("staffgroup-staff-spacing", pure, start, end);
1014 return me->get_maybe_pure_property ("default-staff-staff-spacing", pure, start, end);
1017 ADD_INTERFACE (Axis_group_interface,
1018 "An object that groups other layout objects.",
1020 // TODO: some of these properties are specific to
1021 // VerticalAxisGroup. We should split off a
1022 // vertical-axis-group-interface.
1024 "adjacent-pure-heights "
1026 "bound-alignment-interfaces "
1027 "default-staff-staff-spacing "
1031 "nonstaff-nonstaff-spacing "
1032 "nonstaff-relatedstaff-spacing "
1033 "nonstaff-unrelatedstaff-spacing "
1034 "outside-staff-placement-directive "
1035 "pure-relevant-grobs "
1036 "pure-relevant-items "
1037 "pure-relevant-spanners "
1041 "staff-staff-spacing "
1043 "vertical-skyline-elements "