]> git.donarmstrong.com Git - lilypond.git/blob - lily/rest-collision.cc
release: 1.3.92
[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--2000 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include <math.h>               // ceil.
10
11 #include "debug.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
22 Real
23 Rest_collision::force_shift_callback (Score_element *them, Axis a)
24 {
25   assert (a == Y_AXIS);
26
27   Score_element * rc = unsmob_element (them->get_elt_property ("rest-collision"));
28
29   if (rc)
30     {
31       /*
32         Done: destruct pointers, so we do the shift only once.
33       */
34       SCM elts = rc->get_elt_property ("elements");
35       rc->set_elt_property ("elements", SCM_EOL);
36
37       do_shift (rc, elts);
38     }
39   
40   return 0.0;
41 }
42
43 void
44 Rest_collision::add_column (Score_element*me,Score_element *p)
45 {
46   me->add_dependency (p);
47   Pointer_group_interface gi (me);  
48   gi.add_element (p);
49
50   p->add_offset_callback (&Rest_collision::force_shift_callback, Y_AXIS);
51   p->set_elt_property ("rest-collision", me->self_scm ());
52 }
53
54
55 /*
56   Combination of dot-count and duration-log.
57  */
58 static SCM
59 head_characteristic (Score_element * col)
60 {
61   Score_element * s = unsmob_element (col->get_elt_property ("rest"));
62
63   if (!s)
64     return SCM_BOOL_F;
65   else
66     return gh_cons (s->get_elt_property ("duration-log"),
67                     gh_int2scm (Rhythmic_head::dot_count (s)));
68 }
69
70 /*
71   TODO: fixme, fucks up if called twice on the same set of rests.
72  */
73 SCM
74 Rest_collision::do_shift (Score_element *me, SCM elts)
75 {
76   /*
77     ugh. -> score  elt type
78    */
79   Link_array<Score_element> rests;
80   Link_array<Score_element> notes;
81
82   for (SCM s = elts; gh_pair_p (s); s = gh_cdr (s))
83     {
84       Score_element * e = unsmob_element (gh_car (s));
85       if (e && unsmob_element (e->get_elt_property ("rest")))
86         rests.push (e);
87       else
88         notes.push (e);
89     }
90
91   
92   /* 
93      handle rest-rest and rest-note collisions
94
95      [todo]
96      * decide not to print rest if too crowded?
97
98      * ignore rests under beams.
99    */
100
101   // no rests to collide
102   if (!rests.size())
103     return SCM_UNSPECIFIED;
104
105   // no partners to collide with
106   if (rests.size() + notes.size () < 2)
107     return SCM_UNSPECIFIED;
108
109   // meisjes met meisjes
110   if (!notes.size()) 
111     {
112       SCM characteristic = head_characteristic  (rests[0]);
113       int i = 1;
114       for (; i < rests.size (); i++)
115         {
116           if (!gh_equal_p (head_characteristic  (rests[i]), characteristic))
117             break;
118         }
119
120       /*
121         If all durations are the same, we'll check if there are more
122         rests than maximum-rest-count.
123         Otherwise (different durations), we'll try to display them all
124         (urg: all 3 of them, currently).
125        */
126       int display_count;
127       SCM s = me->get_elt_property ("maximum-rest-count");
128       if (i == rests.size ()
129           && gh_number_p (s) && gh_scm2int (s) < rests.size ())
130         {
131           display_count = gh_scm2int (s);
132           for (; i > display_count; i--)
133             {
134               Score_element* r = unsmob_element (rests[i-1]->get_elt_property ("rest"));
135               if (r)
136                 r->suicide ();
137               rests[i-1]->suicide ();
138             }
139         }
140       else
141         display_count = rests.size ();
142       
143       /*
144         Ugh. Should have minimum dist.
145
146         Ugh. What do we do if we have three different rests?
147         
148        */
149       int dy = display_count > 2 ? 6 : 4; // FIXME Should get dims from table.
150       if (display_count > 1)
151         {
152           Direction d0 = Note_column::dir (rests[0]);
153           Direction d1 = Note_column::dir (rests[1]);     
154
155           if (!d0 && !d1)
156             {
157               d0= UP;
158               d1 = DOWN;
159             }
160           else if (!d0)
161             d0 = - d1;
162           else if (!d1)
163             d1 = -d0;
164                 
165           Note_column::translate_rests (rests[0],d0 *dy);       
166           Note_column::translate_rests (rests[1], d1 *dy);
167         }
168     }
169   // meisjes met jongetjes
170   else 
171     {
172       if (rests.size () > 1)
173         {
174           warning (_("too many colliding rests"));
175         }
176       if (notes.size () > 1)
177         {
178           warning (_("too many notes for rest collision"));
179         }
180       Score_element * rcol = rests[0];
181
182       // try to be opposite of noteheads. 
183       Direction dir = - Note_column::dir (notes[0]);
184
185       Interval restdim = Note_column::rest_dim (rcol);
186       if (restdim.empty_b ())
187         return SCM_UNSPECIFIED;
188       
189       // staff ref'd?
190       Real staff_space = me->paper_l()->get_var ("staffspace");
191
192         /* FIXME
193           staff_space =  rcol->rests[0]->staff_space ();
194         */
195       Real minimum_dist = gh_scm2double (me->get_elt_property ("minimum-distance")) * staff_space;
196       
197       /*
198         assumption: ref points are the same. 
199        */
200       Interval notedim;
201       for (int i = 0; i < notes.size(); i++) 
202         {
203           notedim.unite (notes[i]->extent (Y_AXIS));
204         }
205
206       Interval inter (notedim);
207       inter.intersect (restdim);
208
209       Real dist =
210         minimum_dist +  dir * (notedim[dir] - restdim[-dir]) >? 0;
211
212
213       // FIXME
214       //int stafflines = 5; // rcol->rests[0]->line_count;
215       int stafflines = Staff_symbol_referencer::line_count (me);
216       // hurg?
217       stafflines = stafflines != 0 ? stafflines : 5;
218       
219       // move discretely by half spaces.
220       int discrete_dist = int (ceil (dist / (0.5 *staff_space)));
221
222       // move by whole spaces inside the staff.
223       if (discrete_dist < stafflines+1)
224         discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
225       
226       Note_column::translate_rests (rcol,dir * discrete_dist);
227     }
228   return SCM_UNSPECIFIED;
229 }
230
231 void
232 Rest_collision::set_interface (Score_element*me)
233 {
234   me->set_extent_callback (0, X_AXIS);
235   me->set_extent_callback (0, Y_AXIS);
236 }
237