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>
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"
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"
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"),
47 /* must be ordered, because Align_interface also uses
48 Axis_group_interface */
49 Pointer_group_interface::add_grob (me
, ly_symbol2scm ("elements"), e
);
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
));
61 Axis_group_interface::relative_group_extent (vector
<Grob
*> const &elts
,
65 for (vsize i
= 0; i
< elts
.size (); i
++)
68 if (!to_boolean (se
->get_property ("cross-staff")))
70 Interval dims
= se
->extent (common
, a
);
71 if (!dims
.is_empty ())
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
));
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
);
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);
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 ();
119 for (vsize i
= 0; i
+ 1 < breaks
.size (); i
++)
121 int r
= Paper_column::get_rank (cols
[breaks
[i
]]);
126 ext
.unite (ly_scm2interval (scm_c_vector_ref (measure_extents
, i
)));
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
);
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
);
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
);
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
);
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 ();
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 ())
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 ())
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
);
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
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
);
321 programming_error ("combining skylines that don't belong to me");
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
));
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 */
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. */
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
);
373 Axis_group_interface::calc_pure_elts_and_common (Grob
*me
)
375 if (Grob
*c
= unsmob_grob (me
->get_object ("pure-Y-common")))
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
]);
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
);
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
);
431 me
->programming_error ("No common parent found in calc_common axis.");
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
);
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
));
465 Axis_group_interface::get_children (Grob
*me
, vector
<Grob
*> *found
)
467 found
->push_back (me
);
469 if (!has_interface (me
))
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
);
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
)
488 else if (priority_1
> priority_2
)
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
))
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
;
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
));
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:
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.
548 add_grobs_of_one_priority (Skyline_pair
*const skylines
,
549 vector
<Grob
*> elements
,
554 Drul_array
<Real
> last_affected_position
;
557 while (!elements
.empty ())
559 last_affected_position
[UP
] = -infinity_f
;
560 last_affected_position
[DOWN
] = -infinity_f
;
562 for (vsize i
= elements
.size (); i
--;)
564 Direction dir
= get_grob_direction (elements
[i
]);
567 warning (_ ("an outside-staff object should have a direction, defaulting to 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
])
579 if (!b
[X_AXIS
].is_empty () && !b
[Y_AXIS
].is_empty ())
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
;
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.
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
);
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")))
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]);
660 add_grobs_of_one_priority (&skylines
, current_elts
, x_common
, y_common
);
662 skylines
.shift (-me
->relative_coordinate (x_common
, X_AXIS
));
666 MAKE_SCHEME_CALLBACK (Axis_group_interface
, print
, 1)
668 Axis_group_interface::print (SCM smob
)
673 Grob
*me
= unsmob_grob (smob
);
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"));
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");
698 return grouper
->get_property ("between-staff-spacing");
700 return me
->get_property ("default-next-staff-spacing");
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
));
711 return (*s1
)[DOWN
].distance ((*s2
)[UP
]);
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.
724 "adjacent-pure-heights "
726 "default-next-staff-spacing "
728 "inter-loose-line-spacing "
729 "inter-staff-spacing "
730 "keep-fixed-while-stretching "
732 "non-affinity-spacing "
733 "next-staff-spacing "
736 "pure-relevant-items "
737 "pure-relevant-spanners "