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")))
249 bool outside_staff = scm_is_number (g->get_property ("outside-staff-priority"));
250 Real padding = robust_scm2double (g->get_property ("outside-staff-padding"), get_default_outside_staff_padding ());
252 // When we encounter the first outside-staff grob, make a copy
253 // of the current heights to use as an estimate for the staff heights.
254 // Note that the outside-staff approximation that we use here doesn't
255 // consider any collisions that might occur between outside-staff grobs,
256 // but only the fact that outside-staff grobs may need to be raised above
258 if (outside_staff && begin_line_staff_heights.empty ())
260 begin_line_staff_heights = begin_line_heights;
261 mid_line_staff_heights = mid_line_heights;
264 // TODO: consider a pure version of get_grob_direction?
265 Direction d = to_dir (g->get_property_data ("direction"));
266 d = (d == CENTER) ? UP : d;
268 Interval_t<int> rank_span = g->spanned_rank_interval ();
269 vsize first_break = lower_bound (ranks, (vsize)rank_span[LEFT], less<vsize> ());
270 if (first_break > 0 && ranks[first_break] >= (vsize)rank_span[LEFT])
273 for (vsize j = first_break; j + 1 < ranks.size () && (int)ranks[j] <= rank_span[RIGHT]; ++j)
275 int start = ranks[j];
276 int end = ranks[j + 1];
278 // Take grobs that are visible with respect to a slightly longer line.
279 // Otherwise, we will never include grobs at breakpoints which aren't
280 // end-of-line-visible.
281 int visibility_end = j + 2 < ranks.size () ? ranks[j + 2] : end;
283 if (g->pure_is_visible (start, visibility_end))
285 Interval dims = g->pure_height (common, start, end);
286 if (!dims.is_empty ())
288 if (rank_span[LEFT] <= start)
291 begin_line_heights[j].unite (begin_line_staff_heights[j].union_disjoint (dims, padding, d));
293 begin_line_heights[j].unite (dims);
295 if (rank_span[RIGHT] > start)
298 mid_line_heights[j].unite (mid_line_staff_heights[j].union_disjoint (dims, padding, d));
300 mid_line_heights[j].unite (dims);
307 // Convert begin_line_heights and min_line_heights to SCM.
308 SCM begin_scm = scm_c_make_vector (ranks.size () - 1, SCM_EOL);
309 SCM mid_scm = scm_c_make_vector (ranks.size () - 1, SCM_EOL);
310 for (vsize i = 0; i < begin_line_heights.size (); ++i)
312 scm_vector_set_x (begin_scm, scm_from_int (i), ly_interval2scm (begin_line_heights[i]));
313 scm_vector_set_x (mid_scm, scm_from_int (i), ly_interval2scm (mid_line_heights[i]));
316 return scm_cons (begin_scm, mid_scm);
320 Axis_group_interface::relative_pure_height (Grob *me, int start, int end)
322 /* It saves a _lot_ of time if we assume a VerticalAxisGroup is additive
323 (ie. height (i, k) = max (height (i, j) height (j, k)) for all i <= j <= k).
324 Unfortunately, it isn't always true, particularly if there is a
325 VerticalAlignment somewhere in the descendants.
327 Usually, the only VerticalAlignment comes from Score. This makes it
328 reasonably safe to assume that if our parent is a VerticalAlignment,
329 we can assume additivity and cache things nicely. */
330 Grob *p = me->get_parent (Y_AXIS);
331 if (p && Align_interface::has_interface (p))
332 return Axis_group_interface::sum_partial_pure_heights (me, start, end);
334 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
335 extract_grob_set (me, "pure-relevant-grobs", elts);
338 for (vsize i = 0; i < elts.size (); i++)
341 Interval_t<int> rank_span = g->spanned_rank_interval ();
342 if (rank_span[LEFT] <= end && rank_span[RIGHT] >= start
343 && g->pure_is_visible (start, end)
344 && !(to_boolean (g->get_property ("cross-staff"))
345 && Stem::has_interface (g)))
347 Interval dims = g->pure_height (common, start, end);
348 if (!dims.is_empty ())
355 MAKE_SCHEME_CALLBACK (Axis_group_interface, width, 1);
357 Axis_group_interface::width (SCM smob)
359 Grob *me = unsmob_grob (smob);
360 return generic_group_extent (me, X_AXIS);
363 MAKE_SCHEME_CALLBACK (Axis_group_interface, height, 1);
365 Axis_group_interface::height (SCM smob)
367 Grob *me = unsmob_grob (smob);
368 return generic_group_extent (me, Y_AXIS);
371 MAKE_SCHEME_CALLBACK (Axis_group_interface, pure_height, 3);
373 Axis_group_interface::pure_height (SCM smob, SCM start_scm, SCM end_scm)
375 int start = robust_scm2int (start_scm, 0);
376 int end = robust_scm2int (end_scm, INT_MAX);
377 Grob *me = unsmob_grob (smob);
379 /* Maybe we are in the second pass of a two-pass spacing run. In that
380 case, the Y-extent of a system is already given to us */
381 System *system = dynamic_cast<System *> (me);
384 SCM line_break_details = system->column (start)->get_property ("line-break-system-details");
385 SCM system_y_extent = scm_assq (ly_symbol2scm ("system-Y-extent"), line_break_details);
386 if (scm_is_pair (system_y_extent))
387 return scm_cdr (system_y_extent);
390 return ly_interval2scm (pure_group_height (me, start, end));
393 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_skylines, 1);
395 Axis_group_interface::calc_skylines (SCM smob)
397 Grob *me = unsmob_grob (smob);
398 Skyline_pair skylines = skyline_spacing (me);
399 return skylines.smobbed_copy ();
402 /* whereas calc_skylines calculates skylines for axis-groups with a lot of
403 visible children, combine_skylines is designed for axis-groups whose only
404 children are other axis-groups (ie. VerticalAlignment). Rather than
405 calculating all the skylines from scratch, we just merge the skylines
408 MAKE_SCHEME_CALLBACK (Axis_group_interface, combine_skylines, 1);
410 Axis_group_interface::combine_skylines (SCM smob)
412 Grob *me = unsmob_grob (smob);
413 extract_grob_set (me, "elements", elements);
414 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
415 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
418 programming_error ("combining skylines that don't belong to me");
421 for (vsize i = 0; i < elements.size (); i++)
423 SCM skyline_scm = elements[i]->get_property ("vertical-skylines");
424 if (Skyline_pair::unsmob (skyline_scm))
426 Real offset = elements[i]->relative_coordinate (y_common, Y_AXIS);
427 Skyline_pair other = *Skyline_pair::unsmob (skyline_scm);
428 other.raise (offset);
429 other.shift (elements[i]->relative_coordinate (x_common, X_AXIS));
433 return ret.smobbed_copy ();
437 Axis_group_interface::generic_group_extent (Grob *me, Axis a)
439 extract_grob_set (me, "elements", elts);
441 /* trigger the callback to do skyline-spacing on the children */
443 for (vsize i = 0; i < elts.size (); i++)
444 if (!(Stem::has_interface (elts[i])
445 && to_boolean (elts[i]->get_property ("cross-staff"))))
446 (void) elts[i]->get_property ("vertical-skylines");
448 Grob *common = common_refpoint_of_array (elts, me, a);
450 Real my_coord = me->relative_coordinate (common, a);
451 Interval r (relative_group_extent (elts, common, a));
453 return ly_interval2scm (r - my_coord);
456 /* This is like generic_group_extent, but it only counts the grobs that
457 are children of some other axis-group. This is uncached; if it becomes
458 commonly used, it may be necessary to cache it somehow. */
460 Axis_group_interface::staff_extent (Grob *me, Grob *refp, Axis ext_a, Grob *staff, Axis parent_a)
462 extract_grob_set (me, "elements", elts);
463 vector<Grob *> new_elts;
465 for (vsize i = 0; i < elts.size (); i++)
466 if (elts[i]->common_refpoint (staff, parent_a) == staff)
467 new_elts.push_back (elts[i]);
469 return relative_group_extent (new_elts, refp, ext_a);
472 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_relevant_grobs, 1);
474 Axis_group_interface::calc_pure_relevant_grobs (SCM smob)
476 Grob *me = unsmob_grob (smob);
477 /* TODO: Filter out elements that belong to a different Axis_group,
479 << \new Staff=A { c'1~ \change Staff=B c'}
480 \new Staff=B { \clef bass R1 R } >>
481 because thier location relative to this Axis_group is not known before
482 page layout. For now, we need to trap this case in calc_pure_y_common.
484 return internal_calc_pure_relevant_grobs (me, "elements");
488 Axis_group_interface::internal_calc_pure_relevant_grobs (Grob *me, const string &grob_set_name)
490 extract_grob_set (me, grob_set_name.c_str (), elts);
492 vector<Grob *> relevant_grobs;
494 for (vsize i = 0; i < elts.size (); i++)
496 if (elts[i] && elts[i]->is_live ())
497 relevant_grobs.push_back (elts[i]);
499 TODO (mikesol): it is probably bad that we're reading prebroken
500 pieces from potentially suicided elements. This behavior
501 has been in current master since at least 2.16.
503 We need to fully suicide all Items, meaning that their
504 prebroken pieces should not be accessible, which means that
505 Item::handle_prebroken_dependencies should only be called
506 AFTER this list is composed. The list composition function
507 should probably not check for suicided items or NULL pointers
508 but leave that to the various methods that use it.
510 if (Item *it = dynamic_cast<Item *> (elts[i]))
512 for (LEFT_and_RIGHT (d))
514 Item *piece = it->find_prebroken_piece (d);
515 if (piece && piece->is_live ())
516 relevant_grobs.push_back (piece);
521 vector_sort (relevant_grobs, pure_staff_priority_less);
522 SCM grobs_scm = Grob_array::make_array ();
523 unsmob_grob_array (grobs_scm)->set_array (relevant_grobs);
528 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_y_common, 1);
530 Axis_group_interface::calc_pure_y_common (SCM smob)
532 Grob *me = unsmob_grob (smob);
534 extract_grob_set (me, "pure-relevant-grobs", elts);
535 Grob *common = common_refpoint_of_array (elts, me, Y_AXIS);
536 if (common != me && Align_interface::has_interface (common))
538 me->programming_error("My pure_y_common is a VerticalAlignment,"
539 " which might contain several staves.");
544 me->programming_error ("No common parent found in calc_pure_y_common.");
548 return common->self_scm ();
552 Axis_group_interface::calc_common (Grob *me, Axis axis)
554 extract_grob_set (me, "elements", elts);
555 Grob *common = common_refpoint_of_array (elts, me, axis);
558 me->programming_error ("No common parent found in calc_common axis.");
562 return common->self_scm ();
565 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_x_common, 1);
567 Axis_group_interface::calc_x_common (SCM grob)
569 return calc_common (unsmob_grob (grob), X_AXIS);
572 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_y_common, 1);
574 Axis_group_interface::calc_y_common (SCM grob)
576 return calc_common (unsmob_grob (grob), Y_AXIS);
580 Axis_group_interface::pure_group_height (Grob *me, int start, int end)
582 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
586 programming_error ("no pure Y common refpoint");
589 Real my_coord = me->pure_relative_y_coordinate (common, start, end);
590 Interval r (relative_pure_height (me, start, end));
596 Axis_group_interface::get_children (Grob *me, vector<Grob *> *found)
598 found->push_back (me);
600 if (!has_interface (me))
603 extract_grob_set (me, "elements", elements);
604 for (vsize i = 0; i < elements.size (); i++)
606 Grob *e = elements[i];
607 Axis_group_interface::get_children (e, found);
612 staff_priority_less (Grob *const &g1, Grob *const &g2)
614 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
615 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
617 if (priority_1 < priority_2)
619 else if (priority_1 > priority_2)
622 /* if neither grob has an outside-staff priority, the ordering will have no
623 effect -- we just need to choose a consistent ordering. We do this to
624 avoid the side-effect of calculating extents. */
625 if (isinf (priority_1))
628 /* if there is no preference in staff priority, choose the left-most one */
629 Grob *common = g1->common_refpoint (g2, X_AXIS);
630 Real start_1 = g1->extent (common, X_AXIS)[LEFT];
631 Real start_2 = g2->extent (common, X_AXIS)[LEFT];
632 return start_1 < start_2;
636 pure_staff_priority_less (Grob *const &g1, Grob *const &g2)
638 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
639 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
641 return priority_1 < priority_2;
645 add_interior_skylines (Grob *me, Grob *x_common, Grob *y_common, vector<Skyline_pair> *skylines)
647 if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements")))
649 for (vsize i = 0; i < elements->size (); i++)
650 add_interior_skylines (elements->grob (i), x_common, y_common, skylines);
652 else if (!scm_is_number (me->get_property ("outside-staff-priority"))
653 && !to_boolean (me->get_property ("cross-staff")))
655 Skyline_pair *maybe_pair = Skyline_pair::unsmob (me->get_property ("vertical-skylines"));
658 if (maybe_pair->is_empty ())
660 skylines->push_back (Skyline_pair (*maybe_pair));
661 skylines->back ().shift (me->relative_coordinate (x_common, X_AXIS));
662 skylines->back ().raise (me->relative_coordinate (y_common, Y_AXIS));
666 // Raises the grob elt (whose skylines are given by h_skyline
667 // and v_skyline) so that it doesn't intersect with staff_skyline,
668 // or with anything in other_h_skylines and other_v_skylines.
670 avoid_outside_staff_collisions (Grob *elt,
671 Skyline_pair *v_skyline,
673 Real horizon_padding,
674 vector<Skyline_pair> const &other_v_skylines,
675 vector<Real> const &other_padding,
676 vector<Real> const &other_horizon_padding,
679 assert (other_v_skylines.size () == other_padding.size ());
680 assert (other_v_skylines.size () == other_horizon_padding.size ());
681 vector<Interval> forbidden_intervals;
682 for (vsize j = 0; j < other_v_skylines.size (); j++)
684 Skyline_pair const &v_other = other_v_skylines[j];
685 Real pad = max (padding, other_padding[j]);
686 Real horizon_pad = max (horizon_padding, other_horizon_padding[j]);
688 // We need to push elt up by at least this much to be above v_other.
689 Real up = (*v_skyline)[DOWN].distance (v_other[UP], horizon_pad) + pad;
690 // We need to push elt down by at least this much to be below v_other.
691 Real down = (*v_skyline)[UP].distance (v_other[DOWN], horizon_pad) + pad;
693 forbidden_intervals.push_back (Interval (-down, up));
696 Interval_set allowed_shifts
697 = Interval_set::interval_union (forbidden_intervals).complement ();
698 Real move = allowed_shifts.nearest_point (0, dir);
699 v_skyline->raise (move);
700 elt->translate_axis (move, Y_AXIS);
704 valid_outside_staff_placement_directive (Grob *me)
706 SCM directive = me->get_property ("outside-staff-placement-directive");
708 if ((directive == ly_symbol2scm ("left-to-right-greedy"))
709 || (directive == ly_symbol2scm ("left-to-right-polite"))
710 || (directive == ly_symbol2scm ("right-to-left-greedy"))
711 || (directive == ly_symbol2scm ("right-to-left-polite")))
714 me->warning (_f ("\"%s\" is not a valid outside-staff-placement-directive",
715 robust_symbol2string (directive, "").c_str ()));
717 return ly_symbol2scm ("left-to-right-polite");
720 // Shifts the grobs in elements to ensure that they (and any
721 // connected riders) don't collide with the staff skylines
722 // or anything in all_X_skylines. Afterwards, the skylines
723 // of the grobs in elements will be added to all_v_skylines.
725 add_grobs_of_one_priority (Grob *me,
726 Drul_array<vector<Skyline_pair> > *all_v_skylines,
727 Drul_array<vector<Real> > *all_paddings,
728 Drul_array<vector<Real> > *all_horizon_paddings,
729 vector<Grob *> elements,
732 multimap<Grob *, Grob *> const &riders)
736 = valid_outside_staff_placement_directive (me);
738 bool l2r = ((directive == ly_symbol2scm ("left-to-right-greedy"))
739 || (directive == ly_symbol2scm ("left-to-right-polite")));
741 bool polite = ((directive == ly_symbol2scm ("left-to-right-polite"))
742 || (directive == ly_symbol2scm ("right-to-left-polite")));
745 vector<Skyline_pair> skylines_to_merge;
747 // We want to avoid situations like this:
751 // -------------------
753 // -------------------
755 // The point is that "still more text" should be positioned under
756 // "more text". In order to achieve this, we place the grobs in several
757 // passes. We keep track of the right-most horizontal position that has been
758 // affected by the current pass so far (actually we keep track of 2
759 // positions, one for above the staff, one for below).
761 // In each pass, we loop through the unplaced grobs from left to right.
762 // If the grob doesn't overlap the right-most affected position, we place it
763 // (and then update the right-most affected position to point to the right
764 // edge of the just-placed grob). Otherwise, we skip it until the next pass.
765 while (!elements.empty ())
767 Drul_array<Real> last_end (-infinity_f, -infinity_f);
768 vector<Grob *> skipped_elements;
769 for (vsize i = l2r ? 0 : elements.size ();
770 l2r ? i < elements.size () : i--;
773 Grob *elt = elements[i];
775 = robust_scm2double (elt->get_property ("outside-staff-padding"),
777 ::get_default_outside_staff_padding ());
779 = robust_scm2double (elt->get_property ("outside-staff-horizontal-padding"), 0.0);
780 Interval x_extent = elt->extent (x_common, X_AXIS);
781 x_extent.widen (horizon_padding);
783 Direction dir = get_grob_direction (elt);
786 warning (_ ("an outside-staff object should have a direction, defaulting to up"));
790 if (x_extent[LEFT] <= last_end[dir] && polite)
792 skipped_elements.push_back (elt);
795 last_end[dir] = x_extent[RIGHT];
797 Skyline_pair *v_orig = Skyline_pair::unsmob (elt->get_property ("vertical-skylines"));
798 if (v_orig->is_empty ())
801 // Find the riders associated with this grob, and merge their
802 // skylines with elt's skyline.
803 typedef multimap<Grob *, Grob *>::const_iterator GrobMapIterator;
804 pair<GrobMapIterator, GrobMapIterator> range = riders.equal_range (elt);
805 vector<Skyline_pair> rider_v_skylines;
806 for (GrobMapIterator j = range.first; j != range.second; j++)
808 Grob *rider = j->second;
809 Skyline_pair *v_rider = Skyline_pair::unsmob (rider->get_property ("vertical-skylines"));
812 Skyline_pair copy (*v_rider);
813 copy.shift (rider->relative_coordinate (x_common, X_AXIS));
814 copy.raise (rider->relative_coordinate (y_common, Y_AXIS));
815 rider_v_skylines.push_back (copy);
818 Skyline_pair v_skylines (*v_orig);
819 v_skylines.shift (elt->relative_coordinate (x_common, X_AXIS));
820 v_skylines.raise (elt->relative_coordinate (y_common, Y_AXIS));
821 v_skylines.merge (Skyline_pair (rider_v_skylines));
823 avoid_outside_staff_collisions (elt,
827 (*all_v_skylines)[dir],
828 (*all_paddings)[dir],
829 (*all_horizon_paddings)[dir],
832 elt->set_property ("outside-staff-priority", SCM_BOOL_F);
833 (*all_v_skylines)[dir].push_back (v_skylines);
834 (*all_paddings)[dir].push_back (padding);
835 (*all_horizon_paddings)[dir].push_back (horizon_padding);
837 swap (elements, skipped_elements);
838 skipped_elements.clear ();
842 // If the Grob has a Y-ancestor with outside-staff-priority, return it.
843 // Otherwise, return 0.
845 Axis_group_interface::outside_staff_ancestor (Grob *me)
847 Grob *parent = me->get_parent (Y_AXIS);
851 if (scm_is_number (parent->get_property ("outside-staff-priority")))
854 return outside_staff_ancestor (parent);
857 // It is tricky to correctly handle skyline placement of cross-staff grobs.
858 // For example, cross-staff beams cannot be formatted until the distance between
859 // staves is known and therefore any grobs that depend on the beam cannot be placed
860 // until the skylines are known. On the other hand, the distance between staves should
861 // really depend on position of the cross-staff grobs that lie between them.
862 // Currently, we just leave cross-staff grobs out of the
863 // skyline altogether, but this could mean that staves are placed so close together
864 // that there is no room for the cross-staff grob. It also means, of course, that
865 // we don't get the benefits of skyline placement for cross-staff grobs.
867 Axis_group_interface::skyline_spacing (Grob *me)
869 extract_grob_set (me, Grob_array::unsmob (me->get_object ("vertical-skyline-elements")) ? "vertical-skyline-elements" : "elements", fakeelements);
870 vector<Grob *> elements (fakeelements);
871 for (vsize i = 0; i < elements.size (); i++)
873 As a sanity check, we make sure that no grob with an outside staff priority
874 has a Y-parent that also has an outside staff priority, which would result
877 if (scm_is_number (elements[i]->get_property ("outside-staff-priority"))
878 && outside_staff_ancestor (elements[i]))
880 elements[i]->warning ("Cannot set outside-staff-priority for element and elements' Y parent.");
881 elements[i]->set_property ("outside-staff-priority", SCM_BOOL_F);
884 /* For grobs with an outside-staff-priority, the sorting function might
885 call extent and cause suicide. This breaks the contract that is required
886 for the STL sort function. To avoid this, we make sure that any suicides
887 are triggered beforehand.
889 for (vsize i = 0; i < elements.size (); i++)
890 if (scm_is_number (elements[i]->get_property ("outside-staff-priority")))
891 elements[i]->extent (elements[i], X_AXIS);
893 vector_sort (elements, staff_priority_less);
894 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
895 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
899 me->programming_error("Some of my vertical-skyline-elements"
900 " are outside my VerticalAxisGroup.");
904 // A rider is a grob that is not outside-staff, but has an outside-staff
905 // ancestor. In that case, the rider gets moved along with its ancestor.
906 multimap<Grob *, Grob *> riders;
909 vector<Skyline_pair> inside_staff_skylines;
911 for (i = 0; i < elements.size ()
912 && !scm_is_number (elements[i]->get_property ("outside-staff-priority")); i++)
914 Grob *elt = elements[i];
915 Grob *ancestor = outside_staff_ancestor (elt);
916 if (!(to_boolean (elt->get_property ("cross-staff")) || ancestor))
917 add_interior_skylines (elt, x_common, y_common, &inside_staff_skylines);
919 riders.insert (pair<Grob *, Grob *> (ancestor, elt));
922 Skyline_pair skylines (inside_staff_skylines);
924 // These are the skylines of all outside-staff grobs
925 // that have already been processed. We keep them around in order to
926 // check them for collisions with the currently active outside-staff grob.
927 Drul_array<vector<Skyline_pair> > all_v_skylines;
928 Drul_array<vector<Real> > all_paddings;
929 Drul_array<vector<Real> > all_horizon_paddings;
930 for (UP_and_DOWN (d))
932 all_v_skylines[d].push_back (skylines);
933 all_paddings[d].push_back (0);
934 all_horizon_paddings[d].push_back (0);
937 for (; i < elements.size (); i++)
939 if (to_boolean (elements[i]->get_property ("cross-staff")))
942 // Collect all the outside-staff grobs that have a particular priority.
943 SCM priority = elements[i]->get_property ("outside-staff-priority");
944 vector<Grob *> current_elts;
945 current_elts.push_back (elements[i]);
946 while (i + 1 < elements.size ()
947 && scm_is_eq (elements[i + 1]->get_property ("outside-staff-priority"), priority))
949 if (!to_boolean (elements[i + 1]->get_property ("cross-staff")))
950 current_elts.push_back (elements[i + 1]);
954 add_grobs_of_one_priority (me,
957 &all_horizon_paddings,
964 // Now everything in all_v_skylines has been shifted appropriately; merge
965 // them all into skylines to get the complete outline.
966 Skyline_pair other_skylines (all_v_skylines[UP]);
967 other_skylines.merge (Skyline_pair (all_v_skylines[DOWN]));
968 skylines.merge (other_skylines);
970 // We began by shifting my skyline to be relative to the common refpoint; now
972 skylines.shift (-me->relative_coordinate (x_common, X_AXIS));
977 MAKE_SCHEME_CALLBACK (Axis_group_interface, print, 1)
979 Axis_group_interface::print (SCM smob)
984 Grob *me = unsmob_grob (smob);
986 if (Skyline_pair *s = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
988 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[UP].to_points (X_AXIS))
989 .in_color (255, 0, 255));
990 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[DOWN].to_points (X_AXIS))
991 .in_color (0, 255, 255));
993 return ret.smobbed_copy ();
996 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_staff_staff_spacing, 3)
998 Axis_group_interface::calc_pure_staff_staff_spacing (SCM smob, SCM start, SCM end)
1000 return calc_maybe_pure_staff_staff_spacing (unsmob_grob (smob),
1006 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_staff_staff_spacing, 1)
1008 Axis_group_interface::calc_staff_staff_spacing (SCM smob)
1010 return calc_maybe_pure_staff_staff_spacing (unsmob_grob (smob),
1017 Axis_group_interface::calc_maybe_pure_staff_staff_spacing (Grob *me, bool pure, int start, int end)
1019 Grob *grouper = unsmob_grob (me->get_object ("staff-grouper"));
1023 bool within_group = Staff_grouper_interface::maybe_pure_within_group (grouper, me, pure, start, end);
1025 return grouper->get_maybe_pure_property ("staff-staff-spacing", pure, start, end);
1027 return grouper->get_maybe_pure_property ("staffgroup-staff-spacing", pure, start, end);
1029 return me->get_maybe_pure_property ("default-staff-staff-spacing", pure, start, end);
1032 ADD_INTERFACE (Axis_group_interface,
1033 "An object that groups other layout objects.",
1035 // TODO: some of these properties are specific to
1036 // VerticalAxisGroup. We should split off a
1037 // vertical-axis-group-interface.
1039 "adjacent-pure-heights "
1041 "bound-alignment-interfaces "
1042 "default-staff-staff-spacing "
1046 "nonstaff-nonstaff-spacing "
1047 "nonstaff-relatedstaff-spacing "
1048 "nonstaff-unrelatedstaff-spacing "
1049 "outside-staff-placement-directive "
1050 "pure-relevant-grobs "
1051 "pure-relevant-items "
1052 "pure-relevant-spanners "
1056 "staff-staff-spacing "
1058 "vertical-skyline-elements "