]> git.donarmstrong.com Git - lilypond.git/blob - lily/slur.cc
release: 1.1.55
[lilypond.git] / lily / slur.cc
1 /*
2   slur.cc -- implement  Slur
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1996,  1997--1999, 1998 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7     Jan Nieuwenhuizen <janneke@gnu.org>
8 */
9
10 /*
11   [TODO]
12     * URG: share code with tie
13     * begin and end should be treated as a Script.
14     * damping
15     * slur from notehead to stemend: c''()b''
16  */
17
18 #include "slur.hh"
19 #include "scalar.hh"
20 #include "lookup.hh"
21 #include "paper-def.hh"
22 #include "note-column.hh"
23 #include "stem.hh"
24 #include "p-col.hh"
25 #include "molecule.hh"
26 #include "debug.hh"
27 #include "box.hh"
28 #include "bezier.hh"
29 #include "encompass-info.hh"
30 #include "main.hh"
31
32
33 Slur::Slur ()
34 {
35 }
36
37 void
38 Slur::add_column (Note_column*n)
39 {
40   if (!n->head_l_arr_.size ())
41     warning (_ ("Putting slur over rest. Ignoring"));
42   else
43     {
44       encompass_arr_.push (n);
45       //  n->stem_l_->slur_l_ = this;
46       add_dependency (n);
47     }
48 }
49
50 Direction
51 Slur::get_default_dir () const
52 {
53   Direction d = DOWN;
54   for (int i=0; i < encompass_arr_.size (); i ++) 
55     {
56       if (encompass_arr_[i]->dir () < 0) 
57         {
58           d = UP;
59           break;
60         }
61     }
62   return d;
63 }
64
65 void
66 Slur::do_add_processing ()
67 {
68   set_bounds (LEFT, encompass_arr_[0]);    
69   if (encompass_arr_.size () > 1)
70     set_bounds (RIGHT, encompass_arr_.top ());
71 }
72
73 void
74 Slur::do_pre_processing ()
75 {
76   // don't set directions
77 }
78
79 void
80 Slur::do_substitute_element_pointer (Score_element*o, Score_element*n)
81 {
82   int i;
83   while ((i = encompass_arr_.find_i (dynamic_cast<Note_column *> (o))) >=0) 
84     {
85       if (n)
86         encompass_arr_[i] = dynamic_cast<Note_column *> (n);
87       else
88         encompass_arr_.del (i);
89     }
90 }
91
92 static int 
93 Note_column_compare (Note_column *const&n1 , Note_column* const&n2)
94 {
95   return Item::left_right_compare (n1, n2);
96 }
97
98 bool
99 Slur::broken_edge_b ( Direction dir) const
100 {
101   return extrema ()[dir] != spanned_drul_[dir];
102 }
103
104 bool
105 Slur::normal_edge_b ( Direction dir) const
106 {
107   Note_column *n = extrema ()[dir];
108   return !broken_edge_b ( dir)
109     && n->stem_l_
110     && n->stem_l_->get_elt_property (transparent_scm_sym) == SCM_BOOL_F
111     && n->head_l_arr_.size ();
112 }
113
114 Drul_array<Note_column*>
115 Slur::extrema ()const
116 {
117   Drul_array<Note_column*> extrema;
118   extrema[LEFT] = encompass_arr_[0];
119   extrema[RIGHT] = encompass_arr_.top ();
120   return extrema;
121 }
122
123 /*
124   TODO.
125
126   Unhair this.
127  */
128 void
129 Slur::do_post_processing ()
130 {
131   encompass_arr_.sort (Note_column_compare);
132   if (!dir_)
133     dir_ = get_default_dir ();
134
135   Real interline_f = paper_l ()->get_realvar (interline_scm_sym);
136   Real internote_f = interline_f / 2;
137   // URG
138   Real notewidth_f = paper_l ()->note_width () * 0.8;
139
140   /* 
141    [OSU]: slur and tie placement
142
143    slurs:
144    * x = center of head (upside-down: inner raakpunt stem) - d * gap
145
146    * y = length < 5ss : horizontal raakpunt + d * 0.25 ss
147      y = length >= 5ss : y next interline - d * 0.25 ss
148      --> height <= 5 length ?? we use <= 3 length, now...
149    */
150   
151   Real gap_f = paper_l ()->get_var ("slur_x_gap");
152
153
154   Direction d=LEFT;
155  
156   do 
157     {
158       if (broken_edge_b (d))
159         {
160           // ugh -- check if needed
161           dx_f_drul_[d] = -d 
162             *(spanned_drul_[d]->extent (X_AXIS).length () - 0.5 * notewidth_f);
163
164           // prebreak
165           if (d == RIGHT)
166             {
167               dx_f_drul_[LEFT] = spanned_drul_[LEFT]->extent (X_AXIS).length ();
168               
169               // urg -- check if needed
170               if (encompass_arr_.size () > 1)
171                 dx_f_drul_[RIGHT] += notewidth_f;
172             }
173         }
174       /*
175         normal slur
176        */
177       else if (normal_edge_b (d))
178         {
179           Real notewidth_f = extrema ()[d]->extent (X_AXIS).length ();
180           dy_f_drul_[d] = (int)rint (extrema ()[d]->stem_l_-> extent (Y_AXIS)[dir_]);
181           dx_f_drul_[d] += 0.5 * notewidth_f - d * gap_f;
182           if (dir_ == extrema ()[d]->stem_l_->dir_)
183             {
184               if (dir_ == d)
185                 dx_f_drul_[d] += 0.5 * dir_ * notewidth_f;
186               else
187                 dx_f_drul_[d] += 0.25 * dir_ * notewidth_f;
188             }
189         }
190         else 
191           {
192             Real notewidth_f = extrema ()[d]->extent (X_AXIS).length ();
193             dy_f_drul_[d] = (int)rint (extrema ()[d]->head_positions_interval ()
194                                        [dir_]) * internote_f;
195             dx_f_drul_[d] += 0.5 * notewidth_f - d * gap_f;
196         }
197         dy_f_drul_[d] += dir_ * interline_f;
198         if (extrema ()[d]->stem_l_ && (dir_ == extrema ()[d]->stem_l_->dir_))
199           dy_f_drul_[d] -= dir_ * internote_f;
200       }
201   while (flip(&d) != LEFT);
202
203   // now that both are set, do dependent
204   do 
205     {
206       if (broken_edge_b (d))
207         {
208           Direction u = d;
209           flip(&u);
210
211           // postbreak
212           if (d == LEFT)
213             dy_f_drul_[u] += dir_ * internote_f;
214
215           dy_f_drul_[d] = dy_f_drul_[u];
216         }
217      }
218   while (flip(&d) != LEFT);
219
220   /*
221     Slur should follow line of music
222    */
223   if (normal_edge_b (LEFT)
224       && normal_edge_b (RIGHT)
225       && (extrema ()[LEFT]->stem_l_ != extrema ()[RIGHT]->stem_l_))
226     {
227       Real note_dy = extrema ()[RIGHT]->stem_l_->head_positions ()[dir_]
228         - extrema ()[LEFT]->stem_l_->head_positions ()[dir_];
229       Real dy = dy_f_drul_[RIGHT] - dy_f_drul_[LEFT];
230       /*
231         Should we always follow note-heads, (like a tie)?
232         For now, only if the note_dy != slur_dy, we'll do
233         slur_dy := note_dy * factor.
234       */
235       if (sign (dy) != sign (note_dy))
236         {
237           Real damp_f = paper_l ()->get_var ("slur_slope_follow_music_factor");
238           Real realdy = note_dy * damp_f;
239           Direction adjust_dir = (Direction)(- dir_ * sign (realdy));
240           if (!adjust_dir)
241             adjust_dir = -dir_;
242           /*
243             adjust only if no beam gets in the way
244            */
245           if (!extrema ()[adjust_dir]->stem_l_->beam_l_
246               || (adjust_dir == extrema ()[adjust_dir]->stem_l_->dir_)
247               || (extrema ()[adjust_dir]->stem_l_->beams_i_drul_[-adjust_dir] < 1))
248             {
249               dy_f_drul_[adjust_dir] = dy_f_drul_[-adjust_dir]
250                 + 2 * adjust_dir * realdy;
251               Real dx = notewidth_f / 2;
252               if (adjust_dir != extrema ()[adjust_dir]->stem_l_->dir_)
253                 dx /= 2;
254               dx_f_drul_[adjust_dir] -= adjust_dir * dx;
255             }
256         }
257     }
258
259   /*
260     Avoid too steep slurs.
261    */
262   Real damp_f = paper_l ()->get_var ("slur_slope_damping");
263   Offset d_off = Offset (dx_f_drul_[RIGHT] - dx_f_drul_[LEFT],
264     dy_f_drul_[RIGHT] - dy_f_drul_[LEFT]);
265   d_off[X_AXIS] += extent (X_AXIS).length ();
266
267   Real ratio_f = abs (d_off[Y_AXIS] / d_off[X_AXIS]);
268   if (ratio_f > damp_f)
269     dy_f_drul_[(Direction)(- dir_ * sign (d_off[Y_AXIS]))] +=
270       dir_ * (ratio_f - damp_f) * d_off[X_AXIS];
271 }
272
273 Array<Offset>
274 Slur::get_encompass_offset_arr () const
275 {
276   Real notewidth = paper_l ()->note_width () * 0.8;
277   Real gap = paper_l ()->get_var ("slur_x_gap");
278
279   /*
280   urg.  Calcs done wrt the leftmost note.  Fixme.
281
282   Calcs ignore possibility of pre/postbreak.
283
284
285   */
286
287   Offset left = Offset (dx_f_drul_[LEFT], dy_f_drul_[LEFT]);
288   left[X_AXIS] += encompass_arr_[0]->stem_l_->hpos_f ();
289
290   Real internote = encompass_arr_[0]->stem_l_->staff_line_leading_f ()/2.0;
291
292   /*
293     <URG>
294     i don't understand these two, but *must* for symmetry 
295     look at encompass array: 
296        lilypond -D input/test/slur-symmetry*.ly
297        lilypond -D input/test/sleur.ly
298
299     do_post_processing should have calculated these into
300     dx_f_drul_[], no??
301
302    */
303
304   if (dir_ != encompass_arr_[0]->stem_l_->dir_)
305     left[X_AXIS] += - 0.5 * notewidth * encompass_arr_[0]->stem_l_->dir_
306       + gap;
307   else if (encompass_arr_[0]->stem_l_->dir_ == UP)
308     left[X_AXIS] -= notewidth;
309
310   if ((dir_ == encompass_arr_[0]->stem_l_->dir_) 
311     && (encompass_arr_[0]->stem_l_->dir_ == DOWN))
312     left[Y_AXIS] -= internote * encompass_arr_[0]->stem_l_->dir_;
313   /* </URG> */
314
315   Offset d = Offset (dx_f_drul_[RIGHT] - dx_f_drul_[LEFT],
316     dy_f_drul_[RIGHT] - dy_f_drul_[LEFT]);
317   d[X_AXIS] += extent (X_AXIS).length ();
318
319   int first = 1;
320   int last = encompass_arr_.size () - 1;
321
322
323   Array<Offset> notes;
324   notes.push (Offset (0,0));
325
326   // prebreak
327   if (broken_edge_b (RIGHT))
328     last++;
329   else
330     {
331       Encompass_info info (encompass_arr_.top (), dir_, this);
332       d[Y_AXIS] += info.interstaff_f_;
333     }
334   
335   // postbreak
336   if (broken_edge_b (LEFT))
337     {
338       first--;
339       /*
340         interstaff postbreak: slur begins at height of last note
341        */
342       Encompass_info info (encompass_arr_[0], dir_, this);
343       notes[0][Y_AXIS] += info.interstaff_f_;
344     }
345   else
346     {
347       Encompass_info info (encompass_arr_[0], dir_, this);
348       notes[0][Y_AXIS] += info.interstaff_f_;
349     }
350
351   for (int i = first; i < last; i++)
352     {
353       Encompass_info info (encompass_arr_[i], dir_, this);
354       notes.push (info.o_ - left);
355     }
356
357   /*
358     interstaff prebreak: slur ends at height of last note
359    */
360   if (broken_edge_b (RIGHT))
361     d[Y_AXIS] = notes.top ()[Y_AXIS];
362
363   notes.push (d);
364   
365   return notes;
366 }
367
368
369 Array<Rod>
370 Slur::get_rods () const
371 {
372   Array<Rod> a;
373   Rod r;
374   r.item_l_drul_ = spanned_drul_;
375   r.distance_f_ = paper_l ()->get_var ("slur_x_minimum");
376
377   a.push (r);
378   return a;
379 }
380