]> git.donarmstrong.com Git - lilypond.git/blob - lily/rest-collision.cc
* lily/rest.cc (polyphonic_offset_callback): new function. Do
[lilypond.git] / lily / rest-collision.cc
1 /*
2   rest-collision.cc -- implement Rest_collision
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include <math.h>               // ceil.
10
11 #include "warn.hh"
12 #include "rest-collision.hh"
13 #include "note-column.hh"
14 #include "stem.hh"
15 #include "rhythmic-head.hh"
16 #include "paper-def.hh"
17 #include "rest.hh"
18 #include "group-interface.hh"
19 #include "staff-symbol-referencer.hh"
20 #include "duration.hh"
21 #include "directional-element-interface.hh"
22
23 MAKE_SCHEME_CALLBACK (Rest_collision,force_shift_callback,2);
24 SCM
25 Rest_collision::force_shift_callback (SCM element_smob, SCM axis)
26 {
27   Grob *them = unsmob_grob (element_smob);
28   Axis a = (Axis) gh_scm2int (axis);
29   assert (a == Y_AXIS);
30
31   Grob * rc = unsmob_grob (them->get_grob_property ("rest-collision"));
32
33   if (rc && !to_boolean (rc->get_grob_property ("positioning-done")))
34     {
35       rc->set_grob_property ("positioning-done", SCM_BOOL_T);
36
37       do_shift (rc);
38     }
39   
40   return gh_double2scm (0.0);
41 }
42
43
44
45 void
46 Rest_collision::add_column (Grob*me,Grob *p)
47 {
48   me->add_dependency (p);
49   Pointer_group_interface::add_grob (me, ly_symbol2scm ("elements"), p);
50
51   /*
52     only add callback for the rests, since we don't move anything
53     else.
54
55     (not?)
56   */
57   p->add_offset_callback (Rest_collision::force_shift_callback_proc, Y_AXIS);
58   p->set_grob_property ("rest-collision", me->self_scm ());
59 }
60
61
62 /*
63   Combination of dot-count and duration-log.
64  */
65 static SCM
66 head_characteristic (Grob * col)
67 {
68   Grob * s = unsmob_grob (col->get_grob_property ("rest"));
69
70   if (!s)
71     return SCM_BOOL_F;
72   else
73     return gh_cons (s->get_grob_property ("duration-log"),
74                     gh_int2scm (Rhythmic_head::dot_count (s)));
75 }
76
77 /*
78   TODO: fixme, fucks up if called twice on the same set of rests.
79
80   TODO: look at horizontal-shift to determine ordering between rests
81   for more than two voices.
82
83   
84  */
85 SCM
86 Rest_collision::do_shift (Grob *me)
87 {
88   SCM elts = me->get_grob_property ("elements");
89
90   Link_array<Grob> rests;
91   Link_array<Grob> notes;
92
93   for (SCM s = elts; gh_pair_p (s); s = ly_cdr (s))
94     {
95       Grob * e = unsmob_grob (ly_car (s));
96       if (unsmob_grob (e->get_grob_property ("rest")))
97         {
98           /*
99             Ignore rests under beam.
100            */
101           Grob* st = unsmob_grob (e->get_grob_property ("stem"));
102           if (st && unsmob_grob (st->get_grob_property ("beam")))
103             continue;
104           
105           rests.push (e);
106         }
107       else
108         notes.push (e);
109     }
110
111   
112   /* 
113      handle rest-rest and rest-note collisions
114
115      [todo]
116      * decide not to print rest if too crowded?
117    */
118
119   /*
120     no partners to collide with
121    */
122   if (rests.size () + notes.size () < 2)
123     return SCM_UNSPECIFIED;
124
125
126   Real staff_space = Staff_symbol_referencer::staff_space (me);
127   /*
128     only rests
129   */
130   if (!notes.size ()) 
131     {
132
133       /*
134         This is incomplete: in case of an uneven number of rests, the
135         center one should be centered on the staff.
136        */
137       Drul_array< Link_array <Grob > > ordered_rests;
138       for (int i= 0; i < rests.size (); i++)
139         {
140           Grob * r = Note_column::get_rest (rests[i]);
141           
142           Direction d = get_grob_direction (r);
143           if (d)
144             {
145               ordered_rests[d].push (rests[i]);
146             }
147           else
148             rests[d]->warning (_("rest direction not set.  Cannot resolve collision."));
149         }
150
151       Direction d =  LEFT;
152       do {
153         ordered_rests[d].sort (Note_column::shift_compare);
154       } while (flip (&d) != LEFT);
155       
156       if (ordered_rests[UP].size () + ordered_rests[DOWN].size () < 2)
157         return SCM_UNSPECIFIED;
158
159       Grob *common = common_refpoint_of_array (ordered_rests[DOWN], me, Y_AXIS);
160       common =  common_refpoint_of_array (ordered_rests[UP], common, Y_AXIS);
161
162       Real diff = 
163         (ordered_rests[DOWN].top ()->extent (common, Y_AXIS)[UP]
164          - ordered_rests[UP].top ()->extent (common, Y_AXIS)[DOWN]) /staff_space;
165
166       if (diff > 0)
167         {
168           int amount_down = (int) ceil (diff / 2); 
169           diff -= amount_down;
170           Note_column::translate_rests (ordered_rests[DOWN].top (),
171                                         -2 * amount_down);
172           if (diff > 0)
173             Note_column::translate_rests (ordered_rests[UP].top (),
174                                           2 * int (ceil (diff)));
175         }
176
177       do {
178         for (int i = ordered_rests[d].size () -1; i-- > 0;)
179           {
180             Real last_y = ordered_rests[d][i+1]->extent (common, Y_AXIS)[d];
181             Real y = ordered_rests[d][i]->extent (common, Y_AXIS)[-d];
182
183             Real diff = d * ((last_y - y) /staff_space);
184             if (diff > 0)
185               Note_column::translate_rests (ordered_rests[d][i],d * (int) ceil (diff) * 2);
186           }
187       } while (flip (&d) != LEFT);
188     }
189   else 
190     {
191       /*
192         Rests and notes.
193        */
194       if (rests.size () > 1)
195         {
196           warning (_ ("too many colliding rests"));
197         }
198       Grob * rcol = rests[0];
199       Grob *common = common_refpoint_of_array (notes, rcol, Y_AXIS);
200       
201       Direction dir = Note_column::dir (rests[0]);
202       
203       Interval restdim = rcol->extent (common, Y_AXIS);
204       if (restdim.is_empty ())
205         return SCM_UNSPECIFIED;
206       
207       Real staff_space = Staff_symbol_referencer::staff_space (rcol);
208       Real minimum_dist = robust_scm2double (me->get_grob_property ("minimum-distance"), 1.0) * staff_space;
209
210       Interval notedim;
211       for (int i = 0; i < notes.size (); i++) 
212         {
213           notedim.unite (notes[i]->extent (common, Y_AXIS));
214         }
215
216       Real dist =
217         minimum_dist +  dir * (notedim[dir] - restdim[-dir]) >? 0;
218
219       int stafflines = Staff_symbol_referencer::line_count (me);
220       if (!stafflines)
221         {
222           programming_error ("No staff line count ? ");
223           stafflines =5;
224         }
225       
226       // move discretely by half spaces.
227       int discrete_dist = int (ceil (dist / (0.5 *staff_space)));
228
229       // move by whole spaces inside the staff.
230       if (discrete_dist < stafflines+1)
231         discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
232
233       Note_column::translate_rests (rcol,dir * discrete_dist);
234     }
235   return SCM_UNSPECIFIED;
236 }
237
238
239 ADD_INTERFACE (Rest_collision,"rest-collision-interface",
240   "Move around ordinary rests (not multi-measure-rests) to avoid "
241 "conflicts.",
242   "maximum-rest-count minimum-distance positioning-done elements");
243