]> git.donarmstrong.com Git - lilypond.git/blob - lily/rest-collision.cc
release: 1.3.69
[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,Note_column *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 static SCM
55 head_characteristic (Score_element * col)
56 {
57   Score_element * s = unsmob_element (col->get_elt_property ("rest"));
58
59   if (!s)
60     return SCM_BOOL_F;
61   else
62     return gh_cons (s->get_elt_property ("duration-log"),
63                     gh_int2scm (Rhythmic_head::dot_count (s)));
64 }
65
66 /*
67   TODO: fixme, fucks up if called twice on the same set of rests.
68  */
69 SCM
70 Rest_collision::do_shift (Score_element *me, SCM elts)
71 {
72   /*
73     ugh. -> score  elt type
74    */
75   Link_array<Note_column> rests;
76   Link_array<Note_column> notes;
77
78   for (SCM s = elts; gh_pair_p (s); s = gh_cdr (s))
79     {
80       Score_element * e = unsmob_element (gh_car (s));
81       if (e && unsmob_element (e->get_elt_property ("rest")))
82         rests.push (dynamic_cast<Note_column*> (e));
83       else
84         notes.push (dynamic_cast<Note_column*> (e));
85     }
86
87   
88   /* 
89      handle rest-rest and rest-note collisions
90
91      [todo]
92      * decide not to print rest if too crowded?
93
94      * ignore rests under beams.
95    */
96
97   // no rests to collide
98   if (!rests.size())
99     return SCM_UNDEFINED;
100
101   // no partners to collide with
102   if (rests.size() + notes.size () < 2)
103     return SCM_UNDEFINED;
104
105   // meisjes met meisjes
106   if (!notes.size()) 
107     {
108
109       /*
110         FIXME: col2rhythmic_head and rhythmic_head2mom sucks bigtime.
111         
112       */
113       SCM characteristic = head_characteristic  (rests[0]);
114       int i = 1;
115       for (; i < rests.size (); i++)
116         {
117           if (!gh_equal_p (head_characteristic  (rests[i]), characteristic))
118             break;
119         }
120
121       /*
122         If all durations are the same, we'll check if there are more
123         rests than maximum-rest-count.
124         Otherwise (different durations), we'll try to display them all
125         (urg: all 3 of them, currently).
126        */
127       int display_count;
128       SCM s = me->get_elt_property ("maximum-rest-count");
129       if (i == rests.size ()
130           && gh_number_p (s) && gh_scm2int (s) < rests.size ())
131         {
132           display_count = gh_scm2int (s);
133           for (; i > display_count; i--)
134             {
135               Score_element* r = unsmob_element (rests[i-1]->get_elt_property ("rest"));
136               if (r)
137                 r->suicide ();
138               rests[i-1]->suicide ();
139             }
140         }
141       else
142         display_count = rests.size ();
143       
144       /*
145         UGH.  Should get dims from table.  Should have minimum dist.
146        */
147       int dy = display_count > 2 ? 6 : 4;
148       if (display_count > 1)
149         {
150           rests[0]->translate_rests (dy);       
151           rests[1]->translate_rests (-dy);
152         }
153     }
154   // meisjes met jongetjes
155   else 
156     {
157       if (rests.size () > 1)
158         {
159           warning (_("too many colliding rests"));
160         }
161       if (notes.size () > 1)
162         {
163           warning (_("too many notes for rest collision"));
164         }
165       Note_column * rcol = rests[0];
166
167       // try to be opposite of noteheads. 
168       Direction dir = - notes[0]->dir();
169
170       Interval restdim = rcol->rest_dim ();
171       if (restdim.empty_b ())
172         return SCM_UNDEFINED;
173       
174       // staff ref'd?
175       Real staff_space = me->paper_l()->get_var ("interline");
176
177         /* FIXME
178           staff_space =  rcol->rests[0]->staff_space ();
179         */
180       Real minimum_dist = gh_scm2double (me->get_elt_property ("minimum-distance")) * staff_space;
181       
182       /*
183         assumption: ref points are the same. 
184        */
185       Interval notedim;
186       for (int i = 0; i < notes.size(); i++) 
187         {
188           notedim.unite (notes[i]->extent (Y_AXIS));
189         }
190
191       Interval inter (notedim);
192       inter.intersect (restdim);
193
194       Real dist =
195         minimum_dist +  dir * (notedim[dir] - restdim[-dir]) >? 0;
196
197
198       // FIXME
199       //int stafflines = 5; // rcol->rests[0]->line_count;
200       int stafflines = Staff_symbol_referencer::line_count (me);
201       // hurg?
202       stafflines = stafflines != 0 ? stafflines : 5;
203       
204       // move discretely by half spaces.
205       int discrete_dist = int (ceil (dist / (0.5 *staff_space)));
206
207       // move by whole spaces inside the staff.
208       if (discrete_dist < stafflines+1)
209         discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
210       
211       rcol->translate_rests (dir * discrete_dist);
212     }
213   return SCM_UNDEFINED;
214 }
215
216 void
217 Rest_collision::set_interface (Score_element*me)
218 {
219   me->set_extent_callback (0, X_AXIS);
220   me->set_extent_callback (0, Y_AXIS);
221   me->set_elt_property ("elements", SCM_EOL);
222 }
223