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 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,
79 for (vsize i = 0; i < elts.size (); i++)
82 if (!to_boolean (se->get_property ("cross-staff")))
84 Interval dims = se->extent (common, a);
85 if (!dims.is_empty ())
93 Axis_group_interface::cached_pure_height (Grob *me, int start, int end)
95 Interval iv = begin_of_line_pure_height (me, start);
96 iv.unite (rest_of_line_pure_height (me, start, end));
102 Axis_group_interface::rest_of_line_pure_height (Grob *me, int start, int end)
104 SCM adjacent_pure_heights = me->get_property ("adjacent-pure-heights");
106 if (!scm_is_pair (adjacent_pure_heights)
107 || !scm_is_vector (scm_cdr (adjacent_pure_heights)))
108 return Interval (0, 0);
110 return combine_pure_heights (me, scm_cdr (adjacent_pure_heights), start, end);
114 Axis_group_interface::begin_of_line_pure_height (Grob *me, int start)
116 SCM adjacent_pure_heights = me->get_property ("adjacent-pure-heights");
118 if (!scm_is_pair (adjacent_pure_heights)
119 || !scm_is_vector (scm_car (adjacent_pure_heights)))
120 return Interval (0, 0);
122 return combine_pure_heights (me, scm_car (adjacent_pure_heights), start, start+1);
126 Axis_group_interface::combine_pure_heights (Grob *me, SCM measure_extents, int start, int end)
128 Paper_score *ps = get_root_system (me)->paper_score ();
129 vector<vsize> breaks = ps->get_break_indices ();
130 vector<Grob*> cols = ps->get_columns ();
133 for (vsize i = 0; i + 1 < breaks.size (); i++)
135 int r = Paper_column::get_rank (cols[breaks[i]]);
140 ext.unite (ly_scm2interval (scm_c_vector_ref (measure_extents, i)));
147 // adjacent-pure-heights is a pair of vectors, each of which has one element
148 // for every measure in the score. The first vector stores, for each measure,
149 // the combined height of the elements that are present only when the bar
150 // is at the beginning of a line. The second vector stores, for each measure,
151 // the combined height of the elements that are present only when the bar
152 // is not at the beginning of a line.
154 MAKE_SCHEME_CALLBACK (Axis_group_interface, adjacent_pure_heights, 1)
156 Axis_group_interface::adjacent_pure_heights (SCM smob)
158 Grob *me = unsmob_grob (smob);
160 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
161 extract_grob_set (me, "pure-relevant-grobs", elts);
163 Paper_score *ps = get_root_system (me)->paper_score ();
164 vector<vsize> ranks = ps->get_break_ranks ();
166 vector<Interval> begin_line_heights;
167 vector<Interval> mid_line_heights;
168 begin_line_heights.resize (ranks.size () - 1);
169 mid_line_heights.resize (ranks.size () - 1);
171 for (vsize i = 0; i < elts.size (); ++i)
175 if (to_boolean (g->get_property ("cross-staff")))
178 bool outside_staff = scm_is_number (g->get_property ("outside-staff-priority"));
179 Real padding = robust_scm2double (g->get_property ("outside-staff-padding"), 0.5);
181 // TODO: consider a pure version of get_grob_direction?
182 Direction d = to_dir (g->get_property_data ("direction"));
183 d = (d == CENTER) ? UP : d;
185 Interval_t<int> rank_span = g->spanned_rank_interval ();
186 vsize first_break = lower_bound (ranks, (vsize)rank_span[LEFT], less<vsize> ());
187 if (first_break > 0 && ranks[first_break] >= (vsize)rank_span[LEFT])
190 for (vsize j = first_break; j+1 < ranks.size () && (int)ranks[j] <= rank_span[RIGHT]; ++j)
192 int start = ranks[j];
193 int end = ranks[j+1];
195 // Take grobs that are visible with respect to a slightly longer line.
196 // Otherwise, we will never include grobs at breakpoints which aren't
197 // end-of-line-visible.
198 int visibility_end = j + 2 < ranks.size () ? ranks[j+2] : end;
200 if (g->pure_is_visible (start, visibility_end))
202 Interval dims = g->pure_height (common, start, end);
203 if (!dims.is_empty ())
205 if (rank_span[LEFT] <= start)
208 begin_line_heights[j].unite_disjoint (dims, padding, d);
210 begin_line_heights[j].unite (dims);
212 if (rank_span[RIGHT] > start)
215 mid_line_heights[j].unite_disjoint (dims, padding, d);
217 mid_line_heights[j].unite (dims);
224 // Convert begin_line_heights and min_line_heights to SCM.
225 SCM begin_scm = scm_c_make_vector (ranks.size () - 1, SCM_EOL);
226 SCM mid_scm = scm_c_make_vector (ranks.size () - 1, SCM_EOL);
227 for (vsize i = 0; i < begin_line_heights.size (); ++i)
229 scm_vector_set_x (begin_scm, scm_from_int (i), ly_interval2scm (begin_line_heights[i]));
230 scm_vector_set_x (mid_scm, scm_from_int (i), ly_interval2scm (mid_line_heights[i]));
233 return scm_cons (begin_scm, mid_scm);
237 Axis_group_interface::relative_pure_height (Grob *me, int start, int end)
239 /* It saves a _lot_ of time if we assume a VerticalAxisGroup is additive
240 (ie. height (i, k) = max (height (i, j) height (j, k)) for all i <= j <= k).
241 Unfortunately, it isn't always true, particularly if there is a
242 VerticalAlignment somewhere in the descendants.
244 Usually, the only VerticalAlignment comes from Score. This makes it
245 reasonably safe to assume that if our parent is a VerticalAlignment,
246 we can assume additivity and cache things nicely. */
247 Grob *p = me->get_parent (Y_AXIS);
248 if (p && Align_interface::has_interface (p))
249 return Axis_group_interface::cached_pure_height (me, start, end);
251 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
252 extract_grob_set (me, "pure-relevant-grobs", elts);
255 for (vsize i = 0; i < elts.size (); i++)
258 Interval_t<int> rank_span = g->spanned_rank_interval ();
259 if (rank_span[LEFT] <= end && rank_span[RIGHT] >= start
260 && g->pure_is_visible (start, end)
261 && !to_boolean (g->get_property ("cross-staff")))
263 Interval dims = g->pure_height (common, start, end);
264 if (!dims.is_empty ())
271 MAKE_SCHEME_CALLBACK (Axis_group_interface, width, 1);
273 Axis_group_interface::width (SCM smob)
275 Grob *me = unsmob_grob (smob);
276 return generic_group_extent (me, X_AXIS);
279 MAKE_SCHEME_CALLBACK (Axis_group_interface, height, 1);
281 Axis_group_interface::height (SCM smob)
283 Grob *me = unsmob_grob (smob);
284 return generic_group_extent (me, Y_AXIS);
287 MAKE_SCHEME_CALLBACK (Axis_group_interface, pure_height, 3);
289 Axis_group_interface::pure_height (SCM smob, SCM start_scm, SCM end_scm)
291 int start = robust_scm2int (start_scm, 0);
292 int end = robust_scm2int (end_scm, INT_MAX);
293 Grob *me = unsmob_grob (smob);
295 /* Maybe we are in the second pass of a two-pass spacing run. In that
296 case, the Y-extent of a system is already given to us */
297 System *system = dynamic_cast<System*> (me);
300 SCM line_break_details = system->column (start)->get_property ("line-break-system-details");
301 SCM system_y_extent = scm_assq (ly_symbol2scm ("system-Y-extent"), line_break_details);
302 if (scm_is_pair (system_y_extent))
303 return scm_cdr (system_y_extent);
306 return ly_interval2scm (pure_group_height (me, start, end));
309 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_skylines, 1);
311 Axis_group_interface::calc_skylines (SCM smob)
313 Grob *me = unsmob_grob (smob);
314 extract_grob_set (me, "elements", elts);
315 Skyline_pair skylines = skyline_spacing (me, elts);
317 return skylines.smobbed_copy ();
320 /* whereas calc_skylines calculates skylines for axis-groups with a lot of
321 visible children, combine_skylines is designed for axis-groups whose only
322 children are other axis-groups (ie. VerticalAlignment). Rather than
323 calculating all the skylines from scratch, we just merge the skylines
326 MAKE_SCHEME_CALLBACK (Axis_group_interface, combine_skylines, 1);
328 Axis_group_interface::combine_skylines (SCM smob)
330 Grob *me = unsmob_grob (smob);
331 extract_grob_set (me, "elements", elements);
332 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
333 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
336 programming_error ("combining skylines that don't belong to me");
339 for (vsize i = 0; i < elements.size (); i++)
341 SCM skyline_scm = elements[i]->get_property ("vertical-skylines");
342 if (Skyline_pair::unsmob (skyline_scm))
344 Real offset = elements[i]->relative_coordinate (y_common, Y_AXIS);
345 Skyline_pair other = *Skyline_pair::unsmob (skyline_scm);
346 other.raise (offset);
347 other.shift (elements[i]->relative_coordinate (x_common, X_AXIS));
351 return ret.smobbed_copy ();
355 Axis_group_interface::generic_group_extent (Grob *me, Axis a)
357 /* trigger the callback to do skyline-spacing on the children */
359 (void) me->get_property ("vertical-skylines");
361 extract_grob_set (me, "elements", elts);
362 Grob *common = common_refpoint_of_array (elts, me, a);
364 Real my_coord = me->relative_coordinate (common, a);
365 Interval r (relative_group_extent (elts, common, a));
367 return ly_interval2scm (r - my_coord);
370 /* This is like generic_group_extent, but it only counts the grobs that
371 are children of some other axis-group. This is uncached; if it becomes
372 commonly used, it may be necessary to cache it somehow. */
374 Axis_group_interface::staff_extent (Grob *me, Grob *refp, Axis ext_a, Grob *staff, Axis parent_a)
376 extract_grob_set (me, "elements", elts);
377 vector<Grob*> new_elts;
379 for (vsize i = 0; i < elts.size (); i++)
380 if (elts[i]->common_refpoint (staff, parent_a) == staff)
381 new_elts.push_back (elts[i]);
383 return relative_group_extent (new_elts, refp, ext_a);
387 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_relevant_grobs, 1);
389 Axis_group_interface::calc_pure_relevant_grobs (SCM smob)
391 Grob *me = unsmob_grob (smob);
393 extract_grob_set (me, "elements", elts);
395 vector<Grob*> relevant_grobs;
396 SCM pure_relevant_p = ly_lily_module_constant ("pure-relevant?");
398 for (vsize i = 0; i < elts.size (); i++)
400 if (to_boolean (scm_apply_1 (pure_relevant_p, elts[i]->self_scm (), SCM_EOL)))
401 relevant_grobs.push_back (elts[i]);
403 if (Item *it = dynamic_cast<Item*> (elts[i]))
408 Item *piece = it->find_prebroken_piece (d);
409 if (piece && to_boolean (scm_apply_1 (pure_relevant_p, piece->self_scm (), SCM_EOL)))
410 relevant_grobs.push_back (piece);
412 while (flip (&d) != LEFT);
416 vector_sort (relevant_grobs, pure_staff_priority_less);
417 SCM grobs_scm = Grob_array::make_array ();
418 unsmob_grob_array (grobs_scm)->set_array (relevant_grobs);
423 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_y_common, 1);
425 Axis_group_interface::calc_pure_y_common (SCM smob)
427 Grob *me = unsmob_grob (smob);
429 extract_grob_set (me, "pure-relevant-grobs", elts);
430 Grob *common = common_refpoint_of_array (elts, me, Y_AXIS);
433 me->programming_error ("No common parent found in calc_pure_y_common.");
437 return common->self_scm ();
441 Axis_group_interface::calc_common (Grob *me, Axis axis)
443 extract_grob_set (me, "elements", elts);
444 Grob *common = common_refpoint_of_array (elts, me, axis);
447 me->programming_error ("No common parent found in calc_common axis.");
451 return common->self_scm ();
455 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_x_common, 1);
457 Axis_group_interface::calc_x_common (SCM grob)
459 return calc_common (unsmob_grob (grob), X_AXIS);
462 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_y_common, 1);
464 Axis_group_interface::calc_y_common (SCM grob)
466 return calc_common (unsmob_grob (grob), Y_AXIS);
470 Axis_group_interface::pure_group_height (Grob *me, int start, int end)
472 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
476 programming_error ("no pure Y common refpoint");
479 Real my_coord = me->relative_coordinate (common, Y_AXIS);
480 Interval r (relative_pure_height (me, start, end));
486 Axis_group_interface::get_children (Grob *me, vector<Grob*> *found)
488 found->push_back (me);
490 if (!has_interface (me))
493 extract_grob_set (me, "elements", elements);
494 for (vsize i = 0; i < elements.size (); i++)
496 Grob *e = elements[i];
497 Axis_group_interface::get_children (e, found);
502 staff_priority_less (Grob * const &g1, Grob * const &g2)
504 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
505 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
507 if (priority_1 < priority_2)
509 else if (priority_1 > priority_2)
512 /* if neither grob has an outside-staff priority, the ordering will have no
513 effect -- we just need to choose a consistent ordering. We do this to
514 avoid the side-effect of calculating extents. */
515 if (isinf (priority_1))
518 /* if there is no preference in staff priority, choose the left-most one */
519 Grob *common = g1->common_refpoint (g2, X_AXIS);
520 Real start_1 = g1->extent (common, X_AXIS)[LEFT];
521 Real start_2 = g2->extent (common, X_AXIS)[LEFT];
522 return start_1 < start_2;
526 pure_staff_priority_less (Grob * const &g1, Grob * const &g2)
528 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
529 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
531 return priority_1 < priority_2;
535 add_boxes (Grob *me, Grob *x_common, Grob *y_common, vector<Box> *const boxes, Skyline_pair *skylines)
537 /* if a child has skylines, use them instead of the extent box */
538 if (Skyline_pair *pair = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
540 Skyline_pair s = *pair;
541 s.shift (me->relative_coordinate (x_common, X_AXIS));
542 s.raise (me->relative_coordinate (y_common, Y_AXIS));
545 else if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements")))
547 for (vsize i = 0; i < elements->size (); i++)
548 add_boxes (elements->grob (i), x_common, y_common, boxes, skylines);
550 else if (!scm_is_number (me->get_property ("outside-staff-priority"))
551 && !to_boolean (me->get_property ("cross-staff")))
553 boxes->push_back (Box (me->extent (x_common, X_AXIS),
554 me->extent (y_common, Y_AXIS)));
558 /* We want to avoid situations like this:
566 The point is that "still more text" should be positioned under
567 "more text". In order to achieve this, we place the grobs in several
568 passes. We keep track of the right-most horizontal position that has been
569 affected by the current pass so far (actually we keep track of 2
570 positions, one for above the staff, one for below).
572 In each pass, we loop through the unplaced grobs from left to right.
573 If the grob doesn't overlap the right-most affected position, we place it
574 (and then update the right-most affected position to point to the right
575 edge of the just-placed grob). Otherwise, we skip it until the next pass.
578 add_grobs_of_one_priority (Skyline_pair *const skylines,
579 vector<Grob*> elements,
584 Drul_array<Real> last_affected_position;
587 while (!elements.empty ())
589 last_affected_position[UP] = -infinity_f;
590 last_affected_position[DOWN] = -infinity_f;
592 for (vsize i = elements.size (); i--;)
594 Direction dir = get_grob_direction (elements[i]);
597 warning (_ ("an outside-staff object should have a direction, defaulting to up"));
601 Box b (elements[i]->extent (x_common, X_AXIS),
602 elements[i]->extent (y_common, Y_AXIS));
603 SCM horizon_padding_scm = elements[i]->get_property ("outside-staff-horizontal-padding");
604 Real horizon_padding = robust_scm2double (horizon_padding_scm, 0.0);
606 if (b[X_AXIS][LEFT] - 2*horizon_padding < last_affected_position[dir])
609 if (!b[X_AXIS].is_empty () && !b[Y_AXIS].is_empty ())
613 Skyline other = Skyline (boxes, horizon_padding, X_AXIS, -dir);
614 Real padding = robust_scm2double (elements[i]->get_property ("outside-staff-padding"), 0.5);
615 Real dist = (*skylines)[dir].distance (other) + padding;
619 b.translate (Offset (0, dir*dist));
620 elements[i]->translate_axis (dir*dist, Y_AXIS);
622 skylines->insert (b, 0, X_AXIS);
623 elements[i]->set_property ("outside-staff-priority", SCM_BOOL_F);
624 last_affected_position[dir] = b[X_AXIS][RIGHT];
628 Ugh: quadratic. --hwn
630 elements.erase (elements.begin () + i);
635 // TODO: it is tricky to correctly handle skyline placement of cross-staff grobs.
636 // For example, cross-staff beams cannot be formatted until the distance between
637 // staves is known and therefore any grobs that depend on the beam cannot be placed
638 // until the skylines are known. On the other hand, the distance between staves should
639 // really depend on position of the cross-staff grobs that lie between them.
640 // Currently, we just leave cross-staff grobs out of the
641 // skyline altogether, but this could mean that staves are placed so close together
642 // that there is no room for the cross-staff grob. It also means, of course, that
643 // we don't get the benefits of skyline placement for cross-staff grobs.
645 Axis_group_interface::skyline_spacing (Grob *me, vector<Grob*> elements)
647 /* For grobs with an outside-staff-priority, the sorting function might
648 call extent and cause suicide. This breaks the contract that is required
649 for the STL sort function. To avoid this, we make sure that any suicides
650 are triggered beforehand.
652 for (vsize i = 0; i < elements.size (); i++)
653 if (scm_is_number (elements[i]->get_property ("outside-staff-priority")))
654 elements[i]->extent (elements[i], X_AXIS);
656 vector_sort (elements, staff_priority_less);
657 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
658 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
660 assert (y_common == me);
665 Skyline_pair skylines;
666 for (i = 0; i < elements.size ()
667 && !scm_is_number (elements[i]->get_property ("outside-staff-priority")); i++)
668 if (!to_boolean (elements[i]->get_property ("cross-staff")))
669 add_boxes (elements[i], x_common, y_common, &boxes, &skylines);
671 SCM padding_scm = me->get_property ("skyline-horizontal-padding");
672 Real padding = robust_scm2double (padding_scm, 0.1);
673 skylines.merge (Skyline_pair (boxes, padding, X_AXIS));
674 for (; i < elements.size (); i++)
676 if (to_boolean (elements[i]->get_property ("cross-staff")))
679 SCM priority = elements[i]->get_property ("outside-staff-priority");
680 vector<Grob*> current_elts;
681 current_elts.push_back (elements[i]);
682 while (i + 1 < elements.size ()
683 && scm_eq_p (elements[i+1]->get_property ("outside-staff-priority"), priority))
685 if (!to_boolean (elements[i+1]->get_property ("cross-staff")))
686 current_elts.push_back (elements[i+1]);
690 add_grobs_of_one_priority (&skylines, current_elts, x_common, y_common);
692 skylines.shift (-me->relative_coordinate (x_common, X_AXIS));
696 MAKE_SCHEME_CALLBACK (Axis_group_interface, print, 1)
698 Axis_group_interface::print (SCM smob)
703 Grob *me = unsmob_grob (smob);
705 if (Skyline_pair *s = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
707 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[UP].to_points (X_AXIS))
708 .in_color (255, 0, 255));
709 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[DOWN].to_points (X_AXIS))
710 .in_color (0, 255, 255));
712 return ret.smobbed_copy ();
715 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_next_staff_spacing, 3)
717 Axis_group_interface::calc_pure_next_staff_spacing (SCM smob, SCM start, SCM end)
719 return calc_maybe_pure_next_staff_spacing (unsmob_grob (smob),
725 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_next_staff_spacing, 1)
727 Axis_group_interface::calc_next_staff_spacing (SCM smob)
729 return calc_maybe_pure_next_staff_spacing (unsmob_grob (smob),
736 Axis_group_interface::calc_maybe_pure_next_staff_spacing (Grob *me, bool pure, int start, int end)
738 Grob *grouper = unsmob_grob (me->get_object ("staff-grouper"));
742 Grob *last_in_group = Staff_grouper_interface::get_maybe_pure_last_grob (grouper, pure, start, end);
743 if (me == last_in_group)
744 return grouper->get_maybe_pure_property ("after-last-staff-spacing", pure, start, end);
746 return grouper->get_maybe_pure_property ("between-staff-spacing", pure, start, end);
748 return me->get_maybe_pure_property ("default-next-staff-spacing", pure, start, end);
752 Axis_group_interface::minimum_distance (Grob *g1, Grob *g2, Axis a)
754 SCM sym = ly_symbol2scm ((a == Y_AXIS) ? "vertical-skylines" : "horizontal-skylines");
756 Skyline_pair *s1 = Skyline_pair::unsmob (g1->get_property (sym));
757 Skyline_pair *s2 = Skyline_pair::unsmob (g2->get_property (sym));
759 return (*s1)[DOWN].distance ((*s2)[UP]);
763 ADD_INTERFACE (Axis_group_interface,
764 "An object that groups other layout objects.",
766 // TODO: some of these properties are specific to
767 // VerticalAxisGroup. We should split off a
768 // vertical-axis-group-interface.
772 "adjacent-pure-heights "
774 "default-next-staff-spacing "
776 "inter-loose-line-spacing "
777 "inter-staff-spacing "
779 "non-affinity-spacing "
780 "next-staff-spacing "
783 "pure-relevant-grobs "
784 "pure-relevant-items "
785 "pure-relevant-spanners "