LEFT | RIGHT |
1 /* | 1 /* |
2 This file is part of LilyPond, the GNU music typesetter. | 2 This file is part of LilyPond, the GNU music typesetter. |
3 | 3 |
4 Copyright (C) 2000--2012 Han-Wen Nienhuys <hanwen@xs4all.nl> | 4 Copyright (C) 2000--2012 Han-Wen Nienhuys <hanwen@xs4all.nl> |
5 | 5 |
6 LilyPond is free software: you can redistribute it and/or modify | 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 | 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 | 8 the Free Software Foundation, either version 3 of the License, or |
9 (at your option) any later version. | 9 (at your option) any later version. |
10 | 10 |
11 LilyPond is distributed in the hope that it will be useful, | 11 LilyPond is distributed in the hope that it will be useful, |
12 but WITHOUT ANY WARRANTY; without even the implied warranty of | 12 but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 GNU General Public License for more details. | 14 GNU General Public License for more details. |
15 | 15 |
16 You should have received a copy of the GNU General Public License | 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/>. | 17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>. |
18 */ | 18 */ |
19 | 19 |
20 #include "axis-group-interface.hh" | 20 #include "axis-group-interface.hh" |
| 21 |
| 22 #include <map> |
21 | 23 |
22 #include "align-interface.hh" | 24 #include "align-interface.hh" |
23 #include "directional-element-interface.hh" | 25 #include "directional-element-interface.hh" |
24 #include "grob-array.hh" | 26 #include "grob-array.hh" |
25 #include "hara-kiri-group-spanner.hh" | 27 #include "hara-kiri-group-spanner.hh" |
26 #include "international.hh" | 28 #include "international.hh" |
| 29 #include "interval-set.hh" |
27 #include "lookup.hh" | 30 #include "lookup.hh" |
28 #include "paper-column.hh" | 31 #include "paper-column.hh" |
29 #include "paper-score.hh" | 32 #include "paper-score.hh" |
30 #include "pointer-group-interface.hh" | 33 #include "pointer-group-interface.hh" |
31 #include "separation-item.hh" | 34 #include "separation-item.hh" |
32 #include "skyline-pair.hh" | 35 #include "skyline-pair.hh" |
33 #include "staff-grouper-interface.hh" | 36 #include "staff-grouper-interface.hh" |
34 #include "stem.hh" | 37 #include "stem.hh" |
35 #include "stencil.hh" | 38 #include "stencil.hh" |
36 #include "system.hh" | 39 #include "system.hh" |
37 #include "warn.hh" | 40 #include "warn.hh" |
38 #include "unpure-pure-container.hh" | 41 #include "unpure-pure-container.hh" |
39 | 42 |
40 static bool | 43 static bool |
41 pure_staff_priority_less (Grob *const &g1, Grob *const &g2); | 44 pure_staff_priority_less (Grob *const &g1, Grob *const &g2); |
42 | 45 |
| 46 Real Axis_group_interface::default_outside_staff_padding_ = 0.46; |
| 47 |
| 48 Real |
| 49 Axis_group_interface::get_default_outside_staff_padding () |
| 50 { |
| 51 return default_outside_staff_padding_; |
| 52 } |
| 53 |
43 void | 54 void |
44 Axis_group_interface::add_element (Grob *me, Grob *e) | 55 Axis_group_interface::add_element (Grob *me, Grob *e) |
45 { | 56 { |
46 SCM axes = me->get_property ("axes"); | 57 SCM axes = me->get_property ("axes"); |
47 if (!scm_is_pair (axes)) | 58 if (!scm_is_pair (axes)) |
48 programming_error ("axes should be nonempty"); | 59 programming_error ("axes should be nonempty"); |
49 | 60 |
50 for (SCM ax = axes; scm_is_pair (ax); ax = scm_cdr (ax)) | 61 for (SCM ax = axes; scm_is_pair (ax); ax = scm_cdr (ax)) |
51 { | 62 { |
52 Axis a = (Axis) scm_to_int (scm_car (ax)); | 63 Axis a = (Axis) scm_to_int (scm_car (ax)); |
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
226 mid_line_heights.resize (ranks.size () - 1); | 237 mid_line_heights.resize (ranks.size () - 1); |
227 | 238 |
228 for (vsize i = 0; i < elts.size (); ++i) | 239 for (vsize i = 0; i < elts.size (); ++i) |
229 { | 240 { |
230 Grob *g = elts[i]; | 241 Grob *g = elts[i]; |
231 | 242 |
232 if (to_boolean (g->get_property ("cross-staff"))) | 243 if (to_boolean (g->get_property ("cross-staff"))) |
233 continue; | 244 continue; |
234 | 245 |
235 bool outside_staff = scm_is_number (g->get_property ("outside-staff-priori
ty")); | 246 bool outside_staff = scm_is_number (g->get_property ("outside-staff-priori
ty")); |
236 Real padding = robust_scm2double (g->get_property ("outside-staff-padding"
), 0.5); | 247 Real padding = robust_scm2double (g->get_property ("outside-staff-padding"
), get_default_outside_staff_padding ()); |
237 | 248 |
238 // When we encounter the first outside-staff grob, make a copy | 249 // When we encounter the first outside-staff grob, make a copy |
239 // of the current heights to use as an estimate for the staff heights. | 250 // of the current heights to use as an estimate for the staff heights. |
240 // Note that the outside-staff approximation that we use here doesn't | 251 // Note that the outside-staff approximation that we use here doesn't |
241 // consider any collisions that might occur between outside-staff grobs, | 252 // consider any collisions that might occur between outside-staff grobs, |
242 // but only the fact that outside-staff grobs may need to be raised above | 253 // but only the fact that outside-staff grobs may need to be raised above |
243 // the staff. | 254 // the staff. |
244 if (outside_staff && begin_line_staff_heights.empty ()) | 255 if (outside_staff && begin_line_staff_heights.empty ()) |
245 { | 256 { |
246 begin_line_staff_heights = begin_line_heights; | 257 begin_line_staff_heights = begin_line_heights; |
(...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
374 } | 385 } |
375 | 386 |
376 return ly_interval2scm (pure_group_height (me, start, end)); | 387 return ly_interval2scm (pure_group_height (me, start, end)); |
377 } | 388 } |
378 | 389 |
379 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_skylines, 1); | 390 MAKE_SCHEME_CALLBACK (Axis_group_interface, calc_skylines, 1); |
380 SCM | 391 SCM |
381 Axis_group_interface::calc_skylines (SCM smob) | 392 Axis_group_interface::calc_skylines (SCM smob) |
382 { | 393 { |
383 Grob *me = unsmob_grob (smob); | 394 Grob *me = unsmob_grob (smob); |
384 extract_grob_set (me, "elements", elts); | 395 extract_grob_set (me, Grob_array::unsmob (me->get_object ("vertical-skyline-el
ements")) ? "vertical-skyline-elements" : "elements", elts); |
385 Skyline_pair skylines = skyline_spacing (me, elts); | 396 Skyline_pair skylines = skyline_spacing (me, elts); |
386 | 397 |
387 return skylines.smobbed_copy (); | 398 return skylines.smobbed_copy (); |
388 } | 399 } |
389 | 400 |
390 /* whereas calc_skylines calculates skylines for axis-groups with a lot of | 401 /* whereas calc_skylines calculates skylines for axis-groups with a lot of |
391 visible children, combine_skylines is designed for axis-groups whose only | 402 visible children, combine_skylines is designed for axis-groups whose only |
392 children are other axis-groups (ie. VerticalAlignment). Rather than | 403 children are other axis-groups (ie. VerticalAlignment). Rather than |
393 calculating all the skylines from scratch, we just merge the skylines | 404 calculating all the skylines from scratch, we just merge the skylines |
394 of the children. | 405 of the children. |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
469 vector<Grob *> relevant_grobs; | 480 vector<Grob *> relevant_grobs; |
470 SCM pure_relevant_p = ly_lily_module_constant ("pure-relevant?"); | 481 SCM pure_relevant_p = ly_lily_module_constant ("pure-relevant?"); |
471 | 482 |
472 for (vsize i = 0; i < elts.size (); i++) | 483 for (vsize i = 0; i < elts.size (); i++) |
473 { | 484 { |
474 if (to_boolean (scm_apply_1 (pure_relevant_p, elts[i]->self_scm (), SCM_EO
L))) | 485 if (to_boolean (scm_apply_1 (pure_relevant_p, elts[i]->self_scm (), SCM_EO
L))) |
475 relevant_grobs.push_back (elts[i]); | 486 relevant_grobs.push_back (elts[i]); |
476 | 487 |
477 if (Item *it = dynamic_cast<Item *> (elts[i])) | 488 if (Item *it = dynamic_cast<Item *> (elts[i])) |
478 { | 489 { |
479 Direction d = LEFT; | 490 for (LEFT_and_RIGHT (d)) |
480 do | |
481 { | 491 { |
482 Item *piece = it->find_prebroken_piece (d); | 492 Item *piece = it->find_prebroken_piece (d); |
483 if (piece && to_boolean (scm_apply_1 (pure_relevant_p, piece->self
_scm (), SCM_EOL))) | 493 if (piece && to_boolean (scm_apply_1 (pure_relevant_p, piece->self
_scm (), SCM_EOL))) |
484 relevant_grobs.push_back (piece); | 494 relevant_grobs.push_back (piece); |
485 } | 495 } |
486 while (flip (&d) != LEFT); | |
487 } | 496 } |
488 } | 497 } |
489 | 498 |
490 vector_sort (relevant_grobs, pure_staff_priority_less); | 499 vector_sort (relevant_grobs, pure_staff_priority_less); |
491 SCM grobs_scm = Grob_array::make_array (); | 500 SCM grobs_scm = Grob_array::make_array (); |
492 unsmob_grob_array (grobs_scm)->set_array (relevant_grobs); | 501 unsmob_grob_array (grobs_scm)->set_array (relevant_grobs); |
493 | 502 |
494 return grobs_scm; | 503 return grobs_scm; |
495 } | 504 } |
496 | 505 |
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
598 static bool | 607 static bool |
599 pure_staff_priority_less (Grob *const &g1, Grob *const &g2) | 608 pure_staff_priority_less (Grob *const &g1, Grob *const &g2) |
600 { | 609 { |
601 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority
"), -infinity_f); | 610 Real priority_1 = robust_scm2double (g1->get_property ("outside-staff-priority
"), -infinity_f); |
602 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority
"), -infinity_f); | 611 Real priority_2 = robust_scm2double (g2->get_property ("outside-staff-priority
"), -infinity_f); |
603 | 612 |
604 return priority_1 < priority_2; | 613 return priority_1 < priority_2; |
605 } | 614 } |
606 | 615 |
607 static void | 616 static void |
608 add_boxes (Grob *me, Grob *x_common, Grob *y_common, vector<Box> *const boxes, S
kyline_pair *skylines) | 617 add_interior_skylines (Grob *me, Grob *x_common, Grob *y_common, vector<Skyline_
pair> *skylines) |
609 { | 618 { |
610 /* if a child has skylines, use them instead of the extent box */ | 619 if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements"))) |
611 if (Skyline_pair *pair = Skyline_pair::unsmob (me->get_property ("vertical-sky
lines"))) | |
612 { | |
613 if (!scm_is_number (me->get_property ("outside-staff-priority")) | |
614 && !to_boolean (me->get_property ("cross-staff"))) | |
615 { | |
616 Skyline_pair s = Skyline_pair (*pair); | |
617 s.shift (me->relative_coordinate (x_common, X_AXIS)); | |
618 s.raise (me->relative_coordinate (y_common, Y_AXIS)); | |
619 skylines->merge (s); | |
620 } | |
621 } | |
622 else if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements")
)) | |
623 { | 620 { |
624 for (vsize i = 0; i < elements->size (); i++) | 621 for (vsize i = 0; i < elements->size (); i++) |
625 add_boxes (elements->grob (i), x_common, y_common, boxes, skylines); | 622 add_interior_skylines (elements->grob (i), x_common, y_common, skylines)
; |
626 } | 623 } |
627 else if (!scm_is_number (me->get_property ("outside-staff-priority")) | 624 else if (!scm_is_number (me->get_property ("outside-staff-priority")) |
628 && !to_boolean (me->get_property ("cross-staff"))) | 625 && !to_boolean (me->get_property ("cross-staff"))) |
629 { | 626 { |
630 boxes->push_back (Box (me->extent (x_common, X_AXIS), | 627 Skyline_pair *maybe_pair = Skyline_pair::unsmob (me->get_property ("vertic
al-skylines")); |
631 me->extent (y_common, Y_AXIS))); | 628 if (!maybe_pair) |
632 } | 629 return; |
633 } | 630 if (maybe_pair->is_empty ()) |
634 | 631 return; |
635 /* We want to avoid situations like this: | 632 skylines->push_back (Skyline_pair (*maybe_pair)); |
636 still more text | 633 skylines->back ().shift (me->relative_coordinate (x_common, X_AXIS)); |
637 more text | 634 skylines->back ().raise (me->relative_coordinate (y_common, Y_AXIS)); |
638 text | 635 } |
639 ------------------- | 636 } |
640 staff | 637 |
641 ------------------- | 638 // Raises the grob elt (whose skylines are given by h_skyline |
642 | 639 // and v_skyline) so that it doesn't intersect with staff_skyline, |
643 The point is that "still more text" should be positioned under | 640 // or with anything in other_h_skylines and other_v_skylines. |
644 "more text". In order to achieve this, we place the grobs in several | 641 void |
645 passes. We keep track of the right-most horizontal position that has been | 642 avoid_outside_staff_collisions (Grob *elt, |
646 affected by the current pass so far (actually we keep track of 2 | 643 Skyline_pair *v_skyline, |
647 positions, one for above the staff, one for below). | 644 Real padding, |
648 | 645 Real horizon_padding, |
649 In each pass, we loop through the unplaced grobs from left to right. | 646 vector<Skyline_pair> const &other_v_skylines, |
650 If the grob doesn't overlap the right-most affected position, we place it | 647 vector<Real> const &other_padding, |
651 (and then update the right-most affected position to point to the right | 648 vector<Real> const &other_horizon_padding, |
652 edge of the just-placed grob). Otherwise, we skip it until the next pass. | 649 Direction const dir) |
653 */ | 650 { |
| 651 assert (other_v_skylines.size () == other_padding.size ()); |
| 652 assert (other_v_skylines.size () == other_horizon_padding.size ()); |
| 653 vector<Interval> forbidden_intervals; |
| 654 for (vsize j = 0; j < other_v_skylines.size (); j++) |
| 655 { |
| 656 Skyline_pair const &v_other = other_v_skylines[j]; |
| 657 Real pad = (padding + other_padding[j]); |
| 658 Real horizon_pad = (horizon_padding + other_horizon_padding[j]); |
| 659 |
| 660 // We need to push elt up by at least this much to be above v_other. |
| 661 Real up = (*v_skyline)[DOWN].distance (v_other[UP], horizon_pad) + pad; |
| 662 // We need to push elt down by at least this much to be below v_other. |
| 663 Real down = (*v_skyline)[UP].distance (v_other[DOWN], horizon_pad) + pad; |
| 664 |
| 665 forbidden_intervals.push_back (Interval (-down, up)); |
| 666 } |
| 667 |
| 668 Interval_set allowed_shifts |
| 669 = Interval_set::interval_union (forbidden_intervals).complement (); |
| 670 Real move = allowed_shifts.nearest_point (0, dir); |
| 671 v_skyline->raise (move); |
| 672 elt->translate_axis (move, Y_AXIS); |
| 673 } |
| 674 |
| 675 SCM |
| 676 valid_outside_staff_placement_directive (Grob *me) |
| 677 { |
| 678 SCM directive = me->get_property ("outside-staff-placement-directive"); |
| 679 |
| 680 if ((directive == ly_symbol2scm ("left-to-right-greedy")) |
| 681 || (directive == ly_symbol2scm ("left-to-right-polite")) |
| 682 || (directive == ly_symbol2scm ("right-to-left-greedy")) |
| 683 || (directive == ly_symbol2scm ("right-to-left-polite"))) |
| 684 return directive; |
| 685 |
| 686 me->warning (_f ("\"%s\" is not a valid outside-staff-placement-directive", |
| 687 robust_symbol2string (directive, "").c_str ())); |
| 688 |
| 689 return ly_symbol2scm ("left-to-right-polite"); |
| 690 } |
| 691 |
| 692 // Shifts the grobs in elements to ensure that they (and any |
| 693 // connected riders) don't collide with the staff skylines |
| 694 // or anything in all_X_skylines. Afterwards, the skylines |
| 695 // of the grobs in elements will be added to all_v_skylines. |
654 static void | 696 static void |
655 add_grobs_of_one_priority (Skyline_pair *const skylines, | 697 add_grobs_of_one_priority (Grob *me, |
| 698 Drul_array<vector<Skyline_pair> > *all_v_skylines, |
| 699 Drul_array<vector<Real> > *all_paddings, |
| 700 Drul_array<vector<Real> > *all_horizon_paddings, |
656 vector<Grob *> elements, | 701 vector<Grob *> elements, |
657 Grob *x_common, | 702 Grob *x_common, |
658 Grob *y_common, | 703 Grob *y_common, |
659 vector<Grob *> *riders) | 704 multimap<Grob *, Grob *> const &riders) |
660 { | 705 { |
| 706 |
| 707 SCM directive |
| 708 = valid_outside_staff_placement_directive (me); |
| 709 |
| 710 bool l2r = ((directive == ly_symbol2scm ("left-to-right-greedy")) |
| 711 || (directive == ly_symbol2scm ("left-to-right-polite"))); |
| 712 |
| 713 bool polite = ((directive == ly_symbol2scm ("left-to-right-polite")) |
| 714 || (directive == ly_symbol2scm ("right-to-left-polite"))); |
| 715 |
661 vector<Box> boxes; | 716 vector<Box> boxes; |
662 Drul_array<Real> last_affected_position; | 717 vector<Skyline_pair> skylines_to_merge; |
663 | 718 |
664 reverse (elements); | 719 // We want to avoid situations like this: |
| 720 // still more text |
| 721 // more text |
| 722 // text |
| 723 // ------------------- |
| 724 // staff |
| 725 // ------------------- |
| 726 |
| 727 // The point is that "still more text" should be positioned under |
| 728 // "more text". In order to achieve this, we place the grobs in several |
| 729 // passes. We keep track of the right-most horizontal position that has been |
| 730 // affected by the current pass so far (actually we keep track of 2 |
| 731 // positions, one for above the staff, one for below). |
| 732 |
| 733 // In each pass, we loop through the unplaced grobs from left to right. |
| 734 // If the grob doesn't overlap the right-most affected position, we place it |
| 735 // (and then update the right-most affected position to point to the right |
| 736 // edge of the just-placed grob). Otherwise, we skip it until the next pass. |
665 while (!elements.empty ()) | 737 while (!elements.empty ()) |
666 { | 738 { |
667 last_affected_position[UP] = -infinity_f; | 739 Drul_array<Real> last_end (-infinity_f, -infinity_f); |
668 last_affected_position[DOWN] = -infinity_f; | 740 vector<Grob *> skipped_elements; |
669 /* do one pass */ | 741 for (vsize i = l2r ? 0 : elements.size (); |
670 for (vsize i = elements.size (); i--;) | 742 l2r ? i < elements.size () : i--; |
| 743 l2r ? i++ : 0) |
671 { | 744 { |
672 Direction dir = get_grob_direction (elements[i]); | 745 Grob *elt = elements[i]; |
| 746 Real padding |
| 747 = robust_scm2double (elt->get_property ("outside-staff-padding"), 0.
25); |
| 748 Real horizon_padding |
| 749 = robust_scm2double (elt->get_property ("outside-staff-horizontal-pa
dding"), 0.0); |
| 750 Interval x_extent = elt->extent (x_common, X_AXIS); |
| 751 x_extent.widen (horizon_padding); |
| 752 |
| 753 Direction dir = get_grob_direction (elt); |
673 if (dir == CENTER) | 754 if (dir == CENTER) |
674 { | 755 { |
675 warning (_ ("an outside-staff object should have a direction, defa
ulting to up")); | 756 warning (_ ("an outside-staff object should have a direction, defa
ulting to up")); |
676 dir = UP; | 757 dir = UP; |
677 } | 758 } |
678 | 759 |
679 SCM horizon_padding_scm = elements[i]->get_property ("outside-staff-ho
rizontal-padding"); | 760 if (x_extent[LEFT] <= last_end[dir] && polite) |
680 Real horizon_padding = robust_scm2double (horizon_padding_scm, 0.0); | |
681 Skyline other; | |
682 Box b; | |
683 bool do_add = false; | |
684 bool before_last_affected_position = false; | |
685 bool uses_boxes = false; | |
686 Skyline_pair pair; | |
687 | |
688 if (Skyline_pair::unsmob (elements[i]->get_property ("vertical-skyline
s"))) | |
689 { | 761 { |
690 pair = Skyline_pair (*Skyline_pair::unsmob (elements[i]->get_prope
rty ("vertical-skylines"))); | 762 skipped_elements.push_back (elt); |
691 do_add = !pair.is_empty (); | |
692 if (do_add) | |
693 { | |
694 pair.shift (elements[i]->relative_coordinate (x_common, X_AXIS
)); | |
695 pair.raise (elements[i]->relative_coordinate (y_common, Y_AXIS
)); | |
696 } | |
697 before_last_affected_position = pair[-dir].left () - 2 * horizon_p
adding < last_affected_position[dir]; | |
698 } | |
699 else | |
700 { | |
701 uses_boxes = true; | |
702 b = Box (elements[i]->extent (x_common, X_AXIS), | |
703 elements[i]->extent (y_common, Y_AXIS)); | |
704 | |
705 before_last_affected_position = b[X_AXIS][LEFT] - 2 * horizon_padd
ing < last_affected_position[dir]; | |
706 do_add = !b[X_AXIS].is_empty () && !b[Y_AXIS].is_empty (); | |
707 } | |
708 | |
709 if (before_last_affected_position) | |
710 continue; | |
711 | |
712 /* | |
713 Finding extents may have trigged another skyline lookup, which will
have | |
714 done the shifting. In this case, we simply add the boxes. | |
715 */ | |
716 if (!scm_is_number (elements[i]->get_property ("outside-staff-priority
"))) | |
717 { | |
718 add_boxes (elements[i], x_common, y_common, &boxes, skylines); | |
719 if (boxes.size ()) | |
720 skylines->merge (Skyline_pair (boxes, 0.0, X_AXIS)); | |
721 boxes.clear (); | |
722 elements.erase (elements.begin () + i); | |
723 continue; | 763 continue; |
724 } | 764 } |
725 | 765 last_end[dir] = x_extent[RIGHT]; |
726 if (do_add) | 766 |
| 767 Skyline_pair *v_orig = Skyline_pair::unsmob (elt->get_property ("verti
cal-skylines")); |
| 768 if (v_orig->is_empty ()) |
| 769 continue; |
| 770 |
| 771 // Find the riders associated with this grob, and merge their |
| 772 // skylines with elt's skyline. |
| 773 typedef multimap<Grob *, Grob *>::const_iterator GrobMapIterator; |
| 774 pair<GrobMapIterator, GrobMapIterator> range = riders.equal_range (elt
); |
| 775 vector<Skyline_pair> rider_v_skylines; |
| 776 for (GrobMapIterator j = range.first; j != range.second; j++) |
727 { | 777 { |
728 boxes.clear (); | 778 Grob *rider = j->second; |
729 if (uses_boxes) | 779 Skyline_pair *v_rider = Skyline_pair::unsmob (rider->get_property
("vertical-skylines")); |
| 780 if (v_rider) |
730 { | 781 { |
731 boxes.push_back (b); | 782 Skyline_pair copy (*v_rider); |
732 other = Skyline (boxes, horizon_padding, X_AXIS, -dir); | 783 copy.shift (rider->relative_coordinate (x_common, X_AXIS)); |
| 784 copy.raise (rider->relative_coordinate (y_common, Y_AXIS)); |
| 785 rider_v_skylines.push_back (copy); |
733 } | 786 } |
734 | |
735 Real padding = robust_scm2double (elements[i]->get_property ("outs
ide-staff-padding"), 0.5); | |
736 Real dist = (*skylines)[dir].distance (uses_boxes ? other : pair[-
dir]) + padding; | |
737 | |
738 if (dist > 0) | |
739 { | |
740 if (uses_boxes) | |
741 b.translate (Offset (0, dir * dist)); | |
742 else | |
743 pair.raise (dir * dist); | |
744 elements[i]->translate_axis (dir * dist, Y_AXIS); | |
745 } | |
746 if (uses_boxes) | |
747 skylines->insert (b, 0, X_AXIS); | |
748 else | |
749 skylines->merge (pair); | |
750 elements[i]->set_property ("outside-staff-priority", SCM_BOOL_F); | |
751 if (uses_boxes) | |
752 last_affected_position[dir] = b[X_AXIS][RIGHT]; | |
753 else | |
754 last_affected_position[dir] = pair.right (); | |
755 other.clear (); | |
756 } | 787 } |
757 // if a grob previously had an outside staff parent but no longer does
, | 788 Skyline_pair v_skylines (*v_orig); |
758 // it is safe now to add it to the skyline | 789 v_skylines.shift (elt->relative_coordinate (x_common, X_AXIS)); |
759 for (vsize j = 0; j < riders->size (); j++) | 790 v_skylines.raise (elt->relative_coordinate (y_common, Y_AXIS)); |
760 if (!Axis_group_interface::has_outside_staff_parent (riders->at (j))
) | 791 v_skylines.merge (Skyline_pair (rider_v_skylines)); |
761 { | 792 |
762 add_boxes (riders->at (j), x_common, y_common, &boxes, skylines)
; | 793 avoid_outside_staff_collisions (elt, |
763 if (boxes.size ()) | 794 &v_skylines, |
764 skylines->merge (Skyline_pair (boxes, 0.0, X_AXIS)); | 795 padding, |
765 boxes.clear (); | 796 horizon_padding, |
766 } | 797 (*all_v_skylines)[dir], |
767 /* | 798 (*all_paddings)[dir], |
768 Ugh: quadratic. --hwn | 799 (*all_horizon_paddings)[dir], |
769 */ | 800 dir); |
770 elements.erase (elements.begin () + i); | 801 |
| 802 elt->set_property ("outside-staff-priority", SCM_BOOL_F); |
| 803 (*all_v_skylines)[dir].push_back (v_skylines); |
| 804 (*all_paddings)[dir].push_back (padding); |
| 805 (*all_horizon_paddings)[dir].push_back (horizon_padding); |
771 } | 806 } |
772 } | 807 swap (elements, skipped_elements); |
773 } | 808 skipped_elements.clear (); |
774 | 809 } |
775 bool | 810 } |
776 Axis_group_interface::has_outside_staff_parent (Grob *me) | 811 |
777 { | 812 // If the Grob has a Y-ancestor with outside-staff-priority, return it. |
778 if (!me->get_parent (Y_AXIS)) | 813 // Otherwise, return 0. |
779 return false; | 814 Grob * |
780 | 815 Axis_group_interface::outside_staff_ancestor (Grob *me) |
781 return scm_is_number (me->get_parent (Y_AXIS)->get_property ("outside-staff-pr
iority")) | 816 { |
782 || has_outside_staff_parent (me->get_parent (Y_AXIS)); | 817 Grob *parent = me->get_parent (Y_AXIS); |
783 } | 818 if (!parent) |
784 | 819 return 0; |
785 // TODO: it is tricky to correctly handle skyline placement of cross-staff grobs
. | 820 |
| 821 if (scm_is_number (parent->get_property ("outside-staff-priority"))) |
| 822 return parent; |
| 823 |
| 824 return outside_staff_ancestor (parent); |
| 825 } |
| 826 |
| 827 // It is tricky to correctly handle skyline placement of cross-staff grobs. |
786 // For example, cross-staff beams cannot be formatted until the distance between | 828 // For example, cross-staff beams cannot be formatted until the distance between |
787 // staves is known and therefore any grobs that depend on the beam cannot be pla
ced | 829 // staves is known and therefore any grobs that depend on the beam cannot be pla
ced |
788 // until the skylines are known. On the other hand, the distance between staves
should | 830 // until the skylines are known. On the other hand, the distance between staves
should |
789 // really depend on position of the cross-staff grobs that lie between them. | 831 // really depend on position of the cross-staff grobs that lie between them. |
790 // Currently, we just leave cross-staff grobs out of the | 832 // Currently, we just leave cross-staff grobs out of the |
791 // skyline altogether, but this could mean that staves are placed so close toget
her | 833 // skyline altogether, but this could mean that staves are placed so close toget
her |
792 // that there is no room for the cross-staff grob. It also means, of course, tha
t | 834 // that there is no room for the cross-staff grob. It also means, of course, tha
t |
793 // we don't get the benefits of skyline placement for cross-staff grobs. | 835 // we don't get the benefits of skyline placement for cross-staff grobs. |
794 Skyline_pair | 836 Skyline_pair |
795 Axis_group_interface::skyline_spacing (Grob *me, vector<Grob *> elements) | 837 Axis_group_interface::skyline_spacing (Grob *me, vector<Grob *> elements) |
796 { | 838 { |
797 /* | |
798 As a sanity check, we make sure that no grob with an outside staff priority | |
799 has a Y-parent that also has an outside staff priority, which would result | |
800 in two movings. | |
801 */ | |
802 for (vsize i = 0; i < elements.size (); i++) | 839 for (vsize i = 0; i < elements.size (); i++) |
| 840 /* |
| 841 As a sanity check, we make sure that no grob with an outside staff priorit
y |
| 842 has a Y-parent that also has an outside staff priority, which would result |
| 843 in two movings. |
| 844 */ |
803 if (scm_is_number (elements[i]->get_property ("outside-staff-priority")) | 845 if (scm_is_number (elements[i]->get_property ("outside-staff-priority")) |
804 && has_outside_staff_parent (elements[i])) | 846 && outside_staff_ancestor (elements[i])) |
805 { | 847 { |
806 elements[i]->warning ("Cannot set outside-staff-priority for element and
elements' Y parent."); | 848 elements[i]->warning ("Cannot set outside-staff-priority for element and
elements' Y parent."); |
807 elements[i]->set_property ("outside-staff-priority", SCM_BOOL_F); | 849 elements[i]->set_property ("outside-staff-priority", SCM_BOOL_F); |
808 } | 850 } |
809 | 851 |
810 /* For grobs with an outside-staff-priority, the sorting function might | 852 /* For grobs with an outside-staff-priority, the sorting function might |
811 call extent and cause suicide. This breaks the contract that is required | 853 call extent and cause suicide. This breaks the contract that is required |
812 for the STL sort function. To avoid this, we make sure that any suicides | 854 for the STL sort function. To avoid this, we make sure that any suicides |
813 are triggered beforehand. | 855 are triggered beforehand. |
814 */ | 856 */ |
815 for (vsize i = 0; i < elements.size (); i++) | 857 for (vsize i = 0; i < elements.size (); i++) |
816 if (scm_is_number (elements[i]->get_property ("outside-staff-priority"))) | 858 if (scm_is_number (elements[i]->get_property ("outside-staff-priority"))) |
817 elements[i]->extent (elements[i], X_AXIS); | 859 elements[i]->extent (elements[i], X_AXIS); |
818 | 860 |
819 vector_sort (elements, staff_priority_less); | 861 vector_sort (elements, staff_priority_less); |
820 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS); | 862 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS); |
821 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS); | 863 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS); |
822 | 864 |
823 assert (y_common == me); | 865 assert (y_common == me); |
824 | 866 |
| 867 // A rider is a grob that is not outside-staff, but has an outside-staff |
| 868 // ancestor. In that case, the rider gets moved along with its ancestor. |
| 869 multimap<Grob *, Grob *> riders; |
| 870 |
825 vsize i = 0; | 871 vsize i = 0; |
826 vector<Box> boxes; | 872 vector<Skyline_pair> inside_staff_skylines; |
827 vector<Grob *> riders; | |
828 | |
829 Skyline_pair skylines; | |
830 for (i = 0; i < elements.size () | 873 for (i = 0; i < elements.size () |
831 && !scm_is_number (elements[i]->get_property ("outside-staff-priority"));
i++) | 874 && !scm_is_number (elements[i]->get_property ("outside-staff-priority"));
i++) |
832 { | 875 { |
833 if (!(to_boolean (elements[i]->get_property ("cross-staff")) || has_outsid
e_staff_parent (elements[i]))) | 876 Grob *elt = elements[i]; |
834 add_boxes (elements[i], x_common, y_common, &boxes, &skylines); | 877 Grob *ancestor = outside_staff_ancestor (elt); |
835 if (has_outside_staff_parent (elements[i])) | 878 if (!(to_boolean (elt->get_property ("cross-staff")) || ancestor)) |
836 riders.push_back (elements[i]); | 879 add_interior_skylines (elt, x_common, y_common, &inside_staff_skylines); |
837 } | 880 if (ancestor) |
838 | 881 riders.insert (pair<Grob *, Grob *> (ancestor, elt)); |
839 SCM padding_scm = me->get_property ("skyline-horizontal-padding"); | 882 } |
840 Real padding = robust_scm2double (padding_scm, 0.1); | 883 |
841 skylines.merge (Skyline_pair (boxes, 0.0, X_AXIS)); // use padding later | 884 Skyline_pair skylines (inside_staff_skylines); |
| 885 |
| 886 // These are the skylines of all outside-staff grobs |
| 887 // that have already been processed. We keep them around in order to |
| 888 // check them for collisions with the currently active outside-staff grob. |
| 889 Drul_array<vector<Skyline_pair> > all_v_skylines; |
| 890 Drul_array<vector<Real> > all_paddings; |
| 891 Drul_array<vector<Real> > all_horizon_paddings; |
| 892 for (UP_and_DOWN (d)) |
| 893 { |
| 894 all_v_skylines[d].push_back (skylines); |
| 895 all_paddings[d].push_back (0); |
| 896 all_horizon_paddings[d].push_back (0); |
| 897 } |
| 898 |
842 for (; i < elements.size (); i++) | 899 for (; i < elements.size (); i++) |
843 { | 900 { |
844 if (to_boolean (elements[i]->get_property ("cross-staff"))) | 901 if (to_boolean (elements[i]->get_property ("cross-staff"))) |
845 continue; | 902 continue; |
846 | 903 |
| 904 // Collect all the outside-staff grobs that have a particular priority. |
847 SCM priority = elements[i]->get_property ("outside-staff-priority"); | 905 SCM priority = elements[i]->get_property ("outside-staff-priority"); |
848 vector<Grob *> current_elts; | 906 vector<Grob *> current_elts; |
849 current_elts.push_back (elements[i]); | 907 current_elts.push_back (elements[i]); |
850 while (i + 1 < elements.size () | 908 while (i + 1 < elements.size () |
851 && scm_is_eq (elements[i + 1]->get_property ("outside-staff-priorit
y"), priority)) | 909 && scm_is_eq (elements[i + 1]->get_property ("outside-staff-priorit
y"), priority)) |
852 { | 910 { |
853 if (!to_boolean (elements[i + 1]->get_property ("cross-staff"))) | 911 if (!to_boolean (elements[i + 1]->get_property ("cross-staff"))) |
854 current_elts.push_back (elements[i + 1]); | 912 current_elts.push_back (elements[i + 1]); |
855 ++i; | 913 ++i; |
856 } | 914 } |
857 | 915 |
858 add_grobs_of_one_priority (&skylines, current_elts, x_common, y_common, &r
iders); | 916 add_grobs_of_one_priority (me, |
859 for (vsize j = riders.size (); j--;) | 917 &all_v_skylines, |
860 if (!has_outside_staff_parent (riders[j])) | 918 &all_paddings, |
861 riders.erase (riders.begin () + j); | 919 &all_horizon_paddings, |
862 } | 920 current_elts, |
863 | 921 x_common, |
864 for (vsize j = 0; j < riders.size (); j++) | 922 y_common, |
865 if (Skyline_pair::unsmob (riders[j]->get_property ("vertical-skylines")) | 923 riders); |
866 || !riders[j]->extent (y_common, Y_AXIS).is_empty ()) | 924 } |
867 riders[j]->programming_error ("Vertical skylines will not be high enough."
); | 925 |
868 | 926 // Now everything in all_v_skylines has been shifted appropriately; merge |
| 927 // them all into skylines to get the complete outline. |
| 928 Skyline_pair other_skylines (all_v_skylines[UP]); |
| 929 other_skylines.merge (Skyline_pair (all_v_skylines[DOWN])); |
| 930 skylines.merge (other_skylines); |
| 931 |
| 932 // We began by shifting my skyline to be relative to the common refpoint; now |
| 933 // shift it back. |
869 skylines.shift (-me->relative_coordinate (x_common, X_AXIS)); | 934 skylines.shift (-me->relative_coordinate (x_common, X_AXIS)); |
870 skylines.rebuild_skyline_padding (padding, X_AXIS); | 935 |
871 return skylines; | 936 return skylines; |
872 } | 937 } |
873 | 938 |
874 MAKE_SCHEME_CALLBACK (Axis_group_interface, print, 1) | 939 MAKE_SCHEME_CALLBACK (Axis_group_interface, print, 1) |
875 SCM | 940 SCM |
876 Axis_group_interface::print (SCM smob) | 941 Axis_group_interface::print (SCM smob) |
877 { | 942 { |
878 if (!debug_skylines) | 943 if (!debug_skylines) |
879 return SCM_BOOL_F; | 944 return SCM_BOOL_F; |
880 | 945 |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
917 | 982 |
918 if (grouper) | 983 if (grouper) |
919 { | 984 { |
920 bool within_group = Staff_grouper_interface::maybe_pure_within_group (grou
per, me, pure, start, end); | 985 bool within_group = Staff_grouper_interface::maybe_pure_within_group (grou
per, me, pure, start, end); |
921 if (within_group) | 986 if (within_group) |
922 return grouper->get_maybe_pure_property ("staff-staff-spacing", pure, st
art, end); | 987 return grouper->get_maybe_pure_property ("staff-staff-spacing", pure, st
art, end); |
923 else | 988 else |
924 return grouper->get_maybe_pure_property ("staffgroup-staff-spacing", pur
e, start, end); | 989 return grouper->get_maybe_pure_property ("staffgroup-staff-spacing", pur
e, start, end); |
925 } | 990 } |
926 return me->get_maybe_pure_property ("default-staff-staff-spacing", pure, start
, end); | 991 return me->get_maybe_pure_property ("default-staff-staff-spacing", pure, start
, end); |
927 } | |
928 | |
929 Real | |
930 Axis_group_interface::minimum_distance (Grob *g1, Grob *g2, Axis a) | |
931 { | |
932 SCM sym = ly_symbol2scm ((a == Y_AXIS) ? "vertical-skylines" : "horizontal-sky
lines"); | |
933 | |
934 Skyline_pair *s1 = Skyline_pair::unsmob (g1->get_property (sym)); | |
935 Skyline_pair *s2 = Skyline_pair::unsmob (g2->get_property (sym)); | |
936 if (s1 && s2) | |
937 return (*s1)[DOWN].distance ((*s2)[UP]); | |
938 return 0; | |
939 } | 992 } |
940 | 993 |
941 ADD_INTERFACE (Axis_group_interface, | 994 ADD_INTERFACE (Axis_group_interface, |
942 "An object that groups other layout objects.", | 995 "An object that groups other layout objects.", |
943 | 996 |
944 // TODO: some of these properties are specific to | 997 // TODO: some of these properties are specific to |
945 // VerticalAxisGroup. We should split off a | 998 // VerticalAxisGroup. We should split off a |
946 // vertical-axis-group-interface. | 999 // vertical-axis-group-interface. |
947 /* properties */ | 1000 /* properties */ |
948 "adjacent-pure-heights " | 1001 "adjacent-pure-heights " |
949 "axes " | 1002 "axes " |
950 "bound-alignment-interfaces " | 1003 "bound-alignment-interfaces " |
951 "default-staff-staff-spacing " | 1004 "default-staff-staff-spacing " |
952 "elements " | 1005 "elements " |
953 "max-stretch " | 1006 "max-stretch " |
954 "no-alignment " | 1007 "no-alignment " |
955 "nonstaff-nonstaff-spacing " | 1008 "nonstaff-nonstaff-spacing " |
956 "nonstaff-relatedstaff-spacing " | 1009 "nonstaff-relatedstaff-spacing " |
957 "nonstaff-unrelatedstaff-spacing " | 1010 "nonstaff-unrelatedstaff-spacing " |
| 1011 "outside-staff-placement-directive " |
958 "pure-relevant-grobs " | 1012 "pure-relevant-grobs " |
959 "pure-relevant-items " | 1013 "pure-relevant-items " |
960 "pure-relevant-spanners " | 1014 "pure-relevant-spanners " |
961 "pure-Y-common " | 1015 "pure-Y-common " |
962 "staff-affinity " | 1016 "staff-affinity " |
963 "staff-grouper " | 1017 "staff-grouper " |
964 "staff-staff-spacing " | 1018 "staff-staff-spacing " |
965 "system-Y-offset " | 1019 "system-Y-offset " |
| 1020 "vertical-skyline-elements " |
966 "X-common " | 1021 "X-common " |
967 "Y-common " | 1022 "Y-common " |
968 ); | 1023 ); |
LEFT | RIGHT |