LSR: Update.
[lilypond/mpolesky.git] / lily / axis-group-interface.cc
blob18994f19fd94ae575f8cd090cc5c6c1518b0704b
1 /*
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"
27 #include "lookup.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"
34 #include "stencil.hh"
35 #include "system.hh"
36 #include "warn.hh"
38 void
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"),
55 me->self_scm ());
58 /* must be ordered, because Align_interface also uses
59 Axis_group_interface */
60 Pointer_group_interface::add_grob (me, ly_symbol2scm ("elements"), e);
63 bool
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));
71 Interval
72 Axis_group_interface::relative_group_extent (vector<Grob*> const &elts,
73 Grob *common, Axis a)
75 Interval r;
76 for (vsize i = 0; i < elts.size (); i++)
78 Grob *se = elts[i];
79 if (!to_boolean (se->get_property ("cross-staff")))
81 Interval dims = se->extent (common, a);
82 if (!dims.is_empty ())
83 r.unite (dims);
86 return r;
89 Interval
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));
95 return iv;
98 Interval
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);
110 Interval
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);
122 Interval
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 ();
129 Interval ext;
130 for (vsize i = 0; i + 1 < breaks.size (); i++)
132 int r = Paper_column::get_rank (cols[breaks[i]]);
133 if (r >= end)
134 break;
136 if (r >= start)
137 ext.unite (ly_scm2interval (scm_c_vector_ref (measure_extents, i)));
140 return ext;
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);
167 vsize it_index = 0;
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 // Take grobs that are visible with respect to a slightly longer line.
174 // Otherwise, we will never include grobs at breakpoints which aren't
175 // end-of-line-visible.
176 int visibility_end = i + 2 < breaks.size () ?
177 Paper_column::get_rank (cols[breaks[i+2]]) : end;
179 Interval begin_line_iv;
180 Interval mid_line_iv;
182 for (vsize j = it_index; j < items.size (); j++)
184 Item *it = dynamic_cast<Item*> (items[j]);
185 int rank = it->get_column ()->get_rank ();
187 if (rank <= end && it->pure_is_visible (start, visibility_end)
188 && !to_boolean (it->get_property ("cross-staff")))
190 Interval dims = items[j]->pure_height (common, start, end);
191 Interval &target_iv = start == it->get_column ()->get_rank () ? begin_line_iv : mid_line_iv;
193 if (!dims.is_empty ())
194 target_iv.unite (dims);
197 if (rank < end)
198 it_index++;
199 else if (rank > end)
200 break;
203 for (vsize j = 0; j < spanners.size (); j++)
205 Interval_t<int> rank_span = spanners[j]->spanned_rank_interval ();
206 if (rank_span[LEFT] <= end && rank_span[RIGHT] >= start
207 && !to_boolean (spanners[j]->get_property ("cross-staff")))
209 Interval dims = spanners[j]->pure_height (common, start, end);
211 if (!dims.is_empty ())
213 mid_line_iv.unite (dims);
214 if (rank_span[LEFT] <= start)
215 begin_line_iv.unite (dims);
220 scm_vector_set_x (begin_line_heights, scm_from_int (i), ly_interval2scm (begin_line_iv));
221 scm_vector_set_x (mid_line_heights, scm_from_int (i), ly_interval2scm (mid_line_iv));
223 return scm_cons (begin_line_heights, mid_line_heights);
226 Interval
227 Axis_group_interface::relative_pure_height (Grob *me, int start, int end)
229 /* It saves a _lot_ of time if we assume a VerticalAxisGroup is additive
230 (ie. height (i, k) = max (height (i, j) height (j, k)) for all i <= j <= k).
231 Unfortunately, it isn't always true, particularly if there is a
232 VerticalAlignment somewhere in the descendants.
234 Apart from PianoStaff, which has a fixed VerticalAlignment so it doesn't
235 count, the only VerticalAlignment comes from Score. This makes it
236 reasonably safe to assume that if our parent is a VerticalAlignment,
237 we can assume additivity and cache things nicely. */
238 Grob *p = me->get_parent (Y_AXIS);
239 if (p && Align_interface::has_interface (p))
240 return Axis_group_interface::cached_pure_height (me, start, end);
242 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
243 extract_grob_set (me, "pure-relevant-items", items);
244 extract_grob_set (me, "pure-relevant-spanners", spanners);
246 Interval r;
248 for (vsize i = 0; i < items.size (); i++)
250 Item *it = dynamic_cast<Item*> (items[i]);
251 int rank = it->get_column ()->get_rank ();
253 if (rank > end)
254 break;
255 else if (rank >= start && it->pure_is_visible (start, end)
256 && !to_boolean (it->get_property ("cross-staff")))
258 Interval dims = it->pure_height (common, start, end);
259 if (!dims.is_empty ())
260 r.unite (dims);
264 for (vsize i = 0; i < spanners.size (); i++)
266 Interval_t<int> rank_span = spanners[i]->spanned_rank_interval ();
267 if (rank_span[LEFT] <= end && rank_span[RIGHT] >= start
268 && !to_boolean (spanners[i]->get_property ("cross-staff")))
270 Interval dims = spanners[i]->pure_height (common, start, end);
271 if (!dims.is_empty ())
272 r.unite (dims);
275 return r;
278 MAKE_SCHEME_CALLBACK (Axis_group_interface, width, 1);
280 Axis_group_interface::width (SCM smob)
282 Grob *me = unsmob_grob (smob);
283 return generic_group_extent (me, X_AXIS);
286 MAKE_SCHEME_CALLBACK (Axis_group_interface, height, 1);
288 Axis_group_interface::height (SCM smob)
290 Grob *me = unsmob_grob (smob);
291 return generic_group_extent (me, Y_AXIS);
294 MAKE_SCHEME_CALLBACK (Axis_group_interface, pure_height, 3);
296 Axis_group_interface::pure_height (SCM smob, SCM start_scm, SCM end_scm)
298 int start = robust_scm2int (start_scm, 0);
299 int end = robust_scm2int (end_scm, INT_MAX);
300 Grob *me = unsmob_grob (smob);
302 /* Maybe we are in the second pass of a two-pass spacing run. In that
303 case, the Y-extent of a system is already given to us */
304 System *system = dynamic_cast<System*> (me);
305 if (system)
307 SCM line_break_details = system->column (start)->get_property ("line-break-system-details");
308 SCM system_y_extent = scm_assq (ly_symbol2scm ("system-Y-extent"), line_break_details);
309 if (scm_is_pair (system_y_extent))
310 return scm_cdr (system_y_extent);
313 return ly_interval2scm (pure_group_height (me, start, end));
316 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_skylines, 1);
318 Axis_group_interface::calc_skylines (SCM smob)
320 Grob *me = unsmob_grob (smob);
321 extract_grob_set (me, "elements", elts);
322 Skyline_pair skylines = skyline_spacing (me, elts);
324 return skylines.smobbed_copy ();
327 /* whereas calc_skylines calculates skylines for axis-groups with a lot of
328 visible children, combine_skylines is designed for axis-groups whose only
329 children are other axis-groups (ie. VerticalAlignment). Rather than
330 calculating all the skylines from scratch, we just merge the skylines
331 of the children.
333 MAKE_SCHEME_CALLBACK (Axis_group_interface, combine_skylines, 1);
335 Axis_group_interface::combine_skylines (SCM smob)
337 Grob *me = unsmob_grob (smob);
338 extract_grob_set (me, "elements", elements);
339 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
340 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
342 if (y_common != me)
343 programming_error ("combining skylines that don't belong to me");
345 Skyline_pair ret;
346 for (vsize i = 0; i < elements.size (); i++)
348 SCM skyline_scm = elements[i]->get_property ("vertical-skylines");
349 if (Skyline_pair::unsmob (skyline_scm))
351 Real offset = elements[i]->relative_coordinate (y_common, Y_AXIS);
352 Skyline_pair other = *Skyline_pair::unsmob (skyline_scm);
353 other.raise (offset);
354 other.shift (elements[i]->relative_coordinate (x_common, X_AXIS));
355 ret.merge (other);
358 return ret.smobbed_copy ();
362 Axis_group_interface::generic_group_extent (Grob *me, Axis a)
364 /* trigger the callback to do skyline-spacing on the children */
365 if (a == Y_AXIS)
366 (void) me->get_property ("vertical-skylines");
368 extract_grob_set (me, "elements", elts);
369 Grob *common = common_refpoint_of_array (elts, me, a);
371 Real my_coord = me->relative_coordinate (common, a);
372 Interval r (relative_group_extent (elts, common, a));
374 return ly_interval2scm (r - my_coord);
377 /* This is like generic_group_extent, but it only counts the grobs that
378 are children of some other axis-group. This is uncached; if it becomes
379 commonly used, it may be necessary to cache it somehow. */
380 Interval
381 Axis_group_interface::staff_extent (Grob *me, Grob *refp, Axis ext_a, Grob *staff, Axis parent_a)
383 extract_grob_set (me, "elements", elts);
384 vector<Grob*> new_elts;
386 for (vsize i = 0; i < elts.size (); i++)
387 if (elts[i]->common_refpoint (staff, parent_a) == staff)
388 new_elts.push_back (elts[i]);
390 return relative_group_extent (new_elts, refp, ext_a);
394 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_relevant_grobs, 1);
396 Axis_group_interface::calc_pure_relevant_grobs (SCM smob)
398 Grob *me = unsmob_grob (smob);
400 extract_grob_set (me, "elements", elts);
402 vector<Grob*> relevant_grobs;
403 SCM pure_relevant_p = ly_lily_module_constant ("pure-relevant?");
405 for (vsize i = 0; i < elts.size (); i++)
407 if (to_boolean (scm_apply_1 (pure_relevant_p, elts[i]->self_scm (), SCM_EOL)))
408 relevant_grobs.push_back (elts[i]);
410 if (Item *it = dynamic_cast<Item*> (elts[i]))
412 Direction d = LEFT;
415 Item *piece = it->find_prebroken_piece (d);
416 if (piece && to_boolean (scm_apply_1 (pure_relevant_p, piece->self_scm (), SCM_EOL)))
417 relevant_grobs.push_back (piece);
419 while (flip (&d) != LEFT);
423 SCM grobs_scm = Grob_array::make_array ();
424 unsmob_grob_array (grobs_scm)->set_array (relevant_grobs);
426 return grobs_scm;
429 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_relevant_items, 1);
431 Axis_group_interface::calc_pure_relevant_items (SCM smob)
433 Grob *me = unsmob_grob (smob);
435 extract_grob_set (me, "pure-relevant-grobs", elts);
437 vector<Grob*> relevant_items;
438 for (vsize i = 0; i < elts.size (); i++)
439 if (Item *it = dynamic_cast<Item*> (elts[i]))
440 relevant_items.push_back (it);
442 vector_sort (relevant_items, Item::less);
444 SCM items_scm = Grob_array::make_array ();
445 unsmob_grob_array (items_scm)->set_array (relevant_items);
446 return items_scm;
449 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_relevant_spanners, 1);
451 Axis_group_interface::calc_pure_relevant_spanners (SCM smob)
453 Grob *me = unsmob_grob (smob);
455 extract_grob_set (me, "pure-relevant-grobs", elts);
457 vector<Grob*> relevant_spanners;
458 for (vsize i = 0; i < elts.size (); i++)
459 if (dynamic_cast<Spanner*> (elts[i]))
460 relevant_spanners.push_back (elts[i]);
462 SCM spanners_scm = Grob_array::make_array ();
463 unsmob_grob_array (spanners_scm)->set_array (relevant_spanners);
464 return spanners_scm;
467 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_y_common, 1);
469 Axis_group_interface::calc_pure_y_common (SCM smob)
471 Grob *me = unsmob_grob (smob);
473 extract_grob_set (me, "pure-relevant-grobs", elts);
474 Grob *common = common_refpoint_of_array (elts, me, Y_AXIS);
475 if (!common)
477 me->programming_error ("No common parent found in calc_pure_y_common.");
478 return SCM_EOL;
481 return common->self_scm ();
485 Axis_group_interface::calc_common (Grob *me, Axis axis)
487 extract_grob_set (me, "elements", elts);
488 Grob *common = common_refpoint_of_array (elts, me, axis);
489 if (!common)
491 me->programming_error ("No common parent found in calc_common axis.");
492 return SCM_EOL;
495 return common->self_scm ();
499 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_x_common, 1);
501 Axis_group_interface::calc_x_common (SCM grob)
503 return calc_common (unsmob_grob (grob), X_AXIS);
506 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_y_common, 1);
508 Axis_group_interface::calc_y_common (SCM grob)
510 return calc_common (unsmob_grob (grob), Y_AXIS);
513 Interval
514 Axis_group_interface::pure_group_height (Grob *me, int start, int end)
516 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
518 if (!common)
520 programming_error ("no pure Y common refpoint");
521 return Interval ();
523 Real my_coord = me->relative_coordinate (common, Y_AXIS);
524 Interval r (relative_pure_height (me, start, end));
526 return r - my_coord;
529 void
530 Axis_group_interface::get_children (Grob *me, vector<Grob*> *found)
532 found->push_back (me);
534 if (!has_interface (me))
535 return;
537 extract_grob_set (me, "elements", elements);
538 for (vsize i = 0; i < elements.size (); i++)
540 Grob *e = elements[i];
541 Axis_group_interface::get_children (e, found);
545 bool
546 staff_priority_less (Grob * const &g1, Grob * const &g2)
548 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
549 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
551 if (priority_1 < priority_2)
552 return true;
553 else if (priority_1 > priority_2)
554 return false;
556 /* if neither grob has an outside-staff priority, the ordering will have no
557 effect -- we just need to choose a consistent ordering. We do this to
558 avoid the side-effect of calculating extents. */
559 if (isinf (priority_1))
560 return g1 < g2;
562 /* if there is no preference in staff priority, choose the left-most one */
563 Grob *common = g1->common_refpoint (g2, X_AXIS);
564 Real start_1 = g1->extent (common, X_AXIS)[LEFT];
565 Real start_2 = g2->extent (common, X_AXIS)[LEFT];
566 return start_1 < start_2;
569 static void
570 add_boxes (Grob *me, Grob *x_common, Grob *y_common, vector<Box> *const boxes, Skyline_pair *skylines)
572 /* if a child has skylines, use them instead of the extent box */
573 if (Skyline_pair *pair = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
575 Skyline_pair s = *pair;
576 s.shift (me->relative_coordinate (x_common, X_AXIS));
577 s.raise (me->relative_coordinate (y_common, Y_AXIS));
578 skylines->merge (s);
580 else if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements")))
582 for (vsize i = 0; i < elements->size (); i++)
583 add_boxes (elements->grob (i), x_common, y_common, boxes, skylines);
585 else if (!scm_is_number (me->get_property ("outside-staff-priority"))
586 && !to_boolean (me->get_property ("cross-staff")))
588 boxes->push_back (Box (me->extent (x_common, X_AXIS),
589 me->extent (y_common, Y_AXIS)));
593 /* We want to avoid situations like this:
594 still more text
595 more text
596 text
597 -------------------
598 staff
599 -------------------
601 The point is that "still more text" should be positioned under
602 "more text". In order to achieve this, we place the grobs in several
603 passes. We keep track of the right-most horizontal position that has been
604 affected by the current pass so far (actually we keep track of 2
605 positions, one for above the staff, one for below).
607 In each pass, we loop through the unplaced grobs from left to right.
608 If the grob doesn't overlap the right-most affected position, we place it
609 (and then update the right-most affected position to point to the right
610 edge of the just-placed grob). Otherwise, we skip it until the next pass.
612 static void
613 add_grobs_of_one_priority (Skyline_pair *const skylines,
614 vector<Grob*> elements,
615 Grob *x_common,
616 Grob *y_common)
618 vector<Box> boxes;
619 Drul_array<Real> last_affected_position;
621 reverse (elements);
622 while (!elements.empty ())
624 last_affected_position[UP] = -infinity_f;
625 last_affected_position[DOWN] = -infinity_f;
626 /* do one pass */
627 for (vsize i = elements.size (); i--;)
629 Direction dir = get_grob_direction (elements[i]);
630 if (dir == CENTER)
632 warning (_ ("an outside-staff object should have a direction, defaulting to up"));
633 dir = UP;
636 Box b (elements[i]->extent (x_common, X_AXIS),
637 elements[i]->extent (y_common, Y_AXIS));
638 SCM horizon_padding_scm = elements[i]->get_property ("outside-staff-horizontal-padding");
639 Real horizon_padding = robust_scm2double (horizon_padding_scm, 0.0);
641 if (b[X_AXIS][LEFT] - 2*horizon_padding < last_affected_position[dir])
642 continue;
644 if (!b[X_AXIS].is_empty () && !b[Y_AXIS].is_empty ())
646 boxes.clear ();
647 boxes.push_back (b);
648 Skyline other = Skyline (boxes, horizon_padding, X_AXIS, -dir);
649 Real padding = robust_scm2double (elements[i]->get_property ("outside-staff-padding"), 0.5);
650 Real dist = (*skylines)[dir].distance (other) + padding;
652 if (dist > 0)
654 b.translate (Offset (0, dir*dist));
655 elements[i]->translate_axis (dir*dist, Y_AXIS);
657 skylines->insert (b, 0, X_AXIS);
658 elements[i]->set_property ("outside-staff-priority", SCM_BOOL_F);
659 last_affected_position[dir] = b[X_AXIS][RIGHT];
663 Ugh: quadratic. --hwn
665 elements.erase (elements.begin () + i);
670 // TODO: it is tricky to correctly handle skyline placement of cross-staff grobs.
671 // For example, cross-staff beams cannot be formatted until the distance between
672 // staves is known and therefore any grobs that depend on the beam cannot be placed
673 // until the skylines are known. On the other hand, the distance between staves should
674 // really depend on position of the cross-staff grobs that lie between them.
675 // Currently, we just leave cross-staff grobs out of the
676 // skyline altogether, but this could mean that staves are placed so close together
677 // that there is no room for the cross-staff grob. It also means, of course, that
678 // we don't get the benefits of skyline placement for cross-staff grobs.
679 Skyline_pair
680 Axis_group_interface::skyline_spacing (Grob *me, vector<Grob*> elements)
682 /* For grobs with an outside-staff-priority, the sorting function might
683 call extent and cause suicide. This breaks the contract that is required
684 for the STL sort function. To avoid this, we make sure that any suicides
685 are triggered beforehand.
687 for (vsize i = 0; i < elements.size (); i++)
688 if (scm_is_number (elements[i]->get_property ("outside-staff-priority")))
689 elements[i]->extent (elements[i], X_AXIS);
691 vector_sort (elements, staff_priority_less);
692 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
693 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
695 assert (y_common == me);
697 vsize i = 0;
698 vector<Box> boxes;
700 Skyline_pair skylines;
701 for (i = 0; i < elements.size ()
702 && !scm_is_number (elements[i]->get_property ("outside-staff-priority")); i++)
703 if (!to_boolean (elements[i]->get_property ("cross-staff")))
704 add_boxes (elements[i], x_common, y_common, &boxes, &skylines);
706 SCM padding_scm = me->get_property ("skyline-horizontal-padding");
707 Real padding = robust_scm2double (padding_scm, 0.1);
708 skylines.merge (Skyline_pair (boxes, padding, X_AXIS));
709 for (; i < elements.size (); i++)
711 if (to_boolean (elements[i]->get_property ("cross-staff")))
712 continue;
714 SCM priority = elements[i]->get_property ("outside-staff-priority");
715 vector<Grob*> current_elts;
716 current_elts.push_back (elements[i]);
717 while (i + 1 < elements.size ()
718 && scm_eq_p (elements[i+1]->get_property ("outside-staff-priority"), priority))
720 if (!to_boolean (elements[i+1]->get_property ("cross-staff")))
721 current_elts.push_back (elements[i+1]);
722 ++i;
725 add_grobs_of_one_priority (&skylines, current_elts, x_common, y_common);
727 skylines.shift (-me->relative_coordinate (x_common, X_AXIS));
728 return skylines;
731 MAKE_SCHEME_CALLBACK (Axis_group_interface, print, 1)
733 Axis_group_interface::print (SCM smob)
735 if (!debug_skylines)
736 return SCM_BOOL_F;
738 Grob *me = unsmob_grob (smob);
739 Stencil ret;
740 if (Skyline_pair *s = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
742 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[UP].to_points (X_AXIS))
743 .in_color (255, 0, 255));
744 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[DOWN].to_points (X_AXIS))
745 .in_color (0, 255, 255));
747 return ret.smobbed_copy ();
750 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_next_staff_spacing, 3)
752 Axis_group_interface::calc_pure_next_staff_spacing (SCM smob, SCM start, SCM end)
754 return calc_maybe_pure_next_staff_spacing (unsmob_grob (smob),
755 true,
756 scm_to_int (start),
757 scm_to_int (end));
760 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_next_staff_spacing, 1)
762 Axis_group_interface::calc_next_staff_spacing (SCM smob)
764 return calc_maybe_pure_next_staff_spacing (unsmob_grob (smob),
765 false,
767 INT_MAX);
771 Axis_group_interface::calc_maybe_pure_next_staff_spacing (Grob *me, bool pure, int start, int end)
773 Grob *grouper = unsmob_grob (me->get_object ("staff-grouper"));
775 if (grouper)
777 Grob *last_in_group = Staff_grouper_interface::get_maybe_pure_last_grob (grouper, pure, start, end);
778 if (me == last_in_group)
779 return grouper->get_maybe_pure_property ("after-last-staff-spacing", pure, start, end);
780 else
781 return grouper->get_maybe_pure_property ("between-staff-spacing", pure, start, end);
783 return me->get_maybe_pure_property ("default-next-staff-spacing", pure, start, end);
786 Real
787 Axis_group_interface::minimum_distance (Grob *g1, Grob *g2, Axis a)
789 SCM sym = ly_symbol2scm ((a == Y_AXIS) ? "vertical-skylines" : "horizontal-skylines");
791 Skyline_pair *s1 = Skyline_pair::unsmob (g1->get_property (sym));
792 Skyline_pair *s2 = Skyline_pair::unsmob (g2->get_property (sym));
793 if (s1 && s2)
794 return (*s1)[DOWN].distance ((*s2)[UP]);
795 return 0;
798 ADD_INTERFACE (Axis_group_interface,
799 "An object that groups other layout objects.",
801 // TODO: some of these properties are specific to
802 // VerticalAxisGroup. We should split off a
803 // vertical-axis-group-interface.
804 /* properties */
805 "X-common "
806 "Y-common "
807 "adjacent-pure-heights "
808 "axes "
809 "default-next-staff-spacing "
810 "elements "
811 "inter-loose-line-spacing "
812 "inter-staff-spacing "
813 "keep-fixed-while-stretching "
814 "max-stretch "
815 "non-affinity-spacing "
816 "next-staff-spacing "
817 "no-alignment "
818 "pure-Y-common "
819 "pure-relevant-grobs "
820 "pure-relevant-items "
821 "pure-relevant-spanners "
822 "staff-affinity "
823 "staff-grouper "
824 "system-Y-offset "
825 "vertical-skylines "