]> git.donarmstrong.com Git - lilypond.git/blob - lily/collision.cc
release: 1.5.29
[lilypond.git] / lily / collision.cc
1 /*
2   collision.cc -- implement Collision
3
4   source file of the GNU LilyPond music typesetter
5
6   (c)  1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include "debug.hh"
10 #include "collision.hh"
11 #include "note-column.hh"
12 #include "rhythmic-head.hh"
13 #include "paper-def.hh"
14 #include "axis-group-interface.hh"
15 #include "item.hh"
16 #include "stem.hh"
17
18 MAKE_SCHEME_CALLBACK (Collision,force_shift_callback,2);
19
20 SCM
21 Collision::force_shift_callback (SCM element_smob, SCM axis)
22 {
23   Grob *me = unsmob_grob (element_smob);
24   Axis a = (Axis) gh_scm2int (axis);
25   assert (a == X_AXIS);
26   
27    me = me->get_parent (a);
28   /*
29     ugh. the way DONE is done is not clean
30    */
31   if (!unsmob_grob (me->get_grob_property ("done")))
32     {
33       me->set_grob_property ("done", me->self_scm ());
34       do_shifts (me);
35     }
36   
37   return gh_double2scm (0.0);
38 }
39
40
41 void
42 check_meshing_chords (Grob*me,
43                       Drul_array< Array < Real > > *offsets,
44                       Drul_array< Array < Slice > > const &extents,
45                       Drul_array<Link_array<Grob> > const &clash_groups)
46         
47 {
48   if (!extents[UP].size () || ! extents[DOWN].size ())
49     return ;
50   
51   
52   Grob *cu =clash_groups[UP][0];
53   Grob *cd =clash_groups[DOWN][0];
54
55   Grob * nu_l= Note_column::first_head (cu);
56   Grob * nd_l = Note_column::first_head (cd);
57       
58      
59
60   /*
61     this case (distant half collide), 
62     
63         |
64       x |
65      | x
66      |
67
68    the noteheads may be closer than this case (close half collide)
69
70        |
71        |
72       x 
73      x
74     |
75     |
76     
77    */
78   
79   bool close_half_collide = false;
80   bool distant_half_collide = false;  
81   bool full_collide = false;  
82
83   /*
84     TODO:
85
86     filter out the 'o's in this configuration, since they're no part
87     in the collision.
88
89      |
90     x|o
91     x|o
92     x
93
94     
95    */
96   Array<int> ups = Stem::note_head_positions (Note_column::stem_l (cu));
97   Array<int> dps = Stem::note_head_positions (Note_column::stem_l (cd));
98
99   /*
100     they're too far apart to collide. 
101     
102    */
103
104   if (ups[0] > dps.top () + 1)
105     return ; 
106
107   bool touch = (ups[0] - dps.top () >= 0);
108   
109   bool merge_possible = (ups[0] >= dps[0]) && (ups.top () >= dps.top ());
110
111   merge_possible = merge_possible &&
112     Rhythmic_head::balltype_i (nu_l) == Rhythmic_head::balltype_i (nd_l);
113     
114   if (!to_boolean (me->get_grob_property ("merge-differently-dotted")))
115     merge_possible = merge_possible && Rhythmic_head::dot_count (nu_l) == Rhythmic_head::dot_count (nd_l);
116   
117   int i = 0, j=0;
118   while (i < ups.size () && j < dps.size ())
119   {
120     if (abs (ups[i] - dps[j]) == 1)
121       {
122         merge_possible = false;
123         if (ups[i] > dps[j])
124           close_half_collide = true;
125         else
126           distant_half_collide = true;
127       }
128     else if (ups[i]==dps[j])
129       full_collide = true;
130     else if (ups[i] >dps[0] && ups[i] < dps.top ())
131       merge_possible = false;
132     else if (dps[j] >ups[0] && dps[j] < ups.top ())
133       merge_possible = false;
134     
135     if (ups[i] < dps[j])
136       i++;
137     else if (ups[i] > dps[j])
138       j++;
139     else
140       {
141         i++;
142         j++;
143       }
144   }
145
146   Drul_array<Real> center_note_shifts;
147   center_note_shifts[LEFT] = 0.0;
148   center_note_shifts[RIGHT] = 0.0;
149
150   
151   Real shift_amount = 1;
152
153   if (touch)
154     shift_amount *= -1;
155
156   /*
157     for full collisions, the right hand head may obscure dots, so
158     make sure the dotted heads go to the right.
159    */
160   if ((Rhythmic_head::dot_count (nu_l) > Rhythmic_head::dot_count (nd_l)
161        && full_collide))
162     shift_amount = 1;
163
164   /*
165     TODO: these numbers are magic; should devise a set of grob props
166     to tune this behavior.  */
167   
168   if (merge_possible)
169     shift_amount *= 0.0;
170   else if (close_half_collide && !touch)
171     shift_amount *= 0.52;
172   else if (distant_half_collide && !touch)
173     shift_amount *= 0.4;
174   else if (distant_half_collide || close_half_collide || full_collide)
175     shift_amount *= 0.5;
176   /*
177     we're meshing.
178   */
179   else if (Rhythmic_head::dot_count (nu_l) || Rhythmic_head::dot_count (nd_l))
180     shift_amount *= 0.1;
181   else
182     shift_amount *= 0.25;
183
184   Direction d = UP;
185   do
186     {
187       for (int i=0; i < clash_groups[d].size (); i++)
188         (*offsets)[d][i] += d * shift_amount;
189     }
190   while ((flip (&d))!= UP);
191 }
192
193
194 /*
195   TODO: make callback of this.
196
197   TODO:
198
199   note-width is hardcoded, making it difficult to handle all note
200   heads sanely. We should really look at the widths of the colliding
201   columns, and have a separate setting for "align stems".
202
203   
204  */
205 void
206 Collision::do_shifts (Grob* me)
207 {
208   SCM autos (automatic_shift (me));
209   SCM hand (forced_shift (me));
210   
211   Link_array<Grob> done;
212
213
214   Real wid
215     = gh_scm2double (me->get_grob_property ("note-width"));
216   
217   for (; gh_pair_p (hand); hand =ly_cdr (hand))
218     {
219       Grob * s = unsmob_grob (ly_caar (hand));
220       Real amount = gh_scm2double (ly_cdar (hand));
221       
222       s->translate_axis (amount *wid, X_AXIS);
223       done.push (s);
224     }
225   for (; gh_pair_p (autos); autos =ly_cdr (autos))
226     {
227       Grob * s = unsmob_grob (ly_caar (autos));
228       Real amount = gh_scm2double (ly_cdar (autos));
229       
230       if (!done.find_l (s))
231         s->translate_axis (amount * wid, X_AXIS);
232     }
233 }
234
235 /** This complicated routine moves note columns around horizontally to
236   ensure that notes don't clash.
237
238   This should be put into Scheme.  
239   */
240 SCM
241 Collision::automatic_shift (Grob *me)
242 {
243   Drul_array<Link_array<Grob> > clash_groups;
244   Drul_array<Array<int> > shifts;
245   SCM  tups = SCM_EOL;
246
247   SCM s = me->get_grob_property ("elements");
248   for (; gh_pair_p (s); s = ly_cdr (s))
249     {
250       SCM car = ly_car (s);
251
252       Grob * se = unsmob_grob (car);
253       if (Note_column::has_interface (se))
254         clash_groups[Note_column::dir (se)].push (se);
255     }
256
257   
258   Direction d = UP;
259   do
260     {
261       Array<int> & shift (shifts[d]);
262       Link_array<Grob> & clashes (clash_groups[d]);
263
264       clashes.sort (Note_column::shift_compare);
265
266       for (int i=0; i < clashes.size (); i++)
267         {
268           SCM sh
269             = clashes[i]->get_grob_property ("horizontal-shift");
270
271           if (gh_number_p (sh))
272             shift.push (gh_scm2int (sh));
273           else
274             shift.push (0);
275         }
276       
277       for (int i=1; i < shift.size (); i++)
278         {
279           if (shift[i-1] == shift[i])
280             {
281               warning (_ ("Too many clashing notecolumns.  Ignoring them."));
282               return tups;
283             }
284         }
285     }
286   while ((flip (&d))!= UP);
287
288   Drul_array< Array < Slice > > extents;
289   Drul_array< Array < Real > > offsets;
290   d = UP;
291   do
292     {
293       for (int i=0; i < clash_groups[d].size (); i++)
294         {
295           Slice s (Note_column::head_positions_interval (clash_groups[d][i]));
296           s[LEFT] --;
297           s[RIGHT]++;
298           extents[d].push (s);
299           offsets[d].push (d * 0.5 * i);
300         }
301     }
302   while ((flip (&d))!= UP);
303
304   /*
305     do horizontal shifts of each direction 
306
307        | 
308       x||
309        x||
310         x|
311    */
312   
313   do
314     {
315       for (int i=1; i < clash_groups[d].size (); i++)
316         {
317           Slice prev =extents[d][i-1];
318           prev.intersect (extents[d][i]);
319           if (prev.length ()> 0 ||
320  (extents[-d].size () && d * (extents[d][i][-d] - extents[-d][0][d]) < 0))
321             for (int j = i; j <  clash_groups[d].size (); j++)
322               offsets[d][j] += d * 0.5;
323         }
324     }   
325   while ((flip (&d))!= UP);
326
327
328   /*
329     Check if chords are meshing
330    */
331
332   check_meshing_chords (me, &offsets, extents, clash_groups);
333   
334   do
335     {
336       for (int i=0; i < clash_groups[d].size (); i++)
337         tups = gh_cons (gh_cons (clash_groups[d][i]->self_scm (), gh_double2scm (offsets[d][i])),
338                                  tups);
339     }
340   while (flip (&d) != UP);
341   return tups;
342 }
343
344
345 SCM
346 Collision::forced_shift (Grob *me)
347 {
348   SCM tups = SCM_EOL;
349   
350   SCM s = me->get_grob_property ("elements");
351   for (; gh_pair_p (s); s = ly_cdr (s))
352     {
353       Grob * se = unsmob_grob (ly_car (s));
354
355       SCM force =  se->remove_grob_property ("force-hshift");
356       if (gh_number_p (force))
357         {
358           tups = gh_cons (gh_cons (se->self_scm (), force),
359                           tups);
360         }
361     }
362   return tups;
363 }
364
365 void
366 Collision::add_column (Grob*me,Grob* ncol_l)
367 {
368   ncol_l->add_offset_callback (Collision::force_shift_callback_proc, X_AXIS);
369   Axis_group_interface::add_element (me, ncol_l);
370   me->add_dependency (ncol_l);
371 }