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