Use scalar instead of embedded_scm for context mod overrides.
[lilypond/mpolesky.git] / lily / axis-group-interface.cc
blob0e9ca9264cffa4dc79cbdf1d176a3a2dc3d3e797
1 /*
2 axis-group-interface.cc -- implement Axis_group_interface
4 source file of the GNU LilyPond music typesetter
6 (c) 2000--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
9 #include "axis-group-interface.hh"
11 #include "align-interface.hh"
12 #include "directional-element-interface.hh"
13 #include "grob-array.hh"
14 #include "hara-kiri-group-spanner.hh"
15 #include "international.hh"
16 #include "lookup.hh"
17 #include "paper-column.hh"
18 #include "paper-score.hh"
19 #include "pointer-group-interface.hh"
20 #include "separation-item.hh"
21 #include "skyline-pair.hh"
22 #include "staff-grouper-interface.hh"
23 #include "stencil.hh"
24 #include "system.hh"
25 #include "warn.hh"
27 void
28 Axis_group_interface::add_element (Grob *me, Grob *e)
30 SCM axes = me->get_property ("axes");
31 if (!scm_is_pair (axes))
32 programming_error ("axes should be nonempty");
34 for (SCM ax = axes; scm_is_pair (ax); ax = scm_cdr (ax))
36 Axis a = (Axis) scm_to_int (scm_car (ax));
38 if (!e->get_parent (a))
39 e->set_parent (me, a);
41 e->set_object ((a == X_AXIS)
42 ? ly_symbol2scm ("axis-group-parent-X")
43 : ly_symbol2scm ("axis-group-parent-Y"),
44 me->self_scm ());
47 /* must be ordered, because Align_interface also uses
48 Axis_group_interface */
49 Pointer_group_interface::add_grob (me, ly_symbol2scm ("elements"), e);
52 bool
53 Axis_group_interface::has_axis (Grob *me, Axis a)
55 SCM axes = me->get_property ("axes");
57 return (SCM_BOOL_F != scm_memq (scm_from_int (a), axes));
60 Interval
61 Axis_group_interface::relative_group_extent (vector<Grob*> const &elts,
62 Grob *common, Axis a)
64 Interval r;
65 for (vsize i = 0; i < elts.size (); i++)
67 Grob *se = elts[i];
68 if (!to_boolean (se->get_property ("cross-staff")))
70 Interval dims = se->extent (common, a);
71 if (!dims.is_empty ())
72 r.unite (dims);
75 return r;
78 Interval
79 Axis_group_interface::cached_pure_height (Grob *me, int start, int end)
81 Interval iv = begin_of_line_pure_height (me, start);
82 iv.unite (rest_of_line_pure_height (me, start, end));
84 return iv;
87 Interval
88 Axis_group_interface::rest_of_line_pure_height (Grob *me, int start, int end)
90 SCM adjacent_pure_heights = me->get_property ("adjacent-pure-heights");
92 if (!scm_is_pair (adjacent_pure_heights)
93 || !scm_is_vector (scm_cdr (adjacent_pure_heights)))
94 return Interval (0, 0);
96 return combine_pure_heights (me, scm_cdr (adjacent_pure_heights), start, end);
99 Interval
100 Axis_group_interface::begin_of_line_pure_height (Grob *me, int start)
102 SCM adjacent_pure_heights = me->get_property ("adjacent-pure-heights");
104 if (!scm_is_pair (adjacent_pure_heights)
105 || !scm_is_vector (scm_car (adjacent_pure_heights)))
106 return Interval (0, 0);
108 return combine_pure_heights (me, scm_car (adjacent_pure_heights), start, start+1);
111 Interval
112 Axis_group_interface::combine_pure_heights (Grob *me, SCM measure_extents, int start, int end)
114 Paper_score *ps = get_root_system (me)->paper_score ();
115 vector<vsize> breaks = ps->get_break_indices ();
116 vector<Grob*> cols = ps->get_columns ();
118 Interval ext;
119 for (vsize i = 0; i + 1 < breaks.size (); i++)
121 int r = Paper_column::get_rank (cols[breaks[i]]);
122 if (r >= end)
123 break;
125 if (r >= start)
126 ext.unite (ly_scm2interval (scm_c_vector_ref (measure_extents, i)));
129 return ext;
132 // adjacent-pure-heights is a pair of vectors, each of which has one element
133 // for every measure in the score. The first vector stores, for each measure,
134 // the combined height of the elements that are present only when the bar
135 // is at the beginning of a line. The second vector stores, for each measure,
136 // the combined height of the elements that are present only when the bar
137 // is not at the beginning of a line.
139 MAKE_SCHEME_CALLBACK (Axis_group_interface, adjacent_pure_heights, 1)
141 Axis_group_interface::adjacent_pure_heights (SCM smob)
143 Grob *me = unsmob_grob (smob);
145 Grob *common = calc_pure_elts_and_common (me);
146 extract_grob_set (me, "pure-relevant-items", items);
147 extract_grob_set (me, "pure-relevant-spanners", spanners);
149 Paper_score *ps = get_root_system (me)->paper_score ();
150 vector<vsize> breaks = ps->get_break_indices ();
151 vector<Grob*> cols = ps->get_columns ();
153 SCM begin_line_heights = scm_c_make_vector (breaks.size () - 1, SCM_EOL);
154 SCM mid_line_heights = scm_c_make_vector (breaks.size () - 1, SCM_EOL);
156 vsize it_index = 0;
157 for (vsize i = 0; i + 1 < breaks.size (); i++)
159 int start = Paper_column::get_rank (cols[breaks[i]]);
160 int end = Paper_column::get_rank (cols[breaks[i+1]]);
161 Interval begin_line_iv;
162 Interval mid_line_iv;
164 for (vsize j = it_index; j < items.size (); j++)
166 Item *it = dynamic_cast<Item*> (items[j]);
167 int rank = it->get_column ()->get_rank ();
169 if (rank <= end && it->pure_is_visible (start, end)
170 && !to_boolean (it->get_property ("cross-staff")))
172 Interval dims = items[j]->pure_height (common, start, end);
173 Interval &target_iv = start == it->get_column ()->get_rank () ? begin_line_iv : mid_line_iv;
175 if (!dims.is_empty ())
176 target_iv.unite (dims);
179 if (rank < end)
180 it_index++;
181 else if (rank > end)
182 break;
185 for (vsize j = 0; j < spanners.size (); j++)
187 Interval_t<int> rank_span = spanners[j]->spanned_rank_interval ();
188 if (rank_span[LEFT] <= end && rank_span[RIGHT] >= start
189 && !to_boolean (spanners[j]->get_property ("cross-staff")))
191 Interval dims = spanners[j]->pure_height (common, start, end);
193 if (!dims.is_empty ())
194 mid_line_iv.unite (dims);
198 scm_vector_set_x (begin_line_heights, scm_from_int (i), ly_interval2scm (begin_line_iv));
199 scm_vector_set_x (mid_line_heights, scm_from_int (i), ly_interval2scm (mid_line_iv));
201 return scm_cons (begin_line_heights, mid_line_heights);
204 Interval
205 Axis_group_interface::relative_pure_height (Grob *me, int start, int end)
207 /* It saves a _lot_ of time if we assume a VerticalAxisGroup is additive
208 (ie. height (i, k) = max (height (i, j) height (j, k)) for all i <= j <= k).
209 Unfortunately, it isn't always true, particularly if there is a
210 VerticalAlignment somewhere in the descendants.
212 Apart from PianoStaff, which has a fixed VerticalAlignment so it doesn't
213 count, the only VerticalAlignment comes from Score. This makes it
214 reasonably safe to assume that if our parent is a VerticalAlignment,
215 we can assume additivity and cache things nicely. */
216 Grob *p = me->get_parent (Y_AXIS);
217 if (p && Align_interface::has_interface (p))
218 return Axis_group_interface::cached_pure_height (me, start, end);
220 Grob *common = calc_pure_elts_and_common (me);
221 extract_grob_set (me, "pure-relevant-items", items);
222 extract_grob_set (me, "pure-relevant-spanners", spanners);
224 Interval r;
226 for (vsize i = 0; i < items.size (); i++)
228 Item *it = dynamic_cast<Item*> (items[i]);
229 int rank = it->get_column ()->get_rank ();
231 if (rank > end)
232 break;
233 else if (rank >= start && it->pure_is_visible (start, end)
234 && !to_boolean (it->get_property ("cross-staff")))
236 Interval dims = it->pure_height (common, start, end);
237 if (!dims.is_empty ())
238 r.unite (dims);
242 for (vsize i = 0; i < spanners.size (); i++)
244 Interval_t<int> rank_span = spanners[i]->spanned_rank_interval ();
245 if (rank_span[LEFT] <= end && rank_span[RIGHT] >= start
246 && !to_boolean (spanners[i]->get_property ("cross-staff")))
248 Interval dims = spanners[i]->pure_height (common, start, end);
249 if (!dims.is_empty ())
250 r.unite (dims);
253 return r;
256 MAKE_SCHEME_CALLBACK (Axis_group_interface, width, 1);
258 Axis_group_interface::width (SCM smob)
260 Grob *me = unsmob_grob (smob);
261 return generic_group_extent (me, X_AXIS);
264 MAKE_SCHEME_CALLBACK (Axis_group_interface, height, 1);
266 Axis_group_interface::height (SCM smob)
268 Grob *me = unsmob_grob (smob);
269 return generic_group_extent (me, Y_AXIS);
272 MAKE_SCHEME_CALLBACK (Axis_group_interface, pure_height, 3);
274 Axis_group_interface::pure_height (SCM smob, SCM start_scm, SCM end_scm)
276 int start = robust_scm2int (start_scm, 0);
277 int end = robust_scm2int (end_scm, INT_MAX);
278 Grob *me = unsmob_grob (smob);
280 /* Maybe we are in the second pass of a two-pass spacing run. In that
281 case, the Y-extent of a system is already given to us */
282 System *system = dynamic_cast<System*> (me);
283 if (system)
285 SCM line_break_details = system->column (start)->get_property ("line-break-system-details");
286 SCM system_y_extent = scm_assq (ly_symbol2scm ("system-Y-extent"), line_break_details);
287 if (scm_is_pair (system_y_extent))
288 return scm_cdr (system_y_extent);
291 return ly_interval2scm (pure_group_height (me, start, end));
294 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_skylines, 1);
296 Axis_group_interface::calc_skylines (SCM smob)
298 Grob *me = unsmob_grob (smob);
299 extract_grob_set (me, "elements", elts);
300 Skyline_pair skylines = skyline_spacing (me, elts);
302 return skylines.smobbed_copy ();
305 /* whereas calc_skylines calculates skylines for axis-groups with a lot of
306 visible children, combine_skylines is designed for axis-groups whose only
307 children are other axis-groups (ie. VerticalAlignment). Rather than
308 calculating all the skylines from scratch, we just merge the skylines
309 of the children.
311 MAKE_SCHEME_CALLBACK (Axis_group_interface, combine_skylines, 1);
313 Axis_group_interface::combine_skylines (SCM smob)
315 Grob *me = unsmob_grob (smob);
316 extract_grob_set (me, "elements", elements);
317 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
318 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
320 if (y_common != me)
321 programming_error ("combining skylines that don't belong to me");
323 Skyline_pair ret;
324 for (vsize i = 0; i < elements.size (); i++)
326 SCM skyline_scm = elements[i]->get_property ("vertical-skylines");
327 if (Skyline_pair::unsmob (skyline_scm))
329 Real offset = elements[i]->relative_coordinate (y_common, Y_AXIS);
330 Skyline_pair other = *Skyline_pair::unsmob (skyline_scm);
331 other.raise (offset);
332 other.shift (elements[i]->relative_coordinate (x_common, X_AXIS));
333 ret.merge (other);
336 return ret.smobbed_copy ();
340 Axis_group_interface::generic_group_extent (Grob *me, Axis a)
342 /* trigger the callback to do skyline-spacing on the children */
343 if (a == Y_AXIS)
344 (void) me->get_property ("vertical-skylines");
346 extract_grob_set (me, "elements", elts);
347 Grob *common = common_refpoint_of_array (elts, me, a);
349 Real my_coord = me->relative_coordinate (common, a);
350 Interval r (relative_group_extent (elts, common, a));
352 return ly_interval2scm (r - my_coord);
355 /* This is like generic_group_extent, but it only counts the grobs that
356 are children of some other axis-group. This is uncached; if it becomes
357 commonly used, it may be necessary to cache it somehow. */
358 Interval
359 Axis_group_interface::staff_extent (Grob *me, Grob *refp, Axis ext_a, Grob *staff, Axis parent_a)
361 extract_grob_set (me, "elements", elts);
362 vector<Grob*> new_elts;
364 for (vsize i = 0; i < elts.size (); i++)
365 if (elts[i]->common_refpoint (staff, parent_a) == staff)
366 new_elts.push_back (elts[i]);
368 return relative_group_extent (new_elts, refp, ext_a);
372 Grob *
373 Axis_group_interface::calc_pure_elts_and_common (Grob *me)
375 if (Grob *c = unsmob_grob (me->get_object ("pure-Y-common")))
376 return c;
378 extract_grob_set (me, "elements", elts);
380 vector<Grob*> relevant_items;
381 vector<Grob*> relevant_spanners;
382 SCM pure_relevant_p = ly_lily_module_constant ("pure-relevant?");
384 for (vsize i = 0; i < elts.size (); i++)
386 if (to_boolean (scm_apply_1 (pure_relevant_p, elts[i]->self_scm (), SCM_EOL)))
388 if (dynamic_cast<Item*> (elts[i]))
389 relevant_items.push_back (elts[i]);
390 else if (dynamic_cast<Spanner*> (elts[i]))
391 relevant_spanners.push_back (elts[i]);
395 Item *it = dynamic_cast<Item*> (elts[i]);
396 Direction d = LEFT;
397 if (it)
400 Item *piece = it->find_prebroken_piece (d);
401 if (piece && to_boolean (scm_apply_1 (pure_relevant_p, piece->self_scm (), SCM_EOL)))
402 relevant_items.push_back (piece);
404 while (flip (&d) != LEFT);
406 vector_sort (relevant_items, Item::less);
408 Grob *common = common_refpoint_of_array (relevant_items, me, Y_AXIS);
409 common = common_refpoint_of_array (relevant_spanners, common, Y_AXIS);
411 me->set_object ("pure-Y-common", common->self_scm ());
413 SCM items_scm = Grob_array::make_array ();
414 SCM spanners_scm = Grob_array::make_array ();
416 unsmob_grob_array (items_scm)->set_array (relevant_items);
417 unsmob_grob_array (spanners_scm)->set_array (relevant_spanners);
418 me->set_object ("pure-relevant-items", items_scm);
419 me->set_object ("pure-relevant-spanners", spanners_scm);
421 return common;
425 Axis_group_interface::calc_common (Grob *me, Axis axis)
427 extract_grob_set (me, "elements", elts);
428 Grob *common = common_refpoint_of_array (elts, me, axis);
429 if (!common)
431 me->programming_error ("No common parent found in calc_common axis.");
432 return SCM_EOL;
435 return common->self_scm ();
439 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_x_common, 1);
441 Axis_group_interface::calc_x_common (SCM grob)
443 return calc_common (unsmob_grob (grob), X_AXIS);
446 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_y_common, 1);
448 Axis_group_interface::calc_y_common (SCM grob)
450 return calc_common (unsmob_grob (grob), Y_AXIS);
453 Interval
454 Axis_group_interface::pure_group_height (Grob *me, int start, int end)
456 Grob *common = calc_pure_elts_and_common (me);
458 Real my_coord = me->relative_coordinate (common, Y_AXIS);
459 Interval r (relative_pure_height (me, start, end));
461 return r - my_coord;
464 void
465 Axis_group_interface::get_children (Grob *me, vector<Grob*> *found)
467 found->push_back (me);
469 if (!has_interface (me))
470 return;
472 extract_grob_set (me, "elements", elements);
473 for (vsize i = 0; i < elements.size (); i++)
475 Grob *e = elements[i];
476 Axis_group_interface::get_children (e, found);
480 bool
481 staff_priority_less (Grob * const &g1, Grob * const &g2)
483 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
484 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
486 if (priority_1 < priority_2)
487 return true;
488 else if (priority_1 > priority_2)
489 return false;
491 /* if neither grob has an outside-staff priority, the ordering will have no
492 effect -- we just need to choose a consistent ordering. We do this to
493 avoid the side-effect of calculating extents. */
494 if (isinf (priority_1))
495 return g1 < g2;
497 /* if there is no preference in staff priority, choose the left-most one */
498 Grob *common = g1->common_refpoint (g2, X_AXIS);
499 Real start_1 = g1->extent (common, X_AXIS)[LEFT];
500 Real start_2 = g2->extent (common, X_AXIS)[LEFT];
501 return start_1 < start_2;
504 static void
505 add_boxes (Grob *me, Grob *x_common, Grob *y_common, vector<Box> *const boxes, Skyline_pair *skylines)
507 /* if a child has skylines, use them instead of the extent box */
508 if (Skyline_pair *pair = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
510 Skyline_pair s = *pair;
511 s.shift (me->relative_coordinate (x_common, X_AXIS));
512 s.raise (me->relative_coordinate (y_common, Y_AXIS));
513 skylines->merge (s);
515 else if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements")))
517 for (vsize i = 0; i < elements->size (); i++)
518 add_boxes (elements->grob (i), x_common, y_common, boxes, skylines);
520 else if (!scm_is_number (me->get_property ("outside-staff-priority"))
521 && !to_boolean (me->get_property ("cross-staff")))
523 boxes->push_back (Box (me->extent (x_common, X_AXIS),
524 me->extent (y_common, Y_AXIS)));
528 /* We want to avoid situations like this:
529 still more text
530 more text
531 text
532 -------------------
533 staff
534 -------------------
536 The point is that "still more text" should be positioned under
537 "more text". In order to achieve this, we place the grobs in several
538 passes. We keep track of the right-most horizontal position that has been
539 affected by the current pass so far (actually we keep track of 2
540 positions, one for above the staff, one for below).
542 In each pass, we loop through the unplaced grobs from left to right.
543 If the grob doesn't overlap the right-most affected position, we place it
544 (and then update the right-most affected position to point to the right
545 edge of the just-placed grob). Otherwise, we skip it until the next pass.
547 static void
548 add_grobs_of_one_priority (Skyline_pair *const skylines,
549 vector<Grob*> elements,
550 Grob *x_common,
551 Grob *y_common)
553 vector<Box> boxes;
554 Drul_array<Real> last_affected_position;
556 reverse (elements);
557 while (!elements.empty ())
559 last_affected_position[UP] = -infinity_f;
560 last_affected_position[DOWN] = -infinity_f;
561 /* do one pass */
562 for (vsize i = elements.size (); i--;)
564 Direction dir = get_grob_direction (elements[i]);
565 if (dir == CENTER)
567 warning (_ ("an outside-staff object should have a direction, defaulting to up"));
568 dir = UP;
571 Box b (elements[i]->extent (x_common, X_AXIS),
572 elements[i]->extent (y_common, Y_AXIS));
573 SCM horizon_padding_scm = elements[i]->get_property ("outside-staff-horizontal-padding");
574 Real horizon_padding = robust_scm2double (horizon_padding_scm, 0.0);
576 if (b[X_AXIS][LEFT] - 2*horizon_padding < last_affected_position[dir])
577 continue;
579 if (!b[X_AXIS].is_empty () && !b[Y_AXIS].is_empty ())
581 boxes.clear ();
582 boxes.push_back (b);
583 Skyline other = Skyline (boxes, horizon_padding, X_AXIS, -dir);
584 Real padding = robust_scm2double (elements[i]->get_property ("outside-staff-padding"), 0.5);
585 Real dist = (*skylines)[dir].distance (other) + padding;
587 if (dist > 0)
589 b.translate (Offset (0, dir*dist));
590 elements[i]->translate_axis (dir*dist, Y_AXIS);
592 skylines->insert (b, 0, X_AXIS);
593 elements[i]->set_property ("outside-staff-priority", SCM_BOOL_F);
594 last_affected_position[dir] = b[X_AXIS][RIGHT];
598 Ugh: quadratic. --hwn
600 elements.erase (elements.begin () + i);
605 // TODO: it is tricky to correctly handle skyline placement of cross-staff grobs.
606 // For example, cross-staff beams cannot be formatted until the distance between
607 // staves is known and therefore any grobs that depend on the beam cannot be placed
608 // until the skylines are known. On the other hand, the distance between staves should
609 // really depend on position of the cross-staff grobs that lie between them.
610 // Currently, we just leave cross-staff grobs out of the
611 // skyline altogether, but this could mean that staves are placed so close together
612 // that there is no room for the cross-staff grob. It also means, of course, that
613 // we don't get the benefits of skyline placement for cross-staff grobs.
614 Skyline_pair
615 Axis_group_interface::skyline_spacing (Grob *me, vector<Grob*> elements)
617 /* For grobs with an outside-staff-priority, the sorting function might
618 call extent and cause suicide. This breaks the contract that is required
619 for the STL sort function. To avoid this, we make sure that any suicides
620 are triggered beforehand.
622 for (vsize i = 0; i < elements.size (); i++)
623 if (scm_is_number (elements[i]->get_property ("outside-staff-priority")))
624 elements[i]->extent (elements[i], X_AXIS);
626 vector_sort (elements, staff_priority_less);
627 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
628 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
630 assert (y_common == me);
632 vsize i = 0;
633 vector<Box> boxes;
635 Skyline_pair skylines;
636 for (i = 0; i < elements.size ()
637 && !scm_is_number (elements[i]->get_property ("outside-staff-priority")); i++)
638 if (!to_boolean (elements[i]->get_property ("cross-staff")))
639 add_boxes (elements[i], x_common, y_common, &boxes, &skylines);
641 SCM padding_scm = me->get_property ("skyline-horizontal-padding");
642 Real padding = robust_scm2double (padding_scm, 0.1);
643 skylines.merge (Skyline_pair (boxes, padding, X_AXIS));
644 for (; i < elements.size (); i++)
646 if (to_boolean (elements[i]->get_property ("cross-staff")))
647 continue;
649 SCM priority = elements[i]->get_property ("outside-staff-priority");
650 vector<Grob*> current_elts;
651 current_elts.push_back (elements[i]);
652 while (i + 1 < elements.size ()
653 && scm_eq_p (elements[i+1]->get_property ("outside-staff-priority"), priority))
655 if (!to_boolean (elements[i+1]->get_property ("cross-staff")))
656 current_elts.push_back (elements[i+1]);
657 ++i;
660 add_grobs_of_one_priority (&skylines, current_elts, x_common, y_common);
662 skylines.shift (-me->relative_coordinate (x_common, X_AXIS));
663 return skylines;
666 MAKE_SCHEME_CALLBACK (Axis_group_interface, print, 1)
668 Axis_group_interface::print (SCM smob)
670 if (!debug_skylines)
671 return SCM_BOOL_F;
673 Grob *me = unsmob_grob (smob);
674 Stencil ret;
675 if (Skyline_pair *s = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
677 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[UP].to_points (X_AXIS))
678 .in_color (255, 0, 255));
679 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[DOWN].to_points (X_AXIS))
680 .in_color (0, 255, 255));
682 return ret.smobbed_copy ();
685 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_next_staff_spacing, 1)
687 Axis_group_interface::calc_next_staff_spacing (SCM smob)
689 Grob *me = unsmob_grob (smob);
690 Grob *grouper = unsmob_grob (me->get_object ("staff-grouper"));
692 if (grouper)
694 Grob *last_in_group = Staff_grouper_interface::get_last_grob (grouper);
695 if (me == last_in_group)
696 return grouper->get_property ("after-last-staff-spacing");
697 else
698 return grouper->get_property ("between-staff-spacing");
700 return me->get_property ("default-next-staff-spacing");
703 Real
704 Axis_group_interface::minimum_distance (Grob *g1, Grob *g2, Axis a)
706 SCM sym = ly_symbol2scm ((a == Y_AXIS) ? "vertical-skylines" : "horizontal-skylines");
708 Skyline_pair *s1 = Skyline_pair::unsmob (g1->get_property (sym));
709 Skyline_pair *s2 = Skyline_pair::unsmob (g2->get_property (sym));
710 if (s1 && s2)
711 return (*s1)[DOWN].distance ((*s2)[UP]);
712 return 0;
715 ADD_INTERFACE (Axis_group_interface,
716 "An object that groups other layout objects.",
718 // TODO: some of these properties are specific to
719 // VerticalAxisGroup. We should split off a
720 // vertical-axis-group-interface.
721 /* properties */
722 "X-common "
723 "Y-common "
724 "adjacent-pure-heights "
725 "axes "
726 "default-next-staff-spacing "
727 "elements "
728 "inter-loose-line-spacing "
729 "inter-staff-spacing "
730 "keep-fixed-while-stretching "
731 "max-stretch "
732 "non-affinity-spacing "
733 "next-staff-spacing "
734 "no-alignment "
735 "pure-Y-common "
736 "pure-relevant-items "
737 "pure-relevant-spanners "
738 "staff-affinity "
739 "staff-grouper "
740 "system-Y-offset "
741 "vertical-skylines "