]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam-concave.cc
* flower
[lilypond.git] / lily / beam-concave.cc
1 /*
2   Determine whether a beam is concave.
3 */
4
5 #include <math.h>
6
7 #include "group-interface.hh"
8 #include "array.hh"
9 #include "stem.hh"
10 #include "beam.hh"
11 #include "staff-symbol-referencer.hh"
12
13 bool
14 is_concave_single_notes (Array<int> const &positions, Direction beam_dir)
15 {
16   Interval covering;
17   covering.add_point (positions[0]);
18   covering.add_point (positions.top ());
19
20   bool above = false;
21   bool below = false;
22   bool concave = false;
23
24   /*
25     notes above and below the interval covered by 1st and last note.
26   */
27   for (int i = 1; i < positions.size () - 1; i++)
28     {
29       above = above || (positions[i] > covering[UP]);
30       below = below || (positions[i] < covering[DOWN]);
31     }
32
33   concave = concave || (above && below);
34   /*
35     A note as close or closer to the beam than begin and end, but the
36     note is reached in the opposite direction as the last-first dy
37   */
38   int dy = positions.top () - positions[0];
39   int closest = (beam_dir * positions.top ()) >? (beam_dir *positions[0]);
40   for (int i = 2; !concave && i < positions.size () - 1; i++)
41     {
42       int inner_dy = positions[i] - positions[i - 1];
43       if (sign (inner_dy) != sign (dy)
44           && (beam_dir * positions[i] >= closest
45               || beam_dir * positions[i - 1] >= closest))
46         concave = true;
47     }
48
49   bool all_closer = true;
50   for (int i = 1; all_closer && i < positions.size () - 1; i++)
51     {
52       all_closer = all_closer
53         && (beam_dir * positions[i] > closest);
54     }
55
56   concave = concave || all_closer;
57   return concave;
58 }
59
60 Real
61 calc_concaveness (Array<int> const &positions, Direction beam_dir)
62 {
63   Real dy = positions.top () - positions[0];
64   Real slope = dy / Real (positions.size () - 1);
65   Real concaveness = 0.0;
66   for (int i = 1; i < positions.size () - 1; i++)
67     {
68       Real line_y = slope * i + positions[0];
69
70       concaveness += (beam_dir * (positions[i] - line_y)) >? 0.0;
71     }
72
73   concaveness /= positions.size ();
74
75   /*
76     Normalize. For dy = 0, the slope ends up as 0 anyway, so the
77     scaling of concaveness doesn't matter much.
78   */
79   if (dy)
80     concaveness /= fabs (dy);
81   return concaveness;
82 }
83
84 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
85 SCM
86 Beam::check_concave (SCM smob)
87 {
88   Grob *me = unsmob_grob (smob);
89
90   Link_array<Grob> stems
91     = extract_grob_array (me, ly_symbol2scm ("stems"));
92
93   if (is_knee (me))
94     return SCM_UNSPECIFIED;
95
96   Direction beam_dir = CENTER;
97   for (int i = stems.size (); i--;)
98     {
99       if (Stem::is_invisible (stems[i]))
100         stems.del (i);
101       else
102         {
103           if (Direction dir = Stem::get_direction (stems[i]))
104             beam_dir = dir;
105         }
106     }
107
108   if (stems.size () <= 2)
109     return SCM_UNSPECIFIED;
110
111   Array<int> close_positions;
112   Array<int> far_positions;
113   for (int i = 0; i < stems.size (); i++)
114     {
115       /*
116         For chords, we take the note head that is closest to the beam.
117
118         Hmmm.. wait, for the beams in the last measure of morgenlied,
119         this doesn't look so good. Let's try the heads farthest from
120         the beam.
121
122       */
123       Interval posns = Stem::head_positions (stems[i]);
124
125       close_positions.push ((int) rint (posns[beam_dir]));
126       far_positions.push ((int) rint (posns[-beam_dir]));
127     }
128
129   if (is_concave_single_notes (far_positions, beam_dir))
130     {
131       Drul_array<Real> pos = ly_scm2interval (me->get_property ("positions"));
132       Real r = linear_combination (pos, 0.0);
133
134       r /= Staff_symbol_referencer::staff_space (me);
135       me->set_property ("positions", ly_interval2scm (Drul_array<Real> (r, r)));
136       me->set_property ("least-squares-dy", scm_make_real (0));
137     }
138   else
139     {
140       Real concaveness = (calc_concaveness (far_positions, beam_dir)
141                           + calc_concaveness (close_positions, beam_dir)) / 2;
142
143       me->set_property ("concaveness", scm_from_double (concaveness));
144     }
145
146   return SCM_UNSPECIFIED;
147 }