]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam-collision-engraver.cc
9771799c18338a60d7fc4be6c39edaf698ea8020
[lilypond.git] / lily / beam-collision-engraver.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2011--2012 Mike Solomon <mike@mikesolomon.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 #include "beam.hh"
21 #include "engraver.hh"
22 #include "item.hh"
23 #include "note-head.hh"
24 #include "pointer-group-interface.hh"
25 #include "stem.hh"
26
27 class Beam_collision_engraver : public Engraver
28 {
29 protected:
30   vector<Grob_info> beams_;
31   vector<Grob_info> covered_grobs_;
32
33   DECLARE_ACKNOWLEDGER (note_head);
34   DECLARE_ACKNOWLEDGER (stem);
35   DECLARE_ACKNOWLEDGER (accidental);
36   DECLARE_ACKNOWLEDGER (clef);
37   DECLARE_ACKNOWLEDGER (clef_modifier);
38   DECLARE_ACKNOWLEDGER (key_signature);
39   DECLARE_ACKNOWLEDGER (time_signature);
40   DECLARE_ACKNOWLEDGER (beam);
41   DECLARE_ACKNOWLEDGER (flag);
42
43   virtual void finalize ();
44
45 private:
46   bool covered_grob_has_interface (Grob *covered_grob, Grob *beam);
47
48 public:
49   TRANSLATOR_DECLARATIONS (Beam_collision_engraver);
50 };
51
52 Beam_collision_engraver::Beam_collision_engraver () {}
53
54 bool
55 Beam_collision_engraver::covered_grob_has_interface (Grob *covered_grob, Grob *beam)
56 {
57   SCM interfaces = beam->get_property ("collision-interfaces");
58
59   for (SCM l = interfaces; scm_is_pair (l); l = scm_cdr (l))
60     {
61       if (covered_grob->internal_has_interface (scm_car (l)))
62         return true;
63     }
64
65   return false;
66 }
67
68 void
69 Beam_collision_engraver::finalize ()
70 {
71   if (!covered_grobs_.size ())
72     return;
73
74   vector_sort (covered_grobs_, Grob_info::less);
75   vector_sort (beams_, Grob_info::less);
76   vsize start = 0;
77
78   for (vsize i = 0; i < beams_.size (); i++)
79     {
80       Grob *beam_grob = beams_[i].grob ();
81
82       extract_grob_set (beam_grob, "normal-stems", stems);
83       Interval_t<int> vertical_span;
84       for (vsize j = 0; j < stems.size (); j++)
85         {
86           int vag = Grob::get_vertical_axis_group_index (stems[j]);
87           if (vag >= 0)
88             vertical_span.add_point (vag);
89         }
90       Context *beam_context = beams_[i].context ();
91
92       Interval_t<int> beam_spanned_rank_ = beam_grob->spanned_rank_interval ();
93       // Start considering grobs at the first grob whose end falls at or after the beam's beginning.
94       while (covered_grobs_[start].grob ()->spanned_rank_interval ()[RIGHT] < beam_spanned_rank_[LEFT])
95         start++;
96
97       // Stop when the grob's beginning comes after the beam's end.
98       for (vsize j = start; j < covered_grobs_.size (); j++)
99         {
100           Grob *covered_grob = covered_grobs_[j].grob ();
101           int vag = Grob::get_vertical_axis_group_index (covered_grob);
102           if (!vertical_span.contains (vag))
103             continue;
104           Context *covered_grob_context = covered_grobs_[j].context ();
105
106           Interval_t<int> covered_grob_spanned_rank = covered_grob->spanned_rank_interval ();
107           if (covered_grob_spanned_rank[LEFT] > beam_spanned_rank_[RIGHT])
108             break;
109           /*
110              Only consider grobs whose end falls at or after the beam's beginning.
111              If the grob is a beam, it cannot start before beams_[i].
112              Also, if the user wants to check for collisions only in the beam's voice,
113              then make sure the beam and the covered_grob are in the same voice.
114           */
115           if ((covered_grob_spanned_rank[RIGHT] >= beam_spanned_rank_[LEFT])
116               && !(to_boolean (beam_grob->get_property ("collision-voice-only"))
117                    && (covered_grob_context != beam_context))
118               && !(Beam::has_interface (covered_grob)
119                    && (covered_grob_spanned_rank[LEFT] <= beam_spanned_rank_[LEFT]))
120               && covered_grob_has_interface (covered_grob, beam_grob))
121             {
122               // Do not consider note heads attached to the beam.
123               if (Stem::has_interface (covered_grob))
124                 if (unsmob_grob (covered_grob->get_object ("beam")))
125                   continue;
126
127               if (Grob *stem = unsmob_grob (covered_grob->get_object ("stem")))
128                 if (Grob *beam = unsmob_grob (stem->get_object ("beam")))
129                   if (beam == beam_grob)
130                     continue;
131
132               Pointer_group_interface::add_grob (beam_grob, ly_symbol2scm ("covered-grobs"), covered_grob);
133             }
134         }
135     }
136 }
137
138 void
139 Beam_collision_engraver::acknowledge_note_head (Grob_info i)
140 {
141   covered_grobs_.push_back (i);
142 }
143
144 void
145 Beam_collision_engraver::acknowledge_stem (Grob_info i)
146 {
147   covered_grobs_.push_back (i);
148 }
149
150 void
151 Beam_collision_engraver::acknowledge_accidental (Grob_info i)
152 {
153   if (i.grob ()->internal_has_interface (ly_symbol2scm ("inline-accidental-interface")))
154     covered_grobs_.push_back (i);
155 }
156
157 void
158 Beam_collision_engraver::acknowledge_clef (Grob_info i)
159 {
160   covered_grobs_.push_back (i);
161 }
162
163 void
164 Beam_collision_engraver::acknowledge_key_signature (Grob_info i)
165 {
166   covered_grobs_.push_back (i);
167 }
168
169 void
170 Beam_collision_engraver::acknowledge_clef_modifier (Grob_info i)
171 {
172   covered_grobs_.push_back (i);
173 }
174
175 void
176 Beam_collision_engraver::acknowledge_time_signature (Grob_info i)
177 {
178   covered_grobs_.push_back (i);
179 }
180
181 void
182 Beam_collision_engraver::acknowledge_flag (Grob_info i)
183 {
184   covered_grobs_.push_back (i);
185 }
186
187 void
188 Beam_collision_engraver::acknowledge_beam (Grob_info i)
189 {
190   beams_.push_back (i);
191   covered_grobs_.push_back (i);
192 }
193
194 #include "translator.icc"
195
196 ADD_ACKNOWLEDGER (Beam_collision_engraver, note_head);
197 ADD_ACKNOWLEDGER (Beam_collision_engraver, stem);
198 ADD_ACKNOWLEDGER (Beam_collision_engraver, accidental);
199 ADD_ACKNOWLEDGER (Beam_collision_engraver, clef);
200 ADD_ACKNOWLEDGER (Beam_collision_engraver, key_signature);
201 ADD_ACKNOWLEDGER (Beam_collision_engraver, time_signature);
202 ADD_ACKNOWLEDGER (Beam_collision_engraver, clef_modifier);
203 ADD_ACKNOWLEDGER (Beam_collision_engraver, flag);
204 ADD_ACKNOWLEDGER (Beam_collision_engraver, beam);
205
206 ADD_TRANSLATOR (Beam_collision_engraver,
207                 /* doc */
208                 "Help beams avoid colliding with notes and clefs in other voices.",
209
210                 /* create */
211                 "",
212
213                 /* read */
214                 "",
215
216                 /* write */
217                 ""
218                );