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 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
606 | 615 |
607 static void | 616 static void |
608 add_interior_skylines (Grob *me, Grob *x_common, Grob *y_common, vector<Skyline_
pair> *skylines) | 617 add_interior_skylines (Grob *me, Grob *x_common, Grob *y_common, vector<Skyline_
pair> *skylines) |
609 { | 618 { |
610 if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements"))) | 619 if (Grob_array *elements = unsmob_grob_array (me->get_object ("elements"))) |
611 { | 620 { |
612 for (vsize i = 0; i < elements->size (); i++) | 621 for (vsize i = 0; i < elements->size (); i++) |
613 add_interior_skylines (elements->grob (i), x_common, y_common, skylines)
; | 622 add_interior_skylines (elements->grob (i), x_common, y_common, skylines)
; |
614 } | 623 } |
615 else if (!scm_is_number (me->get_property ("outside-staff-priority")) | 624 else if (!scm_is_number (me->get_property ("outside-staff-priority")) |
616 && !to_boolean (me->get_property ("cross-staff"))) | 625 && !to_boolean (me->get_property ("cross-staff"))) |
617 { | 626 { |
618 Skyline_pair *maybe_pair = Skyline_pair::unsmob (me->get_property ("vertic
al-skylines")); | 627 Skyline_pair *maybe_pair = Skyline_pair::unsmob (me->get_property ("vertic
al-skylines")); |
619 if (!maybe_pair) | 628 if (!maybe_pair) |
620 return; | 629 return; |
621 if (maybe_pair->is_empty ()) | 630 if (maybe_pair->is_empty ()) |
622 return; | 631 return; |
623 Skyline_pair s (*maybe_pair); | 632 skylines->push_back (Skyline_pair (*maybe_pair)); |
624 s.shift (me->relative_coordinate (x_common, X_AXIS)); | 633 skylines->back ().shift (me->relative_coordinate (x_common, X_AXIS)); |
625 s.raise (me->relative_coordinate (y_common, Y_AXIS)); | 634 skylines->back ().raise (me->relative_coordinate (y_common, Y_AXIS)); |
626 skylines->push_back (s); | 635 } |
627 } | 636 } |
628 } | 637 |
629 | 638 // Raises the grob elt (whose skylines are given by h_skyline |
630 /* We want to avoid situations like this: | 639 // and v_skyline) so that it doesn't intersect with staff_skyline, |
631 still more text | 640 // or with anything in other_h_skylines and other_v_skylines. |
632 more text | 641 void |
633 text | 642 avoid_outside_staff_collisions (Grob *elt, |
634 ------------------- | 643 Skyline_pair *v_skyline, |
635 staff | 644 Real padding, |
636 ------------------- | 645 Real horizon_padding, |
637 | 646 vector<Skyline_pair> const &other_v_skylines, |
638 The point is that "still more text" should be positioned under | 647 vector<Real> const &other_padding, |
639 "more text". In order to achieve this, we place the grobs in several | 648 vector<Real> const &other_horizon_padding, |
640 passes. We keep track of the right-most horizontal position that has been | 649 Direction const dir) |
641 affected by the current pass so far (actually we keep track of 2 | 650 { |
642 positions, one for above the staff, one for below). | 651 assert (other_v_skylines.size () == other_padding.size ()); |
643 | 652 assert (other_v_skylines.size () == other_horizon_padding.size ()); |
644 In each pass, we loop through the unplaced grobs from left to right. | 653 vector<Interval> forbidden_intervals; |
645 If the grob doesn't overlap the right-most affected position, we place it | 654 for (vsize j = 0; j < other_v_skylines.size (); j++) |
646 (and then update the right-most affected position to point to the right | 655 { |
647 edge of the just-placed grob). Otherwise, we skip it until the next pass. | 656 Skyline_pair const &v_other = other_v_skylines[j]; |
648 */ | 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. |
649 static void | 696 static void |
650 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, |
651 vector<Grob *> elements, | 701 vector<Grob *> elements, |
652 Grob *x_common, | 702 Grob *x_common, |
653 Grob *y_common, | 703 Grob *y_common, |
654 vector<Grob *> *riders) | 704 multimap<Grob *, Grob *> const &riders) |
655 { | 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 |
656 vector<Box> boxes; | 716 vector<Box> boxes; |
657 Drul_array<Real> last_affected_position; | 717 vector<Skyline_pair> skylines_to_merge; |
658 vector<Skyline_pair> to_constructor; | 718 |
659 | 719 // We want to avoid situations like this: |
660 reverse (elements); | 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. |
661 while (!elements.empty ()) | 737 while (!elements.empty ()) |
662 { | 738 { |
663 last_affected_position[UP] = -infinity_f; | 739 Drul_array<Real> last_end (-infinity_f, -infinity_f); |
664 last_affected_position[DOWN] = -infinity_f; | 740 vector<Grob *> skipped_elements; |
665 /* do one pass */ | 741 for (vsize i = l2r ? 0 : elements.size (); |
666 for (vsize i = elements.size (); i--;) | 742 l2r ? i < elements.size () : i--; |
| 743 l2r ? i++ : 0) |
667 { | 744 { |
668 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); |
669 if (dir == CENTER) | 754 if (dir == CENTER) |
670 { | 755 { |
671 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")); |
672 dir = UP; | 757 dir = UP; |
673 } | 758 } |
674 | 759 |
675 SCM horizon_padding_scm = elements[i]->get_property ("outside-staff-ho
rizontal-padding"); | 760 if (x_extent[LEFT] <= last_end[dir] && polite) |
676 Real horizon_padding = robust_scm2double (horizon_padding_scm, 0.0); | |
677 Skyline other; | |
678 bool do_add = false; | |
679 bool before_last_affected_position = false; | |
680 Skyline_pair *orig = Skyline_pair::unsmob (elements[i]->get_property (
"vertical-skylines")); | |
681 | |
682 /* | |
683 we need two skyline pairs. | |
684 one, pair, has padding built in and is used for spacing. | |
685 the other, to_pass_to_constructor, has no padding and is used to | |
686 construct the actual skyline. this is so that skylines don't | |
687 get double padded with outside-staff-horizontal-padding and | |
688 the padding of the axis group or system | |
689 */ | |
690 | |
691 Skyline_pair pair; | |
692 Skyline_pair to_pass_to_constructor; | |
693 do_add = !(*orig).is_empty (); | |
694 bool use_separate_constructor_skyline = horizon_padding != 0; | |
695 if (do_add) | |
696 { | 761 { |
697 vector<Skyline_pair> construct_from_me; | 762 skipped_elements.push_back (elt); |
698 construct_from_me.push_back (*orig); | 763 continue; |
699 pair = Skyline_pair (construct_from_me, horizon_padding, X_AXIS); | 764 } |
700 pair.shift (elements[i]->relative_coordinate (x_common, X_AXIS)); | 765 last_end[dir] = x_extent[RIGHT]; |
701 pair.raise (elements[i]->relative_coordinate (y_common, Y_AXIS)); | 766 |
702 if (use_separate_constructor_skyline) | 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++) |
| 777 { |
| 778 Grob *rider = j->second; |
| 779 Skyline_pair *v_rider = Skyline_pair::unsmob (rider->get_property
("vertical-skylines")); |
| 780 if (v_rider) |
703 { | 781 { |
704 to_pass_to_constructor = Skyline_pair (construct_from_me, 0.0,
X_AXIS); | 782 Skyline_pair copy (*v_rider); |
705 to_pass_to_constructor.shift (elements[i]->relative_coordinate
(x_common, X_AXIS)); | 783 copy.shift (rider->relative_coordinate (x_common, X_AXIS)); |
706 to_pass_to_constructor.raise (elements[i]->relative_coordinate
(y_common, Y_AXIS)); | 784 copy.raise (rider->relative_coordinate (y_common, Y_AXIS)); |
| 785 rider_v_skylines.push_back (copy); |
707 } | 786 } |
708 } | 787 } |
709 before_last_affected_position = pair[-dir].left () - 2 * horizon_paddi
ng < last_affected_position[dir]; | 788 Skyline_pair v_skylines (*v_orig); |
710 | 789 v_skylines.shift (elt->relative_coordinate (x_common, X_AXIS)); |
711 if (before_last_affected_position) | 790 v_skylines.raise (elt->relative_coordinate (y_common, Y_AXIS)); |
712 continue; | 791 v_skylines.merge (Skyline_pair (rider_v_skylines)); |
713 | 792 |
714 /* | 793 avoid_outside_staff_collisions (elt, |
715 Finding extents may have trigged another skyline lookup, which will
have | 794 &v_skylines, |
716 done the shifting. In this case, we simply add the boxes. | 795 padding, |
717 */ | 796 horizon_padding, |
718 if (!scm_is_number (elements[i]->get_property ("outside-staff-priority
"))) | 797 (*all_v_skylines)[dir], |
719 { | 798 (*all_paddings)[dir], |
720 add_interior_skylines (elements[i], x_common, y_common, &to_constr
uctor); | 799 (*all_horizon_paddings)[dir], |
721 elements.erase (elements.begin () + i); | 800 dir); |
722 continue; | 801 |
723 } | 802 elt->set_property ("outside-staff-priority", SCM_BOOL_F); |
724 | 803 (*all_v_skylines)[dir].push_back (v_skylines); |
725 if (do_add) | 804 (*all_paddings)[dir].push_back (padding); |
726 { | 805 (*all_horizon_paddings)[dir].push_back (horizon_padding); |
727 Real padding = robust_scm2double (elements[i]->get_property ("outs
ide-staff-padding"), 0.5); | |
728 Real dist = (*skylines)[dir].distance (pair[-dir]) + padding; | |
729 | |
730 if (dist > 0) | |
731 { | |
732 pair.raise (dir * dist); | |
733 if (use_separate_constructor_skyline) | |
734 to_pass_to_constructor.raise (dir * dist); | |
735 elements[i]->translate_axis (dir * dist, Y_AXIS); | |
736 } | |
737 | |
738 to_constructor.push_back (use_separate_constructor_skyline ? to_pa
ss_to_constructor : pair); | |
739 elements[i]->set_property ("outside-staff-priority", SCM_BOOL_F); | |
740 last_affected_position[dir] = pair.right (); | |
741 other.clear (); | |
742 } | |
743 // if a grob previously had an outside staff parent but no longer does
, | |
744 // it is safe now to add it to the skyline | |
745 for (vsize j = 0; j < riders->size (); j++) | |
746 if (!Axis_group_interface::has_outside_staff_parent (riders->at (j))
) | |
747 add_interior_skylines (riders->at (j), x_common, y_common, &to_con
structor); | |
748 /* | |
749 Ugh: quadratic. --hwn | |
750 */ | |
751 elements.erase (elements.begin () + i); | |
752 skylines->merge (Skyline_pair (to_constructor, 0.0, X_AXIS)); | |
753 to_constructor.resize (0); | |
754 } | 806 } |
755 } | 807 swap (elements, skipped_elements); |
756 } | 808 skipped_elements.clear (); |
757 | 809 } |
758 bool | 810 } |
759 Axis_group_interface::has_outside_staff_parent (Grob *me) | 811 |
760 { | 812 // If the Grob has a Y-ancestor with outside-staff-priority, return it. |
761 if (!me->get_parent (Y_AXIS)) | 813 // Otherwise, return 0. |
762 return false; | 814 Grob * |
763 | 815 Axis_group_interface::outside_staff_ancestor (Grob *me) |
764 return scm_is_number (me->get_parent (Y_AXIS)->get_property ("outside-staff-pr
iority")) | 816 { |
765 || has_outside_staff_parent (me->get_parent (Y_AXIS)); | 817 Grob *parent = me->get_parent (Y_AXIS); |
766 } | 818 if (!parent) |
767 | 819 return 0; |
768 // 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. |
769 // 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 |
770 // 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 |
771 // 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 |
772 // 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. |
773 // Currently, we just leave cross-staff grobs out of the | 832 // Currently, we just leave cross-staff grobs out of the |
774 // 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 |
775 // 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 |
776 // 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. |
777 Skyline_pair | 836 Skyline_pair |
778 Axis_group_interface::skyline_spacing (Grob *me, vector<Grob *> elements) | 837 Axis_group_interface::skyline_spacing (Grob *me, vector<Grob *> elements) |
779 { | 838 { |
780 /* | |
781 As a sanity check, we make sure that no grob with an outside staff priority | |
782 has a Y-parent that also has an outside staff priority, which would result | |
783 in two movings. | |
784 */ | |
785 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 */ |
786 if (scm_is_number (elements[i]->get_property ("outside-staff-priority")) | 845 if (scm_is_number (elements[i]->get_property ("outside-staff-priority")) |
787 && has_outside_staff_parent (elements[i])) | 846 && outside_staff_ancestor (elements[i])) |
788 { | 847 { |
789 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."); |
790 elements[i]->set_property ("outside-staff-priority", SCM_BOOL_F); | 849 elements[i]->set_property ("outside-staff-priority", SCM_BOOL_F); |
791 } | 850 } |
792 | 851 |
793 /* For grobs with an outside-staff-priority, the sorting function might | 852 /* For grobs with an outside-staff-priority, the sorting function might |
794 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 |
795 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 |
796 are triggered beforehand. | 855 are triggered beforehand. |
797 */ | 856 */ |
798 for (vsize i = 0; i < elements.size (); i++) | 857 for (vsize i = 0; i < elements.size (); i++) |
799 if (scm_is_number (elements[i]->get_property ("outside-staff-priority"))) | 858 if (scm_is_number (elements[i]->get_property ("outside-staff-priority"))) |
800 elements[i]->extent (elements[i], X_AXIS); | 859 elements[i]->extent (elements[i], X_AXIS); |
801 | 860 |
802 vector_sort (elements, staff_priority_less); | 861 vector_sort (elements, staff_priority_less); |
803 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS); | 862 Grob *x_common = common_refpoint_of_array (elements, me, X_AXIS); |
804 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS); | 863 Grob *y_common = common_refpoint_of_array (elements, me, Y_AXIS); |
805 | 864 |
806 assert (y_common == me); | 865 assert (y_common == me); |
807 | 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 |
808 vsize i = 0; | 871 vsize i = 0; |
809 vector<Grob *> riders; | 872 vector<Skyline_pair> inside_staff_skylines; |
810 | |
811 vector<Skyline_pair> to_constructor; | |
812 for (i = 0; i < elements.size () | 873 for (i = 0; i < elements.size () |
813 && !scm_is_number (elements[i]->get_property ("outside-staff-priority"));
i++) | 874 && !scm_is_number (elements[i]->get_property ("outside-staff-priority"));
i++) |
814 { | 875 { |
815 if (!(to_boolean (elements[i]->get_property ("cross-staff")) || has_outsid
e_staff_parent (elements[i]))) | 876 Grob *elt = elements[i]; |
816 add_interior_skylines (elements[i], x_common, y_common, &to_constructor)
; | 877 Grob *ancestor = outside_staff_ancestor (elt); |
817 if (has_outside_staff_parent (elements[i])) | 878 if (!(to_boolean (elt->get_property ("cross-staff")) || ancestor)) |
818 riders.push_back (elements[i]); | 879 add_interior_skylines (elt, x_common, y_common, &inside_staff_skylines); |
819 } | 880 if (ancestor) |
820 | 881 riders.insert (pair<Grob *, Grob *> (ancestor, elt)); |
821 Skyline_pair skylines (to_constructor, 0.0, X_AXIS); | 882 } |
| 883 |
| 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 } |
822 | 898 |
823 for (; i < elements.size (); i++) | 899 for (; i < elements.size (); i++) |
824 { | 900 { |
825 if (to_boolean (elements[i]->get_property ("cross-staff"))) | 901 if (to_boolean (elements[i]->get_property ("cross-staff"))) |
826 continue; | 902 continue; |
827 | 903 |
| 904 // Collect all the outside-staff grobs that have a particular priority. |
828 SCM priority = elements[i]->get_property ("outside-staff-priority"); | 905 SCM priority = elements[i]->get_property ("outside-staff-priority"); |
829 vector<Grob *> current_elts; | 906 vector<Grob *> current_elts; |
830 current_elts.push_back (elements[i]); | 907 current_elts.push_back (elements[i]); |
831 while (i + 1 < elements.size () | 908 while (i + 1 < elements.size () |
832 && 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)) |
833 { | 910 { |
834 if (!to_boolean (elements[i + 1]->get_property ("cross-staff"))) | 911 if (!to_boolean (elements[i + 1]->get_property ("cross-staff"))) |
835 current_elts.push_back (elements[i + 1]); | 912 current_elts.push_back (elements[i + 1]); |
836 ++i; | 913 ++i; |
837 } | 914 } |
838 | 915 |
839 add_grobs_of_one_priority (&skylines, current_elts, x_common, y_common, &r
iders); | 916 add_grobs_of_one_priority (me, |
840 for (vsize j = riders.size (); j--;) | 917 &all_v_skylines, |
841 if (!has_outside_staff_parent (riders[j])) | 918 &all_paddings, |
842 riders.erase (riders.begin () + j); | 919 &all_horizon_paddings, |
843 } | 920 current_elts, |
844 | 921 x_common, |
845 for (vsize j = 0; j < riders.size (); j++) | 922 y_common, |
846 if (Skyline_pair::unsmob (riders[j]->get_property ("vertical-skylines")) | 923 riders); |
847 || !riders[j]->extent (y_common, Y_AXIS).is_empty ()) | 924 } |
848 riders[j]->programming_error ("Vertical skylines will not be high enough."
); | 925 |
849 | 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. |
850 skylines.shift (-me->relative_coordinate (x_common, X_AXIS)); | 934 skylines.shift (-me->relative_coordinate (x_common, X_AXIS)); |
851 Real pad = robust_scm2double (me->get_property ("skyline-horizontal-padding"),
0.0); | 935 |
852 | |
853 if (pad > 0.0) | |
854 { | |
855 vector<Skyline_pair> to_constructor; | |
856 to_constructor.push_back (skylines); | |
857 return Skyline_pair (to_constructor, pad, X_AXIS); | |
858 } | |
859 return skylines; | 936 return skylines; |
860 } | 937 } |
861 | 938 |
862 MAKE_SCHEME_CALLBACK (Axis_group_interface, print, 1) | 939 MAKE_SCHEME_CALLBACK (Axis_group_interface, print, 1) |
863 SCM | 940 SCM |
864 Axis_group_interface::print (SCM smob) | 941 Axis_group_interface::print (SCM smob) |
865 { | 942 { |
866 if (!debug_skylines) | 943 if (!debug_skylines) |
867 return SCM_BOOL_F; | 944 return SCM_BOOL_F; |
868 | 945 |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
905 | 982 |
906 if (grouper) | 983 if (grouper) |
907 { | 984 { |
908 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); |
909 if (within_group) | 986 if (within_group) |
910 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); |
911 else | 988 else |
912 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); |
913 } | 990 } |
914 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); |
915 } | |
916 | |
917 Real | |
918 Axis_group_interface::minimum_distance (Grob *g1, Grob *g2, Axis a) | |
919 { | |
920 SCM sym = ly_symbol2scm ((a == Y_AXIS) ? "vertical-skylines" : "horizontal-sky
lines"); | |
921 | |
922 Skyline_pair *s1 = Skyline_pair::unsmob (g1->get_property (sym)); | |
923 Skyline_pair *s2 = Skyline_pair::unsmob (g2->get_property (sym)); | |
924 if (s1 && s2) | |
925 return (*s1)[DOWN].distance ((*s2)[UP]); | |
926 return 0; | |
927 } | 992 } |
928 | 993 |
929 ADD_INTERFACE (Axis_group_interface, | 994 ADD_INTERFACE (Axis_group_interface, |
930 "An object that groups other layout objects.", | 995 "An object that groups other layout objects.", |
931 | 996 |
932 // TODO: some of these properties are specific to | 997 // TODO: some of these properties are specific to |
933 // VerticalAxisGroup. We should split off a | 998 // VerticalAxisGroup. We should split off a |
934 // vertical-axis-group-interface. | 999 // vertical-axis-group-interface. |
935 /* properties */ | 1000 /* properties */ |
936 "adjacent-pure-heights " | 1001 "adjacent-pure-heights " |
937 "axes " | 1002 "axes " |
938 "bound-alignment-interfaces " | 1003 "bound-alignment-interfaces " |
939 "default-staff-staff-spacing " | 1004 "default-staff-staff-spacing " |
940 "elements " | 1005 "elements " |
941 "max-stretch " | 1006 "max-stretch " |
942 "no-alignment " | 1007 "no-alignment " |
943 "nonstaff-nonstaff-spacing " | 1008 "nonstaff-nonstaff-spacing " |
944 "nonstaff-relatedstaff-spacing " | 1009 "nonstaff-relatedstaff-spacing " |
945 "nonstaff-unrelatedstaff-spacing " | 1010 "nonstaff-unrelatedstaff-spacing " |
| 1011 "outside-staff-placement-directive " |
946 "pure-relevant-grobs " | 1012 "pure-relevant-grobs " |
947 "pure-relevant-items " | 1013 "pure-relevant-items " |
948 "pure-relevant-spanners " | 1014 "pure-relevant-spanners " |
949 "pure-Y-common " | 1015 "pure-Y-common " |
950 "staff-affinity " | 1016 "staff-affinity " |
951 "staff-grouper " | 1017 "staff-grouper " |
952 "staff-staff-spacing " | 1018 "staff-staff-spacing " |
953 "system-Y-offset " | 1019 "system-Y-offset " |
| 1020 "vertical-skyline-elements " |
954 "X-common " | 1021 "X-common " |
955 "Y-common " | 1022 "Y-common " |
956 ); | 1023 ); |
LEFT | RIGHT |