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