2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2000--2011 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"
22 #include "align-interface.hh"
23 #include "directional-element-interface.hh"
24 #include "grob-array.hh"
25 #include "hara-kiri-group-spanner.hh"
26 #include "international.hh"
28 #include "paper-column.hh"
29 #include "paper-score.hh"
30 #include "pointer-group-interface.hh"
31 #include "separation-item.hh"
32 #include "skyline-pair.hh"
33 #include "staff-grouper-interface.hh"
37 #include "unpure-pure-container.hh"
40 pure_staff_priority_less (Grob *const &g1, Grob *const &g2);
43 Axis_group_interface::add_element (Grob *me, Grob *e)
45 SCM axes = me->get_property ("axes");
46 if (!scm_is_pair (axes))
47 programming_error ("axes should be nonempty");
49 for (SCM ax = axes; scm_is_pair (ax); ax = scm_cdr (ax))
51 Axis a = (Axis) scm_to_int (scm_car (ax));
53 if (!e->get_parent (a))
54 e->set_parent (me, a);
56 e->set_object ((a == X_AXIS)
57 ? ly_symbol2scm ("axis-group-parent-X")
58 : ly_symbol2scm ("axis-group-parent-Y"),
62 /* must be ordered, because Align_interface also uses
63 Axis_group_interface */
64 Pointer_group_interface::add_grob (me, ly_symbol2scm ("elements"), e);
68 Axis_group_interface::has_axis (Grob *me, Axis a)
70 SCM axes = me->get_property ("axes");
72 return (SCM_BOOL_F != scm_memq (scm_from_int (a), axes));
76 Axis_group_interface::relative_group_extent (vector<Grob *> const &elts,
79 return relative_maybe_bound_group_extent (elts, common, a, false);
83 Axis_group_interface::relative_maybe_bound_group_extent (vector<Grob *> const &elts,
84 Grob *common, Axis a, bool bound)
87 for (vsize i = 0; i < elts.size (); i++)
90 if (!to_boolean (se->get_property ("cross-staff")))
92 Interval dims = (bound && has_interface (se)
93 ? generic_bound_extent (se, common, a)
94 : se->extent (common, a));
95 if (!dims.is_empty ())
103 Axis_group_interface::generic_bound_extent (Grob *me, Grob *common, Axis a)
105 /* trigger the callback to do skyline-spacing on the children */
107 (void) me->get_property ("vertical-skylines");
109 extract_grob_set (me, "elements", elts);
110 vector<Grob *> new_elts;
112 SCM interfaces = me->get_property ("bound-alignment-interfaces");
114 for (vsize i = 0; i < elts.size (); i++)
115 for (SCM l = interfaces; scm_is_pair (l); l = scm_cdr (l))
116 if (elts[i]->internal_has_interface (scm_car (l)))
117 new_elts.push_back (elts[i]);
119 if (!new_elts.size ())
120 return robust_relative_extent (me, common, a);
123 common = common_refpoint_of_array (new_elts, me, a);
125 return relative_maybe_bound_group_extent (new_elts, common, a, true);
129 Axis_group_interface::sum_partial_pure_heights (Grob *me, int start, int end)
131 Interval iv = begin_of_line_pure_height (me, start);
132 iv.unite (rest_of_line_pure_height (me, start, end));
138 Axis_group_interface::part_of_line_pure_height (Grob *me, bool begin, int start, int end)
140 Spanner *sp = dynamic_cast<Spanner *> (me);
141 SCM cache_symbol = begin
142 ? ly_symbol2scm ("begin-of-line-pure-height")
143 : ly_symbol2scm ("rest-of-line-pure-height");
144 SCM cached = sp->get_cached_pure_property (cache_symbol, start, end);
145 if (scm_is_pair (cached))
146 return robust_scm2interval (cached, Interval (0, 0));
148 SCM adjacent_pure_heights = me->get_property ("adjacent-pure-heights");
151 if (!scm_is_pair (adjacent_pure_heights))
152 ret = Interval (0, 0);
155 SCM these_pure_heights = begin
156 ? scm_car (adjacent_pure_heights)
157 : scm_cdr (adjacent_pure_heights);
159 if (scm_is_vector (these_pure_heights))
160 ret = combine_pure_heights (me, these_pure_heights, start, end);
162 ret = Interval (0, 0);
165 sp->cache_pure_property (cache_symbol, start, end, ly_interval2scm (ret));
170 Axis_group_interface::begin_of_line_pure_height (Grob *me, int start)
172 return part_of_line_pure_height (me, true, start, start + 1);
176 Axis_group_interface::rest_of_line_pure_height (Grob *me, int start, int end)
178 return part_of_line_pure_height (me, false, start, end);
182 Axis_group_interface::combine_pure_heights (Grob *me, SCM measure_extents, int start, int end)
184 Paper_score *ps = get_root_system (me)->paper_score ();
185 vector<vsize> breaks = ps->get_break_indices ();
186 vector<Grob *> cols = ps->get_columns ();
189 for (vsize i = 0; i + 1 < breaks.size (); i++)
191 int r = Paper_column::get_rank (cols[breaks[i]]);
196 ext.unite (ly_scm2interval (scm_c_vector_ref (measure_extents, i)));
202 // adjacent-pure-heights is a pair of vectors, each of which has one element
203 // for every measure in the score. The first vector stores, for each measure,
204 // the combined height of the elements that are present only when the bar
205 // is at the beginning of a line. The second vector stores, for each measure,
206 // the combined height of the elements that are present only when the bar
207 // is not at the beginning of a line.
208 MAKE_SCHEME_CALLBACK (Axis_group_interface, adjacent_pure_heights, 1)
210 Axis_group_interface::adjacent_pure_heights (SCM smob)
212 Grob *me = unsmob_grob (smob);
214 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
215 extract_grob_set (me, "pure-relevant-grobs", elts);
217 Paper_score *ps = get_root_system (me)->paper_score ();
218 vector<vsize> ranks = ps->get_break_ranks ();
220 vector<Interval> begin_line_heights;
221 vector<Interval> mid_line_heights;
222 vector<Interval> begin_line_staff_heights;
223 vector<Interval> mid_line_staff_heights;
224 begin_line_heights.resize (ranks.size () - 1);
225 mid_line_heights.resize (ranks.size () - 1);
227 for (vsize i = 0; i < elts.size (); ++i)
231 if (to_boolean (g->get_property ("cross-staff")))
234 bool outside_staff = scm_is_number (g->get_property ("outside-staff-priority"));
235 Real padding = robust_scm2double (g->get_property ("outside-staff-padding"), 0.5);
237 // When we encounter the first outside-staff grob, make a copy
238 // of the current heights to use as an estimate for the staff heights.
239 // Note that the outside-staff approximation that we use here doesn't
240 // consider any collisions that might occur between outside-staff grobs,
241 // but only the fact that outside-staff grobs may need to be raised above
243 if (outside_staff && begin_line_staff_heights.empty ())
245 begin_line_staff_heights = begin_line_heights;
246 mid_line_staff_heights = mid_line_heights;
249 // TODO: consider a pure version of get_grob_direction?
250 Direction d = to_dir (g->get_property_data ("direction"));
251 d = (d == CENTER) ? UP : d;
253 Interval_t<int> rank_span = g->spanned_rank_interval ();
254 vsize first_break = lower_bound (ranks, (vsize)rank_span[LEFT], less<vsize> ());
255 if (first_break > 0 && ranks[first_break] >= (vsize)rank_span[LEFT])
258 for (vsize j = first_break; j + 1 < ranks.size () && (int)ranks[j] <= rank_span[RIGHT]; ++j)
260 int start = ranks[j];
261 int end = ranks[j + 1];
263 // Take grobs that are visible with respect to a slightly longer line.
264 // Otherwise, we will never include grobs at breakpoints which aren't
265 // end-of-line-visible.
266 int visibility_end = j + 2 < ranks.size () ? ranks[j + 2] : end;
268 if (g->pure_is_visible (start, visibility_end))
270 Interval dims = g->pure_height (common, start, end);
271 if (!dims.is_empty ())
273 if (rank_span[LEFT] <= start)
276 begin_line_heights[j].unite (begin_line_staff_heights[j].union_disjoint (dims, padding, d));
278 begin_line_heights[j].unite (dims);
280 if (rank_span[RIGHT] > start)
283 mid_line_heights[j].unite (mid_line_staff_heights[j].union_disjoint (dims, padding, d));
285 mid_line_heights[j].unite (dims);
292 // Convert begin_line_heights and min_line_heights to SCM.
293 SCM begin_scm = scm_c_make_vector (ranks.size () - 1, SCM_EOL);
294 SCM mid_scm = scm_c_make_vector (ranks.size () - 1, SCM_EOL);
295 for (vsize i = 0; i < begin_line_heights.size (); ++i)
297 scm_vector_set_x (begin_scm, scm_from_int (i), ly_interval2scm (begin_line_heights[i]));
298 scm_vector_set_x (mid_scm, scm_from_int (i), ly_interval2scm (mid_line_heights[i]));
301 return scm_cons (begin_scm, mid_scm);
305 Axis_group_interface::relative_pure_height (Grob *me, int start, int end)
307 /* It saves a _lot_ of time if we assume a VerticalAxisGroup is additive
308 (ie. height (i, k) = max (height (i, j) height (j, k)) for all i <= j <= k).
309 Unfortunately, it isn't always true, particularly if there is a
310 VerticalAlignment somewhere in the descendants.
312 Usually, the only VerticalAlignment comes from Score. This makes it
313 reasonably safe to assume that if our parent is a VerticalAlignment,
314 we can assume additivity and cache things nicely. */
315 Grob *p = me->get_parent (Y_AXIS);
316 if (p && Align_interface::has_interface (p))
317 return Axis_group_interface::sum_partial_pure_heights (me, start, end);
319 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
320 extract_grob_set (me, "pure-relevant-grobs", elts);
323 for (vsize i = 0; i < elts.size (); i++)
326 Interval_t<int> rank_span = g->spanned_rank_interval ();
327 if (rank_span[LEFT] <= end && rank_span[RIGHT] >= start
328 && g->pure_is_visible (start, end)
329 && !to_boolean (g->get_property ("cross-staff")))
331 Interval dims = g->pure_height (common, start, end);
332 if (!dims.is_empty ())
339 MAKE_SCHEME_CALLBACK (Axis_group_interface, width, 1);
341 Axis_group_interface::width (SCM smob)
343 Grob *me = unsmob_grob (smob);
344 return generic_group_extent (me, X_AXIS);
347 MAKE_SCHEME_CALLBACK (Axis_group_interface, height, 1);
349 Axis_group_interface::height (SCM smob)
351 Grob *me = unsmob_grob (smob);
352 return generic_group_extent (me, Y_AXIS);
355 MAKE_SCHEME_CALLBACK (Axis_group_interface, pure_height, 3);
357 Axis_group_interface::pure_height (SCM smob, SCM start_scm, SCM end_scm)
359 int start = robust_scm2int (start_scm, 0);
360 int end = robust_scm2int (end_scm, INT_MAX);
361 Grob *me = unsmob_grob (smob);
363 /* Maybe we are in the second pass of a two-pass spacing run. In that
364 case, the Y-extent of a system is already given to us */
365 System *system = dynamic_cast<System *> (me);
368 SCM line_break_details = system->column (start)->get_property ("line-break-system-details");
369 SCM system_y_extent = scm_assq (ly_symbol2scm ("system-Y-extent"), line_break_details);
370 if (scm_is_pair (system_y_extent))
371 return scm_cdr (system_y_extent);
374 return ly_interval2scm (pure_group_height (me, start, end));
377 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_skylines, 1);
379 Axis_group_interface::calc_skylines (SCM smob)
381 Grob *me = unsmob_grob (smob);
382 extract_grob_set (me, "elements", elts);
383 Skyline_pair skylines = skyline_spacing (me, elts);
385 return skylines.smobbed_copy ();
388 /* whereas calc_skylines calculates skylines for axis-groups with a lot of
389 visible children, combine_skylines is designed for axis-groups whose only
390 children are other axis-groups (ie. VerticalAlignment). Rather than
391 calculating all the skylines from scratch, we just merge the skylines
394 MAKE_SCHEME_CALLBACK (Axis_group_interface, combine_skylines, 1);
396 Axis_group_interface::combine_skylines (SCM smob)
398 Grob *me = unsmob_grob (smob);
399 extract_grob_set (me, "elements", elements);
400 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
401 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
404 programming_error ("combining skylines that don't belong to me");
407 for (vsize i = 0; i < elements.size (); i++)
409 SCM skyline_scm = elements[i]->get_property ("vertical-skylines");
410 if (Skyline_pair::unsmob (skyline_scm))
412 Real offset = elements[i]->relative_coordinate (y_common, Y_AXIS);
413 Skyline_pair other = *Skyline_pair::unsmob (skyline_scm);
414 other.raise (offset);
415 other.shift (elements[i]->relative_coordinate (x_common, X_AXIS));
419 return ret.smobbed_copy ();
423 Axis_group_interface::generic_group_extent (Grob *me, Axis a)
425 /* trigger the callback to do skyline-spacing on the children */
427 (void) me->get_property ("vertical-skylines");
429 extract_grob_set (me, "elements", elts);
430 Grob *common = common_refpoint_of_array (elts, me, a);
432 Real my_coord = me->relative_coordinate (common, a);
433 Interval r (relative_group_extent (elts, common, a));
435 return ly_interval2scm (r - my_coord);
438 /* This is like generic_group_extent, but it only counts the grobs that
439 are children of some other axis-group. This is uncached; if it becomes
440 commonly used, it may be necessary to cache it somehow. */
442 Axis_group_interface::staff_extent (Grob *me, Grob *refp, Axis ext_a, Grob *staff, Axis parent_a)
444 extract_grob_set (me, "elements", elts);
445 vector<Grob *> new_elts;
447 for (vsize i = 0; i < elts.size (); i++)
448 if (elts[i]->common_refpoint (staff, parent_a) == staff)
449 new_elts.push_back (elts[i]);
451 return relative_group_extent (new_elts, refp, ext_a);
454 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_relevant_grobs, 1);
456 Axis_group_interface::calc_pure_relevant_grobs (SCM smob)
458 Grob *me = unsmob_grob (smob);
459 return internal_calc_pure_relevant_grobs (me, "elements");
463 Axis_group_interface::internal_calc_pure_relevant_grobs (Grob *me, string grob_set_name)
465 extract_grob_set (me, grob_set_name.c_str (), elts);
467 vector<Grob *> relevant_grobs;
468 SCM pure_relevant_p = ly_lily_module_constant ("pure-relevant?");
470 for (vsize i = 0; i < elts.size (); i++)
472 if (to_boolean (scm_apply_1 (pure_relevant_p, elts[i]->self_scm (), SCM_EOL)))
473 relevant_grobs.push_back (elts[i]);
475 if (Item *it = dynamic_cast<Item *> (elts[i]))
480 Item *piece = it->find_prebroken_piece (d);
481 if (piece && to_boolean (scm_apply_1 (pure_relevant_p, piece->self_scm (), SCM_EOL)))
482 relevant_grobs.push_back (piece);
484 while (flip (&d) != LEFT);
488 vector_sort (relevant_grobs, pure_staff_priority_less);
489 SCM grobs_scm = Grob_array::make_array ();
490 unsmob_grob_array (grobs_scm)->set_array (relevant_grobs);
495 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_y_common, 1);
497 Axis_group_interface::calc_pure_y_common (SCM smob)
499 Grob *me = unsmob_grob (smob);
501 extract_grob_set (me, "pure-relevant-grobs", elts);
502 Grob *common = common_refpoint_of_array (elts, me, Y_AXIS);
505 me->programming_error ("No common parent found in calc_pure_y_common.");
509 return common->self_scm ();
513 Axis_group_interface::calc_common (Grob *me, Axis axis)
515 extract_grob_set (me, "elements", elts);
516 Grob *common = common_refpoint_of_array (elts, me, axis);
519 me->programming_error ("No common parent found in calc_common axis.");
523 return common->self_scm ();
526 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_x_common, 1);
528 Axis_group_interface::calc_x_common (SCM grob)
530 return calc_common (unsmob_grob (grob), X_AXIS);
533 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_y_common, 1);
535 Axis_group_interface::calc_y_common (SCM grob)
537 return calc_common (unsmob_grob (grob), Y_AXIS);
541 Axis_group_interface::pure_group_height (Grob *me, int start, int end)
543 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
547 programming_error ("no pure Y common refpoint");
550 Real my_coord = me->relative_coordinate (common, Y_AXIS);
551 Interval r (relative_pure_height (me, start, end));
557 Axis_group_interface::get_children (Grob *me, vector<Grob *> *found)
559 found->push_back (me);
561 if (!has_interface (me))
564 extract_grob_set (me, "elements", elements);
565 for (vsize i = 0; i < elements.size (); i++)
567 Grob *e = elements[i];
568 Axis_group_interface::get_children (e, found);
573 staff_priority_less (Grob *const &g1, Grob *const &g2)
575 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
576 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
578 if (priority_1 < priority_2)
580 else if (priority_1 > priority_2)
583 /* if neither grob has an outside-staff priority, the ordering will have no
584 effect -- we just need to choose a consistent ordering. We do this to
585 avoid the side-effect of calculating extents. */
586 if (isinf (priority_1))
589 /* if there is no preference in staff priority, choose the left-most one */
590 Grob *common = g1->common_refpoint (g2, X_AXIS);
591 Real start_1 = g1->extent (common, X_AXIS)[LEFT];
592 Real start_2 = g2->extent (common, X_AXIS)[LEFT];
593 return start_1 < start_2;
597 pure_staff_priority_less (Grob *const &g1, Grob *const &g2)
599 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
600 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
602 return priority_1 < priority_2;
606 add_boxes (Grob *me, Grob *x_common, Grob *y_common, vector<Box> *const boxes, Skyline_pair *skylines)
608 /* if a child has skylines, use them instead of the extent box */
609 if (Skyline_pair *pair = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
611 Skyline_pair s = *pair;
612 s.shift (me->relative_coordinate (x_common, X_AXIS));
613 s.raise (me->relative_coordinate (y_common, Y_AXIS));
616 else if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements")))
618 for (vsize i = 0; i < elements->size (); i++)
619 add_boxes (elements->grob (i), x_common, y_common, boxes, skylines);
621 else if (!scm_is_number (me->get_property ("outside-staff-priority"))
622 && !to_boolean (me->get_property ("cross-staff")))
624 boxes->push_back (Box (me->extent (x_common, X_AXIS),
625 me->extent (y_common, Y_AXIS)));
629 /* We want to avoid situations like this:
637 The point is that "still more text" should be positioned under
638 "more text". In order to achieve this, we place the grobs in several
639 passes. We keep track of the right-most horizontal position that has been
640 affected by the current pass so far (actually we keep track of 2
641 positions, one for above the staff, one for below).
643 In each pass, we loop through the unplaced grobs from left to right.
644 If the grob doesn't overlap the right-most affected position, we place it
645 (and then update the right-most affected position to point to the right
646 edge of the just-placed grob). Otherwise, we skip it until the next pass.
649 add_grobs_of_one_priority (Skyline_pair *const skylines,
650 vector<Grob *> elements,
655 Drul_array<Real> last_affected_position;
658 while (!elements.empty ())
660 last_affected_position[UP] = -infinity_f;
661 last_affected_position[DOWN] = -infinity_f;
663 for (vsize i = elements.size (); i--;)
665 Direction dir = get_grob_direction (elements[i]);
668 warning (_ ("an outside-staff object should have a direction, defaulting to up"));
672 Box b (elements[i]->extent (x_common, X_AXIS),
673 elements[i]->extent (y_common, Y_AXIS));
674 SCM horizon_padding_scm = elements[i]->get_property ("outside-staff-horizontal-padding");
675 Real horizon_padding = robust_scm2double (horizon_padding_scm, 0.0);
677 if (b[X_AXIS][LEFT] - 2 * horizon_padding < last_affected_position[dir])
680 if (!b[X_AXIS].is_empty () && !b[Y_AXIS].is_empty ())
684 Skyline other = Skyline (boxes, horizon_padding, X_AXIS, -dir);
685 Real padding = robust_scm2double (elements[i]->get_property ("outside-staff-padding"), 0.5);
686 Real dist = (*skylines)[dir].distance (other) + padding;
690 b.translate (Offset (0, dir * dist));
691 elements[i]->translate_axis (dir * dist, Y_AXIS);
693 skylines->insert (b, 0, X_AXIS);
694 elements[i]->set_property ("outside-staff-priority", SCM_BOOL_F);
695 last_affected_position[dir] = b[X_AXIS][RIGHT];
699 Ugh: quadratic. --hwn
701 elements.erase (elements.begin () + i);
707 Axis_group_interface::has_outside_staff_parent (Grob *me)
710 ? (scm_is_number (me->get_property ("outside-staff-priority"))
711 || has_outside_staff_parent (me->get_parent (Y_AXIS)))
715 // TODO: it is tricky to correctly handle skyline placement of cross-staff grobs.
716 // For example, cross-staff beams cannot be formatted until the distance between
717 // staves is known and therefore any grobs that depend on the beam cannot be placed
718 // until the skylines are known. On the other hand, the distance between staves should
719 // really depend on position of the cross-staff grobs that lie between them.
720 // Currently, we just leave cross-staff grobs out of the
721 // skyline altogether, but this could mean that staves are placed so close together
722 // that there is no room for the cross-staff grob. It also means, of course, that
723 // we don't get the benefits of skyline placement for cross-staff grobs.
725 Axis_group_interface::skyline_spacing (Grob *me, vector<Grob *> elements)
727 /* For grobs with an outside-staff-priority, the sorting function might
728 call extent and cause suicide. This breaks the contract that is required
729 for the STL sort function. To avoid this, we make sure that any suicides
730 are triggered beforehand.
732 for (vsize i = 0; i < elements.size (); i++)
733 if (scm_is_number (elements[i]->get_property ("outside-staff-priority")))
734 elements[i]->extent (elements[i], X_AXIS);
736 vector_sort (elements, staff_priority_less);
737 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
738 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
740 assert (y_common == me);
745 Skyline_pair skylines;
746 for (i = 0; i < elements.size ()
747 && !scm_is_number (elements[i]->get_property ("outside-staff-priority")); i++)
748 if (!(to_boolean (elements[i]->get_property ("cross-staff")) || has_outside_staff_parent (elements[i])))
749 add_boxes (elements[i], x_common, y_common, &boxes, &skylines);
751 SCM padding_scm = me->get_property ("skyline-horizontal-padding");
752 Real padding = robust_scm2double (padding_scm, 0.1);
753 skylines.merge (Skyline_pair (boxes, padding, X_AXIS));
754 for (; i < elements.size (); i++)
756 if (to_boolean (elements[i]->get_property ("cross-staff")))
759 SCM priority = elements[i]->get_property ("outside-staff-priority");
760 vector<Grob *> current_elts;
761 current_elts.push_back (elements[i]);
762 while (i + 1 < elements.size ()
763 && scm_is_eq (elements[i + 1]->get_property ("outside-staff-priority"), priority))
765 if (!to_boolean (elements[i + 1]->get_property ("cross-staff")))
766 current_elts.push_back (elements[i + 1]);
770 add_grobs_of_one_priority (&skylines, current_elts, x_common, y_common);
772 skylines.shift (-me->relative_coordinate (x_common, X_AXIS));
776 MAKE_SCHEME_CALLBACK (Axis_group_interface, print, 1)
778 Axis_group_interface::print (SCM smob)
783 Grob *me = unsmob_grob (smob);
785 if (Skyline_pair *s = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
787 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[UP].to_points (X_AXIS))
788 .in_color (255, 0, 255));
789 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[DOWN].to_points (X_AXIS))
790 .in_color (0, 255, 255));
792 return ret.smobbed_copy ();
795 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_staff_staff_spacing, 3)
797 Axis_group_interface::calc_pure_staff_staff_spacing (SCM smob, SCM start, SCM end)
799 return calc_maybe_pure_staff_staff_spacing (unsmob_grob (smob),
805 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_staff_staff_spacing, 1)
807 Axis_group_interface::calc_staff_staff_spacing (SCM smob)
809 return calc_maybe_pure_staff_staff_spacing (unsmob_grob (smob),
816 Axis_group_interface::calc_maybe_pure_staff_staff_spacing (Grob *me, bool pure, int start, int end)
818 Grob *grouper = unsmob_grob (me->get_object ("staff-grouper"));
822 bool within_group = Staff_grouper_interface::maybe_pure_within_group (grouper, me, pure, start, end);
824 return grouper->get_maybe_pure_property ("staff-staff-spacing", pure, start, end);
826 return grouper->get_maybe_pure_property ("staffgroup-staff-spacing", pure, start, end);
828 return me->get_maybe_pure_property ("default-staff-staff-spacing", pure, start, end);
832 Axis_group_interface::minimum_distance (Grob *g1, Grob *g2, Axis a)
834 SCM sym = ly_symbol2scm ((a == Y_AXIS) ? "vertical-skylines" : "horizontal-skylines");
836 Skyline_pair *s1 = Skyline_pair::unsmob (g1->get_property (sym));
837 Skyline_pair *s2 = Skyline_pair::unsmob (g2->get_property (sym));
839 return (*s1)[DOWN].distance ((*s2)[UP]);
843 ADD_INTERFACE (Axis_group_interface,
844 "An object that groups other layout objects.",
846 // TODO: some of these properties are specific to
847 // VerticalAxisGroup. We should split off a
848 // vertical-axis-group-interface.
850 "adjacent-pure-heights "
852 "bound-alignment-interfaces "
853 "default-staff-staff-spacing "
857 "nonstaff-nonstaff-spacing "
858 "nonstaff-relatedstaff-spacing "
859 "nonstaff-unrelatedstaff-spacing "
860 "pure-relevant-grobs "
861 "pure-relevant-items "
862 "pure-relevant-spanners "
866 "staff-staff-spacing "