]> git.donarmstrong.com Git - lilypond.git/blob - lily/collision.cc
release: 1.5.9
[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--2001 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->parent_l (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   bool half_collide = false;
61   bool full_collide = false;  
62
63   /*
64     TODO:
65
66     filter out the 'o's in this configuration, since they're no part
67     in the collision.
68
69      |
70     x|o
71     x|o
72     x
73
74     
75    */
76   Array<int> ups = Stem::note_head_positions (Note_column::stem_l (cu));
77   Array<int> dps = Stem::note_head_positions (Note_column::stem_l (cd));
78
79   /*
80     they're too far apart to collide. 
81     
82    */
83
84   if (ups[0] > dps.top () + 1)
85     return ; 
86
87   bool touch = (ups[0] - dps.top () >= 0);
88   
89   bool merge_possible = (ups[0] >= dps[0]) && (ups.top () <= dps.top ());
90
91   merge_possible = merge_possible &&
92     Rhythmic_head::balltype_i (nu_l) == Rhythmic_head::balltype_i (nd_l);
93     
94   if (!to_boolean (me->get_grob_property ("merge-differently-dotted")))
95     merge_possible = merge_possible && Rhythmic_head::dot_count (nu_l) == Rhythmic_head::dot_count (nd_l);
96   
97   int i = 0, j=0;
98   while (i < ups.size () && j < dps.size ())
99   {
100     if (abs (ups[i] - dps[j]) == 1)
101       {
102         merge_possible = false;
103         half_collide = true;
104       }
105     else if (ups[i]==dps[j])
106       full_collide = true;
107     else if (ups[i] >dps[0] && ups[i] < dps.top ())
108       merge_possible = false;
109     else if (dps[j] >ups[0] && dps[j] < ups.top ())
110       merge_possible = false;
111     
112     if (ups[i] < dps[j])
113       i++;
114     else if (ups[i] > dps[j])
115       j++;
116     else
117       {
118         i++;
119         j++;
120       }
121   }
122
123   Drul_array<Real> center_note_shifts;
124   center_note_shifts[LEFT] = 0.0;
125   center_note_shifts[RIGHT] = 0.0;
126
127   
128   Real shift_amount = 1;
129
130   if ( touch)
131     shift_amount *= -1;
132   else
133   /*
134     for full collisions, the right hand head may obscure dots, so
135     make sure the dotted heads go to the right.
136    */
137     if ((Rhythmic_head::dot_count (nu_l) < Rhythmic_head::dot_count (nd_l)
138            && full_collide))
139       shift_amount *= -1;
140
141   if (merge_possible)
142     shift_amount *= 0.0;
143   else if (half_collide || full_collide) 
144     shift_amount *= 0.5;
145   else
146     shift_amount *= 0.25;
147
148   Direction d = UP;
149   do
150     {
151       for (int i=0; i < clash_groups[d].size (); i++)
152         (*offsets)[d][i] += d * shift_amount;
153     }
154   while ((flip (&d))!= UP);
155 }
156
157
158 /*
159   TODO: make callback of this.
160
161   TODO:
162
163   note-width is hardcoded, making it difficult to handle all note
164   heads sanely. We should really look at the widths of the colliding
165   columns, and have a separate setting for "align stems".
166
167   
168  */
169 void
170 Collision::do_shifts (Grob* me)
171 {
172   SCM autos (automatic_shift (me));
173   SCM hand (forced_shift (me));
174   
175   Link_array<Grob> done;
176
177
178   Real wid
179     = gh_scm2double (me->get_grob_property ("note-width"));
180   
181   for (; gh_pair_p (hand); hand =gh_cdr (hand))
182     {
183       Grob * s = unsmob_grob (gh_caar (hand));
184       Real amount = gh_scm2double (gh_cdar (hand));
185       
186       s->translate_axis (amount *wid, X_AXIS);
187       done.push (s);
188     }
189   for (; gh_pair_p (autos); autos =gh_cdr (autos))
190     {
191       Grob * s = unsmob_grob (gh_caar (autos));
192       Real amount = gh_scm2double (gh_cdar (autos));
193       
194       if (!done.find_l (s))
195         s->translate_axis (amount * wid, X_AXIS);
196     }
197 }
198
199 /** This complicated routine moves note columns around horizontally to
200   ensure that notes don't clash.
201
202   This should be put into Scheme.  
203   */
204 SCM
205 Collision::automatic_shift (Grob *me)
206 {
207   Drul_array<Link_array<Grob> > clash_groups;
208   Drul_array<Array<int> > shifts;
209   SCM  tups = SCM_EOL;
210
211   SCM s = me->get_grob_property ("elements");
212   for (; gh_pair_p (s); s = gh_cdr (s))
213     {
214       SCM car = gh_car (s);
215
216       Grob * se = unsmob_grob (car);
217       if (Note_column::has_interface (se))
218         clash_groups[Note_column::dir (se)].push (se);
219     }
220
221   
222   Direction d = UP;
223   do
224     {
225       Array<int> & shift (shifts[d]);
226       Link_array<Grob> & clashes (clash_groups[d]);
227
228       clashes.sort (Note_column::shift_compare);
229
230       for (int i=0; i < clashes.size (); i++)
231         {
232           SCM sh
233             = clashes[i]->get_grob_property ("horizontal-shift");
234
235           if (gh_number_p (sh))
236             shift.push (gh_scm2int (sh));
237           else
238             shift.push (0);
239         }
240       
241       for (int i=1; i < shift.size (); i++)
242         {
243           if (shift[i-1] == shift[i])
244             {
245               warning (_ ("Too many clashing notecolumns.  Ignoring them."));
246               return tups;
247             }
248         }
249     }
250   while ((flip (&d))!= UP);
251
252   Drul_array< Array < Slice > > extents;
253   Drul_array< Array < Real > > offsets;
254   d = UP;
255   do
256     {
257       for (int i=0; i < clash_groups[d].size (); i++)
258         {
259           Slice s (Note_column::head_positions_interval (clash_groups[d][i]));
260           s[LEFT] --;
261           s[RIGHT]++;
262           extents[d].push (s);
263           offsets[d].push (d * 0.5 * i);
264         }
265     }
266   while ((flip (&d))!= UP);
267
268   /*
269     do horizontal shifts of each direction 
270
271        | 
272       x||
273        x||
274         x|
275    */
276   
277   do
278     {
279       for (int i=1; i < clash_groups[d].size (); i++)
280         {
281           Slice prev =extents[d][i-1];
282           prev.intersect (extents[d][i]);
283           if (prev.length ()> 0 ||
284  (extents[-d].size () && d * (extents[d][i][-d] - extents[-d][0][d]) < 0))
285             for (int j = i; j <  clash_groups[d].size (); j++)
286               offsets[d][j] += d * 0.5;
287         }
288     }   
289   while ((flip (&d))!= UP);
290
291
292   /*
293     Check if chords are meshing
294    */
295
296   check_meshing_chords (me, &offsets, extents, clash_groups);
297   
298 #if 0  
299   /*
300     if the up and down version are close, and can not be merged, move
301     all of them again. */
302   if (extents[UP].size () && extents[DOWN].size ())
303     {
304       Grob *cu_l =clash_groups[UP][0];
305       Grob *cd_l =clash_groups[DOWN][0];
306
307
308       /*
309         TODO.
310        */
311       Grob * nu_l= Note_column::first_head (cu_l);
312       Grob * nd_l = Note_column::first_head (cd_l);
313       
314       int downpos = Note_column::head_positions_interval (cd_l)[BIGGER];
315       int uppos = Note_column::head_positions_interval (cu_l)[SMALLER];      
316       
317       bool merge  =
318         downpos == uppos
319         && Rhythmic_head::balltype_i (nu_l) == Rhythmic_head::balltype_i (nd_l);
320
321
322       if (!to_boolean (me->get_grob_property ("merge-differently-dotted")))
323         merge = merge && Rhythmic_head::dot_count (nu_l) == Rhythmic_head::dot_count (nd_l);
324
325       /*
326         notes are close, but can not be merged.  Shift
327        */
328       if (abs (uppos - downpos) < 2 && !merge)
329           do
330           {
331             for (int i=0; i < clash_groups[d].size (); i++)
332               {
333                 if(Rhythmic_head::dot_count (nu_l) > Rhythmic_head::dot_count (nd_l))
334                   offsets[d][i] += d * 0.5;
335                 else 
336                   offsets[d][i] -= d * 0.5;
337               }
338           }
339           while ((flip (&d))!= UP);
340     }
341 #endif
342   
343   do
344     {
345       for (int i=0; i < clash_groups[d].size (); i++)
346         tups = gh_cons (gh_cons (clash_groups[d][i]->self_scm (), gh_double2scm (offsets[d][i])),
347                                  tups);
348     }
349   while (flip (&d) != UP);
350   return tups;
351 }
352
353
354 SCM
355 Collision::forced_shift (Grob *me)
356 {
357   SCM tups = SCM_EOL;
358   
359   SCM s = me->get_grob_property ("elements");
360   for (; gh_pair_p (s); s = gh_cdr (s))
361     {
362       Grob * se = unsmob_grob (gh_car (s));
363
364       SCM force =  se->remove_grob_property ("force-hshift");
365       if (gh_number_p (force))
366         {
367           tups = gh_cons (gh_cons (se->self_scm (), force),
368                           tups);
369         }
370     }
371   return tups;
372 }
373
374 void
375 Collision::add_column (Grob*me,Grob* ncol_l)
376 {
377   ncol_l->add_offset_callback (Collision::force_shift_callback_proc, X_AXIS);
378   Axis_group_interface::add_element (me, ncol_l);
379   me->add_dependency (ncol_l);
380 }