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))
261 bool outside_staff = scm_is_number (g->get_property ("outside-staff-priority"));
262 Real padding = robust_scm2double (g->get_property ("outside-staff-padding"), get_default_outside_staff_padding ());
264 // When we encounter the first outside-staff grob, make a copy
265 // of the current heights to use as an estimate for the staff heights.
266 // Note that the outside-staff approximation that we use here doesn't
267 // consider any collisions that might occur between outside-staff grobs,
268 // but only the fact that outside-staff grobs may need to be raised above
270 if (outside_staff && begin_line_staff_heights.empty ())
272 begin_line_staff_heights = begin_line_heights;
273 mid_line_staff_heights = mid_line_heights;
276 // TODO: consider a pure version of get_grob_direction?
277 Direction d = to_dir (g->get_property_data ("direction"));
278 d = (d == CENTER) ? UP : d;
280 Interval_t<int> rank_span = g->spanned_rank_interval ();
281 vsize first_break = lower_bound (ranks, (vsize)rank_span[LEFT], less<vsize> ());
282 if (first_break > 0 && ranks[first_break] >= (vsize)rank_span[LEFT])
285 for (vsize j = first_break; j + 1 < ranks.size () && (int)ranks[j] <= rank_span[RIGHT]; ++j)
287 int start = ranks[j];
288 int end = ranks[j + 1];
290 // Take grobs that are visible with respect to a slightly longer line.
291 // Otherwise, we will never include grobs at breakpoints which aren't
292 // end-of-line-visible.
293 int visibility_end = j + 2 < ranks.size () ? ranks[j + 2] : end;
295 if (g->pure_is_visible (start, visibility_end))
297 Interval dims = g->pure_height (common, start, end);
298 if (!dims.is_empty ())
300 if (rank_span[LEFT] <= start)
303 begin_line_heights[j].unite (begin_line_staff_heights[j].union_disjoint (dims, padding, d));
305 begin_line_heights[j].unite (dims);
307 if (rank_span[RIGHT] > start)
310 mid_line_heights[j].unite (mid_line_staff_heights[j].union_disjoint (dims, padding, d));
312 mid_line_heights[j].unite (dims);
319 // Convert begin_line_heights and min_line_heights to SCM.
320 SCM begin_scm = scm_c_make_vector (ranks.size () - 1, SCM_EOL);
321 SCM mid_scm = scm_c_make_vector (ranks.size () - 1, SCM_EOL);
322 for (vsize i = 0; i < begin_line_heights.size (); ++i)
324 scm_vector_set_x (begin_scm, scm_from_int (i), ly_interval2scm (begin_line_heights[i]));
325 scm_vector_set_x (mid_scm, scm_from_int (i), ly_interval2scm (mid_line_heights[i]));
328 return scm_cons (begin_scm, mid_scm);
332 Axis_group_interface::relative_pure_height (Grob *me, int start, int end)
334 /* It saves a _lot_ of time if we assume a VerticalAxisGroup is additive
335 (ie. height (i, k) = max (height (i, j) height (j, k)) for all i <= j <= k).
336 Unfortunately, it isn't always true, particularly if there is a
337 VerticalAlignment somewhere in the descendants.
339 Usually, the only VerticalAlignment comes from Score. This makes it
340 reasonably safe to assume that if our parent is a VerticalAlignment,
341 we can assume additivity and cache things nicely. */
342 Grob *p = me->get_parent (Y_AXIS);
343 if (p && Align_interface::has_interface (p))
344 return Axis_group_interface::sum_partial_pure_heights (me, start, end);
346 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
347 extract_grob_set (me, "pure-relevant-grobs", elts);
350 for (vsize i = 0; i < elts.size (); i++)
353 Interval_t<int> rank_span = g->spanned_rank_interval ();
354 if (rank_span[LEFT] <= end && rank_span[RIGHT] >= start
355 && g->pure_is_visible (start, end)
356 && !(to_boolean (g->get_property ("cross-staff"))
357 && Stem::has_interface (g)))
359 Interval dims = g->pure_height (common, start, end);
360 if (!dims.is_empty ())
367 MAKE_SCHEME_CALLBACK (Axis_group_interface, width, 1);
369 Axis_group_interface::width (SCM smob)
371 Grob *me = unsmob_grob (smob);
372 return generic_group_extent (me, X_AXIS);
375 MAKE_SCHEME_CALLBACK (Axis_group_interface, height, 1);
377 Axis_group_interface::height (SCM smob)
379 Grob *me = unsmob_grob (smob);
380 return generic_group_extent (me, Y_AXIS);
383 MAKE_SCHEME_CALLBACK (Axis_group_interface, pure_height, 3);
385 Axis_group_interface::pure_height (SCM smob, SCM start_scm, SCM end_scm)
387 int start = robust_scm2int (start_scm, 0);
388 int end = robust_scm2int (end_scm, INT_MAX);
389 Grob *me = unsmob_grob (smob);
391 /* Maybe we are in the second pass of a two-pass spacing run. In that
392 case, the Y-extent of a system is already given to us */
393 System *system = dynamic_cast<System *> (me);
396 SCM line_break_details = system->column (start)->get_property ("line-break-system-details");
397 SCM system_y_extent = scm_assq (ly_symbol2scm ("system-Y-extent"), line_break_details);
398 if (scm_is_pair (system_y_extent))
399 return scm_cdr (system_y_extent);
402 return ly_interval2scm (pure_group_height (me, start, end));
405 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_skylines, 1);
407 Axis_group_interface::calc_skylines (SCM smob)
409 Grob *me = unsmob_grob (smob);
410 Skyline_pair skylines = skyline_spacing (me);
411 return skylines.smobbed_copy ();
414 /* whereas calc_skylines calculates skylines for axis-groups with a lot of
415 visible children, combine_skylines is designed for axis-groups whose only
416 children are other axis-groups (ie. VerticalAlignment). Rather than
417 calculating all the skylines from scratch, we just merge the skylines
420 MAKE_SCHEME_CALLBACK (Axis_group_interface, combine_skylines, 1);
422 Axis_group_interface::combine_skylines (SCM smob)
424 Grob *me = unsmob_grob (smob);
425 extract_grob_set (me, "elements", elements);
426 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
427 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
430 programming_error ("combining skylines that don't belong to me");
433 for (vsize i = 0; i < elements.size (); i++)
435 SCM skyline_scm = elements[i]->get_property ("vertical-skylines");
436 if (Skyline_pair::unsmob (skyline_scm))
438 Real offset = elements[i]->relative_coordinate (y_common, Y_AXIS);
439 Skyline_pair other = *Skyline_pair::unsmob (skyline_scm);
440 other.raise (offset);
441 other.shift (elements[i]->relative_coordinate (x_common, X_AXIS));
445 return ret.smobbed_copy ();
449 Axis_group_interface::generic_group_extent (Grob *me, Axis a)
451 extract_grob_set (me, "elements", elts);
453 /* trigger the callback to do skyline-spacing on the children */
455 for (vsize i = 0; i < elts.size (); i++)
456 (void) elts[i]->get_property ("vertical-skylines");
458 Grob *common = common_refpoint_of_array (elts, me, a);
460 Real my_coord = me->relative_coordinate (common, a);
461 Interval r (relative_group_extent (elts, common, a));
463 return ly_interval2scm (r - my_coord);
466 /* This is like generic_group_extent, but it only counts the grobs that
467 are children of some other axis-group. This is uncached; if it becomes
468 commonly used, it may be necessary to cache it somehow. */
470 Axis_group_interface::staff_extent (Grob *me, Grob *refp, Axis ext_a, Grob *staff, Axis parent_a)
472 extract_grob_set (me, "elements", elts);
473 vector<Grob *> new_elts;
475 for (vsize i = 0; i < elts.size (); i++)
476 if (elts[i]->common_refpoint (staff, parent_a) == staff)
477 new_elts.push_back (elts[i]);
479 return relative_group_extent (new_elts, refp, ext_a);
482 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_relevant_grobs, 1);
484 Axis_group_interface::calc_pure_relevant_grobs (SCM smob)
486 Grob *me = unsmob_grob (smob);
487 return internal_calc_pure_relevant_grobs (me, "elements");
491 Axis_group_interface::internal_calc_pure_relevant_grobs (Grob *me, string grob_set_name)
493 extract_grob_set (me, grob_set_name.c_str (), elts);
495 vector<Grob *> relevant_grobs;
496 SCM pure_relevant_p = ly_lily_module_constant ("pure-relevant?");
498 for (vsize i = 0; i < elts.size (); i++)
500 if (to_boolean (scm_apply_1 (pure_relevant_p, elts[i]->self_scm (), SCM_EOL)))
501 relevant_grobs.push_back (elts[i]);
503 if (Item *it = dynamic_cast<Item *> (elts[i]))
505 for (LEFT_and_RIGHT (d))
507 Item *piece = it->find_prebroken_piece (d);
508 if (piece && to_boolean (scm_apply_1 (pure_relevant_p, piece->self_scm (), SCM_EOL)))
509 relevant_grobs.push_back (piece);
514 vector_sort (relevant_grobs, pure_staff_priority_less);
515 SCM grobs_scm = Grob_array::make_array ();
516 unsmob_grob_array (grobs_scm)->set_array (relevant_grobs);
521 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_y_common, 1);
523 Axis_group_interface::calc_pure_y_common (SCM smob)
525 Grob *me = unsmob_grob (smob);
527 extract_grob_set (me, "pure-relevant-grobs", elts);
528 Grob *common = common_refpoint_of_array (elts, me, Y_AXIS);
531 me->programming_error ("No common parent found in calc_pure_y_common.");
535 return common->self_scm ();
539 Axis_group_interface::calc_common (Grob *me, Axis axis)
541 extract_grob_set (me, "elements", elts);
542 Grob *common = common_refpoint_of_array (elts, me, axis);
545 me->programming_error ("No common parent found in calc_common axis.");
549 return common->self_scm ();
552 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_x_common, 1);
554 Axis_group_interface::calc_x_common (SCM grob)
556 return calc_common (unsmob_grob (grob), X_AXIS);
559 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_y_common, 1);
561 Axis_group_interface::calc_y_common (SCM grob)
563 return calc_common (unsmob_grob (grob), Y_AXIS);
567 Axis_group_interface::pure_group_height (Grob *me, int start, int end)
569 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
573 programming_error ("no pure Y common refpoint");
576 Real my_coord = me->relative_coordinate (common, Y_AXIS);
577 Interval r (relative_pure_height (me, start, end));
583 Axis_group_interface::get_children (Grob *me, vector<Grob *> *found)
585 found->push_back (me);
587 if (!has_interface (me))
590 extract_grob_set (me, "elements", elements);
591 for (vsize i = 0; i < elements.size (); i++)
593 Grob *e = elements[i];
594 Axis_group_interface::get_children (e, found);
599 staff_priority_less (Grob *const &g1, Grob *const &g2)
601 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
602 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
604 if (priority_1 < priority_2)
606 else if (priority_1 > priority_2)
609 /* if neither grob has an outside-staff priority, the ordering will have no
610 effect -- we just need to choose a consistent ordering. We do this to
611 avoid the side-effect of calculating extents. */
612 if (isinf (priority_1))
615 /* if there is no preference in staff priority, choose the left-most one */
616 Grob *common = g1->common_refpoint (g2, X_AXIS);
617 Real start_1 = g1->extent (common, X_AXIS)[LEFT];
618 Real start_2 = g2->extent (common, X_AXIS)[LEFT];
619 return start_1 < start_2;
623 pure_staff_priority_less (Grob *const &g1, Grob *const &g2)
625 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
626 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
628 return priority_1 < priority_2;
632 add_interior_skylines (Grob *me, Grob *x_common, Grob *y_common, vector<Skyline_pair> *skylines)
634 if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements")))
636 for (vsize i = 0; i < elements->size (); i++)
637 add_interior_skylines (elements->grob (i), x_common, y_common, skylines);
639 else if (!scm_is_number (me->get_property ("outside-staff-priority"))
640 && !to_boolean (me->get_property ("cross-staff")))
642 Skyline_pair *maybe_pair = Skyline_pair::unsmob (me->get_property ("vertical-skylines"));
645 if (maybe_pair->is_empty ())
647 skylines->push_back (Skyline_pair (*maybe_pair));
648 skylines->back ().shift (me->relative_coordinate (x_common, X_AXIS));
649 skylines->back ().raise (me->relative_coordinate (y_common, Y_AXIS));
653 // Raises the grob elt (whose skylines are given by h_skyline
654 // and v_skyline) so that it doesn't intersect with staff_skyline,
655 // or with anything in other_h_skylines and other_v_skylines.
657 avoid_outside_staff_collisions (Grob *elt,
658 Skyline_pair *v_skyline,
660 Real horizon_padding,
661 vector<Skyline_pair> const &other_v_skylines,
662 vector<Real> const &other_padding,
663 vector<Real> const &other_horizon_padding,
666 assert (other_v_skylines.size () == other_padding.size ());
667 assert (other_v_skylines.size () == other_horizon_padding.size ());
668 vector<Interval> forbidden_intervals;
669 for (vsize j = 0; j < other_v_skylines.size (); j++)
671 Skyline_pair const &v_other = other_v_skylines[j];
672 Real pad = (padding + other_padding[j]);
673 Real horizon_pad = (horizon_padding + other_horizon_padding[j]);
675 // We need to push elt up by at least this much to be above v_other.
676 Real up = (*v_skyline)[DOWN].distance (v_other[UP], horizon_pad) + pad;
677 // We need to push elt down by at least this much to be below v_other.
678 Real down = (*v_skyline)[UP].distance (v_other[DOWN], horizon_pad) + pad;
680 forbidden_intervals.push_back (Interval (-down, up));
683 Interval_set allowed_shifts
684 = Interval_set::interval_union (forbidden_intervals).complement ();
685 Real move = allowed_shifts.nearest_point (0, dir);
686 v_skyline->raise (move);
687 elt->translate_axis (move, Y_AXIS);
691 valid_outside_staff_placement_directive (Grob *me)
693 SCM directive = me->get_property ("outside-staff-placement-directive");
695 if ((directive == ly_symbol2scm ("left-to-right-greedy"))
696 || (directive == ly_symbol2scm ("left-to-right-polite"))
697 || (directive == ly_symbol2scm ("right-to-left-greedy"))
698 || (directive == ly_symbol2scm ("right-to-left-polite")))
701 me->warning (_f ("\"%s\" is not a valid outside-staff-placement-directive",
702 robust_symbol2string (directive, "").c_str ()));
704 return ly_symbol2scm ("left-to-right-polite");
707 // Shifts the grobs in elements to ensure that they (and any
708 // connected riders) don't collide with the staff skylines
709 // or anything in all_X_skylines. Afterwards, the skylines
710 // of the grobs in elements will be added to all_v_skylines.
712 add_grobs_of_one_priority (Grob *me,
713 Drul_array<vector<Skyline_pair> > *all_v_skylines,
714 Drul_array<vector<Real> > *all_paddings,
715 Drul_array<vector<Real> > *all_horizon_paddings,
716 vector<Grob *> elements,
719 multimap<Grob *, Grob *> const &riders)
723 = valid_outside_staff_placement_directive (me);
725 bool l2r = ((directive == ly_symbol2scm ("left-to-right-greedy"))
726 || (directive == ly_symbol2scm ("left-to-right-polite")));
728 bool polite = ((directive == ly_symbol2scm ("left-to-right-polite"))
729 || (directive == ly_symbol2scm ("right-to-left-polite")));
732 vector<Skyline_pair> skylines_to_merge;
734 // We want to avoid situations like this:
738 // -------------------
740 // -------------------
742 // The point is that "still more text" should be positioned under
743 // "more text". In order to achieve this, we place the grobs in several
744 // passes. We keep track of the right-most horizontal position that has been
745 // affected by the current pass so far (actually we keep track of 2
746 // positions, one for above the staff, one for below).
748 // In each pass, we loop through the unplaced grobs from left to right.
749 // If the grob doesn't overlap the right-most affected position, we place it
750 // (and then update the right-most affected position to point to the right
751 // edge of the just-placed grob). Otherwise, we skip it until the next pass.
752 while (!elements.empty ())
754 Drul_array<Real> last_end (-infinity_f, -infinity_f);
755 vector<Grob *> skipped_elements;
756 for (vsize i = l2r ? 0 : elements.size ();
757 l2r ? i < elements.size () : i--;
760 Grob *elt = elements[i];
762 = robust_scm2double (elt->get_property ("outside-staff-padding"), 0.25);
764 = robust_scm2double (elt->get_property ("outside-staff-horizontal-padding"), 0.0);
765 Interval x_extent = elt->extent (x_common, X_AXIS);
766 x_extent.widen (horizon_padding);
768 Direction dir = get_grob_direction (elt);
771 warning (_ ("an outside-staff object should have a direction, defaulting to up"));
775 if (x_extent[LEFT] <= last_end[dir] && polite)
777 skipped_elements.push_back (elt);
780 last_end[dir] = x_extent[RIGHT];
782 Skyline_pair *v_orig = Skyline_pair::unsmob (elt->get_property ("vertical-skylines"));
783 if (v_orig->is_empty ())
786 // Find the riders associated with this grob, and merge their
787 // skylines with elt's skyline.
788 typedef multimap<Grob *, Grob *>::const_iterator GrobMapIterator;
789 pair<GrobMapIterator, GrobMapIterator> range = riders.equal_range (elt);
790 vector<Skyline_pair> rider_v_skylines;
791 for (GrobMapIterator j = range.first; j != range.second; j++)
793 Grob *rider = j->second;
794 Skyline_pair *v_rider = Skyline_pair::unsmob (rider->get_property ("vertical-skylines"));
797 Skyline_pair copy (*v_rider);
798 copy.shift (rider->relative_coordinate (x_common, X_AXIS));
799 copy.raise (rider->relative_coordinate (y_common, Y_AXIS));
800 rider_v_skylines.push_back (copy);
803 Skyline_pair v_skylines (*v_orig);
804 v_skylines.shift (elt->relative_coordinate (x_common, X_AXIS));
805 v_skylines.raise (elt->relative_coordinate (y_common, Y_AXIS));
806 v_skylines.merge (Skyline_pair (rider_v_skylines));
808 avoid_outside_staff_collisions (elt,
812 (*all_v_skylines)[dir],
813 (*all_paddings)[dir],
814 (*all_horizon_paddings)[dir],
817 elt->set_property ("outside-staff-priority", SCM_BOOL_F);
818 (*all_v_skylines)[dir].push_back (v_skylines);
819 (*all_paddings)[dir].push_back (padding);
820 (*all_horizon_paddings)[dir].push_back (horizon_padding);
822 swap (elements, skipped_elements);
823 skipped_elements.clear ();
827 // If the Grob has a Y-ancestor with outside-staff-priority, return it.
828 // Otherwise, return 0.
830 Axis_group_interface::outside_staff_ancestor (Grob *me)
832 Grob *parent = me->get_parent (Y_AXIS);
836 if (scm_is_number (parent->get_property ("outside-staff-priority")))
839 return outside_staff_ancestor (parent);
842 // It is tricky to correctly handle skyline placement of cross-staff grobs.
843 // For example, cross-staff beams cannot be formatted until the distance between
844 // staves is known and therefore any grobs that depend on the beam cannot be placed
845 // until the skylines are known. On the other hand, the distance between staves should
846 // really depend on position of the cross-staff grobs that lie between them.
847 // Currently, we just leave cross-staff grobs out of the
848 // skyline altogether, but this could mean that staves are placed so close together
849 // that there is no room for the cross-staff grob. It also means, of course, that
850 // we don't get the benefits of skyline placement for cross-staff grobs.
852 Axis_group_interface::skyline_spacing (Grob *me)
854 extract_grob_set (me, Grob_array::unsmob (me->get_object ("vertical-skyline-elements")) ? "vertical-skyline-elements" : "elements", fakeelements);
855 vector<Grob *> elements (fakeelements);
856 for (vsize i = 0; i < elements.size (); i++)
858 As a sanity check, we make sure that no grob with an outside staff priority
859 has a Y-parent that also has an outside staff priority, which would result
862 if (scm_is_number (elements[i]->get_property ("outside-staff-priority"))
863 && outside_staff_ancestor (elements[i]))
865 elements[i]->warning ("Cannot set outside-staff-priority for element and elements' Y parent.");
866 elements[i]->set_property ("outside-staff-priority", SCM_BOOL_F);
869 /* For grobs with an outside-staff-priority, the sorting function might
870 call extent and cause suicide. This breaks the contract that is required
871 for the STL sort function. To avoid this, we make sure that any suicides
872 are triggered beforehand.
874 for (vsize i = 0; i < elements.size (); i++)
875 if (scm_is_number (elements[i]->get_property ("outside-staff-priority")))
876 elements[i]->extent (elements[i], X_AXIS);
878 vector_sort (elements, staff_priority_less);
879 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
880 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
882 assert (y_common == me);
884 // A rider is a grob that is not outside-staff, but has an outside-staff
885 // ancestor. In that case, the rider gets moved along with its ancestor.
886 multimap<Grob *, Grob *> riders;
889 vector<Skyline_pair> inside_staff_skylines;
891 for (i = 0; i < elements.size ()
892 && !scm_is_number (elements[i]->get_property ("outside-staff-priority")); i++)
894 Grob *elt = elements[i];
895 Grob *ancestor = outside_staff_ancestor (elt);
897 add_interior_skylines (elt, x_common, y_common, &inside_staff_skylines);
899 riders.insert (pair<Grob *, Grob *> (ancestor, elt));
902 Skyline_pair skylines (inside_staff_skylines);
904 // These are the skylines of all outside-staff grobs
905 // that have already been processed. We keep them around in order to
906 // check them for collisions with the currently active outside-staff grob.
907 Drul_array<vector<Skyline_pair> > all_v_skylines;
908 Drul_array<vector<Real> > all_paddings;
909 Drul_array<vector<Real> > all_horizon_paddings;
910 for (UP_and_DOWN (d))
912 all_v_skylines[d].push_back (skylines);
913 all_paddings[d].push_back (0);
914 all_horizon_paddings[d].push_back (0);
917 for (; i < elements.size (); i++)
919 if (to_boolean (elements[i]->get_property ("cross-staff")))
922 // Collect all the outside-staff grobs that have a particular priority.
923 SCM priority = elements[i]->get_property ("outside-staff-priority");
924 vector<Grob *> current_elts;
925 current_elts.push_back (elements[i]);
926 while (i + 1 < elements.size ()
927 && scm_is_eq (elements[i + 1]->get_property ("outside-staff-priority"), priority))
929 if (!to_boolean (elements[i + 1]->get_property ("cross-staff")))
930 current_elts.push_back (elements[i + 1]);
934 add_grobs_of_one_priority (me,
937 &all_horizon_paddings,
944 // Now everything in all_v_skylines has been shifted appropriately; merge
945 // them all into skylines to get the complete outline.
946 Skyline_pair other_skylines (all_v_skylines[UP]);
947 other_skylines.merge (Skyline_pair (all_v_skylines[DOWN]));
948 skylines.merge (other_skylines);
950 // We began by shifting my skyline to be relative to the common refpoint; now
952 skylines.shift (-me->relative_coordinate (x_common, X_AXIS));
957 MAKE_SCHEME_CALLBACK (Axis_group_interface, print, 1)
959 Axis_group_interface::print (SCM smob)
964 Grob *me = unsmob_grob (smob);
966 if (Skyline_pair *s = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
968 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[UP].to_points (X_AXIS))
969 .in_color (255, 0, 255));
970 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[DOWN].to_points (X_AXIS))
971 .in_color (0, 255, 255));
973 return ret.smobbed_copy ();
976 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_staff_staff_spacing, 3)
978 Axis_group_interface::calc_pure_staff_staff_spacing (SCM smob, SCM start, SCM end)
980 return calc_maybe_pure_staff_staff_spacing (unsmob_grob (smob),
986 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_staff_staff_spacing, 1)
988 Axis_group_interface::calc_staff_staff_spacing (SCM smob)
990 return calc_maybe_pure_staff_staff_spacing (unsmob_grob (smob),
997 Axis_group_interface::calc_maybe_pure_staff_staff_spacing (Grob *me, bool pure, int start, int end)
999 Grob *grouper = unsmob_grob (me->get_object ("staff-grouper"));
1003 bool within_group = Staff_grouper_interface::maybe_pure_within_group (grouper, me, pure, start, end);
1005 return grouper->get_maybe_pure_property ("staff-staff-spacing", pure, start, end);
1007 return grouper->get_maybe_pure_property ("staffgroup-staff-spacing", pure, start, end);
1009 return me->get_maybe_pure_property ("default-staff-staff-spacing", pure, start, end);
1012 ADD_INTERFACE (Axis_group_interface,
1013 "An object that groups other layout objects.",
1015 // TODO: some of these properties are specific to
1016 // VerticalAxisGroup. We should split off a
1017 // vertical-axis-group-interface.
1019 "adjacent-pure-heights "
1021 "bound-alignment-interfaces "
1022 "default-staff-staff-spacing "
1026 "nonstaff-nonstaff-spacing "
1027 "nonstaff-relatedstaff-spacing "
1028 "nonstaff-unrelatedstaff-spacing "
1029 "outside-staff-placement-directive "
1030 "pure-relevant-grobs "
1031 "pure-relevant-items "
1032 "pure-relevant-spanners "
1036 "staff-staff-spacing "
1038 "vertical-skyline-elements "