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"
39 pure_staff_priority_less (Grob *const &g1, Grob *const &g2);
42 Axis_group_interface::add_element (Grob *me, Grob *e)
44 SCM axes = me->get_property ("axes");
45 if (!scm_is_pair (axes))
46 programming_error ("axes should be nonempty");
48 for (SCM ax = axes; scm_is_pair (ax); ax = scm_cdr (ax))
50 Axis a = (Axis) scm_to_int (scm_car (ax));
52 if (!e->get_parent (a))
53 e->set_parent (me, a);
55 e->set_object ((a == X_AXIS)
56 ? ly_symbol2scm ("axis-group-parent-X")
57 : ly_symbol2scm ("axis-group-parent-Y"),
61 /* must be ordered, because Align_interface also uses
62 Axis_group_interface */
63 Pointer_group_interface::add_grob (me, ly_symbol2scm ("elements"), e);
67 Axis_group_interface::has_axis (Grob *me, Axis a)
69 SCM axes = me->get_property ("axes");
71 return (SCM_BOOL_F != scm_memq (scm_from_int (a), axes));
75 Axis_group_interface::relative_group_extent (vector<Grob *> const &elts,
78 return relative_maybe_bound_group_extent (elts, common, a, false);
82 Axis_group_interface::relative_maybe_bound_group_extent (vector<Grob *> const &elts,
83 Grob *common, Axis a, bool bound)
86 for (vsize i = 0; i < elts.size (); i++)
89 if (!to_boolean (se->get_property ("cross-staff")))
91 Interval dims = (bound && has_interface (se)
92 ? generic_bound_extent (se, common, a)
93 : se->extent (common, a));
94 if (!dims.is_empty ())
102 Axis_group_interface::generic_bound_extent (Grob *me, Grob *common, Axis a)
104 /* trigger the callback to do skyline-spacing on the children */
106 (void) me->get_property ("vertical-skylines");
108 extract_grob_set (me, "elements", elts);
109 vector<Grob *> new_elts;
111 SCM interfaces = me->get_property ("bound-alignment-interfaces");
113 for (vsize i = 0; i < elts.size (); i++)
114 for (SCM l = interfaces; scm_is_pair (l); l = scm_cdr (l))
115 if (elts[i]->internal_has_interface (scm_car (l)))
116 new_elts.push_back (elts[i]);
118 if (!new_elts.size ())
119 return robust_relative_extent (me, common, a);
122 common = common_refpoint_of_array (new_elts, me, a);
124 return relative_maybe_bound_group_extent (new_elts, common, a, true);
128 Axis_group_interface::sum_partial_pure_heights (Grob *me, int start, int end)
130 Interval iv = begin_of_line_pure_height (me, start);
131 iv.unite (rest_of_line_pure_height (me, start, end));
137 Axis_group_interface::part_of_line_pure_height (Grob *me, bool begin, int start, int end)
139 Spanner *sp = dynamic_cast<Spanner *> (me);
140 SCM cache_symbol = begin
141 ? ly_symbol2scm ("begin-of-line-pure-height")
142 : ly_symbol2scm ("rest-of-line-pure-height");
143 SCM cached = sp->get_cached_pure_property (cache_symbol, start, end);
144 if (scm_is_pair (cached))
145 return robust_scm2interval (cached, Interval (0, 0));
147 SCM adjacent_pure_heights = me->get_property ("adjacent-pure-heights");
150 if (!scm_is_pair (adjacent_pure_heights))
151 ret = Interval (0, 0);
154 SCM these_pure_heights = begin
155 ? scm_car (adjacent_pure_heights)
156 : scm_cdr (adjacent_pure_heights);
158 if (scm_is_vector (these_pure_heights))
159 ret = combine_pure_heights (me, these_pure_heights, start, end);
161 ret = Interval (0, 0);
164 sp->cache_pure_property (cache_symbol, start, end, ly_interval2scm (ret));
169 Axis_group_interface::begin_of_line_pure_height (Grob *me, int start)
171 return part_of_line_pure_height (me, true, start, start + 1);
175 Axis_group_interface::rest_of_line_pure_height (Grob *me, int start, int end)
177 return part_of_line_pure_height (me, false, start, end);
181 Axis_group_interface::combine_pure_heights (Grob *me, SCM measure_extents, int start, int end)
183 Paper_score *ps = get_root_system (me)->paper_score ();
184 vector<vsize> breaks = ps->get_break_indices ();
185 vector<Grob *> cols = ps->get_columns ();
188 for (vsize i = 0; i + 1 < breaks.size (); i++)
190 int r = Paper_column::get_rank (cols[breaks[i]]);
195 ext.unite (ly_scm2interval (scm_c_vector_ref (measure_extents, i)));
201 // adjacent-pure-heights is a pair of vectors, each of which has one element
202 // for every measure in the score. The first vector stores, for each measure,
203 // the combined height of the elements that are present only when the bar
204 // is at the beginning of a line. The second vector stores, for each measure,
205 // the combined height of the elements that are present only when the bar
206 // is not at the beginning of a line.
207 MAKE_SCHEME_CALLBACK (Axis_group_interface, adjacent_pure_heights, 1)
209 Axis_group_interface::adjacent_pure_heights (SCM smob)
211 Grob *me = unsmob_grob (smob);
213 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
214 extract_grob_set (me, "pure-relevant-grobs", elts);
216 Paper_score *ps = get_root_system (me)->paper_score ();
217 vector<vsize> ranks = ps->get_break_ranks ();
219 vector<Interval> begin_line_heights;
220 vector<Interval> mid_line_heights;
221 vector<Interval> begin_line_staff_heights;
222 vector<Interval> mid_line_staff_heights;
223 begin_line_heights.resize (ranks.size () - 1);
224 mid_line_heights.resize (ranks.size () - 1);
226 for (vsize i = 0; i < elts.size (); ++i)
230 if (to_boolean (g->get_property ("cross-staff")))
233 bool outside_staff = scm_is_number (g->get_property ("outside-staff-priority"));
234 Real padding = robust_scm2double (g->get_property ("outside-staff-padding"), 0.5);
236 // When we encounter the first outside-staff grob, make a copy
237 // of the current heights to use as an estimate for the staff heights.
238 // Note that the outside-staff approximation that we use here doesn't
239 // consider any collisions that might occur between outside-staff grobs,
240 // but only the fact that outside-staff grobs may need to be raised above
242 if (outside_staff && begin_line_staff_heights.empty ())
244 begin_line_staff_heights = begin_line_heights;
245 mid_line_staff_heights = mid_line_heights;
248 // TODO: consider a pure version of get_grob_direction?
249 Direction d = to_dir (g->get_property_data ("direction"));
250 d = (d == CENTER) ? UP : d;
252 Interval_t<int> rank_span = g->spanned_rank_interval ();
253 vsize first_break = lower_bound (ranks, (vsize)rank_span[LEFT], less<vsize> ());
254 if (first_break > 0 && ranks[first_break] >= (vsize)rank_span[LEFT])
257 for (vsize j = first_break; j + 1 < ranks.size () && (int)ranks[j] <= rank_span[RIGHT]; ++j)
259 int start = ranks[j];
260 int end = ranks[j + 1];
262 // Take grobs that are visible with respect to a slightly longer line.
263 // Otherwise, we will never include grobs at breakpoints which aren't
264 // end-of-line-visible.
265 int visibility_end = j + 2 < ranks.size () ? ranks[j + 2] : end;
267 if (g->pure_is_visible (start, visibility_end))
269 Interval dims = g->pure_height (common, start, end);
270 if (!dims.is_empty ())
272 if (rank_span[LEFT] <= start)
275 begin_line_heights[j].unite (begin_line_staff_heights[j].union_disjoint (dims, padding, d));
277 begin_line_heights[j].unite (dims);
279 if (rank_span[RIGHT] > start)
282 mid_line_heights[j].unite (mid_line_staff_heights[j].union_disjoint (dims, padding, d));
284 mid_line_heights[j].unite (dims);
291 // Convert begin_line_heights and min_line_heights to SCM.
292 SCM begin_scm = scm_c_make_vector (ranks.size () - 1, SCM_EOL);
293 SCM mid_scm = scm_c_make_vector (ranks.size () - 1, SCM_EOL);
294 for (vsize i = 0; i < begin_line_heights.size (); ++i)
296 scm_vector_set_x (begin_scm, scm_from_int (i), ly_interval2scm (begin_line_heights[i]));
297 scm_vector_set_x (mid_scm, scm_from_int (i), ly_interval2scm (mid_line_heights[i]));
300 return scm_cons (begin_scm, mid_scm);
304 Axis_group_interface::relative_pure_height (Grob *me, int start, int end)
306 /* It saves a _lot_ of time if we assume a VerticalAxisGroup is additive
307 (ie. height (i, k) = max (height (i, j) height (j, k)) for all i <= j <= k).
308 Unfortunately, it isn't always true, particularly if there is a
309 VerticalAlignment somewhere in the descendants.
311 Usually, the only VerticalAlignment comes from Score. This makes it
312 reasonably safe to assume that if our parent is a VerticalAlignment,
313 we can assume additivity and cache things nicely. */
314 Grob *p = me->get_parent (Y_AXIS);
315 if (p && Align_interface::has_interface (p))
316 return Axis_group_interface::sum_partial_pure_heights (me, start, end);
318 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
319 extract_grob_set (me, "pure-relevant-grobs", elts);
322 for (vsize i = 0; i < elts.size (); i++)
325 Interval_t<int> rank_span = g->spanned_rank_interval ();
326 if (rank_span[LEFT] <= end && rank_span[RIGHT] >= start
327 && g->pure_is_visible (start, end)
328 && !to_boolean (g->get_property ("cross-staff")))
330 Interval dims = g->pure_height (common, start, end);
331 if (!dims.is_empty ())
338 MAKE_SCHEME_CALLBACK (Axis_group_interface, width, 1);
340 Axis_group_interface::width (SCM smob)
342 Grob *me = unsmob_grob (smob);
343 return generic_group_extent (me, X_AXIS);
346 MAKE_SCHEME_CALLBACK (Axis_group_interface, height, 1);
348 Axis_group_interface::height (SCM smob)
350 Grob *me = unsmob_grob (smob);
351 return generic_group_extent (me, Y_AXIS);
354 MAKE_SCHEME_CALLBACK (Axis_group_interface, pure_height, 3);
356 Axis_group_interface::pure_height (SCM smob, SCM start_scm, SCM end_scm)
358 int start = robust_scm2int (start_scm, 0);
359 int end = robust_scm2int (end_scm, INT_MAX);
360 Grob *me = unsmob_grob (smob);
362 /* Maybe we are in the second pass of a two-pass spacing run. In that
363 case, the Y-extent of a system is already given to us */
364 System *system = dynamic_cast<System *> (me);
367 SCM line_break_details = system->column (start)->get_property ("line-break-system-details");
368 SCM system_y_extent = scm_assq (ly_symbol2scm ("system-Y-extent"), line_break_details);
369 if (scm_is_pair (system_y_extent))
370 return scm_cdr (system_y_extent);
373 return ly_interval2scm (pure_group_height (me, start, end));
376 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_skylines, 1);
378 Axis_group_interface::calc_skylines (SCM smob)
380 Grob *me = unsmob_grob (smob);
381 extract_grob_set (me, "elements", elts);
382 Skyline_pair skylines = skyline_spacing (me, elts);
384 return skylines.smobbed_copy ();
387 /* whereas calc_skylines calculates skylines for axis-groups with a lot of
388 visible children, combine_skylines is designed for axis-groups whose only
389 children are other axis-groups (ie. VerticalAlignment). Rather than
390 calculating all the skylines from scratch, we just merge the skylines
393 MAKE_SCHEME_CALLBACK (Axis_group_interface, combine_skylines, 1);
395 Axis_group_interface::combine_skylines (SCM smob)
397 Grob *me = unsmob_grob (smob);
398 extract_grob_set (me, "elements", elements);
399 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
400 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
403 programming_error ("combining skylines that don't belong to me");
406 for (vsize i = 0; i < elements.size (); i++)
408 SCM skyline_scm = elements[i]->get_property ("vertical-skylines");
409 if (Skyline_pair::unsmob (skyline_scm))
411 Real offset = elements[i]->relative_coordinate (y_common, Y_AXIS);
412 Skyline_pair other = *Skyline_pair::unsmob (skyline_scm);
413 other.raise (offset);
414 other.shift (elements[i]->relative_coordinate (x_common, X_AXIS));
418 return ret.smobbed_copy ();
422 Axis_group_interface::generic_group_extent (Grob *me, Axis a)
424 /* trigger the callback to do skyline-spacing on the children */
426 (void) me->get_property ("vertical-skylines");
428 extract_grob_set (me, "elements", elts);
429 Grob *common = common_refpoint_of_array (elts, me, a);
431 Real my_coord = me->relative_coordinate (common, a);
432 Interval r (relative_group_extent (elts, common, a));
434 return ly_interval2scm (r - my_coord);
437 /* This is like generic_group_extent, but it only counts the grobs that
438 are children of some other axis-group. This is uncached; if it becomes
439 commonly used, it may be necessary to cache it somehow. */
441 Axis_group_interface::staff_extent (Grob *me, Grob *refp, Axis ext_a, Grob *staff, Axis parent_a)
443 extract_grob_set (me, "elements", elts);
444 vector<Grob *> new_elts;
446 for (vsize i = 0; i < elts.size (); i++)
447 if (elts[i]->common_refpoint (staff, parent_a) == staff)
448 new_elts.push_back (elts[i]);
450 return relative_group_extent (new_elts, refp, ext_a);
453 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_relevant_grobs, 1);
455 Axis_group_interface::calc_pure_relevant_grobs (SCM smob)
457 Grob *me = unsmob_grob (smob);
459 extract_grob_set (me, "elements", elts);
461 vector<Grob *> relevant_grobs;
462 SCM pure_relevant_p = ly_lily_module_constant ("pure-relevant?");
464 for (vsize i = 0; i < elts.size (); i++)
466 if (to_boolean (scm_apply_1 (pure_relevant_p, elts[i]->self_scm (), SCM_EOL)))
467 relevant_grobs.push_back (elts[i]);
469 if (Item *it = dynamic_cast<Item *> (elts[i]))
474 Item *piece = it->find_prebroken_piece (d);
475 if (piece && to_boolean (scm_apply_1 (pure_relevant_p, piece->self_scm (), SCM_EOL)))
476 relevant_grobs.push_back (piece);
478 while (flip (&d) != LEFT);
482 vector_sort (relevant_grobs, pure_staff_priority_less);
483 SCM grobs_scm = Grob_array::make_array ();
484 unsmob_grob_array (grobs_scm)->set_array (relevant_grobs);
489 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_y_common, 1);
491 Axis_group_interface::calc_pure_y_common (SCM smob)
493 Grob *me = unsmob_grob (smob);
495 extract_grob_set (me, "pure-relevant-grobs", elts);
496 Grob *common = common_refpoint_of_array (elts, me, Y_AXIS);
499 me->programming_error ("No common parent found in calc_pure_y_common.");
503 return common->self_scm ();
507 Axis_group_interface::calc_common (Grob *me, Axis axis)
509 extract_grob_set (me, "elements", elts);
510 Grob *common = common_refpoint_of_array (elts, me, axis);
513 me->programming_error ("No common parent found in calc_common axis.");
517 return common->self_scm ();
520 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_x_common, 1);
522 Axis_group_interface::calc_x_common (SCM grob)
524 return calc_common (unsmob_grob (grob), X_AXIS);
527 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_y_common, 1);
529 Axis_group_interface::calc_y_common (SCM grob)
531 return calc_common (unsmob_grob (grob), Y_AXIS);
535 Axis_group_interface::pure_group_height (Grob *me, int start, int end)
537 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
541 programming_error ("no pure Y common refpoint");
544 Real my_coord = me->relative_coordinate (common, Y_AXIS);
545 Interval r (relative_pure_height (me, start, end));
551 Axis_group_interface::get_children (Grob *me, vector<Grob *> *found)
553 found->push_back (me);
555 if (!has_interface (me))
558 extract_grob_set (me, "elements", elements);
559 for (vsize i = 0; i < elements.size (); i++)
561 Grob *e = elements[i];
562 Axis_group_interface::get_children (e, found);
567 staff_priority_less (Grob *const &g1, Grob *const &g2)
569 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
570 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
572 if (priority_1 < priority_2)
574 else if (priority_1 > priority_2)
577 /* if neither grob has an outside-staff priority, the ordering will have no
578 effect -- we just need to choose a consistent ordering. We do this to
579 avoid the side-effect of calculating extents. */
580 if (isinf (priority_1))
583 /* if there is no preference in staff priority, choose the left-most one */
584 Grob *common = g1->common_refpoint (g2, X_AXIS);
585 Real start_1 = g1->extent (common, X_AXIS)[LEFT];
586 Real start_2 = g2->extent (common, X_AXIS)[LEFT];
587 return start_1 < start_2;
591 pure_staff_priority_less (Grob *const &g1, Grob *const &g2)
593 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
594 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
596 return priority_1 < priority_2;
600 add_boxes (Grob *me, Grob *x_common, Grob *y_common, vector<Box> *const boxes, Skyline_pair *skylines)
602 /* if a child has skylines, use them instead of the extent box */
603 if (Skyline_pair *pair = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
605 Skyline_pair s = *pair;
606 s.shift (me->relative_coordinate (x_common, X_AXIS));
607 s.raise (me->relative_coordinate (y_common, Y_AXIS));
610 else if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements")))
612 for (vsize i = 0; i < elements->size (); i++)
613 add_boxes (elements->grob (i), x_common, y_common, boxes, skylines);
615 else if (!scm_is_number (me->get_property ("outside-staff-priority"))
616 && !to_boolean (me->get_property ("cross-staff")))
618 boxes->push_back (Box (me->extent (x_common, X_AXIS),
619 me->extent (y_common, Y_AXIS)));
623 /* We want to avoid situations like this:
631 The point is that "still more text" should be positioned under
632 "more text". In order to achieve this, we place the grobs in several
633 passes. We keep track of the right-most horizontal position that has been
634 affected by the current pass so far (actually we keep track of 2
635 positions, one for above the staff, one for below).
637 In each pass, we loop through the unplaced grobs from left to right.
638 If the grob doesn't overlap the right-most affected position, we place it
639 (and then update the right-most affected position to point to the right
640 edge of the just-placed grob). Otherwise, we skip it until the next pass.
643 add_grobs_of_one_priority (Skyline_pair *const skylines,
644 vector<Grob *> elements,
649 Drul_array<Real> last_affected_position;
652 while (!elements.empty ())
654 last_affected_position[UP] = -infinity_f;
655 last_affected_position[DOWN] = -infinity_f;
657 for (vsize i = elements.size (); i--;)
659 Direction dir = get_grob_direction (elements[i]);
662 warning (_ ("an outside-staff object should have a direction, defaulting to up"));
666 Box b (elements[i]->extent (x_common, X_AXIS),
667 elements[i]->extent (y_common, Y_AXIS));
668 SCM horizon_padding_scm = elements[i]->get_property ("outside-staff-horizontal-padding");
669 Real horizon_padding = robust_scm2double (horizon_padding_scm, 0.0);
671 if (b[X_AXIS][LEFT] - 2 * horizon_padding < last_affected_position[dir])
674 if (!b[X_AXIS].is_empty () && !b[Y_AXIS].is_empty ())
678 Skyline other = Skyline (boxes, horizon_padding, X_AXIS, -dir);
679 Real padding = robust_scm2double (elements[i]->get_property ("outside-staff-padding"), 0.5);
680 Real dist = (*skylines)[dir].distance (other) + padding;
684 b.translate (Offset (0, dir * dist));
685 elements[i]->translate_axis (dir * dist, Y_AXIS);
687 skylines->insert (b, 0, X_AXIS);
688 elements[i]->set_property ("outside-staff-priority", SCM_BOOL_F);
689 last_affected_position[dir] = b[X_AXIS][RIGHT];
693 Ugh: quadratic. --hwn
695 elements.erase (elements.begin () + i);
701 Axis_group_interface::has_outside_staff_parent (Grob *me)
704 ? (scm_is_number (me->get_property ("outside-staff-priority"))
705 || has_outside_staff_parent (me->get_parent (Y_AXIS)))
709 // TODO: it is tricky to correctly handle skyline placement of cross-staff grobs.
710 // For example, cross-staff beams cannot be formatted until the distance between
711 // staves is known and therefore any grobs that depend on the beam cannot be placed
712 // until the skylines are known. On the other hand, the distance between staves should
713 // really depend on position of the cross-staff grobs that lie between them.
714 // Currently, we just leave cross-staff grobs out of the
715 // skyline altogether, but this could mean that staves are placed so close together
716 // that there is no room for the cross-staff grob. It also means, of course, that
717 // we don't get the benefits of skyline placement for cross-staff grobs.
719 Axis_group_interface::skyline_spacing (Grob *me, vector<Grob *> elements)
721 /* For grobs with an outside-staff-priority, the sorting function might
722 call extent and cause suicide. This breaks the contract that is required
723 for the STL sort function. To avoid this, we make sure that any suicides
724 are triggered beforehand.
726 for (vsize i = 0; i < elements.size (); i++)
727 if (scm_is_number (elements[i]->get_property ("outside-staff-priority")))
728 elements[i]->extent (elements[i], X_AXIS);
730 vector_sort (elements, staff_priority_less);
731 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
732 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
734 assert (y_common == me);
739 Skyline_pair skylines;
740 for (i = 0; i < elements.size ()
741 && !scm_is_number (elements[i]->get_property ("outside-staff-priority")); i++)
742 if (!(to_boolean (elements[i]->get_property ("cross-staff")) || has_outside_staff_parent (elements[i])))
743 add_boxes (elements[i], x_common, y_common, &boxes, &skylines);
745 SCM padding_scm = me->get_property ("skyline-horizontal-padding");
746 Real padding = robust_scm2double (padding_scm, 0.1);
747 skylines.merge (Skyline_pair (boxes, padding, X_AXIS));
748 for (; i < elements.size (); i++)
750 if (to_boolean (elements[i]->get_property ("cross-staff")))
753 SCM priority = elements[i]->get_property ("outside-staff-priority");
754 vector<Grob *> current_elts;
755 current_elts.push_back (elements[i]);
756 while (i + 1 < elements.size ()
757 && scm_is_eq (elements[i + 1]->get_property ("outside-staff-priority"), priority))
759 if (!to_boolean (elements[i + 1]->get_property ("cross-staff")))
760 current_elts.push_back (elements[i + 1]);
764 add_grobs_of_one_priority (&skylines, current_elts, x_common, y_common);
766 skylines.shift (-me->relative_coordinate (x_common, X_AXIS));
770 MAKE_SCHEME_CALLBACK (Axis_group_interface, print, 1)
772 Axis_group_interface::print (SCM smob)
777 Grob *me = unsmob_grob (smob);
779 if (Skyline_pair *s = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
781 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[UP].to_points (X_AXIS))
782 .in_color (255, 0, 255));
783 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[DOWN].to_points (X_AXIS))
784 .in_color (0, 255, 255));
786 return ret.smobbed_copy ();
789 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_staff_staff_spacing, 3)
791 Axis_group_interface::calc_pure_staff_staff_spacing (SCM smob, SCM start, SCM end)
793 return calc_maybe_pure_staff_staff_spacing (unsmob_grob (smob),
799 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_staff_staff_spacing, 1)
801 Axis_group_interface::calc_staff_staff_spacing (SCM smob)
803 return calc_maybe_pure_staff_staff_spacing (unsmob_grob (smob),
810 Axis_group_interface::calc_maybe_pure_staff_staff_spacing (Grob *me, bool pure, int start, int end)
812 Grob *grouper = unsmob_grob (me->get_object ("staff-grouper"));
816 bool within_group = Staff_grouper_interface::maybe_pure_within_group (grouper, me, pure, start, end);
818 return grouper->get_maybe_pure_property ("staff-staff-spacing", pure, start, end);
820 return grouper->get_maybe_pure_property ("staffgroup-staff-spacing", pure, start, end);
822 return me->get_maybe_pure_property ("default-staff-staff-spacing", pure, start, end);
826 Axis_group_interface::minimum_distance (Grob *g1, Grob *g2, Axis a)
828 SCM sym = ly_symbol2scm ((a == Y_AXIS) ? "vertical-skylines" : "horizontal-skylines");
830 Skyline_pair *s1 = Skyline_pair::unsmob (g1->get_property (sym));
831 Skyline_pair *s2 = Skyline_pair::unsmob (g2->get_property (sym));
833 return (*s1)[DOWN].distance ((*s2)[UP]);
837 ADD_INTERFACE (Axis_group_interface,
838 "An object that groups other layout objects.",
840 // TODO: some of these properties are specific to
841 // VerticalAxisGroup. We should split off a
842 // vertical-axis-group-interface.
844 "adjacent-pure-heights "
846 "bound-alignment-interfaces "
847 "default-staff-staff-spacing "
851 "nonstaff-nonstaff-spacing "
852 "nonstaff-relatedstaff-spacing "
853 "nonstaff-unrelatedstaff-spacing "
854 "pure-relevant-grobs "
855 "pure-relevant-items "
856 "pure-relevant-spanners "
860 "staff-staff-spacing "