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);
460 extract_grob_set (me, "elements", elts);
462 vector<Grob *> relevant_grobs;
463 SCM pure_relevant_p = ly_lily_module_constant ("pure-relevant?");
465 for (vsize i = 0; i < elts.size (); i++)
467 if (to_boolean (scm_apply_1 (pure_relevant_p, elts[i]->self_scm (), SCM_EOL)))
468 relevant_grobs.push_back (elts[i]);
470 if (Item *it = dynamic_cast<Item *> (elts[i]))
475 Item *piece = it->find_prebroken_piece (d);
476 if (piece && to_boolean (scm_apply_1 (pure_relevant_p, piece->self_scm (), SCM_EOL)))
477 relevant_grobs.push_back (piece);
479 while (flip (&d) != LEFT);
483 vector_sort (relevant_grobs, pure_staff_priority_less);
484 SCM grobs_scm = Grob_array::make_array ();
485 unsmob_grob_array (grobs_scm)->set_array (relevant_grobs);
490 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_y_common, 1);
492 Axis_group_interface::calc_pure_y_common (SCM smob)
494 Grob *me = unsmob_grob (smob);
496 extract_grob_set (me, "pure-relevant-grobs", elts);
497 Grob *common = common_refpoint_of_array (elts, me, Y_AXIS);
500 me->programming_error ("No common parent found in calc_pure_y_common.");
504 return common->self_scm ();
508 Axis_group_interface::calc_common (Grob *me, Axis axis)
510 extract_grob_set (me, "elements", elts);
511 Grob *common = common_refpoint_of_array (elts, me, axis);
514 me->programming_error ("No common parent found in calc_common axis.");
518 return common->self_scm ();
521 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_x_common, 1);
523 Axis_group_interface::calc_x_common (SCM grob)
525 return calc_common (unsmob_grob (grob), X_AXIS);
528 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_y_common, 1);
530 Axis_group_interface::calc_y_common (SCM grob)
532 return calc_common (unsmob_grob (grob), Y_AXIS);
536 Axis_group_interface::pure_group_height (Grob *me, int start, int end)
538 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
542 programming_error ("no pure Y common refpoint");
545 Real my_coord = me->relative_coordinate (common, Y_AXIS);
546 Interval r (relative_pure_height (me, start, end));
552 Axis_group_interface::get_children (Grob *me, vector<Grob *> *found)
554 found->push_back (me);
556 if (!has_interface (me))
559 extract_grob_set (me, "elements", elements);
560 for (vsize i = 0; i < elements.size (); i++)
562 Grob *e = elements[i];
563 Axis_group_interface::get_children (e, found);
568 staff_priority_less (Grob *const &g1, Grob *const &g2)
570 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
571 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
573 if (priority_1 < priority_2)
575 else if (priority_1 > priority_2)
578 /* if neither grob has an outside-staff priority, the ordering will have no
579 effect -- we just need to choose a consistent ordering. We do this to
580 avoid the side-effect of calculating extents. */
581 if (isinf (priority_1))
584 /* if there is no preference in staff priority, choose the left-most one */
585 Grob *common = g1->common_refpoint (g2, X_AXIS);
586 Real start_1 = g1->extent (common, X_AXIS)[LEFT];
587 Real start_2 = g2->extent (common, X_AXIS)[LEFT];
588 return start_1 < start_2;
592 pure_staff_priority_less (Grob *const &g1, Grob *const &g2)
594 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
595 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
597 return priority_1 < priority_2;
601 add_boxes (Grob *me, Grob *x_common, Grob *y_common, vector<Box> *const boxes, Skyline_pair *skylines)
603 /* if a child has skylines, use them instead of the extent box */
604 if (Skyline_pair *pair = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
606 Skyline_pair s = *pair;
607 s.shift (me->relative_coordinate (x_common, X_AXIS));
608 s.raise (me->relative_coordinate (y_common, Y_AXIS));
611 else if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements")))
613 for (vsize i = 0; i < elements->size (); i++)
614 add_boxes (elements->grob (i), x_common, y_common, boxes, skylines);
616 else if (!scm_is_number (me->get_property ("outside-staff-priority"))
617 && !to_boolean (me->get_property ("cross-staff")))
619 boxes->push_back (Box (me->extent (x_common, X_AXIS),
620 me->extent (y_common, Y_AXIS)));
624 /* We want to avoid situations like this:
632 The point is that "still more text" should be positioned under
633 "more text". In order to achieve this, we place the grobs in several
634 passes. We keep track of the right-most horizontal position that has been
635 affected by the current pass so far (actually we keep track of 2
636 positions, one for above the staff, one for below).
638 In each pass, we loop through the unplaced grobs from left to right.
639 If the grob doesn't overlap the right-most affected position, we place it
640 (and then update the right-most affected position to point to the right
641 edge of the just-placed grob). Otherwise, we skip it until the next pass.
644 add_grobs_of_one_priority (Skyline_pair *const skylines,
645 vector<Grob *> elements,
650 Drul_array<Real> last_affected_position;
653 while (!elements.empty ())
655 last_affected_position[UP] = -infinity_f;
656 last_affected_position[DOWN] = -infinity_f;
658 for (vsize i = elements.size (); i--;)
660 Direction dir = get_grob_direction (elements[i]);
663 warning (_ ("an outside-staff object should have a direction, defaulting to up"));
667 Box b (elements[i]->extent (x_common, X_AXIS),
668 elements[i]->extent (y_common, Y_AXIS));
669 SCM horizon_padding_scm = elements[i]->get_property ("outside-staff-horizontal-padding");
670 Real horizon_padding = robust_scm2double (horizon_padding_scm, 0.0);
672 if (b[X_AXIS][LEFT] - 2 * horizon_padding < last_affected_position[dir])
675 if (!b[X_AXIS].is_empty () && !b[Y_AXIS].is_empty ())
679 Skyline other = Skyline (boxes, horizon_padding, X_AXIS, -dir);
680 Real padding = robust_scm2double (elements[i]->get_property ("outside-staff-padding"), 0.5);
681 Real dist = (*skylines)[dir].distance (other) + padding;
685 b.translate (Offset (0, dir * dist));
686 elements[i]->translate_axis (dir * dist, Y_AXIS);
688 skylines->insert (b, 0, X_AXIS);
689 elements[i]->set_property ("outside-staff-priority", SCM_BOOL_F);
690 last_affected_position[dir] = b[X_AXIS][RIGHT];
694 Ugh: quadratic. --hwn
696 elements.erase (elements.begin () + i);
702 Axis_group_interface::has_outside_staff_parent (Grob *me)
705 ? (scm_is_number (me->get_property ("outside-staff-priority"))
706 || has_outside_staff_parent (me->get_parent (Y_AXIS)))
710 // TODO: it is tricky to correctly handle skyline placement of cross-staff grobs.
711 // For example, cross-staff beams cannot be formatted until the distance between
712 // staves is known and therefore any grobs that depend on the beam cannot be placed
713 // until the skylines are known. On the other hand, the distance between staves should
714 // really depend on position of the cross-staff grobs that lie between them.
715 // Currently, we just leave cross-staff grobs out of the
716 // skyline altogether, but this could mean that staves are placed so close together
717 // that there is no room for the cross-staff grob. It also means, of course, that
718 // we don't get the benefits of skyline placement for cross-staff grobs.
720 Axis_group_interface::skyline_spacing (Grob *me, vector<Grob *> elements)
722 /* For grobs with an outside-staff-priority, the sorting function might
723 call extent and cause suicide. This breaks the contract that is required
724 for the STL sort function. To avoid this, we make sure that any suicides
725 are triggered beforehand.
727 for (vsize i = 0; i < elements.size (); i++)
728 if (scm_is_number (elements[i]->get_property ("outside-staff-priority")))
729 elements[i]->extent (elements[i], X_AXIS);
731 vector_sort (elements, staff_priority_less);
732 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
733 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
735 assert (y_common == me);
740 Skyline_pair skylines;
741 for (i = 0; i < elements.size ()
742 && !scm_is_number (elements[i]->get_property ("outside-staff-priority")); i++)
743 if (!(to_boolean (elements[i]->get_property ("cross-staff")) || has_outside_staff_parent (elements[i])))
744 add_boxes (elements[i], x_common, y_common, &boxes, &skylines);
746 SCM padding_scm = me->get_property ("skyline-horizontal-padding");
747 Real padding = robust_scm2double (padding_scm, 0.1);
748 skylines.merge (Skyline_pair (boxes, padding, X_AXIS));
749 for (; i < elements.size (); i++)
751 if (to_boolean (elements[i]->get_property ("cross-staff")))
754 SCM priority = elements[i]->get_property ("outside-staff-priority");
755 vector<Grob *> current_elts;
756 current_elts.push_back (elements[i]);
757 while (i + 1 < elements.size ()
758 && scm_is_eq (elements[i + 1]->get_property ("outside-staff-priority"), priority))
760 if (!to_boolean (elements[i + 1]->get_property ("cross-staff")))
761 current_elts.push_back (elements[i + 1]);
765 add_grobs_of_one_priority (&skylines, current_elts, x_common, y_common);
767 skylines.shift (-me->relative_coordinate (x_common, X_AXIS));
771 MAKE_SCHEME_CALLBACK (Axis_group_interface, print, 1)
773 Axis_group_interface::print (SCM smob)
778 Grob *me = unsmob_grob (smob);
780 if (Skyline_pair *s = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
782 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[UP].to_points (X_AXIS))
783 .in_color (255, 0, 255));
784 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[DOWN].to_points (X_AXIS))
785 .in_color (0, 255, 255));
787 return ret.smobbed_copy ();
790 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_staff_staff_spacing, 3)
792 Axis_group_interface::calc_pure_staff_staff_spacing (SCM smob, SCM start, SCM end)
794 return calc_maybe_pure_staff_staff_spacing (unsmob_grob (smob),
800 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_staff_staff_spacing, 1)
802 Axis_group_interface::calc_staff_staff_spacing (SCM smob)
804 return calc_maybe_pure_staff_staff_spacing (unsmob_grob (smob),
811 Axis_group_interface::calc_maybe_pure_staff_staff_spacing (Grob *me, bool pure, int start, int end)
813 Grob *grouper = unsmob_grob (me->get_object ("staff-grouper"));
817 bool within_group = Staff_grouper_interface::maybe_pure_within_group (grouper, me, pure, start, end);
819 return grouper->get_maybe_pure_property ("staff-staff-spacing", pure, start, end);
821 return grouper->get_maybe_pure_property ("staffgroup-staff-spacing", pure, start, end);
823 return me->get_maybe_pure_property ("default-staff-staff-spacing", pure, start, end);
827 Axis_group_interface::minimum_distance (Grob *g1, Grob *g2, Axis a)
829 SCM sym = ly_symbol2scm ((a == Y_AXIS) ? "vertical-skylines" : "horizontal-skylines");
831 Skyline_pair *s1 = Skyline_pair::unsmob (g1->get_property (sym));
832 Skyline_pair *s2 = Skyline_pair::unsmob (g2->get_property (sym));
834 return (*s1)[DOWN].distance ((*s2)[UP]);
838 ADD_INTERFACE (Axis_group_interface,
839 "An object that groups other layout objects.",
841 // TODO: some of these properties are specific to
842 // VerticalAxisGroup. We should split off a
843 // vertical-axis-group-interface.
845 "adjacent-pure-heights "
847 "bound-alignment-interfaces "
848 "default-staff-staff-spacing "
852 "nonstaff-nonstaff-spacing "
853 "nonstaff-relatedstaff-spacing "
854 "nonstaff-unrelatedstaff-spacing "
855 "pure-relevant-grobs "
856 "pure-relevant-items "
857 "pure-relevant-spanners "
861 "staff-staff-spacing "