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"
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"
38 #include "unpure-pure-container.hh"
41 pure_staff_priority_less (Grob *const &g1, Grob *const &g2);
44 Axis_group_interface::add_element (Grob *me, Grob *e)
46 SCM axes = me->get_property ("axes");
47 if (!scm_is_pair (axes))
48 programming_error ("axes should be nonempty");
50 for (SCM ax = axes; scm_is_pair (ax); ax = scm_cdr (ax))
52 Axis a = (Axis) scm_to_int (scm_car (ax));
54 if (!e->get_parent (a))
55 e->set_parent (me, a);
57 e->set_object ((a == X_AXIS)
58 ? ly_symbol2scm ("axis-group-parent-X")
59 : ly_symbol2scm ("axis-group-parent-Y"),
63 /* must be ordered, because Align_interface also uses
64 Axis_group_interface */
65 Pointer_group_interface::add_grob (me, ly_symbol2scm ("elements"), e);
69 Axis_group_interface::has_axis (Grob *me, Axis a)
71 SCM axes = me->get_property ("axes");
73 return (SCM_BOOL_F != scm_memq (scm_from_int (a), axes));
77 Axis_group_interface::relative_group_extent (vector<Grob *> const &elts,
80 return relative_maybe_bound_group_extent (elts, common, a, false);
84 Axis_group_interface::relative_maybe_bound_group_extent (vector<Grob *> const &elts,
85 Grob *common, Axis a, bool bound)
88 for (vsize i = 0; i < elts.size (); i++)
91 if (!to_boolean (se->get_property ("cross-staff")))
93 Interval dims = (bound && has_interface (se)
94 ? generic_bound_extent (se, common, a)
95 : se->extent (common, a));
96 if (!dims.is_empty ())
104 Axis_group_interface::generic_bound_extent (Grob *me, Grob *common, Axis a)
106 /* trigger the callback to do skyline-spacing on the children */
108 (void) me->get_property ("vertical-skylines");
110 extract_grob_set (me, "elements", elts);
111 vector<Grob *> new_elts;
113 SCM interfaces = me->get_property ("bound-alignment-interfaces");
115 for (vsize i = 0; i < elts.size (); i++)
116 for (SCM l = interfaces; scm_is_pair (l); l = scm_cdr (l))
117 if (elts[i]->internal_has_interface (scm_car (l)))
118 new_elts.push_back (elts[i]);
120 if (!new_elts.size ())
121 return robust_relative_extent (me, common, a);
124 common = common_refpoint_of_array (new_elts, me, a);
126 return relative_maybe_bound_group_extent (new_elts, common, a, true);
130 Axis_group_interface::sum_partial_pure_heights (Grob *me, int start, int end)
132 Interval iv = begin_of_line_pure_height (me, start);
133 iv.unite (rest_of_line_pure_height (me, start, end));
139 Axis_group_interface::part_of_line_pure_height (Grob *me, bool begin, int start, int end)
141 Spanner *sp = dynamic_cast<Spanner *> (me);
142 SCM cache_symbol = begin
143 ? ly_symbol2scm ("begin-of-line-pure-height")
144 : ly_symbol2scm ("rest-of-line-pure-height");
145 SCM cached = sp->get_cached_pure_property (cache_symbol, start, end);
146 if (scm_is_pair (cached))
147 return robust_scm2interval (cached, Interval (0, 0));
149 SCM adjacent_pure_heights = me->get_property ("adjacent-pure-heights");
152 if (!scm_is_pair (adjacent_pure_heights))
153 ret = Interval (0, 0);
156 SCM these_pure_heights = begin
157 ? scm_car (adjacent_pure_heights)
158 : scm_cdr (adjacent_pure_heights);
160 if (scm_is_vector (these_pure_heights))
161 ret = combine_pure_heights (me, these_pure_heights, start, end);
163 ret = Interval (0, 0);
166 sp->cache_pure_property (cache_symbol, start, end, ly_interval2scm (ret));
171 Axis_group_interface::begin_of_line_pure_height (Grob *me, int start)
173 return part_of_line_pure_height (me, true, start, start + 1);
177 Axis_group_interface::rest_of_line_pure_height (Grob *me, int start, int end)
179 return part_of_line_pure_height (me, false, start, end);
183 Axis_group_interface::combine_pure_heights (Grob *me, SCM measure_extents, int start, int end)
185 Paper_score *ps = get_root_system (me)->paper_score ();
186 vector<vsize> breaks = ps->get_break_indices ();
187 vector<Grob *> cols = ps->get_columns ();
190 for (vsize i = 0; i + 1 < breaks.size (); i++)
192 int r = Paper_column::get_rank (cols[breaks[i]]);
197 ext.unite (ly_scm2interval (scm_c_vector_ref (measure_extents, i)));
203 // adjacent-pure-heights is a pair of vectors, each of which has one element
204 // for every measure in the score. The first vector stores, for each measure,
205 // the combined height of the elements that are present only when the bar
206 // is at the beginning of a line. The second vector stores, for each measure,
207 // the combined height of the elements that are present only when the bar
208 // is not at the beginning of a line.
209 MAKE_SCHEME_CALLBACK (Axis_group_interface, adjacent_pure_heights, 1)
211 Axis_group_interface::adjacent_pure_heights (SCM smob)
213 Grob *me = unsmob_grob (smob);
215 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
216 extract_grob_set (me, "pure-relevant-grobs", elts);
218 Paper_score *ps = get_root_system (me)->paper_score ();
219 vector<vsize> ranks = ps->get_break_ranks ();
221 vector<Interval> begin_line_heights;
222 vector<Interval> mid_line_heights;
223 vector<Interval> begin_line_staff_heights;
224 vector<Interval> mid_line_staff_heights;
225 begin_line_heights.resize (ranks.size () - 1);
226 mid_line_heights.resize (ranks.size () - 1);
228 for (vsize i = 0; i < elts.size (); ++i)
232 if (to_boolean (g->get_property ("cross-staff")))
235 bool outside_staff = scm_is_number (g->get_property ("outside-staff-priority"));
236 Real padding = robust_scm2double (g->get_property ("outside-staff-padding"), 0.5);
238 // When we encounter the first outside-staff grob, make a copy
239 // of the current heights to use as an estimate for the staff heights.
240 // Note that the outside-staff approximation that we use here doesn't
241 // consider any collisions that might occur between outside-staff grobs,
242 // but only the fact that outside-staff grobs may need to be raised above
244 if (outside_staff && begin_line_staff_heights.empty ())
246 begin_line_staff_heights = begin_line_heights;
247 mid_line_staff_heights = mid_line_heights;
250 // TODO: consider a pure version of get_grob_direction?
251 Direction d = to_dir (g->get_property_data ("direction"));
252 d = (d == CENTER) ? UP : d;
254 Interval_t<int> rank_span = g->spanned_rank_interval ();
255 vsize first_break = lower_bound (ranks, (vsize)rank_span[LEFT], less<vsize> ());
256 if (first_break > 0 && ranks[first_break] >= (vsize)rank_span[LEFT])
259 for (vsize j = first_break; j + 1 < ranks.size () && (int)ranks[j] <= rank_span[RIGHT]; ++j)
261 int start = ranks[j];
262 int end = ranks[j + 1];
264 // Take grobs that are visible with respect to a slightly longer line.
265 // Otherwise, we will never include grobs at breakpoints which aren't
266 // end-of-line-visible.
267 int visibility_end = j + 2 < ranks.size () ? ranks[j + 2] : end;
269 if (g->pure_is_visible (start, visibility_end))
271 Interval dims = g->pure_height (common, start, end);
272 if (!dims.is_empty ())
274 if (rank_span[LEFT] <= start)
277 begin_line_heights[j].unite (begin_line_staff_heights[j].union_disjoint (dims, padding, d));
279 begin_line_heights[j].unite (dims);
281 if (rank_span[RIGHT] > start)
284 mid_line_heights[j].unite (mid_line_staff_heights[j].union_disjoint (dims, padding, d));
286 mid_line_heights[j].unite (dims);
293 // Convert begin_line_heights and min_line_heights to SCM.
294 SCM begin_scm = scm_c_make_vector (ranks.size () - 1, SCM_EOL);
295 SCM mid_scm = scm_c_make_vector (ranks.size () - 1, SCM_EOL);
296 for (vsize i = 0; i < begin_line_heights.size (); ++i)
298 scm_vector_set_x (begin_scm, scm_from_int (i), ly_interval2scm (begin_line_heights[i]));
299 scm_vector_set_x (mid_scm, scm_from_int (i), ly_interval2scm (mid_line_heights[i]));
302 return scm_cons (begin_scm, mid_scm);
306 Axis_group_interface::relative_pure_height (Grob *me, int start, int end)
308 /* It saves a _lot_ of time if we assume a VerticalAxisGroup is additive
309 (ie. height (i, k) = max (height (i, j) height (j, k)) for all i <= j <= k).
310 Unfortunately, it isn't always true, particularly if there is a
311 VerticalAlignment somewhere in the descendants.
313 Usually, the only VerticalAlignment comes from Score. This makes it
314 reasonably safe to assume that if our parent is a VerticalAlignment,
315 we can assume additivity and cache things nicely. */
316 Grob *p = me->get_parent (Y_AXIS);
317 if (p && Align_interface::has_interface (p))
318 return Axis_group_interface::sum_partial_pure_heights (me, start, end);
320 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
321 extract_grob_set (me, "pure-relevant-grobs", elts);
324 for (vsize i = 0; i < elts.size (); i++)
327 Interval_t<int> rank_span = g->spanned_rank_interval ();
328 if (rank_span[LEFT] <= end && rank_span[RIGHT] >= start
329 && g->pure_is_visible (start, end)
330 && !(to_boolean (g->get_property ("cross-staff"))
331 && Stem::has_interface (g)))
333 Interval dims = g->pure_height (common, start, end);
334 if (!dims.is_empty ())
341 MAKE_SCHEME_CALLBACK (Axis_group_interface, width, 1);
343 Axis_group_interface::width (SCM smob)
345 Grob *me = unsmob_grob (smob);
346 return generic_group_extent (me, X_AXIS);
349 MAKE_SCHEME_CALLBACK (Axis_group_interface, height, 1);
351 Axis_group_interface::height (SCM smob)
353 Grob *me = unsmob_grob (smob);
354 return generic_group_extent (me, Y_AXIS);
357 MAKE_SCHEME_CALLBACK (Axis_group_interface, pure_height, 3);
359 Axis_group_interface::pure_height (SCM smob, SCM start_scm, SCM end_scm)
361 int start = robust_scm2int (start_scm, 0);
362 int end = robust_scm2int (end_scm, INT_MAX);
363 Grob *me = unsmob_grob (smob);
365 /* Maybe we are in the second pass of a two-pass spacing run. In that
366 case, the Y-extent of a system is already given to us */
367 System *system = dynamic_cast<System *> (me);
370 SCM line_break_details = system->column (start)->get_property ("line-break-system-details");
371 SCM system_y_extent = scm_assq (ly_symbol2scm ("system-Y-extent"), line_break_details);
372 if (scm_is_pair (system_y_extent))
373 return scm_cdr (system_y_extent);
376 return ly_interval2scm (pure_group_height (me, start, end));
379 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_skylines, 1);
381 Axis_group_interface::calc_skylines (SCM smob)
383 Grob *me = unsmob_grob (smob);
384 extract_grob_set (me, "elements", elts);
385 Skyline_pair skylines = skyline_spacing (me, elts);
387 return skylines.smobbed_copy ();
390 /* whereas calc_skylines calculates skylines for axis-groups with a lot of
391 visible children, combine_skylines is designed for axis-groups whose only
392 children are other axis-groups (ie. VerticalAlignment). Rather than
393 calculating all the skylines from scratch, we just merge the skylines
396 MAKE_SCHEME_CALLBACK (Axis_group_interface, combine_skylines, 1);
398 Axis_group_interface::combine_skylines (SCM smob)
400 Grob *me = unsmob_grob (smob);
401 extract_grob_set (me, "elements", elements);
402 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
403 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
406 programming_error ("combining skylines that don't belong to me");
409 for (vsize i = 0; i < elements.size (); i++)
411 SCM skyline_scm = elements[i]->get_property ("vertical-skylines");
412 if (Skyline_pair::unsmob (skyline_scm))
414 Real offset = elements[i]->relative_coordinate (y_common, Y_AXIS);
415 Skyline_pair other = *Skyline_pair::unsmob (skyline_scm);
416 other.raise (offset);
417 other.shift (elements[i]->relative_coordinate (x_common, X_AXIS));
421 return ret.smobbed_copy ();
425 Axis_group_interface::generic_group_extent (Grob *me, Axis a)
427 /* trigger the callback to do skyline-spacing on the children */
429 (void) me->get_property ("vertical-skylines");
431 extract_grob_set (me, "elements", elts);
432 Grob *common = common_refpoint_of_array (elts, me, a);
434 Real my_coord = me->relative_coordinate (common, a);
435 Interval r (relative_group_extent (elts, common, a));
437 return ly_interval2scm (r - my_coord);
440 /* This is like generic_group_extent, but it only counts the grobs that
441 are children of some other axis-group. This is uncached; if it becomes
442 commonly used, it may be necessary to cache it somehow. */
444 Axis_group_interface::staff_extent (Grob *me, Grob *refp, Axis ext_a, Grob *staff, Axis parent_a)
446 extract_grob_set (me, "elements", elts);
447 vector<Grob *> new_elts;
449 for (vsize i = 0; i < elts.size (); i++)
450 if (elts[i]->common_refpoint (staff, parent_a) == staff)
451 new_elts.push_back (elts[i]);
453 return relative_group_extent (new_elts, refp, ext_a);
456 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_relevant_grobs, 1);
458 Axis_group_interface::calc_pure_relevant_grobs (SCM smob)
460 Grob *me = unsmob_grob (smob);
461 return internal_calc_pure_relevant_grobs (me, "elements");
465 Axis_group_interface::internal_calc_pure_relevant_grobs (Grob *me, string grob_set_name)
467 extract_grob_set (me, grob_set_name.c_str (), elts);
469 vector<Grob *> relevant_grobs;
470 SCM pure_relevant_p = ly_lily_module_constant ("pure-relevant?");
472 for (vsize i = 0; i < elts.size (); i++)
474 if (to_boolean (scm_apply_1 (pure_relevant_p, elts[i]->self_scm (), SCM_EOL)))
475 relevant_grobs.push_back (elts[i]);
477 if (Item *it = dynamic_cast<Item *> (elts[i]))
479 for (LEFT_and_RIGHT (d))
481 Item *piece = it->find_prebroken_piece (d);
482 if (piece && to_boolean (scm_apply_1 (pure_relevant_p, piece->self_scm (), SCM_EOL)))
483 relevant_grobs.push_back (piece);
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 "