LEFT | RIGHT |
(no file at all) | |
1 /* | |
2 This file is part of LilyPond, the GNU music typesetter. | |
3 | |
4 Copyright (C) 2004 Han-Wen Nienhuys <hanwen@lilypond.org> | |
5 | |
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. | |
10 | |
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. | |
15 | |
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/>. | |
18 */ | |
19 | |
20 /* | |
21 Determine whether a beam is concave. | |
22 | |
23 A beam is concave when the middle notes get closer to the | |
24 beam than the left and right edge notes. | |
25 | |
26 This is determined in two ways: by looking at the positions of the | |
27 middle notes, or by looking at the deviation of the inside notes | |
28 compared to the line connecting first and last. | |
29 | |
30 The tricky thing is what to do with beams with chords. There are no | |
31 real guidelines in this case. | |
32 */ | |
33 | |
34 #include "pointer-group-interface.hh" | |
35 #include "stem.hh" | |
36 #include "beam.hh" | |
37 #include "grob.hh" | |
38 #include "staff-symbol-referencer.hh" | |
39 #include "directional-element-interface.hh" | |
40 | |
41 bool | |
42 is_concave_single_notes (vector<int> const &positions, Direction beam_dir) | |
43 { | |
44 Interval covering; | |
45 covering.add_point (positions[0]); | |
46 covering.add_point (positions.back ()); | |
47 | |
48 bool above = false; | |
49 bool below = false; | |
50 bool concave = false; | |
51 | |
52 /* | |
53 notes above and below the interval covered by 1st and last note. | |
54 */ | |
55 for (vsize i = 1; i + 1 < positions.size (); i++) | |
56 { | |
57 above = above || (positions[i] > covering[UP]); | |
58 below = below || (positions[i] < covering[DOWN]); | |
59 } | |
60 | |
61 concave = concave || (above && below); | |
62 /* | |
63 A note as close or closer to the beam than begin and end, but the | |
64 note is reached in the opposite direction as the last-first dy | |
65 */ | |
66 int dy = positions.back () - positions[0]; | |
67 int closest = max (beam_dir * positions.back (), beam_dir * positions[0]); | |
68 for (vsize i = 2; !concave && i + 1 < positions.size (); i++) | |
69 { | |
70 int inner_dy = positions[i] - positions[i - 1]; | |
71 if (sign (inner_dy) != sign (dy) | |
72 && (beam_dir * positions[i] >= closest | |
73 || beam_dir * positions[i - 1] >= closest)) | |
74 concave = true; | |
75 } | |
76 | |
77 bool all_closer = true; | |
78 for (vsize i = 1; all_closer && i + 1 < positions.size (); i++) | |
79 { | |
80 all_closer = all_closer | |
81 && (beam_dir * positions[i] > closest); | |
82 } | |
83 | |
84 concave = concave || all_closer; | |
85 return concave; | |
86 } | |
87 | |
88 Real | |
89 calc_positions_concaveness (vector<int> const &positions, Direction beam_dir) | |
90 { | |
91 Real dy = positions.back () - positions[0]; | |
92 Real slope = dy / Real (positions.size () - 1); | |
93 Real concaveness = 0.0; | |
94 for (vsize i = 1; i + 1 < positions.size (); i++) | |
95 { | |
96 Real line_y = slope * i + positions[0]; | |
97 | |
98 concaveness += max (beam_dir * (positions[i] - line_y), 0.0); | |
99 } | |
100 | |
101 concaveness /= positions.size (); | |
102 | |
103 /* | |
104 Normalize. For dy = 0, the slope ends up as 0 anyway, so the | |
105 scaling of concaveness doesn't matter much. | |
106 */ | |
107 if (dy) | |
108 concaveness /= fabs (dy); | |
109 return concaveness; | |
110 } | |
111 | |
112 MAKE_SCHEME_CALLBACK (Beam, calc_concaveness, 1); | |
113 SCM | |
114 Beam::calc_concaveness (SCM smob) | |
115 { | |
116 Grob *me = unsmob_grob (smob); | |
117 | |
118 vector<Grob *> stems | |
119 = extract_grob_array (me, "stems"); | |
120 | |
121 if (is_knee (me)) | |
122 return scm_from_double (0.0); | |
123 | |
124 Direction beam_dir = CENTER; | |
125 for (vsize i = stems.size (); i--;) | |
126 { | |
127 if (Stem::is_normal_stem (stems[i])) | |
128 { | |
129 if (Direction dir = get_grob_direction (stems[i])) | |
130 beam_dir = dir; | |
131 } | |
132 else | |
133 stems.erase (stems.begin () + i); | |
134 } | |
135 | |
136 if (stems.size () <= 2) | |
137 return scm_from_int (0); | |
138 | |
139 vector<int> close_positions; | |
140 vector<int> far_positions; | |
141 for (vsize i = 0; i < stems.size (); i++) | |
142 { | |
143 /* | |
144 For chords, we take the note head that is closest to the beam. | |
145 | |
146 Hmmm.. wait, for the beams in the last measure of morgenlied, | |
147 this doesn't look so good. Let's try the heads farthest from | |
148 the beam. | |
149 */ | |
150 Interval posns = Stem::head_positions (stems[i]); | |
151 | |
152 close_positions.push_back ((int) rint (posns[beam_dir])); | |
153 far_positions.push_back ((int) rint (posns[-beam_dir])); | |
154 } | |
155 | |
156 Real concaveness = 0.0; | |
157 | |
158 if (is_concave_single_notes (beam_dir == UP ? close_positions : far_positions,
beam_dir)) | |
159 { | |
160 concaveness = 10000; | |
161 } | |
162 else | |
163 { | |
164 concaveness = (calc_positions_concaveness (far_positions, beam_dir) | |
165 + calc_positions_concaveness (close_positions, beam_dir)) /
2; | |
166 } | |
167 | |
168 return scm_from_double (concaveness); | |
169 } | |
170 | |
LEFT | RIGHT |