Fixes Issue 1504, allowing feather beam line breaking.
[lilypond/patrick.git] / lily / axis-group-interface.cc
blob0b399fd2482e5fa8ef9da603a416471dd9281ac8
1 /*
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"
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 static bool
39 pure_staff_priority_less (Grob *const &g1, Grob *const &g2);
41 void
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"),
58 me->self_scm ());
61 /* must be ordered, because Align_interface also uses
62 Axis_group_interface */
63 Pointer_group_interface::add_grob (me, ly_symbol2scm ("elements"), e);
66 bool
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));
74 Interval
75 Axis_group_interface::relative_group_extent (vector<Grob*> const &elts,
76 Grob *common, Axis a)
78 Interval r;
79 for (vsize i = 0; i < elts.size (); i++)
81 Grob *se = elts[i];
82 if (!to_boolean (se->get_property ("cross-staff")))
84 Interval dims = se->extent (common, a);
85 if (!dims.is_empty ())
86 r.unite (dims);
89 return r;
92 Interval
93 Axis_group_interface::sum_partial_pure_heights (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));
98 return iv;
101 Interval
102 Axis_group_interface::part_of_line_pure_height (Grob *me, bool begin, int start, int end)
104 Spanner *sp = dynamic_cast<Spanner*> (me);
105 SCM cache_symbol = begin
106 ? ly_symbol2scm ("begin-of-line-pure-height")
107 : ly_symbol2scm ("rest-of-line-pure-height");
108 SCM cached = sp->get_cached_pure_property (cache_symbol, start, end);
109 if (scm_is_pair (cached))
110 return robust_scm2interval (cached, Interval (0, 0));
112 SCM adjacent_pure_heights = me->get_property ("adjacent-pure-heights");
113 Interval ret;
115 if (!scm_is_pair (adjacent_pure_heights))
116 ret = Interval (0, 0);
117 else
119 SCM these_pure_heights = begin
120 ? scm_car (adjacent_pure_heights)
121 : scm_cdr (adjacent_pure_heights);
123 if (scm_is_vector (these_pure_heights))
124 ret = combine_pure_heights (me, these_pure_heights, start, end);
125 else
126 ret = Interval (0, 0);
129 sp->cache_pure_property (cache_symbol, start, end, ly_interval2scm (ret));
130 return ret;
133 Interval
134 Axis_group_interface::begin_of_line_pure_height (Grob *me, int start)
136 return part_of_line_pure_height (me, true, start, start + 1);
139 Interval
140 Axis_group_interface::rest_of_line_pure_height (Grob *me, int start, int end)
142 return part_of_line_pure_height (me, false, start, end);
145 Interval
146 Axis_group_interface::combine_pure_heights (Grob *me, SCM measure_extents, int start, int end)
148 Paper_score *ps = get_root_system (me)->paper_score ();
149 vector<vsize> breaks = ps->get_break_indices ();
150 vector<Grob*> cols = ps->get_columns ();
152 Interval ext;
153 for (vsize i = 0; i + 1 < breaks.size (); i++)
155 int r = Paper_column::get_rank (cols[breaks[i]]);
156 if (r >= end)
157 break;
159 if (r >= start)
160 ext.unite (ly_scm2interval (scm_c_vector_ref (measure_extents, i)));
163 return ext;
166 // adjacent-pure-heights is a pair of vectors, each of which has one element
167 // for every measure in the score. The first vector stores, for each measure,
168 // the combined height of the elements that are present only when the bar
169 // is at the beginning of a line. The second vector stores, for each measure,
170 // the combined height of the elements that are present only when the bar
171 // is not at the beginning of a line.
172 MAKE_SCHEME_CALLBACK (Axis_group_interface, adjacent_pure_heights, 1)
174 Axis_group_interface::adjacent_pure_heights (SCM smob)
176 Grob *me = unsmob_grob (smob);
178 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
179 extract_grob_set (me, "pure-relevant-grobs", elts);
181 Paper_score *ps = get_root_system (me)->paper_score ();
182 vector<vsize> ranks = ps->get_break_ranks ();
184 vector<Interval> begin_line_heights;
185 vector<Interval> mid_line_heights;
186 vector<Interval> begin_line_staff_heights;
187 vector<Interval> mid_line_staff_heights;
188 begin_line_heights.resize (ranks.size () - 1);
189 mid_line_heights.resize (ranks.size () - 1);
191 for (vsize i = 0; i < elts.size (); ++i)
193 Grob *g = elts[i];
195 if (to_boolean (g->get_property ("cross-staff")))
196 continue;
198 bool outside_staff = scm_is_number (g->get_property ("outside-staff-priority"));
199 Real padding = robust_scm2double (g->get_property ("outside-staff-padding"), 0.5);
201 // When we encounter the first outside-staff grob, make a copy
202 // of the current heights to use as an estimate for the staff heights.
203 // Note that the outside-staff approximation that we use here doesn't
204 // consider any collisions that might occur between outside-staff grobs,
205 // but only the fact that outside-staff grobs may need to be raised above
206 // the staff.
207 if (outside_staff && begin_line_staff_heights.empty ())
209 begin_line_staff_heights = begin_line_heights;
210 mid_line_staff_heights = mid_line_heights;
213 // TODO: consider a pure version of get_grob_direction?
214 Direction d = to_dir (g->get_property_data ("direction"));
215 d = (d == CENTER) ? UP : d;
217 Interval_t<int> rank_span = g->spanned_rank_interval ();
218 vsize first_break = lower_bound (ranks, (vsize)rank_span[LEFT], less<vsize> ());
219 if (first_break > 0 && ranks[first_break] >= (vsize)rank_span[LEFT])
220 first_break--;
222 for (vsize j = first_break; j+1 < ranks.size () && (int)ranks[j] <= rank_span[RIGHT]; ++j)
224 int start = ranks[j];
225 int end = ranks[j+1];
227 // Take grobs that are visible with respect to a slightly longer line.
228 // Otherwise, we will never include grobs at breakpoints which aren't
229 // end-of-line-visible.
230 int visibility_end = j + 2 < ranks.size () ? ranks[j+2] : end;
232 if (g->pure_is_visible (start, visibility_end))
234 Interval dims = g->pure_height (common, start, end);
235 if (!dims.is_empty ())
237 if (rank_span[LEFT] <= start)
239 if (outside_staff)
240 begin_line_heights[j].unite (
241 begin_line_staff_heights[j].union_disjoint (dims, padding, d));
242 else
243 begin_line_heights[j].unite (dims);
245 if (rank_span[RIGHT] > start)
247 if (outside_staff)
248 mid_line_heights[j].unite (
249 mid_line_staff_heights[j].union_disjoint (dims, padding, d));
250 else
251 mid_line_heights[j].unite (dims);
258 // Convert begin_line_heights and min_line_heights to SCM.
259 SCM begin_scm = scm_c_make_vector (ranks.size () - 1, SCM_EOL);
260 SCM mid_scm = scm_c_make_vector (ranks.size () - 1, SCM_EOL);
261 for (vsize i = 0; i < begin_line_heights.size (); ++i)
263 scm_vector_set_x (begin_scm, scm_from_int (i), ly_interval2scm (begin_line_heights[i]));
264 scm_vector_set_x (mid_scm, scm_from_int (i), ly_interval2scm (mid_line_heights[i]));
267 return scm_cons (begin_scm, mid_scm);
270 Interval
271 Axis_group_interface::relative_pure_height (Grob *me, int start, int end)
273 /* It saves a _lot_ of time if we assume a VerticalAxisGroup is additive
274 (ie. height (i, k) = max (height (i, j) height (j, k)) for all i <= j <= k).
275 Unfortunately, it isn't always true, particularly if there is a
276 VerticalAlignment somewhere in the descendants.
278 Usually, the only VerticalAlignment comes from Score. This makes it
279 reasonably safe to assume that if our parent is a VerticalAlignment,
280 we can assume additivity and cache things nicely. */
281 Grob *p = me->get_parent (Y_AXIS);
282 if (p && Align_interface::has_interface (p))
283 return Axis_group_interface::sum_partial_pure_heights (me, start, end);
285 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
286 extract_grob_set (me, "pure-relevant-grobs", elts);
288 Interval r;
289 for (vsize i = 0; i < elts.size (); i++)
291 Grob *g = elts[i];
292 Interval_t<int> rank_span = g->spanned_rank_interval ();
293 if (rank_span[LEFT] <= end && rank_span[RIGHT] >= start
294 && g->pure_is_visible (start, end)
295 && !to_boolean (g->get_property ("cross-staff")))
297 Interval dims = g->pure_height (common, start, end);
298 if (!dims.is_empty ())
299 r.unite (dims);
302 return r;
305 MAKE_SCHEME_CALLBACK (Axis_group_interface, width, 1);
307 Axis_group_interface::width (SCM smob)
309 Grob *me = unsmob_grob (smob);
310 return generic_group_extent (me, X_AXIS);
313 MAKE_SCHEME_CALLBACK (Axis_group_interface, height, 1);
315 Axis_group_interface::height (SCM smob)
317 Grob *me = unsmob_grob (smob);
318 return generic_group_extent (me, Y_AXIS);
321 MAKE_SCHEME_CALLBACK (Axis_group_interface, pure_height, 3);
323 Axis_group_interface::pure_height (SCM smob, SCM start_scm, SCM end_scm)
325 int start = robust_scm2int (start_scm, 0);
326 int end = robust_scm2int (end_scm, INT_MAX);
327 Grob *me = unsmob_grob (smob);
329 /* Maybe we are in the second pass of a two-pass spacing run. In that
330 case, the Y-extent of a system is already given to us */
331 System *system = dynamic_cast<System*> (me);
332 if (system)
334 SCM line_break_details = system->column (start)->get_property ("line-break-system-details");
335 SCM system_y_extent = scm_assq (ly_symbol2scm ("system-Y-extent"), line_break_details);
336 if (scm_is_pair (system_y_extent))
337 return scm_cdr (system_y_extent);
340 return ly_interval2scm (pure_group_height (me, start, end));
343 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_skylines, 1);
345 Axis_group_interface::calc_skylines (SCM smob)
347 Grob *me = unsmob_grob (smob);
348 extract_grob_set (me, "elements", elts);
349 Skyline_pair skylines = skyline_spacing (me, elts);
351 return skylines.smobbed_copy ();
354 /* whereas calc_skylines calculates skylines for axis-groups with a lot of
355 visible children, combine_skylines is designed for axis-groups whose only
356 children are other axis-groups (ie. VerticalAlignment). Rather than
357 calculating all the skylines from scratch, we just merge the skylines
358 of the children.
360 MAKE_SCHEME_CALLBACK (Axis_group_interface, combine_skylines, 1);
362 Axis_group_interface::combine_skylines (SCM smob)
364 Grob *me = unsmob_grob (smob);
365 extract_grob_set (me, "elements", elements);
366 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
367 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
369 if (y_common != me)
370 programming_error ("combining skylines that don't belong to me");
372 Skyline_pair ret;
373 for (vsize i = 0; i < elements.size (); i++)
375 SCM skyline_scm = elements[i]->get_property ("vertical-skylines");
376 if (Skyline_pair::unsmob (skyline_scm))
378 Real offset = elements[i]->relative_coordinate (y_common, Y_AXIS);
379 Skyline_pair other = *Skyline_pair::unsmob (skyline_scm);
380 other.raise (offset);
381 other.shift (elements[i]->relative_coordinate (x_common, X_AXIS));
382 ret.merge (other);
385 return ret.smobbed_copy ();
389 Axis_group_interface::generic_group_extent (Grob *me, Axis a)
391 /* trigger the callback to do skyline-spacing on the children */
392 if (a == Y_AXIS)
393 (void) me->get_property ("vertical-skylines");
395 extract_grob_set (me, "elements", elts);
396 Grob *common = common_refpoint_of_array (elts, me, a);
398 Real my_coord = me->relative_coordinate (common, a);
399 Interval r (relative_group_extent (elts, common, a));
401 return ly_interval2scm (r - my_coord);
404 /* This is like generic_group_extent, but it only counts the grobs that
405 are children of some other axis-group. This is uncached; if it becomes
406 commonly used, it may be necessary to cache it somehow. */
407 Interval
408 Axis_group_interface::staff_extent (Grob *me, Grob *refp, Axis ext_a, Grob *staff, Axis parent_a)
410 extract_grob_set (me, "elements", elts);
411 vector<Grob*> new_elts;
413 for (vsize i = 0; i < elts.size (); i++)
414 if (elts[i]->common_refpoint (staff, parent_a) == staff)
415 new_elts.push_back (elts[i]);
417 return relative_group_extent (new_elts, refp, ext_a);
421 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_relevant_grobs, 1);
423 Axis_group_interface::calc_pure_relevant_grobs (SCM smob)
425 Grob *me = unsmob_grob (smob);
427 extract_grob_set (me, "elements", elts);
429 vector<Grob*> relevant_grobs;
430 SCM pure_relevant_p = ly_lily_module_constant ("pure-relevant?");
432 for (vsize i = 0; i < elts.size (); i++)
434 if (to_boolean (scm_apply_1 (pure_relevant_p, elts[i]->self_scm (), SCM_EOL)))
435 relevant_grobs.push_back (elts[i]);
437 if (Item *it = dynamic_cast<Item*> (elts[i]))
439 Direction d = LEFT;
442 Item *piece = it->find_prebroken_piece (d);
443 if (piece && to_boolean (scm_apply_1 (pure_relevant_p, piece->self_scm (), SCM_EOL)))
444 relevant_grobs.push_back (piece);
446 while (flip (&d) != LEFT);
450 vector_sort (relevant_grobs, pure_staff_priority_less);
451 SCM grobs_scm = Grob_array::make_array ();
452 unsmob_grob_array (grobs_scm)->set_array (relevant_grobs);
454 return grobs_scm;
457 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_y_common, 1);
459 Axis_group_interface::calc_pure_y_common (SCM smob)
461 Grob *me = unsmob_grob (smob);
463 extract_grob_set (me, "pure-relevant-grobs", elts);
464 Grob *common = common_refpoint_of_array (elts, me, Y_AXIS);
465 if (!common)
467 me->programming_error ("No common parent found in calc_pure_y_common.");
468 return SCM_EOL;
471 return common->self_scm ();
475 Axis_group_interface::calc_common (Grob *me, Axis axis)
477 extract_grob_set (me, "elements", elts);
478 Grob *common = common_refpoint_of_array (elts, me, axis);
479 if (!common)
481 me->programming_error ("No common parent found in calc_common axis.");
482 return SCM_EOL;
485 return common->self_scm ();
489 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_x_common, 1);
491 Axis_group_interface::calc_x_common (SCM grob)
493 return calc_common (unsmob_grob (grob), X_AXIS);
496 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_y_common, 1);
498 Axis_group_interface::calc_y_common (SCM grob)
500 return calc_common (unsmob_grob (grob), Y_AXIS);
503 Interval
504 Axis_group_interface::pure_group_height (Grob *me, int start, int end)
506 Grob *common = unsmob_grob (me->get_object ("pure-Y-common"));
508 if (!common)
510 programming_error ("no pure Y common refpoint");
511 return Interval ();
513 Real my_coord = me->relative_coordinate (common, Y_AXIS);
514 Interval r (relative_pure_height (me, start, end));
516 return r - my_coord;
519 void
520 Axis_group_interface::get_children (Grob *me, vector<Grob*> *found)
522 found->push_back (me);
524 if (!has_interface (me))
525 return;
527 extract_grob_set (me, "elements", elements);
528 for (vsize i = 0; i < elements.size (); i++)
530 Grob *e = elements[i];
531 Axis_group_interface::get_children (e, found);
535 static bool
536 staff_priority_less (Grob * const &g1, Grob * const &g2)
538 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
539 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
541 if (priority_1 < priority_2)
542 return true;
543 else if (priority_1 > priority_2)
544 return false;
546 /* if neither grob has an outside-staff priority, the ordering will have no
547 effect -- we just need to choose a consistent ordering. We do this to
548 avoid the side-effect of calculating extents. */
549 if (isinf (priority_1))
550 return g1 < g2;
552 /* if there is no preference in staff priority, choose the left-most one */
553 Grob *common = g1->common_refpoint (g2, X_AXIS);
554 Real start_1 = g1->extent (common, X_AXIS)[LEFT];
555 Real start_2 = g2->extent (common, X_AXIS)[LEFT];
556 return start_1 < start_2;
559 static bool
560 pure_staff_priority_less (Grob * const &g1, Grob * const &g2)
562 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority"), -infinity_f);
563 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority"), -infinity_f);
565 return priority_1 < priority_2;
568 static void
569 add_boxes (Grob *me, Grob *x_common, Grob *y_common, vector<Box> *const boxes, Skyline_pair *skylines)
571 /* if a child has skylines, use them instead of the extent box */
572 if (Skyline_pair *pair = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
574 Skyline_pair s = *pair;
575 s.shift (me->relative_coordinate (x_common, X_AXIS));
576 s.raise (me->relative_coordinate (y_common, Y_AXIS));
577 skylines->merge (s);
579 else if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements")))
581 for (vsize i = 0; i < elements->size (); i++)
582 add_boxes (elements->grob (i), x_common, y_common, boxes, skylines);
584 else if (!scm_is_number (me->get_property ("outside-staff-priority"))
585 && !to_boolean (me->get_property ("cross-staff")))
587 boxes->push_back (Box (me->extent (x_common, X_AXIS),
588 me->extent (y_common, Y_AXIS)));
592 /* We want to avoid situations like this:
593 still more text
594 more text
595 text
596 -------------------
597 staff
598 -------------------
600 The point is that "still more text" should be positioned under
601 "more text". In order to achieve this, we place the grobs in several
602 passes. We keep track of the right-most horizontal position that has been
603 affected by the current pass so far (actually we keep track of 2
604 positions, one for above the staff, one for below).
606 In each pass, we loop through the unplaced grobs from left to right.
607 If the grob doesn't overlap the right-most affected position, we place it
608 (and then update the right-most affected position to point to the right
609 edge of the just-placed grob). Otherwise, we skip it until the next pass.
611 static void
612 add_grobs_of_one_priority (Skyline_pair *const skylines,
613 vector<Grob*> elements,
614 Grob *x_common,
615 Grob *y_common)
617 vector<Box> boxes;
618 Drul_array<Real> last_affected_position;
620 reverse (elements);
621 while (!elements.empty ())
623 last_affected_position[UP] = -infinity_f;
624 last_affected_position[DOWN] = -infinity_f;
625 /* do one pass */
626 for (vsize i = elements.size (); i--;)
628 Direction dir = get_grob_direction (elements[i]);
629 if (dir == CENTER)
631 warning (_ ("an outside-staff object should have a direction, defaulting to up"));
632 dir = UP;
635 Box b (elements[i]->extent (x_common, X_AXIS),
636 elements[i]->extent (y_common, Y_AXIS));
637 SCM horizon_padding_scm = elements[i]->get_property ("outside-staff-horizontal-padding");
638 Real horizon_padding = robust_scm2double (horizon_padding_scm, 0.0);
640 if (b[X_AXIS][LEFT] - 2*horizon_padding < last_affected_position[dir])
641 continue;
643 if (!b[X_AXIS].is_empty () && !b[Y_AXIS].is_empty ())
645 boxes.clear ();
646 boxes.push_back (b);
647 Skyline other = Skyline (boxes, horizon_padding, X_AXIS, -dir);
648 Real padding = robust_scm2double (elements[i]->get_property ("outside-staff-padding"), 0.5);
649 Real dist = (*skylines)[dir].distance (other) + padding;
651 if (dist > 0)
653 b.translate (Offset (0, dir*dist));
654 elements[i]->translate_axis (dir*dist, Y_AXIS);
656 skylines->insert (b, 0, X_AXIS);
657 elements[i]->set_property ("outside-staff-priority", SCM_BOOL_F);
658 last_affected_position[dir] = b[X_AXIS][RIGHT];
662 Ugh: quadratic. --hwn
664 elements.erase (elements.begin () + i);
669 // TODO: it is tricky to correctly handle skyline placement of cross-staff grobs.
670 // For example, cross-staff beams cannot be formatted until the distance between
671 // staves is known and therefore any grobs that depend on the beam cannot be placed
672 // until the skylines are known. On the other hand, the distance between staves should
673 // really depend on position of the cross-staff grobs that lie between them.
674 // Currently, we just leave cross-staff grobs out of the
675 // skyline altogether, but this could mean that staves are placed so close together
676 // that there is no room for the cross-staff grob. It also means, of course, that
677 // we don't get the benefits of skyline placement for cross-staff grobs.
678 Skyline_pair
679 Axis_group_interface::skyline_spacing (Grob *me, vector<Grob*> elements)
681 /* For grobs with an outside-staff-priority, the sorting function might
682 call extent and cause suicide. This breaks the contract that is required
683 for the STL sort function. To avoid this, we make sure that any suicides
684 are triggered beforehand.
686 for (vsize i = 0; i < elements.size (); i++)
687 if (scm_is_number (elements[i]->get_property ("outside-staff-priority")))
688 elements[i]->extent (elements[i], X_AXIS);
690 vector_sort (elements, staff_priority_less);
691 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS);
692 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS);
694 assert (y_common == me);
696 vsize i = 0;
697 vector<Box> boxes;
699 Skyline_pair skylines;
700 for (i = 0; i < elements.size ()
701 && !scm_is_number (elements[i]->get_property ("outside-staff-priority")); i++)
702 if (!to_boolean (elements[i]->get_property ("cross-staff")))
703 add_boxes (elements[i], x_common, y_common, &boxes, &skylines);
705 SCM padding_scm = me->get_property ("skyline-horizontal-padding");
706 Real padding = robust_scm2double (padding_scm, 0.1);
707 skylines.merge (Skyline_pair (boxes, padding, X_AXIS));
708 for (; i < elements.size (); i++)
710 if (to_boolean (elements[i]->get_property ("cross-staff")))
711 continue;
713 SCM priority = elements[i]->get_property ("outside-staff-priority");
714 vector<Grob*> current_elts;
715 current_elts.push_back (elements[i]);
716 while (i + 1 < elements.size ()
717 && scm_eq_p (elements[i+1]->get_property ("outside-staff-priority"), priority))
719 if (!to_boolean (elements[i+1]->get_property ("cross-staff")))
720 current_elts.push_back (elements[i+1]);
721 ++i;
724 add_grobs_of_one_priority (&skylines, current_elts, x_common, y_common);
726 skylines.shift (-me->relative_coordinate (x_common, X_AXIS));
727 return skylines;
730 MAKE_SCHEME_CALLBACK (Axis_group_interface, print, 1)
732 Axis_group_interface::print (SCM smob)
734 if (!debug_skylines)
735 return SCM_BOOL_F;
737 Grob *me = unsmob_grob (smob);
738 Stencil ret;
739 if (Skyline_pair *s = Skyline_pair::unsmob (me->get_property ("vertical-skylines")))
741 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[UP].to_points (X_AXIS))
742 .in_color (255, 0, 255));
743 ret.add_stencil (Lookup::points_to_line_stencil (0.1, (*s)[DOWN].to_points (X_AXIS))
744 .in_color (0, 255, 255));
746 return ret.smobbed_copy ();
749 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_pure_staff_staff_spacing, 3)
751 Axis_group_interface::calc_pure_staff_staff_spacing (SCM smob, SCM start, SCM end)
753 return calc_maybe_pure_staff_staff_spacing (unsmob_grob (smob),
754 true,
755 scm_to_int (start),
756 scm_to_int (end));
759 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_staff_staff_spacing, 1)
761 Axis_group_interface::calc_staff_staff_spacing (SCM smob)
763 return calc_maybe_pure_staff_staff_spacing (unsmob_grob (smob),
764 false,
766 INT_MAX);
770 Axis_group_interface::calc_maybe_pure_staff_staff_spacing (Grob *me, bool pure, int start, int end)
772 Grob *grouper = unsmob_grob (me->get_object ("staff-grouper"));
774 if (grouper)
776 bool within_group = Staff_grouper_interface::maybe_pure_within_group (grouper, me, pure, start, end);
777 if (within_group)
778 return grouper->get_maybe_pure_property ("staff-staff-spacing", pure, start, end);
779 else
780 return grouper->get_maybe_pure_property ("staffgroup-staff-spacing", pure, start, end);
782 return me->get_maybe_pure_property ("default-staff-staff-spacing", pure, start, end);
785 Real
786 Axis_group_interface::minimum_distance (Grob *g1, Grob *g2, Axis a)
788 SCM sym = ly_symbol2scm ((a == Y_AXIS) ? "vertical-skylines" : "horizontal-skylines");
790 Skyline_pair *s1 = Skyline_pair::unsmob (g1->get_property (sym));
791 Skyline_pair *s2 = Skyline_pair::unsmob (g2->get_property (sym));
792 if (s1 && s2)
793 return (*s1)[DOWN].distance ((*s2)[UP]);
794 return 0;
797 ADD_INTERFACE (Axis_group_interface,
798 "An object that groups other layout objects.",
800 // TODO: some of these properties are specific to
801 // VerticalAxisGroup. We should split off a
802 // vertical-axis-group-interface.
803 /* properties */
804 "adjacent-pure-heights "
805 "axes "
806 "default-staff-staff-spacing "
807 "elements "
808 "max-stretch "
809 "no-alignment "
810 "nonstaff-nonstaff-spacing "
811 "nonstaff-relatedstaff-spacing "
812 "nonstaff-unrelatedstaff-spacing "
813 "pure-relevant-grobs "
814 "pure-relevant-items "
815 "pure-relevant-spanners "
816 "pure-Y-common "
817 "staff-affinity "
818 "staff-grouper "
819 "staff-staff-spacing "
820 "system-Y-offset "
821 "vertical-skylines "
822 "X-common "
823 "Y-common "