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