]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam-collision-engraver.cc
Merge branch 'lilypond/translation' of ssh://git.sv.gnu.org/srv/git/lilypond into...
[lilypond.git] / lily / beam-collision-engraver.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2011 Mike Solomon <mike@apollinemike.com>
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 #include "beam.hh"
21 #include "engraver.hh"
22 #include "item.hh"
23 #include "note-head.hh"
24 #include "pointer-group-interface.hh"
25
26 class Beam_collision_engraver : public Engraver
27 {
28 protected:
29   vector<Grob *> active_beams_;
30   vector<Grob *> signaled_beams_;
31   vector<Grob *> end_beams_;
32   vector<Grob *> covered_grobs_;
33   vector<Grob *> covered_interior_grobs_;
34
35   DECLARE_ACKNOWLEDGER (note_head);
36   DECLARE_ACKNOWLEDGER (accidental);
37   DECLARE_ACKNOWLEDGER (clef);
38   DECLARE_ACKNOWLEDGER (key_signature);
39   DECLARE_ACKNOWLEDGER (time_signature);
40   DECLARE_ACKNOWLEDGER (beam);
41   DECLARE_END_ACKNOWLEDGER (beam);
42   void stop_translation_timestep ();
43 public:
44   TRANSLATOR_DECLARATIONS (Beam_collision_engraver);
45 };
46
47 void
48 Beam_collision_engraver::stop_translation_timestep ()
49 {
50   /* 
51      First, for all grobs that fall to the left of a beam during
52      a timestep (i.e. clefs, time signatures), add these to
53      the beams that are currently active.
54   */
55   for (vsize i = 0; i < covered_interior_grobs_.size (); i++)
56     for (vsize j = 0; j < active_beams_.size (); j++)
57       Pointer_group_interface::add_grob (active_beams_[j], ly_symbol2scm ("covered-grobs"), covered_interior_grobs_[i]);
58
59   covered_interior_grobs_.clear ();
60
61   /*
62      If a signaled beam is already in active_beams_, we erase it so as
63      not to have a beam represented in active_beams_ more than once.
64    */
65   
66   for (vsize i = 0; i < active_beams_.size (); i++)
67     for (vsize j = 0; j < signaled_beams_.size (); j++)
68       if (active_beams_[i] == signaled_beams_[j])
69         {
70           signaled_beams_.erase (signaled_beams_.begin () + j);
71           break;
72         }
73
74   /*
75      In auto beaming, beams both begin and end during the same timestep.
76      This means that if there is a beam that is both in signaled_beams_ and
77      end_beams_, it must either be an auto beam (likely) or a beam that
78      has no notes under it (highly unlikely).  In either case, we cannot account
79      for the grobs under this beam, and we erase it from signaled beams.
80   */
81   for (vsize i = 0; i < end_beams_.size (); i++)
82     for (vsize j = 0; j < signaled_beams_.size (); j++)
83       if (end_beams_[i] == signaled_beams_[j])
84         {
85           signaled_beams_.erase (signaled_beams_.begin () + j);
86           break;
87         }
88
89   /*
90      We want to know how big active beams was originally so that we do not
91      get any cyclical dependencies (see below).
92   */
93   vsize orig_size = active_beams_.size ();
94
95   /*
96      All signaled beams that are left now become active beams that are fair
97      game to collect covered grobs.
98   */
99   for (vsize i=0; i < signaled_beams_.size (); i++)
100     active_beams_.push_back (signaled_beams_[i]);
101
102   /*
103      Add all covered grobs that fall to the right of a beam (like noteheads)
104      as to covered-grobs of the beam.  Note that noteheads that part of a beam
105      are not added to that list, as note heads should not never collide with
106      their own beams due to minimum stem length penalties in beam-quanting.cc.
107   */
108   for (vsize i = 0; i < covered_grobs_.size (); i++)
109     for (vsize j = 0; j < active_beams_.size (); j++)
110       {
111         bool my_beam = false;
112         if (Grob *stem = unsmob_grob (covered_grobs_[i]->get_object ("stem")))
113           if (Grob *beam = unsmob_grob (stem->get_object ("beam")))
114             if (beam == active_beams_.at (j))
115               my_beam = true;
116         if (!my_beam)
117           Pointer_group_interface::add_grob (active_beams_.at (j), ly_symbol2scm ("covered-grobs"), covered_grobs_[i]);
118       }
119
120   covered_grobs_.clear ();
121
122   /*
123      This is where cyclical dependencies are avoided.  In beam collision avoidance,
124      beams often need to avoid other beams.  To do this, they need to know the beam's
125      position.  But, if that second beam needs to know the first beam's position, we
126      have a cyclical dependency.  So, we only ever add signaled beams to active_beams_
127      that existed BEFORE this time step.  This is controlled by the orig_size variable.
128      The for loop stops before it gets to the signaled beams added above so that beams
129      added during this timestep are never dependent on each other for positioning.
130   */
131   for (vsize i = 0; i < signaled_beams_.size (); i++)
132     for (vsize j = 0; j < orig_size; j++)
133       Pointer_group_interface::add_grob (active_beams_[j], ly_symbol2scm ("covered-grobs"), signaled_beams_[i]);
134
135   signaled_beams_.clear ();
136
137   /*
138      If the end of a beam has been announced, it is no longer active.  So, remove this beam
139      from active_beams_.
140   */
141   for (vsize i = 0; i < end_beams_.size (); i++)
142     for (vsize j = 0; j < active_beams_.size (); j++)
143       if (end_beams_[i] == active_beams_[j])
144         {
145           active_beams_.erase (active_beams_.begin () + j);
146           break;
147         }
148
149   end_beams_.clear ();
150 }
151
152 Beam_collision_engraver::Beam_collision_engraver () {}
153
154 void
155 Beam_collision_engraver::acknowledge_note_head (Grob_info i)
156 {
157   covered_grobs_.push_back (i.grob ());
158 }
159
160 void
161 Beam_collision_engraver::acknowledge_accidental (Grob_info i)
162 {
163   if (i.grob ()->internal_has_interface (ly_symbol2scm ("inline-accidental-interface")))
164     covered_grobs_.push_back (i.grob ());
165 }
166
167 void
168 Beam_collision_engraver::acknowledge_clef (Grob_info i)
169 {
170   covered_interior_grobs_.push_back (i.grob ());
171 }
172
173 void
174 Beam_collision_engraver::acknowledge_key_signature (Grob_info i)
175 {
176   covered_interior_grobs_.push_back (i.grob ());
177 }
178
179 void
180 Beam_collision_engraver::acknowledge_time_signature (Grob_info i)
181 {
182   covered_interior_grobs_.push_back (i.grob ());
183 }
184
185 void
186 Beam_collision_engraver::acknowledge_beam (Grob_info i)
187 {
188   signaled_beams_.push_back (i.grob ());
189 }
190
191 void
192 Beam_collision_engraver::acknowledge_end_beam (Grob_info i)
193 {
194   end_beams_.push_back (i.grob ());
195 }
196
197 #include "translator.icc"
198
199 ADD_ACKNOWLEDGER (Beam_collision_engraver, note_head);
200 ADD_ACKNOWLEDGER (Beam_collision_engraver, accidental);
201 ADD_ACKNOWLEDGER (Beam_collision_engraver, clef);
202 ADD_ACKNOWLEDGER (Beam_collision_engraver, key_signature);
203 ADD_ACKNOWLEDGER (Beam_collision_engraver, time_signature);
204 ADD_ACKNOWLEDGER (Beam_collision_engraver, beam);
205 ADD_END_ACKNOWLEDGER (Beam_collision_engraver, beam);
206
207 ADD_TRANSLATOR (Beam_collision_engraver,
208                 /* doc */
209                 "Help beams avoid colliding with notes and clefs in other voices.",
210
211                 /* create */
212                 "",
213
214                 /* read */
215                 "",
216
217                 /* write */
218                 ""
219                 );