2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2000--2010 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 Axis_group_interface::add_element (Grob *me, Grob *e)
41 SCM axes = me->get_property ("axes");
42 if (!scm_is_pair (axes))
43 programming_error ("axes should be nonempty");
45 for (SCM ax = axes; scm_is_pair (ax); ax = scm_cdr (ax))
47 Axis a = (Axis) scm_to_int (scm_car (ax));
49 if (!e->get_parent (a))
50 e->set_parent (me, a);
52 e->set_object ((a == X_AXIS)
53 ? ly_symbol2scm ("axis-group-parent-X")
54 : ly_symbol2scm ("axis-group-parent-Y"),
58 /* must be ordered, because Align_interface also uses
59 Axis_group_interface */
60 Pointer_group_interface::add_grob (me, ly_symbol2scm ("elements"), e);
64 Axis_group_interface::has_axis (Grob *me, Axis a)
66 SCM axes = me->get_property ("axes");
68 return (SCM_BOOL_F != scm_memq (scm_from_int (a), axes));
72 Axis_group_interface::relative_group_extent (vector<Grob*> const &elts,
76 for (vsize i = 0; i < elts.size (); i++)
79 if (!to_boolean (se->get_property ("cross-staff")))
81 Interval dims = se->extent (common, a);
82 if (!dims.is_empty ())
90 Axis_group_interface::cached_pure_height (Grob *me, int start, int end)
92 Interval iv = begin_of_line_pure_height (me, start);
93 iv.unite (rest_of_line_pure_height (me, start, end));
99 Axis_group_interface::rest_of_line_pure_height (Grob *me, int start, int end)
101 SCM adjacent_pure_heights = me->get_property ("adjacent-pure-heights");
103 if (!scm_is_pair (adjacent_pure_heights)
104 || !scm_is_vector (scm_cdr (adjacent_pure_heights)))
105 return Interval (0, 0);
107 return combine_pure_heights (me, scm_cdr (adjacent_pure_heights), start, end);
111 Axis_group_interface::begin_of_line_pure_height (Grob *me, int start)
113 SCM adjacent_pure_heights = me->get_property ("adjacent-pure-heights");
115 if (!scm_is_pair (adjacent_pure_heights)
116 || !scm_is_vector (scm_car (adjacent_pure_heights)))
117 return Interval (0, 0);
119 return combine_pure_heights (me, scm_car (adjacent_pure_heights), start, start+1);
123 Axis_group_interface::combine_pure_heights (Grob *me, SCM measure_extents, int start, int end)
125 Paper_score *ps = get_root_system (me)->paper_score ();
126 vector<vsize> breaks = ps->get_break_indices ();
127 vector<Grob*> cols = ps->get_columns ();
130 for (vsize i = 0; i + 1 < breaks.size (); i++)
132 int r = Paper_column::get_rank (cols[breaks[i]]);
137 ext.unite (ly_scm2interval (scm_c_vector_ref (measure_extents, i)));
143 // adjacent-pure-heights is a pair of vectors, each of which has one element
144 // for every measure in the score. The first vector stores, for each measure,
145 // the combined height of the elements that are present only when the bar
146 // is at the beginning of a line. The second vector stores, for each measure,
147 // the combined height of the elements that are present only when the bar
148 // is not at the beginning of a line.
150 MAKE_SCHEME_CALLBACK (Axis_group_interface, adjacent_pure_heights, 1)
152 Axis_group_interface::adjacent_pure_heights (SCM smob)
154 Grob *me = unsmob_grob (smob);
156 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
157 extract_grob_set (me, "pure-relevant-items", items);
158 extract_grob_set (me, "pure-relevant-spanners", spanners);
160 Paper_score *ps = get_root_system (me)->paper_score ();
161 vector<vsize> breaks = ps->get_break_indices ();
162 vector<Grob*> cols = ps->get_columns ();
164 SCM begin_line_heights = scm_c_make_vector (breaks.size () - 1, SCM_EOL);
165 SCM mid_line_heights = scm_c_make_vector (breaks.size () - 1, SCM_EOL);
168 for (vsize i = 0; i + 1 < breaks.size (); i++)
170 int start = Paper_column::get_rank (cols[breaks[i]]);
171 int end = Paper_column::get_rank (cols[breaks[i+1]]);
173 Interval begin_line_iv;
174 Interval mid_line_iv;
176 for (vsize j = it_index; j < items.size (); j++)
178 Item *it = dynamic_cast<Item*> (items[j]);
179 int rank = it->get_column ()->get_rank ();
181 if (rank <= end && it->pure_is_visible (start, end)
182 && !to_boolean (it->get_property ("cross-staff")))
184 Interval dims = items[j]->pure_height (common, start, end);
185 Interval &target_iv = start == it->get_column ()->get_rank () ? begin_line_iv : mid_line_iv;
187 if (!dims.is_empty ())
188 target_iv.unite (dims);
197 for (vsize j = 0; j < spanners.size (); j++)
199 Interval_t<int> rank_span = spanners[j]->spanned_rank_interval ();
200 if (rank_span[LEFT] <= end && rank_span[RIGHT] >= start
201 && !to_boolean (spanners[j]->get_property ("cross-staff")))
203 Interval dims = spanners[j]->pure_height (common, start, end);
205 if (!dims.is_empty ())
206 mid_line_iv.unite (dims);
210 scm_vector_set_x (begin_line_heights, scm_from_int (i), ly_interval2scm (begin_line_iv));
211 scm_vector_set_x (mid_line_heights, scm_from_int (i), ly_interval2scm (mid_line_iv));
213 return scm_cons (begin_line_heights, mid_line_heights);
217 Axis_group_interface::relative_pure_height (Grob *me, int start, int end)
219 /* It saves a _lot_ of time if we assume a VerticalAxisGroup is additive
220 (ie. height (i, k) = max (height (i, j) height (j, k)) for all i <= j <= k).
221 Unfortunately, it isn't always true, particularly if there is a
222 VerticalAlignment somewhere in the descendants.
224 Apart from PianoStaff, which has a fixed VerticalAlignment so it doesn't
225 count, the only VerticalAlignment comes from Score. This makes it
226 reasonably safe to assume that if our parent is a VerticalAlignment,
227 we can assume additivity and cache things nicely. */
228 Grob *p = me->get_parent (Y_AXIS);
229 if (p && Align_interface::has_interface (p))
230 return Axis_group_interface::cached_pure_height (me, start, end);
232 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
233 extract_grob_set (me, "pure-relevant-items", items);
234 extract_grob_set (me, "pure-relevant-spanners", spanners);
238 for (vsize i = 0; i < items.size (); i++)
240 Item *it = dynamic_cast<Item*> (items[i]);
241 int rank = it->get_column ()->get_rank ();
245 else if (rank >= start && it->pure_is_visible (start, end)
246 && !to_boolean (it->get_property ("cross-staff")))
248 Interval dims = it->pure_height (common, start, end);
249 if (!dims.is_empty ())
254 for (vsize i = 0; i < spanners.size (); i++)
256 Interval_t<int> rank_span = spanners[i]->spanned_rank_interval ();
257 if (rank_span[LEFT] <= end && rank_span[RIGHT] >= start
258 && !to_boolean (spanners[i]->get_property ("cross-staff")))
260 Interval dims = spanners[i]->pure_height (common, start, end);
261 if (!dims.is_empty ())
268 MAKE_SCHEME_CALLBACK (Axis_group_interface, width, 1);
270 Axis_group_interface::width (SCM smob)
272 Grob *me = unsmob_grob (smob);
273 return generic_group_extent (me, X_AXIS);
276 MAKE_SCHEME_CALLBACK (Axis_group_interface, height, 1);
278 Axis_group_interface::height (SCM smob)
280 Grob *me = unsmob_grob (smob);
281 return generic_group_extent (me, Y_AXIS);
284 MAKE_SCHEME_CALLBACK (Axis_group_interface, pure_height, 3);
286 Axis_group_interface::pure_height (SCM smob, SCM start_scm, SCM end_scm)
288 int start = robust_scm2int (start_scm, 0);
289 int end = robust_scm2int (end_scm, INT_MAX);
290 Grob *me = unsmob_grob (smob);
292 /* Maybe we are in the second pass of a two-pass spacing run. In that
293 case, the Y-extent of a system is already given to us */
294 System *system = dynamic_cast<System*> (me);
297 SCM line_break_details = system->column (start)->get_property ("line-break-system-details");
298 SCM system_y_extent = scm_assq (ly_symbol2scm ("system-Y-extent"), line_break_details);
299 if (scm_is_pair (system_y_extent))
300 return scm_cdr (system_y_extent);
303 return ly_interval2scm (pure_group_height (me, start, end));
306 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_skylines, 1);
308 Axis_group_interface::calc_skylines (SCM smob)
310 Grob *me = unsmob_grob (smob);
311 extract_grob_set (me, "elements", elts);
312 Skyline_pair skylines = skyline_spacing (me, elts);
314 return skylines.smobbed_copy ();
317 /* whereas calc_skylines calculates skylines for axis-groups with a lot of
318 visible children, combine_skylines is designed for axis-groups whose only
319 children are other axis-groups (ie. VerticalAlignment). Rather than
320 calculating all the skylines from scratch, we just merge the skylines
323 MAKE_SCHEME_CALLBACK (Axis_group_interface, combine_skylines, 1);
325 Axis_group_interface::combine_skylines (SCM smob)
327 Grob *me = unsmob_grob (smob);
328 extract_grob_set (me, "elements", elements);
329 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
330 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
333 programming_error ("combining skylines that don't belong to me");
336 for (vsize i = 0; i < elements.size (); i++)
338 SCM skyline_scm = elements[i]->get_property ("vertical-skylines");
339 if (Skyline_pair::unsmob (skyline_scm))
341 Real offset = elements[i]->relative_coordinate (y_common, Y_AXIS);
342 Skyline_pair other = *Skyline_pair::unsmob (skyline_scm);
343 other.raise (offset);
344 other.shift (elements[i]->relative_coordinate (x_common, X_AXIS));
348 return ret.smobbed_copy ();
352 Axis_group_interface::generic_group_extent (Grob *me, Axis a)
354 /* trigger the callback to do skyline-spacing on the children */
356 (void) me->get_property ("vertical-skylines");
358 extract_grob_set (me, "elements", elts);
359 Grob *common = common_refpoint_of_array (elts, me, a);
361 Real my_coord = me->relative_coordinate (common, a);
362 Interval r (relative_group_extent (elts, common, a));
364 return ly_interval2scm (r - my_coord);
367 /* This is like generic_group_extent, but it only counts the grobs that
368 are children of some other axis-group. This is uncached; if it becomes
369 commonly used, it may be necessary to cache it somehow. */
371 Axis_group_interface::staff_extent (Grob *me, Grob *refp, Axis ext_a, Grob *staff, Axis parent_a)
373 extract_grob_set (me, "elements", elts);
374 vector<Grob*> new_elts;
376 for (vsize i = 0; i < elts.size (); i++)
377 if (elts[i]->common_refpoint (staff, parent_a) == staff)
378 new_elts.push_back (elts[i]);
380 return relative_group_extent (new_elts, refp, ext_a);
384 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_relevant_grobs, 1);
386 Axis_group_interface::calc_pure_relevant_grobs (SCM smob)
388 Grob *me = unsmob_grob (smob);
390 extract_grob_set (me, "elements", elts);
392 vector<Grob*> relevant_grobs;
393 SCM pure_relevant_p = ly_lily_module_constant ("pure-relevant?");
395 for (vsize i = 0; i < elts.size (); i++)
397 if (to_boolean (scm_apply_1 (pure_relevant_p, elts[i]->self_scm (), SCM_EOL)))
398 relevant_grobs.push_back (elts[i]);
400 if (Item *it = dynamic_cast<Item*> (elts[i]))
405 Item *piece = it->find_prebroken_piece (d);
406 if (piece && to_boolean (scm_apply_1 (pure_relevant_p, piece->self_scm (), SCM_EOL)))
407 relevant_grobs.push_back (piece);
409 while (flip (&d) != LEFT);
413 SCM grobs_scm = Grob_array::make_array ();
414 unsmob_grob_array (grobs_scm)->set_array (relevant_grobs);
419 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_relevant_items, 1);
421 Axis_group_interface::calc_pure_relevant_items (SCM smob)
423 Grob *me = unsmob_grob (smob);
425 extract_grob_set (me, "pure-relevant-grobs", elts);
427 vector<Grob*> relevant_items;
428 for (vsize i = 0; i < elts.size (); i++)
429 if (Item *it = dynamic_cast<Item*> (elts[i]))
430 relevant_items.push_back (it);
432 vector_sort (relevant_items, Item::less);
434 SCM items_scm = Grob_array::make_array ();
435 unsmob_grob_array (items_scm)->set_array (relevant_items);
439 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_relevant_spanners, 1);
441 Axis_group_interface::calc_pure_relevant_spanners (SCM smob)
443 Grob *me = unsmob_grob (smob);
445 extract_grob_set (me, "pure-relevant-grobs", elts);
447 vector<Grob*> relevant_spanners;
448 for (vsize i = 0; i < elts.size (); i++)
449 if (dynamic_cast<Spanner*> (elts[i]))
450 relevant_spanners.push_back (elts[i]);
452 SCM spanners_scm = Grob_array::make_array ();
453 unsmob_grob_array (spanners_scm)->set_array (relevant_spanners);
457 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_y_common, 1);
459 Axis_group_interface::calc_pure_y_common (SCM smob)
461 Grob *me = unsmob_grob (smob);
463 extract_grob_set (me, "pure-relevant-grobs", elts);
464 Grob *common = common_refpoint_of_array (elts, me, Y_AXIS);
467 me->programming_error ("No common parent found in calc_pure_y_common.");
471 return common->self_scm ();
475 Axis_group_interface::calc_common (Grob *me, Axis axis)
477 extract_grob_set (me, "elements", elts);
478 Grob *common = common_refpoint_of_array (elts, me, axis);
481 me->programming_error ("No common parent found in calc_common axis.");
485 return common->self_scm ();
489 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_x_common, 1);
491 Axis_group_interface::calc_x_common (SCM grob)
493 return calc_common (unsmob_grob (grob), X_AXIS);
496 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_y_common, 1);
498 Axis_group_interface::calc_y_common (SCM grob)
500 return calc_common (unsmob_grob (grob), Y_AXIS);
504 Axis_group_interface::pure_group_height (Grob *me, int start, int end)
506 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
510 programming_error ("no pure Y common refpoint");
513 Real my_coord = me->relative_coordinate (common, Y_AXIS);
514 Interval r (relative_pure_height (me, start, end));
520 Axis_group_interface::get_children (Grob *me, vector<Grob*> *found)
522 found->push_back (me);
524 if (!has_interface (me))
527 extract_grob_set (me, "elements", elements);
528 for (vsize i = 0; i < elements.size (); i++)
530 Grob *e = elements[i];
531 Axis_group_interface::get_children (e, found);
536 staff_priority_less (Grob * const &g1, Grob * const &g2)
538 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
539 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
541 if (priority_1 < priority_2)
543 else if (priority_1 > priority_2)
546 /* if neither grob has an outside-staff priority, the ordering will have no
547 effect -- we just need to choose a consistent ordering. We do this to
548 avoid the side-effect of calculating extents. */
549 if (isinf (priority_1))
552 /* if there is no preference in staff priority, choose the left-most one */
553 Grob *common = g1->common_refpoint (g2, X_AXIS);
554 Real start_1 = g1->extent (common, X_AXIS)[LEFT];
555 Real start_2 = g2->extent (common, X_AXIS)[LEFT];
556 return start_1 < start_2;
560 add_boxes (Grob *me, Grob *x_common, Grob *y_common, vector<Box> *const boxes, Skyline_pair *skylines)
562 /* if a child has skylines, use them instead of the extent box */
563 if (Skyline_pair *pair = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
565 Skyline_pair s = *pair;
566 s.shift (me->relative_coordinate (x_common, X_AXIS));
567 s.raise (me->relative_coordinate (y_common, Y_AXIS));
570 else if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements")))
572 for (vsize i = 0; i < elements->size (); i++)
573 add_boxes (elements->grob (i), x_common, y_common, boxes, skylines);
575 else if (!scm_is_number (me->get_property ("outside-staff-priority"))
576 && !to_boolean (me->get_property ("cross-staff")))
578 boxes->push_back (Box (me->extent (x_common, X_AXIS),
579 me->extent (y_common, Y_AXIS)));
583 /* We want to avoid situations like this:
591 The point is that "still more text" should be positioned under
592 "more text". In order to achieve this, we place the grobs in several
593 passes. We keep track of the right-most horizontal position that has been
594 affected by the current pass so far (actually we keep track of 2
595 positions, one for above the staff, one for below).
597 In each pass, we loop through the unplaced grobs from left to right.
598 If the grob doesn't overlap the right-most affected position, we place it
599 (and then update the right-most affected position to point to the right
600 edge of the just-placed grob). Otherwise, we skip it until the next pass.
603 add_grobs_of_one_priority (Skyline_pair *const skylines,
604 vector<Grob*> elements,
609 Drul_array<Real> last_affected_position;
612 while (!elements.empty ())
614 last_affected_position[UP] = -infinity_f;
615 last_affected_position[DOWN] = -infinity_f;
617 for (vsize i = elements.size (); i--;)
619 Direction dir = get_grob_direction (elements[i]);
622 warning (_ ("an outside-staff object should have a direction, defaulting to up"));
626 Box b (elements[i]->extent (x_common, X_AXIS),
627 elements[i]->extent (y_common, Y_AXIS));
628 SCM horizon_padding_scm = elements[i]->get_property ("outside-staff-horizontal-padding");
629 Real horizon_padding = robust_scm2double (horizon_padding_scm, 0.0);
631 if (b[X_AXIS][LEFT] - 2*horizon_padding < last_affected_position[dir])
634 if (!b[X_AXIS].is_empty () && !b[Y_AXIS].is_empty ())
638 Skyline other = Skyline (boxes, horizon_padding, X_AXIS, -dir);
639 Real padding = robust_scm2double (elements[i]->get_property ("outside-staff-padding"), 0.5);
640 Real dist = (*skylines)[dir].distance (other) + padding;
644 b.translate (Offset (0, dir*dist));
645 elements[i]->translate_axis (dir*dist, Y_AXIS);
647 skylines->insert (b, 0, X_AXIS);
648 elements[i]->set_property ("outside-staff-priority", SCM_BOOL_F);
649 last_affected_position[dir] = b[X_AXIS][RIGHT];
653 Ugh: quadratic. --hwn
655 elements.erase (elements.begin () + i);
660 // TODO: it is tricky to correctly handle skyline placement of cross-staff grobs.
661 // For example, cross-staff beams cannot be formatted until the distance between
662 // staves is known and therefore any grobs that depend on the beam cannot be placed
663 // until the skylines are known. On the other hand, the distance between staves should
664 // really depend on position of the cross-staff grobs that lie between them.
665 // Currently, we just leave cross-staff grobs out of the
666 // skyline altogether, but this could mean that staves are placed so close together
667 // that there is no room for the cross-staff grob. It also means, of course, that
668 // we don't get the benefits of skyline placement for cross-staff grobs.
670 Axis_group_interface::skyline_spacing (Grob *me, vector<Grob*> elements)
672 /* For grobs with an outside-staff-priority, the sorting function might
673 call extent and cause suicide. This breaks the contract that is required
674 for the STL sort function. To avoid this, we make sure that any suicides
675 are triggered beforehand.
677 for (vsize i = 0; i < elements.size (); i++)
678 if (scm_is_number (elements[i]->get_property ("outside-staff-priority")))
679 elements[i]->extent (elements[i], X_AXIS);
681 vector_sort (elements, staff_priority_less);
682 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
683 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
685 assert (y_common == me);
690 Skyline_pair skylines;
691 for (i = 0; i < elements.size ()
692 && !scm_is_number (elements[i]->get_property ("outside-staff-priority")); i++)
693 if (!to_boolean (elements[i]->get_property ("cross-staff")))
694 add_boxes (elements[i], x_common, y_common, &boxes, &skylines);
696 SCM padding_scm = me->get_property ("skyline-horizontal-padding");
697 Real padding = robust_scm2double (padding_scm, 0.1);
698 skylines.merge (Skyline_pair (boxes, padding, X_AXIS));
699 for (; i < elements.size (); i++)
701 if (to_boolean (elements[i]->get_property ("cross-staff")))
704 SCM priority = elements[i]->get_property ("outside-staff-priority");
705 vector<Grob*> current_elts;
706 current_elts.push_back (elements[i]);
707 while (i + 1 < elements.size ()
708 && scm_eq_p (elements[i+1]->get_property ("outside-staff-priority"), priority))
710 if (!to_boolean (elements[i+1]->get_property ("cross-staff")))
711 current_elts.push_back (elements[i+1]);
715 add_grobs_of_one_priority (&skylines, current_elts, x_common, y_common);
717 skylines.shift (-me->relative_coordinate (x_common, X_AXIS));
721 MAKE_SCHEME_CALLBACK (Axis_group_interface, print, 1)
723 Axis_group_interface::print (SCM smob)
728 Grob *me = unsmob_grob (smob);
730 if (Skyline_pair *s = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
732 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[UP].to_points (X_AXIS))
733 .in_color (255, 0, 255));
734 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[DOWN].to_points (X_AXIS))
735 .in_color (0, 255, 255));
737 return ret.smobbed_copy ();
740 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_next_staff_spacing, 3)
742 Axis_group_interface::calc_pure_next_staff_spacing (SCM smob, SCM start, SCM end)
744 return calc_maybe_pure_next_staff_spacing (unsmob_grob (smob),
750 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_next_staff_spacing, 1)
752 Axis_group_interface::calc_next_staff_spacing (SCM smob)
754 return calc_maybe_pure_next_staff_spacing (unsmob_grob (smob),
761 Axis_group_interface::calc_maybe_pure_next_staff_spacing (Grob *me, bool pure, int start, int end)
763 Grob *grouper = unsmob_grob (me->get_object ("staff-grouper"));
767 Grob *last_in_group = Staff_grouper_interface::get_maybe_pure_last_grob (grouper, pure, start, end);
768 if (me == last_in_group)
769 return grouper->get_maybe_pure_property ("after-last-staff-spacing", pure, start, end);
771 return grouper->get_maybe_pure_property ("between-staff-spacing", pure, start, end);
773 return me->get_maybe_pure_property ("default-next-staff-spacing", pure, start, end);
777 Axis_group_interface::minimum_distance (Grob *g1, Grob *g2, Axis a)
779 SCM sym = ly_symbol2scm ((a == Y_AXIS) ? "vertical-skylines" : "horizontal-skylines");
781 Skyline_pair *s1 = Skyline_pair::unsmob (g1->get_property (sym));
782 Skyline_pair *s2 = Skyline_pair::unsmob (g2->get_property (sym));
784 return (*s1)[DOWN].distance ((*s2)[UP]);
788 ADD_INTERFACE (Axis_group_interface,
789 "An object that groups other layout objects.",
791 // TODO: some of these properties are specific to
792 // VerticalAxisGroup. We should split off a
793 // vertical-axis-group-interface.
797 "adjacent-pure-heights "
799 "default-next-staff-spacing "
801 "inter-loose-line-spacing "
802 "inter-staff-spacing "
803 "keep-fixed-while-stretching "
805 "non-affinity-spacing "
806 "next-staff-spacing "
809 "pure-relevant-grobs "
810 "pure-relevant-items "
811 "pure-relevant-spanners "