2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2000--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
20 #include "axis-group-interface.hh"
22 #include "align-interface.hh"
23 #include "directional-element-interface.hh"
24 #include "grob-array.hh"
25 #include "hara-kiri-group-spanner.hh"
26 #include "international.hh"
28 #include "paper-column.hh"
29 #include "paper-score.hh"
30 #include "pointer-group-interface.hh"
31 #include "separation-item.hh"
32 #include "skyline-pair.hh"
33 #include "staff-grouper-interface.hh"
39 pure_staff_priority_less (Grob *const &g1, Grob *const &g2);
42 Axis_group_interface::add_element (Grob *me, Grob *e)
44 SCM axes = me->get_property ("axes");
45 if (!scm_is_pair (axes))
46 programming_error ("axes should be nonempty");
48 for (SCM ax = axes; scm_is_pair (ax); ax = scm_cdr (ax))
50 Axis a = (Axis) scm_to_int (scm_car (ax));
52 if (!e->get_parent (a))
53 e->set_parent (me, a);
55 e->set_object ((a == X_AXIS)
56 ? ly_symbol2scm ("axis-group-parent-X")
57 : ly_symbol2scm ("axis-group-parent-Y"),
61 /* must be ordered, because Align_interface also uses
62 Axis_group_interface */
63 Pointer_group_interface::add_grob (me, ly_symbol2scm ("elements"), e);
67 Axis_group_interface::has_axis (Grob *me, Axis a)
69 SCM axes = me->get_property ("axes");
71 return (SCM_BOOL_F != scm_memq (scm_from_int (a), axes));
75 Axis_group_interface::relative_group_extent (vector<Grob*> const &elts,
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)));
146 // adjacent-pure-heights is a pair of vectors, each of which has one element
147 // for every measure in the score. The first vector stores, for each measure,
148 // the combined height of the elements that are present only when the bar
149 // is at the beginning of a line. The second vector stores, for each measure,
150 // the combined height of the elements that are present only when the bar
151 // is not at the beginning of a line.
152 MAKE_SCHEME_CALLBACK (Axis_group_interface, adjacent_pure_heights, 1)
154 Axis_group_interface::adjacent_pure_heights (SCM smob)
156 Grob *me = unsmob_grob (smob);
158 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
159 extract_grob_set (me, "pure-relevant-grobs", elts);
161 Paper_score *ps = get_root_system (me)->paper_score ();
162 vector<vsize> ranks = ps->get_break_ranks ();
164 vector<Interval> begin_line_heights;
165 vector<Interval> mid_line_heights;
166 vector<Interval> begin_line_staff_heights;
167 vector<Interval> mid_line_staff_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 // When we encounter the first outside-staff grob, make a copy
182 // of the current heights to use as an estimate for the staff heights.
183 // Note that the outside-staff approximation that we use here doesn't
184 // consider any collisions that might occur between outside-staff grobs,
185 // but only the fact that outside-staff grobs may need to be raised above
187 if (outside_staff && begin_line_staff_heights.empty ())
189 begin_line_staff_heights = begin_line_heights;
190 mid_line_staff_heights = mid_line_heights;
193 // TODO: consider a pure version of get_grob_direction?
194 Direction d = to_dir (g->get_property_data ("direction"));
195 d = (d == CENTER) ? UP : d;
197 Interval_t<int> rank_span = g->spanned_rank_interval ();
198 vsize first_break = lower_bound (ranks, (vsize)rank_span[LEFT], less<vsize> ());
199 if (first_break > 0 && ranks[first_break] >= (vsize)rank_span[LEFT])
202 for (vsize j = first_break; j+1 < ranks.size () && (int)ranks[j] <= rank_span[RIGHT]; ++j)
204 int start = ranks[j];
205 int end = ranks[j+1];
207 // Take grobs that are visible with respect to a slightly longer line.
208 // Otherwise, we will never include grobs at breakpoints which aren't
209 // end-of-line-visible.
210 int visibility_end = j + 2 < ranks.size () ? ranks[j+2] : end;
212 if (g->pure_is_visible (start, visibility_end))
214 Interval dims = g->pure_height (common, start, end);
215 if (!dims.is_empty ())
217 if (rank_span[LEFT] <= start)
220 begin_line_heights[j].unite (
221 begin_line_staff_heights[j].union_disjoint (dims, padding, d));
223 begin_line_heights[j].unite (dims);
225 if (rank_span[RIGHT] > start)
228 mid_line_heights[j].unite (
229 mid_line_staff_heights[j].union_disjoint (dims, padding, d));
231 mid_line_heights[j].unite (dims);
238 // Convert begin_line_heights and min_line_heights to SCM.
239 SCM begin_scm = scm_c_make_vector (ranks.size () - 1, SCM_EOL);
240 SCM mid_scm = scm_c_make_vector (ranks.size () - 1, SCM_EOL);
241 for (vsize i = 0; i < begin_line_heights.size (); ++i)
243 scm_vector_set_x (begin_scm, scm_from_int (i), ly_interval2scm (begin_line_heights[i]));
244 scm_vector_set_x (mid_scm, scm_from_int (i), ly_interval2scm (mid_line_heights[i]));
247 return scm_cons (begin_scm, mid_scm);
251 Axis_group_interface::relative_pure_height (Grob *me, int start, int end)
253 /* It saves a _lot_ of time if we assume a VerticalAxisGroup is additive
254 (ie. height (i, k) = max (height (i, j) height (j, k)) for all i <= j <= k).
255 Unfortunately, it isn't always true, particularly if there is a
256 VerticalAlignment somewhere in the descendants.
258 Usually, the only VerticalAlignment comes from Score. This makes it
259 reasonably safe to assume that if our parent is a VerticalAlignment,
260 we can assume additivity and cache things nicely. */
261 Grob *p = me->get_parent (Y_AXIS);
262 if (p && Align_interface::has_interface (p))
263 return Axis_group_interface::cached_pure_height (me, start, end);
265 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
266 extract_grob_set (me, "pure-relevant-grobs", elts);
269 for (vsize i = 0; i < elts.size (); i++)
272 Interval_t<int> rank_span = g->spanned_rank_interval ();
273 if (rank_span[LEFT] <= end && rank_span[RIGHT] >= start
274 && g->pure_is_visible (start, end)
275 && !to_boolean (g->get_property ("cross-staff")))
277 Interval dims = g->pure_height (common, start, end);
278 if (!dims.is_empty ())
285 MAKE_SCHEME_CALLBACK (Axis_group_interface, width, 1);
287 Axis_group_interface::width (SCM smob)
289 Grob *me = unsmob_grob (smob);
290 return generic_group_extent (me, X_AXIS);
293 MAKE_SCHEME_CALLBACK (Axis_group_interface, height, 1);
295 Axis_group_interface::height (SCM smob)
297 Grob *me = unsmob_grob (smob);
298 return generic_group_extent (me, Y_AXIS);
301 MAKE_SCHEME_CALLBACK (Axis_group_interface, pure_height, 3);
303 Axis_group_interface::pure_height (SCM smob, SCM start_scm, SCM end_scm)
305 int start = robust_scm2int (start_scm, 0);
306 int end = robust_scm2int (end_scm, INT_MAX);
307 Grob *me = unsmob_grob (smob);
309 /* Maybe we are in the second pass of a two-pass spacing run. In that
310 case, the Y-extent of a system is already given to us */
311 System *system = dynamic_cast<System*> (me);
314 SCM line_break_details = system->column (start)->get_property ("line-break-system-details");
315 SCM system_y_extent = scm_assq (ly_symbol2scm ("system-Y-extent"), line_break_details);
316 if (scm_is_pair (system_y_extent))
317 return scm_cdr (system_y_extent);
320 return ly_interval2scm (pure_group_height (me, start, end));
323 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_skylines, 1);
325 Axis_group_interface::calc_skylines (SCM smob)
327 Grob *me = unsmob_grob (smob);
328 extract_grob_set (me, "elements", elts);
329 Skyline_pair skylines = skyline_spacing (me, elts);
331 return skylines.smobbed_copy ();
334 /* whereas calc_skylines calculates skylines for axis-groups with a lot of
335 visible children, combine_skylines is designed for axis-groups whose only
336 children are other axis-groups (ie. VerticalAlignment). Rather than
337 calculating all the skylines from scratch, we just merge the skylines
340 MAKE_SCHEME_CALLBACK (Axis_group_interface, combine_skylines, 1);
342 Axis_group_interface::combine_skylines (SCM smob)
344 Grob *me = unsmob_grob (smob);
345 extract_grob_set (me, "elements", elements);
346 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
347 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
350 programming_error ("combining skylines that don't belong to me");
353 for (vsize i = 0; i < elements.size (); i++)
355 SCM skyline_scm = elements[i]->get_property ("vertical-skylines");
356 if (Skyline_pair::unsmob (skyline_scm))
358 Real offset = elements[i]->relative_coordinate (y_common, Y_AXIS);
359 Skyline_pair other = *Skyline_pair::unsmob (skyline_scm);
360 other.raise (offset);
361 other.shift (elements[i]->relative_coordinate (x_common, X_AXIS));
365 return ret.smobbed_copy ();
369 Axis_group_interface::generic_group_extent (Grob *me, Axis a)
371 /* trigger the callback to do skyline-spacing on the children */
373 (void) me->get_property ("vertical-skylines");
375 extract_grob_set (me, "elements", elts);
376 Grob *common = common_refpoint_of_array (elts, me, a);
378 Real my_coord = me->relative_coordinate (common, a);
379 Interval r (relative_group_extent (elts, common, a));
381 return ly_interval2scm (r - my_coord);
384 /* This is like generic_group_extent, but it only counts the grobs that
385 are children of some other axis-group. This is uncached; if it becomes
386 commonly used, it may be necessary to cache it somehow. */
388 Axis_group_interface::staff_extent (Grob *me, Grob *refp, Axis ext_a, Grob *staff, Axis parent_a)
390 extract_grob_set (me, "elements", elts);
391 vector<Grob*> new_elts;
393 for (vsize i = 0; i < elts.size (); i++)
394 if (elts[i]->common_refpoint (staff, parent_a) == staff)
395 new_elts.push_back (elts[i]);
397 return relative_group_extent (new_elts, refp, ext_a);
401 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_relevant_grobs, 1);
403 Axis_group_interface::calc_pure_relevant_grobs (SCM smob)
405 Grob *me = unsmob_grob (smob);
407 extract_grob_set (me, "elements", elts);
409 vector<Grob*> relevant_grobs;
410 SCM pure_relevant_p = ly_lily_module_constant ("pure-relevant?");
412 for (vsize i = 0; i < elts.size (); i++)
414 if (to_boolean (scm_apply_1 (pure_relevant_p, elts[i]->self_scm (), SCM_EOL)))
415 relevant_grobs.push_back (elts[i]);
417 if (Item *it = dynamic_cast<Item*> (elts[i]))
422 Item *piece = it->find_prebroken_piece (d);
423 if (piece && to_boolean (scm_apply_1 (pure_relevant_p, piece->self_scm (), SCM_EOL)))
424 relevant_grobs.push_back (piece);
426 while (flip (&d) != LEFT);
430 vector_sort (relevant_grobs, pure_staff_priority_less);
431 SCM grobs_scm = Grob_array::make_array ();
432 unsmob_grob_array (grobs_scm)->set_array (relevant_grobs);
437 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_y_common, 1);
439 Axis_group_interface::calc_pure_y_common (SCM smob)
441 Grob *me = unsmob_grob (smob);
443 extract_grob_set (me, "pure-relevant-grobs", elts);
444 Grob *common = common_refpoint_of_array (elts, me, Y_AXIS);
447 me->programming_error ("No common parent found in calc_pure_y_common.");
451 return common->self_scm ();
455 Axis_group_interface::calc_common (Grob *me, Axis axis)
457 extract_grob_set (me, "elements", elts);
458 Grob *common = common_refpoint_of_array (elts, me, axis);
461 me->programming_error ("No common parent found in calc_common axis.");
465 return common->self_scm ();
469 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_x_common, 1);
471 Axis_group_interface::calc_x_common (SCM grob)
473 return calc_common (unsmob_grob (grob), X_AXIS);
476 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_y_common, 1);
478 Axis_group_interface::calc_y_common (SCM grob)
480 return calc_common (unsmob_grob (grob), Y_AXIS);
484 Axis_group_interface::pure_group_height (Grob *me, int start, int end)
486 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
490 programming_error ("no pure Y common refpoint");
493 Real my_coord = me->relative_coordinate (common, Y_AXIS);
494 Interval r (relative_pure_height (me, start, end));
500 Axis_group_interface::get_children (Grob *me, vector<Grob*> *found)
502 found->push_back (me);
504 if (!has_interface (me))
507 extract_grob_set (me, "elements", elements);
508 for (vsize i = 0; i < elements.size (); i++)
510 Grob *e = elements[i];
511 Axis_group_interface::get_children (e, found);
516 staff_priority_less (Grob * const &g1, Grob * const &g2)
518 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
519 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
521 if (priority_1 < priority_2)
523 else if (priority_1 > priority_2)
526 /* if neither grob has an outside-staff priority, the ordering will have no
527 effect -- we just need to choose a consistent ordering. We do this to
528 avoid the side-effect of calculating extents. */
529 if (isinf (priority_1))
532 /* if there is no preference in staff priority, choose the left-most one */
533 Grob *common = g1->common_refpoint (g2, X_AXIS);
534 Real start_1 = g1->extent (common, X_AXIS)[LEFT];
535 Real start_2 = g2->extent (common, X_AXIS)[LEFT];
536 return start_1 < start_2;
540 pure_staff_priority_less (Grob * const &g1, Grob * const &g2)
542 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
543 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
545 return priority_1 < priority_2;
549 add_boxes (Grob *me, Grob *x_common, Grob *y_common, vector<Box> *const boxes, Skyline_pair *skylines)
551 /* if a child has skylines, use them instead of the extent box */
552 if (Skyline_pair *pair = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
554 Skyline_pair s = *pair;
555 s.shift (me->relative_coordinate (x_common, X_AXIS));
556 s.raise (me->relative_coordinate (y_common, Y_AXIS));
559 else if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements")))
561 for (vsize i = 0; i < elements->size (); i++)
562 add_boxes (elements->grob (i), x_common, y_common, boxes, skylines);
564 else if (!scm_is_number (me->get_property ("outside-staff-priority"))
565 && !to_boolean (me->get_property ("cross-staff")))
567 boxes->push_back (Box (me->extent (x_common, X_AXIS),
568 me->extent (y_common, Y_AXIS)));
572 /* We want to avoid situations like this:
580 The point is that "still more text" should be positioned under
581 "more text". In order to achieve this, we place the grobs in several
582 passes. We keep track of the right-most horizontal position that has been
583 affected by the current pass so far (actually we keep track of 2
584 positions, one for above the staff, one for below).
586 In each pass, we loop through the unplaced grobs from left to right.
587 If the grob doesn't overlap the right-most affected position, we place it
588 (and then update the right-most affected position to point to the right
589 edge of the just-placed grob). Otherwise, we skip it until the next pass.
592 add_grobs_of_one_priority (Skyline_pair *const skylines,
593 vector<Grob*> elements,
598 Drul_array<Real> last_affected_position;
601 while (!elements.empty ())
603 last_affected_position[UP] = -infinity_f;
604 last_affected_position[DOWN] = -infinity_f;
606 for (vsize i = elements.size (); i--;)
608 Direction dir = get_grob_direction (elements[i]);
611 warning (_ ("an outside-staff object should have a direction, defaulting to up"));
615 Box b (elements[i]->extent (x_common, X_AXIS),
616 elements[i]->extent (y_common, Y_AXIS));
617 SCM horizon_padding_scm = elements[i]->get_property ("outside-staff-horizontal-padding");
618 Real horizon_padding = robust_scm2double (horizon_padding_scm, 0.0);
620 if (b[X_AXIS][LEFT] - 2*horizon_padding < last_affected_position[dir])
623 if (!b[X_AXIS].is_empty () && !b[Y_AXIS].is_empty ())
627 Skyline other = Skyline (boxes, horizon_padding, X_AXIS, -dir);
628 Real padding = robust_scm2double (elements[i]->get_property ("outside-staff-padding"), 0.5);
629 Real dist = (*skylines)[dir].distance (other) + padding;
633 b.translate (Offset (0, dir*dist));
634 elements[i]->translate_axis (dir*dist, Y_AXIS);
636 skylines->insert (b, 0, X_AXIS);
637 elements[i]->set_property ("outside-staff-priority", SCM_BOOL_F);
638 last_affected_position[dir] = b[X_AXIS][RIGHT];
642 Ugh: quadratic. --hwn
644 elements.erase (elements.begin () + i);
649 // TODO: it is tricky to correctly handle skyline placement of cross-staff grobs.
650 // For example, cross-staff beams cannot be formatted until the distance between
651 // staves is known and therefore any grobs that depend on the beam cannot be placed
652 // until the skylines are known. On the other hand, the distance between staves should
653 // really depend on position of the cross-staff grobs that lie between them.
654 // Currently, we just leave cross-staff grobs out of the
655 // skyline altogether, but this could mean that staves are placed so close together
656 // that there is no room for the cross-staff grob. It also means, of course, that
657 // we don't get the benefits of skyline placement for cross-staff grobs.
659 Axis_group_interface::skyline_spacing (Grob *me, vector<Grob*> elements)
661 /* For grobs with an outside-staff-priority, the sorting function might
662 call extent and cause suicide. This breaks the contract that is required
663 for the STL sort function. To avoid this, we make sure that any suicides
664 are triggered beforehand.
666 for (vsize i = 0; i < elements.size (); i++)
667 if (scm_is_number (elements[i]->get_property ("outside-staff-priority")))
668 elements[i]->extent (elements[i], X_AXIS);
670 vector_sort (elements, staff_priority_less);
671 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
672 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
674 assert (y_common == me);
679 Skyline_pair skylines;
680 for (i = 0; i < elements.size ()
681 && !scm_is_number (elements[i]->get_property ("outside-staff-priority")); i++)
682 if (!to_boolean (elements[i]->get_property ("cross-staff")))
683 add_boxes (elements[i], x_common, y_common, &boxes, &skylines);
685 SCM padding_scm = me->get_property ("skyline-horizontal-padding");
686 Real padding = robust_scm2double (padding_scm, 0.1);
687 skylines.merge (Skyline_pair (boxes, padding, X_AXIS));
688 for (; i < elements.size (); i++)
690 if (to_boolean (elements[i]->get_property ("cross-staff")))
693 SCM priority = elements[i]->get_property ("outside-staff-priority");
694 vector<Grob*> current_elts;
695 current_elts.push_back (elements[i]);
696 while (i + 1 < elements.size ()
697 && scm_eq_p (elements[i+1]->get_property ("outside-staff-priority"), priority))
699 if (!to_boolean (elements[i+1]->get_property ("cross-staff")))
700 current_elts.push_back (elements[i+1]);
704 add_grobs_of_one_priority (&skylines, current_elts, x_common, y_common);
706 skylines.shift (-me->relative_coordinate (x_common, X_AXIS));
710 MAKE_SCHEME_CALLBACK (Axis_group_interface, print, 1)
712 Axis_group_interface::print (SCM smob)
717 Grob *me = unsmob_grob (smob);
719 if (Skyline_pair *s = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
721 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[UP].to_points (X_AXIS))
722 .in_color (255, 0, 255));
723 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[DOWN].to_points (X_AXIS))
724 .in_color (0, 255, 255));
726 return ret.smobbed_copy ();
729 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_staff_staff_spacing, 3)
731 Axis_group_interface::calc_pure_staff_staff_spacing (SCM smob, SCM start, SCM end)
733 return calc_maybe_pure_staff_staff_spacing (unsmob_grob (smob),
739 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_staff_staff_spacing, 1)
741 Axis_group_interface::calc_staff_staff_spacing (SCM smob)
743 return calc_maybe_pure_staff_staff_spacing (unsmob_grob (smob),
750 Axis_group_interface::calc_maybe_pure_staff_staff_spacing (Grob *me, bool pure, int start, int end)
752 Grob *grouper = unsmob_grob (me->get_object ("staff-grouper"));
756 Grob *last_in_group = Staff_grouper_interface::get_maybe_pure_last_grob (grouper, pure, start, end);
757 if (me == last_in_group)
758 return grouper->get_maybe_pure_property ("staffgroup-staff-spacing", pure, start, end);
760 return grouper->get_maybe_pure_property ("staff-staff-spacing", pure, start, end);
762 return me->get_maybe_pure_property ("default-staff-staff-spacing", pure, start, end);
766 Axis_group_interface::minimum_distance (Grob *g1, Grob *g2, Axis a)
768 SCM sym = ly_symbol2scm ((a == Y_AXIS) ? "vertical-skylines" : "horizontal-skylines");
770 Skyline_pair *s1 = Skyline_pair::unsmob (g1->get_property (sym));
771 Skyline_pair *s2 = Skyline_pair::unsmob (g2->get_property (sym));
773 return (*s1)[DOWN].distance ((*s2)[UP]);
777 ADD_INTERFACE (Axis_group_interface,
778 "An object that groups other layout objects.",
780 // TODO: some of these properties are specific to
781 // VerticalAxisGroup. We should split off a
782 // vertical-axis-group-interface.
784 "adjacent-pure-heights "
786 "default-staff-staff-spacing "
790 "nonstaff-nonstaff-spacing "
791 "nonstaff-relatedstaff-spacing "
792 "nonstaff-unrelatedstaff-spacing "
793 "pure-relevant-grobs "
794 "pure-relevant-items "
795 "pure-relevant-spanners "
799 "staff-staff-spacing "