]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam-concave.cc
Imported Upstream version 2.14.2
[lilypond.git] / lily / beam-concave.cc
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
113 MAKE_SCHEME_CALLBACK (Beam, calc_concaveness, 1);
114 SCM
115 Beam::calc_concaveness (SCM smob)
116 {
117   Grob *me = unsmob_grob (smob);
118
119   vector<Grob*> stems
120     = extract_grob_array (me, "stems");
121
122   if (is_knee (me))
123     return scm_from_double (0.0);
124
125   Direction beam_dir = CENTER;
126   for (vsize i = stems.size (); i--;)
127     {
128       if (Stem::is_normal_stem (stems[i]))
129         {
130           if (Direction dir = get_grob_direction (stems[i]))
131             beam_dir = dir;
132         }
133       else
134         stems.erase (stems.begin () + i);
135     }
136
137   if (stems.size () <= 2)
138     return scm_from_int (0);
139
140   vector<int> close_positions;
141   vector<int> far_positions;
142   for (vsize i = 0; i < stems.size (); i++)
143     {
144       /*
145         For chords, we take the note head that is closest to the beam.
146
147         Hmmm.. wait, for the beams in the last measure of morgenlied,
148         this doesn't look so good. Let's try the heads farthest from
149         the beam.
150       */
151       Interval posns = Stem::head_positions (stems[i]);
152
153       close_positions.push_back ((int) rint (posns[beam_dir]));
154       far_positions.push_back ((int) rint (posns[-beam_dir]));
155     }
156
157   Real concaveness = 0.0;
158
159   if (is_concave_single_notes (beam_dir == UP ? close_positions : far_positions, beam_dir))
160     {
161       concaveness = 10000;
162     }
163   else
164     {
165       concaveness = (calc_positions_concaveness (far_positions, beam_dir)
166                      + calc_positions_concaveness (close_positions, beam_dir)) / 2;
167     }
168
169   return scm_from_double (concaveness);
170 }
171
172
173