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_;
55 Axis_group_interface::add_element (Grob *me, Grob *e)
57 SCM axes = me->get_property ("axes");
58 if (!scm_is_pair (axes))
59 programming_error ("axes should be nonempty");
61 for (SCM ax = axes; scm_is_pair (ax); ax = scm_cdr (ax))
63 Axis a = (Axis) scm_to_int (scm_car (ax));
65 if (!e->get_parent (a))
66 e->set_parent (me, a);
68 e->set_object ((a == X_AXIS)
69 ? ly_symbol2scm ("axis-group-parent-X")
70 : ly_symbol2scm ("axis-group-parent-Y"),
74 /* must be ordered, because Align_interface also uses
75 Axis_group_interface */
76 Pointer_group_interface::add_grob (me, ly_symbol2scm ("elements"), e);
80 Axis_group_interface::has_axis (Grob *me, Axis a)
82 SCM axes = me->get_property ("axes");
84 return (SCM_BOOL_F != scm_memq (scm_from_int (a), axes));
88 Axis_group_interface::relative_group_extent (vector<Grob *> const &elts,
91 return relative_maybe_bound_group_extent (elts, common, a, false);
95 Axis_group_interface::relative_maybe_bound_group_extent (vector<Grob *> const &elts,
96 Grob *common, Axis a, bool bound)
99 for (vsize i = 0; i < elts.size (); i++)
102 if (!to_boolean (se->get_property ("cross-staff")))
104 Interval dims = (bound && has_interface (se)
105 ? generic_bound_extent (se, common, a)
106 : se->extent (common, a));
107 if (!dims.is_empty ())
115 Axis_group_interface::generic_bound_extent (Grob *me, Grob *common, Axis a)
117 /* trigger the callback to do skyline-spacing on the children */
119 (void) me->get_property ("vertical-skylines");
121 extract_grob_set (me, "elements", elts);
122 vector<Grob *> new_elts;
124 SCM interfaces = me->get_property ("bound-alignment-interfaces");
126 for (vsize i = 0; i < elts.size (); i++)
127 for (SCM l = interfaces; scm_is_pair (l); l = scm_cdr (l))
128 if (elts[i]->internal_has_interface (scm_car (l)))
129 new_elts.push_back (elts[i]);
131 if (!new_elts.size ())
132 return robust_relative_extent (me, common, a);
135 common = common_refpoint_of_array (new_elts, me, a);
137 return relative_maybe_bound_group_extent (new_elts, common, a, true);
141 Axis_group_interface::sum_partial_pure_heights (Grob *me, int start, int end)
143 Interval iv = begin_of_line_pure_height (me, start);
144 iv.unite (rest_of_line_pure_height (me, start, end));
150 Axis_group_interface::part_of_line_pure_height (Grob *me, bool begin, int start, int end)
152 Spanner *sp = dynamic_cast<Spanner *> (me);
153 SCM cache_symbol = begin
154 ? ly_symbol2scm ("begin-of-line-pure-height")
155 : ly_symbol2scm ("rest-of-line-pure-height");
156 SCM cached = sp->get_cached_pure_property (cache_symbol, start, end);
157 if (scm_is_pair (cached))
158 return robust_scm2interval (cached, Interval (0, 0));
160 SCM adjacent_pure_heights = me->get_property ("adjacent-pure-heights");
163 if (!scm_is_pair (adjacent_pure_heights))
164 ret = Interval (0, 0);
167 SCM these_pure_heights = begin
168 ? scm_car (adjacent_pure_heights)
169 : scm_cdr (adjacent_pure_heights);
171 if (scm_is_vector (these_pure_heights))
172 ret = combine_pure_heights (me, these_pure_heights, start, end);
174 ret = Interval (0, 0);
177 sp->cache_pure_property (cache_symbol, start, end, ly_interval2scm (ret));
182 Axis_group_interface::begin_of_line_pure_height (Grob *me, int start)
184 return part_of_line_pure_height (me, true, start, start + 1);
188 Axis_group_interface::rest_of_line_pure_height (Grob *me, int start, int end)
190 return part_of_line_pure_height (me, false, start, end);
194 Axis_group_interface::combine_pure_heights (Grob *me, SCM measure_extents, int start, int end)
196 Paper_score *ps = get_root_system (me)->paper_score ();
197 vector<vsize> breaks = ps->get_break_indices ();
198 vector<Grob *> cols = ps->get_columns ();
201 for (vsize i = 0; i + 1 < breaks.size (); i++)
203 int r = Paper_column::get_rank (cols[breaks[i]]);
208 ext.unite (ly_scm2interval (scm_c_vector_ref (measure_extents, i)));
214 // adjacent-pure-heights is a pair of vectors, each of which has one element
215 // for every measure in the score. The first vector stores, for each measure,
216 // the combined height of the elements that are present only when the bar
217 // is at the beginning of a line. The second vector stores, for each measure,
218 // the combined height of the elements that are present only when the bar
219 // is not at the beginning of a line.
220 MAKE_SCHEME_CALLBACK (Axis_group_interface, adjacent_pure_heights, 1)
222 Axis_group_interface::adjacent_pure_heights (SCM smob)
224 Grob *me = unsmob_grob (smob);
226 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
227 extract_grob_set (me, "pure-relevant-grobs", elts);
229 Paper_score *ps = get_root_system (me)->paper_score ();
230 vector<vsize> ranks = ps->get_break_ranks ();
232 vector<Interval> begin_line_heights;
233 vector<Interval> mid_line_heights;
234 vector<Interval> begin_line_staff_heights;
235 vector<Interval> mid_line_staff_heights;
236 begin_line_heights.resize (ranks.size () - 1);
237 mid_line_heights.resize (ranks.size () - 1);
239 for (vsize i = 0; i < elts.size (); ++i)
243 if (to_boolean (g->get_property ("cross-staff")))
246 bool outside_staff = scm_is_number (g->get_property ("outside-staff-priority"));
247 Real padding = robust_scm2double (g->get_property ("outside-staff-padding"), get_default_outside_staff_padding ());
249 // When we encounter the first outside-staff grob, make a copy
250 // of the current heights to use as an estimate for the staff heights.
251 // Note that the outside-staff approximation that we use here doesn't
252 // consider any collisions that might occur between outside-staff grobs,
253 // but only the fact that outside-staff grobs may need to be raised above
255 if (outside_staff && begin_line_staff_heights.empty ())
257 begin_line_staff_heights = begin_line_heights;
258 mid_line_staff_heights = mid_line_heights;
261 // TODO: consider a pure version of get_grob_direction?
262 Direction d = to_dir (g->get_property_data ("direction"));
263 d = (d == CENTER) ? UP : d;
265 Interval_t<int> rank_span = g->spanned_rank_interval ();
266 vsize first_break = lower_bound (ranks, (vsize)rank_span[LEFT], less<vsize> ());
267 if (first_break > 0 && ranks[first_break] >= (vsize)rank_span[LEFT])
270 for (vsize j = first_break; j + 1 < ranks.size () && (int)ranks[j] <= rank_span[RIGHT]; ++j)
272 int start = ranks[j];
273 int end = ranks[j + 1];
275 // Take grobs that are visible with respect to a slightly longer line.
276 // Otherwise, we will never include grobs at breakpoints which aren't
277 // end-of-line-visible.
278 int visibility_end = j + 2 < ranks.size () ? ranks[j + 2] : end;
280 if (g->pure_is_visible (start, visibility_end))
282 Interval dims = g->pure_height (common, start, end);
283 if (!dims.is_empty ())
285 if (rank_span[LEFT] <= start)
288 begin_line_heights[j].unite (begin_line_staff_heights[j].union_disjoint (dims, padding, d));
290 begin_line_heights[j].unite (dims);
292 if (rank_span[RIGHT] > start)
295 mid_line_heights[j].unite (mid_line_staff_heights[j].union_disjoint (dims, padding, d));
297 mid_line_heights[j].unite (dims);
304 // Convert begin_line_heights and min_line_heights to SCM.
305 SCM begin_scm = scm_c_make_vector (ranks.size () - 1, SCM_EOL);
306 SCM mid_scm = scm_c_make_vector (ranks.size () - 1, SCM_EOL);
307 for (vsize i = 0; i < begin_line_heights.size (); ++i)
309 scm_vector_set_x (begin_scm, scm_from_int (i), ly_interval2scm (begin_line_heights[i]));
310 scm_vector_set_x (mid_scm, scm_from_int (i), ly_interval2scm (mid_line_heights[i]));
313 return scm_cons (begin_scm, mid_scm);
317 Axis_group_interface::relative_pure_height (Grob *me, int start, int end)
319 /* It saves a _lot_ of time if we assume a VerticalAxisGroup is additive
320 (ie. height (i, k) = max (height (i, j) height (j, k)) for all i <= j <= k).
321 Unfortunately, it isn't always true, particularly if there is a
322 VerticalAlignment somewhere in the descendants.
324 Usually, the only VerticalAlignment comes from Score. This makes it
325 reasonably safe to assume that if our parent is a VerticalAlignment,
326 we can assume additivity and cache things nicely. */
327 Grob *p = me->get_parent (Y_AXIS);
328 if (p && Align_interface::has_interface (p))
329 return Axis_group_interface::sum_partial_pure_heights (me, start, end);
331 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
332 extract_grob_set (me, "pure-relevant-grobs", elts);
335 for (vsize i = 0; i < elts.size (); i++)
338 Interval_t<int> rank_span = g->spanned_rank_interval ();
339 if (rank_span[LEFT] <= end && rank_span[RIGHT] >= start
340 && g->pure_is_visible (start, end)
341 && !(to_boolean (g->get_property ("cross-staff"))
342 && Stem::has_interface (g)))
344 Interval dims = g->pure_height (common, start, end);
345 if (!dims.is_empty ())
352 MAKE_SCHEME_CALLBACK (Axis_group_interface, width, 1);
354 Axis_group_interface::width (SCM smob)
356 Grob *me = unsmob_grob (smob);
357 return generic_group_extent (me, X_AXIS);
360 MAKE_SCHEME_CALLBACK (Axis_group_interface, height, 1);
362 Axis_group_interface::height (SCM smob)
364 Grob *me = unsmob_grob (smob);
365 return generic_group_extent (me, Y_AXIS);
368 MAKE_SCHEME_CALLBACK (Axis_group_interface, pure_height, 3);
370 Axis_group_interface::pure_height (SCM smob, SCM start_scm, SCM end_scm)
372 int start = robust_scm2int (start_scm, 0);
373 int end = robust_scm2int (end_scm, INT_MAX);
374 Grob *me = unsmob_grob (smob);
376 /* Maybe we are in the second pass of a two-pass spacing run. In that
377 case, the Y-extent of a system is already given to us */
378 System *system = dynamic_cast<System *> (me);
381 SCM line_break_details = system->column (start)->get_property ("line-break-system-details");
382 SCM system_y_extent = scm_assq (ly_symbol2scm ("system-Y-extent"), line_break_details);
383 if (scm_is_pair (system_y_extent))
384 return scm_cdr (system_y_extent);
387 return ly_interval2scm (pure_group_height (me, start, end));
390 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_skylines, 1);
392 Axis_group_interface::calc_skylines (SCM smob)
394 Grob *me = unsmob_grob (smob);
395 extract_grob_set (me, Grob_array::unsmob (me->get_object ("vertical-skyline-elements")) ? "vertical-skyline-elements" : "elements", elts);
396 Skyline_pair skylines = skyline_spacing (me, elts);
398 return skylines.smobbed_copy ();
401 /* whereas calc_skylines calculates skylines for axis-groups with a lot of
402 visible children, combine_skylines is designed for axis-groups whose only
403 children are other axis-groups (ie. VerticalAlignment). Rather than
404 calculating all the skylines from scratch, we just merge the skylines
407 MAKE_SCHEME_CALLBACK (Axis_group_interface, combine_skylines, 1);
409 Axis_group_interface::combine_skylines (SCM smob)
411 Grob *me = unsmob_grob (smob);
412 extract_grob_set (me, "elements", elements);
413 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
414 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
417 programming_error ("combining skylines that don't belong to me");
420 for (vsize i = 0; i < elements.size (); i++)
422 SCM skyline_scm = elements[i]->get_property ("vertical-skylines");
423 if (Skyline_pair::unsmob (skyline_scm))
425 Real offset = elements[i]->relative_coordinate (y_common, Y_AXIS);
426 Skyline_pair other = *Skyline_pair::unsmob (skyline_scm);
427 other.raise (offset);
428 other.shift (elements[i]->relative_coordinate (x_common, X_AXIS));
432 return ret.smobbed_copy ();
436 Axis_group_interface::generic_group_extent (Grob *me, Axis a)
438 /* trigger the callback to do skyline-spacing on the children */
440 (void) me->get_property ("vertical-skylines");
442 extract_grob_set (me, "elements", elts);
443 Grob *common = common_refpoint_of_array (elts, me, a);
445 Real my_coord = me->relative_coordinate (common, a);
446 Interval r (relative_group_extent (elts, common, a));
448 return ly_interval2scm (r - my_coord);
451 /* This is like generic_group_extent, but it only counts the grobs that
452 are children of some other axis-group. This is uncached; if it becomes
453 commonly used, it may be necessary to cache it somehow. */
455 Axis_group_interface::staff_extent (Grob *me, Grob *refp, Axis ext_a, Grob *staff, Axis parent_a)
457 extract_grob_set (me, "elements", elts);
458 vector<Grob *> new_elts;
460 for (vsize i = 0; i < elts.size (); i++)
461 if (elts[i]->common_refpoint (staff, parent_a) == staff)
462 new_elts.push_back (elts[i]);
464 return relative_group_extent (new_elts, refp, ext_a);
467 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_relevant_grobs, 1);
469 Axis_group_interface::calc_pure_relevant_grobs (SCM smob)
471 Grob *me = unsmob_grob (smob);
472 return internal_calc_pure_relevant_grobs (me, "elements");
476 Axis_group_interface::internal_calc_pure_relevant_grobs (Grob *me, string grob_set_name)
478 extract_grob_set (me, grob_set_name.c_str (), elts);
480 vector<Grob *> relevant_grobs;
481 SCM pure_relevant_p = ly_lily_module_constant ("pure-relevant?");
483 for (vsize i = 0; i < elts.size (); i++)
485 if (to_boolean (scm_apply_1 (pure_relevant_p, elts[i]->self_scm (), SCM_EOL)))
486 relevant_grobs.push_back (elts[i]);
488 if (Item *it = dynamic_cast<Item *> (elts[i]))
490 for (LEFT_and_RIGHT (d))
492 Item *piece = it->find_prebroken_piece (d);
493 if (piece && to_boolean (scm_apply_1 (pure_relevant_p, piece->self_scm (), SCM_EOL)))
494 relevant_grobs.push_back (piece);
499 vector_sort (relevant_grobs, pure_staff_priority_less);
500 SCM grobs_scm = Grob_array::make_array ();
501 unsmob_grob_array (grobs_scm)->set_array (relevant_grobs);
506 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_y_common, 1);
508 Axis_group_interface::calc_pure_y_common (SCM smob)
510 Grob *me = unsmob_grob (smob);
512 extract_grob_set (me, "pure-relevant-grobs", elts);
513 Grob *common = common_refpoint_of_array (elts, me, Y_AXIS);
516 me->programming_error ("No common parent found in calc_pure_y_common.");
520 return common->self_scm ();
524 Axis_group_interface::calc_common (Grob *me, Axis axis)
526 extract_grob_set (me, "elements", elts);
527 Grob *common = common_refpoint_of_array (elts, me, axis);
530 me->programming_error ("No common parent found in calc_common axis.");
534 return common->self_scm ();
537 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_x_common, 1);
539 Axis_group_interface::calc_x_common (SCM grob)
541 return calc_common (unsmob_grob (grob), X_AXIS);
544 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_y_common, 1);
546 Axis_group_interface::calc_y_common (SCM grob)
548 return calc_common (unsmob_grob (grob), Y_AXIS);
552 Axis_group_interface::pure_group_height (Grob *me, int start, int end)
554 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
558 programming_error ("no pure Y common refpoint");
561 Real my_coord = me->relative_coordinate (common, Y_AXIS);
562 Interval r (relative_pure_height (me, start, end));
568 Axis_group_interface::get_children (Grob *me, vector<Grob *> *found)
570 found->push_back (me);
572 if (!has_interface (me))
575 extract_grob_set (me, "elements", elements);
576 for (vsize i = 0; i < elements.size (); i++)
578 Grob *e = elements[i];
579 Axis_group_interface::get_children (e, found);
584 staff_priority_less (Grob *const &g1, Grob *const &g2)
586 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
587 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
589 if (priority_1 < priority_2)
591 else if (priority_1 > priority_2)
594 /* if neither grob has an outside-staff priority, the ordering will have no
595 effect -- we just need to choose a consistent ordering. We do this to
596 avoid the side-effect of calculating extents. */
597 if (isinf (priority_1))
600 /* if there is no preference in staff priority, choose the left-most one */
601 Grob *common = g1->common_refpoint (g2, X_AXIS);
602 Real start_1 = g1->extent (common, X_AXIS)[LEFT];
603 Real start_2 = g2->extent (common, X_AXIS)[LEFT];
604 return start_1 < start_2;
608 pure_staff_priority_less (Grob *const &g1, Grob *const &g2)
610 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
611 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
613 return priority_1 < priority_2;
617 add_interior_skylines (Grob *me, Grob *x_common, Grob *y_common, vector<Skyline_pair> *skylines)
619 if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements")))
621 for (vsize i = 0; i < elements->size (); i++)
622 add_interior_skylines (elements->grob (i), x_common, y_common, skylines);
624 else if (!scm_is_number (me->get_property ("outside-staff-priority"))
625 && !to_boolean (me->get_property ("cross-staff")))
627 Skyline_pair *maybe_pair = Skyline_pair::unsmob (me->get_property ("vertical-skylines"));
630 if (maybe_pair->is_empty ())
632 skylines->push_back (Skyline_pair (*maybe_pair));
633 skylines->back ().shift (me->relative_coordinate (x_common, X_AXIS));
634 skylines->back ().raise (me->relative_coordinate (y_common, Y_AXIS));
638 // Raises the grob elt (whose skylines are given by h_skyline
639 // and v_skyline) so that it doesn't intersect with staff_skyline,
640 // or with anything in other_h_skylines and other_v_skylines.
642 avoid_outside_staff_collisions (Grob *elt,
643 Skyline_pair *v_skyline,
645 Real horizon_padding,
646 vector<Skyline_pair> const &other_v_skylines,
647 vector<Real> const &other_padding,
648 vector<Real> const &other_horizon_padding,
651 assert (other_v_skylines.size () == other_padding.size ());
652 assert (other_v_skylines.size () == other_horizon_padding.size ());
653 vector<Interval> forbidden_intervals;
654 for (vsize j = 0; j < other_v_skylines.size (); j++)
656 Skyline_pair const &v_other = other_v_skylines[j];
657 Real pad = (padding + other_padding[j]);
658 Real horizon_pad = (horizon_padding + other_horizon_padding[j]);
660 // We need to push elt up by at least this much to be above v_other.
661 Real up = (*v_skyline)[DOWN].distance (v_other[UP], horizon_pad) + pad;
662 // We need to push elt down by at least this much to be below v_other.
663 Real down = (*v_skyline)[UP].distance (v_other[DOWN], horizon_pad) + pad;
665 forbidden_intervals.push_back (Interval (-down, up));
668 Interval_set allowed_shifts
669 = Interval_set::interval_union (forbidden_intervals).complement ();
670 Real move = allowed_shifts.nearest_point (0, dir);
671 v_skyline->raise (move);
672 elt->translate_axis (move, Y_AXIS);
676 valid_outside_staff_placement_directive (Grob *me)
678 SCM directive = me->get_property ("outside-staff-placement-directive");
680 if ((directive == ly_symbol2scm ("left-to-right-greedy"))
681 || (directive == ly_symbol2scm ("left-to-right-polite"))
682 || (directive == ly_symbol2scm ("right-to-left-greedy"))
683 || (directive == ly_symbol2scm ("right-to-left-polite")))
686 me->warning (_f ("\"%s\" is not a valid outside-staff-placement-directive",
687 robust_symbol2string (directive, "").c_str ()));
689 return ly_symbol2scm ("left-to-right-polite");
692 // Shifts the grobs in elements to ensure that they (and any
693 // connected riders) don't collide with the staff skylines
694 // or anything in all_X_skylines. Afterwards, the skylines
695 // of the grobs in elements will be added to all_v_skylines.
697 add_grobs_of_one_priority (Grob *me,
698 Drul_array<vector<Skyline_pair> > *all_v_skylines,
699 Drul_array<vector<Real> > *all_paddings,
700 Drul_array<vector<Real> > *all_horizon_paddings,
701 vector<Grob *> elements,
704 multimap<Grob *, Grob *> const &riders)
708 = valid_outside_staff_placement_directive (me);
710 bool l2r = ((directive == ly_symbol2scm ("left-to-right-greedy"))
711 || (directive == ly_symbol2scm ("left-to-right-polite")));
713 bool polite = ((directive == ly_symbol2scm ("left-to-right-polite"))
714 || (directive == ly_symbol2scm ("right-to-left-polite")));
717 vector<Skyline_pair> skylines_to_merge;
719 // We want to avoid situations like this:
723 // -------------------
725 // -------------------
727 // The point is that "still more text" should be positioned under
728 // "more text". In order to achieve this, we place the grobs in several
729 // passes. We keep track of the right-most horizontal position that has been
730 // affected by the current pass so far (actually we keep track of 2
731 // positions, one for above the staff, one for below).
733 // In each pass, we loop through the unplaced grobs from left to right.
734 // If the grob doesn't overlap the right-most affected position, we place it
735 // (and then update the right-most affected position to point to the right
736 // edge of the just-placed grob). Otherwise, we skip it until the next pass.
737 while (!elements.empty ())
739 Drul_array<Real> last_end (-infinity_f, -infinity_f);
740 vector<Grob *> skipped_elements;
741 for (vsize i = l2r ? 0 : elements.size ();
742 l2r ? i < elements.size () : i--;
745 Grob *elt = elements[i];
747 = robust_scm2double (elt->get_property ("outside-staff-padding"), 0.25);
749 = robust_scm2double (elt->get_property ("outside-staff-horizontal-padding"), 0.0);
750 Interval x_extent = elt->extent (x_common, X_AXIS);
751 x_extent.widen (horizon_padding);
753 Direction dir = get_grob_direction (elt);
756 warning (_ ("an outside-staff object should have a direction, defaulting to up"));
760 if (x_extent[LEFT] <= last_end[dir] && polite)
762 skipped_elements.push_back (elt);
765 last_end[dir] = x_extent[RIGHT];
767 Skyline_pair *v_orig = Skyline_pair::unsmob (elt->get_property ("vertical-skylines"));
768 if (v_orig->is_empty ())
771 // Find the riders associated with this grob, and merge their
772 // skylines with elt's skyline.
773 typedef multimap<Grob *, Grob *>::const_iterator GrobMapIterator;
774 pair<GrobMapIterator, GrobMapIterator> range = riders.equal_range (elt);
775 vector<Skyline_pair> rider_v_skylines;
776 for (GrobMapIterator j = range.first; j != range.second; j++)
778 Grob *rider = j->second;
779 Skyline_pair *v_rider = Skyline_pair::unsmob (rider->get_property ("vertical-skylines"));
782 Skyline_pair copy (*v_rider);
783 copy.shift (rider->relative_coordinate (x_common, X_AXIS));
784 copy.raise (rider->relative_coordinate (y_common, Y_AXIS));
785 rider_v_skylines.push_back (copy);
788 Skyline_pair v_skylines (*v_orig);
789 v_skylines.shift (elt->relative_coordinate (x_common, X_AXIS));
790 v_skylines.raise (elt->relative_coordinate (y_common, Y_AXIS));
791 v_skylines.merge (Skyline_pair (rider_v_skylines));
793 avoid_outside_staff_collisions (elt,
797 (*all_v_skylines)[dir],
798 (*all_paddings)[dir],
799 (*all_horizon_paddings)[dir],
802 elt->set_property ("outside-staff-priority", SCM_BOOL_F);
803 (*all_v_skylines)[dir].push_back (v_skylines);
804 (*all_paddings)[dir].push_back (padding);
805 (*all_horizon_paddings)[dir].push_back (horizon_padding);
807 swap (elements, skipped_elements);
808 skipped_elements.clear ();
812 // If the Grob has a Y-ancestor with outside-staff-priority, return it.
813 // Otherwise, return 0.
815 Axis_group_interface::outside_staff_ancestor (Grob *me)
817 Grob *parent = me->get_parent (Y_AXIS);
821 if (scm_is_number (parent->get_property ("outside-staff-priority")))
824 return outside_staff_ancestor (parent);
827 // It is tricky to correctly handle skyline placement of cross-staff grobs.
828 // For example, cross-staff beams cannot be formatted until the distance between
829 // staves is known and therefore any grobs that depend on the beam cannot be placed
830 // until the skylines are known. On the other hand, the distance between staves should
831 // really depend on position of the cross-staff grobs that lie between them.
832 // Currently, we just leave cross-staff grobs out of the
833 // skyline altogether, but this could mean that staves are placed so close together
834 // that there is no room for the cross-staff grob. It also means, of course, that
835 // we don't get the benefits of skyline placement for cross-staff grobs.
837 Axis_group_interface::skyline_spacing (Grob *me, vector<Grob *> elements)
839 for (vsize i = 0; i < elements.size (); i++)
841 As a sanity check, we make sure that no grob with an outside staff priority
842 has a Y-parent that also has an outside staff priority, which would result
845 if (scm_is_number (elements[i]->get_property ("outside-staff-priority"))
846 && outside_staff_ancestor (elements[i]))
848 elements[i]->warning ("Cannot set outside-staff-priority for element and elements' Y parent.");
849 elements[i]->set_property ("outside-staff-priority", SCM_BOOL_F);
852 /* For grobs with an outside-staff-priority, the sorting function might
853 call extent and cause suicide. This breaks the contract that is required
854 for the STL sort function. To avoid this, we make sure that any suicides
855 are triggered beforehand.
857 for (vsize i = 0; i < elements.size (); i++)
858 if (scm_is_number (elements[i]->get_property ("outside-staff-priority")))
859 elements[i]->extent (elements[i], X_AXIS);
861 vector_sort (elements, staff_priority_less);
862 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
863 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
865 assert (y_common == me);
867 // A rider is a grob that is not outside-staff, but has an outside-staff
868 // ancestor. In that case, the rider gets moved along with its ancestor.
869 multimap<Grob *, Grob *> riders;
872 vector<Skyline_pair> inside_staff_skylines;
873 for (i = 0; i < elements.size ()
874 && !scm_is_number (elements[i]->get_property ("outside-staff-priority")); i++)
876 Grob *elt = elements[i];
877 Grob *ancestor = outside_staff_ancestor (elt);
878 if (!(to_boolean (elt->get_property ("cross-staff")) || ancestor))
879 add_interior_skylines (elt, x_common, y_common, &inside_staff_skylines);
881 riders.insert (pair<Grob *, Grob *> (ancestor, elt));
884 Skyline_pair skylines (inside_staff_skylines);
886 // These are the skylines of all outside-staff grobs
887 // that have already been processed. We keep them around in order to
888 // check them for collisions with the currently active outside-staff grob.
889 Drul_array<vector<Skyline_pair> > all_v_skylines;
890 Drul_array<vector<Real> > all_paddings;
891 Drul_array<vector<Real> > all_horizon_paddings;
892 for (UP_and_DOWN (d))
894 all_v_skylines[d].push_back (skylines);
895 all_paddings[d].push_back (0);
896 all_horizon_paddings[d].push_back (0);
899 for (; i < elements.size (); i++)
901 if (to_boolean (elements[i]->get_property ("cross-staff")))
904 // Collect all the outside-staff grobs that have a particular priority.
905 SCM priority = elements[i]->get_property ("outside-staff-priority");
906 vector<Grob *> current_elts;
907 current_elts.push_back (elements[i]);
908 while (i + 1 < elements.size ()
909 && scm_is_eq (elements[i + 1]->get_property ("outside-staff-priority"), priority))
911 if (!to_boolean (elements[i + 1]->get_property ("cross-staff")))
912 current_elts.push_back (elements[i + 1]);
916 add_grobs_of_one_priority (me,
919 &all_horizon_paddings,
926 // Now everything in all_v_skylines has been shifted appropriately; merge
927 // them all into skylines to get the complete outline.
928 Skyline_pair other_skylines (all_v_skylines[UP]);
929 other_skylines.merge (Skyline_pair (all_v_skylines[DOWN]));
930 skylines.merge (other_skylines);
932 // We began by shifting my skyline to be relative to the common refpoint; now
934 skylines.shift (-me->relative_coordinate (x_common, X_AXIS));
939 MAKE_SCHEME_CALLBACK (Axis_group_interface, print, 1)
941 Axis_group_interface::print (SCM smob)
946 Grob *me = unsmob_grob (smob);
948 if (Skyline_pair *s = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
950 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[UP].to_points (X_AXIS))
951 .in_color (255, 0, 255));
952 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[DOWN].to_points (X_AXIS))
953 .in_color (0, 255, 255));
955 return ret.smobbed_copy ();
958 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_staff_staff_spacing, 3)
960 Axis_group_interface::calc_pure_staff_staff_spacing (SCM smob, SCM start, SCM end)
962 return calc_maybe_pure_staff_staff_spacing (unsmob_grob (smob),
968 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_staff_staff_spacing, 1)
970 Axis_group_interface::calc_staff_staff_spacing (SCM smob)
972 return calc_maybe_pure_staff_staff_spacing (unsmob_grob (smob),
979 Axis_group_interface::calc_maybe_pure_staff_staff_spacing (Grob *me, bool pure, int start, int end)
981 Grob *grouper = unsmob_grob (me->get_object ("staff-grouper"));
985 bool within_group = Staff_grouper_interface::maybe_pure_within_group (grouper, me, pure, start, end);
987 return grouper->get_maybe_pure_property ("staff-staff-spacing", pure, start, end);
989 return grouper->get_maybe_pure_property ("staffgroup-staff-spacing", pure, start, end);
991 return me->get_maybe_pure_property ("default-staff-staff-spacing", pure, start, end);
994 ADD_INTERFACE (Axis_group_interface,
995 "An object that groups other layout objects.",
997 // TODO: some of these properties are specific to
998 // VerticalAxisGroup. We should split off a
999 // vertical-axis-group-interface.
1001 "adjacent-pure-heights "
1003 "bound-alignment-interfaces "
1004 "default-staff-staff-spacing "
1008 "nonstaff-nonstaff-spacing "
1009 "nonstaff-relatedstaff-spacing "
1010 "nonstaff-unrelatedstaff-spacing "
1011 "outside-staff-placement-directive "
1012 "pure-relevant-grobs "
1013 "pure-relevant-items "
1014 "pure-relevant-spanners "
1018 "staff-staff-spacing "
1020 "vertical-skyline-elements "