]> git.donarmstrong.com Git - lilypond.git/blob - lily/slur.cc
patch::: 1.3.64.jcn1
[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--2000 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7     Jan Nieuwenhuizen <janneke@gnu.org>
8 */
9
10 /*
11   [TODO]
12     * begin and end should be treated as a/acknowledge Scripts.
13     * broken slur should have uniform trend
14  */
15
16 #include "directional-element-interface.hh"
17 #include "group-interface.hh"
18 #include "slur.hh"
19 #include "lookup.hh"
20 #include "paper-def.hh"
21 #include "note-column.hh"
22 #include "stem.hh"
23 #include "paper-column.hh"
24 #include "molecule.hh"
25 #include "debug.hh"
26 #include "slur-bezier-bow.hh"
27 #include "main.hh"
28 #include "cross-staff.hh"
29 #include "group-interface.hh"
30 #include "staff-symbol-referencer.hh"
31
32 Slur::Slur (SCM s)
33   : Spanner (s)
34 {
35   /*
36     silly value for testing
37    */
38   set_elt_property ("attachment", gh_cons (ly_symbol2scm ("alongside-stem"),
39                                            ly_symbol2scm ("alongside-stem")));
40   set_elt_pointer ("note-columns", SCM_EOL);
41   set_elt_property ("control-points", SCM_EOL);
42 }
43
44 void
45 Slur::add_column (Note_column*n)
46 {
47   if (!gh_pair_p (n->get_elt_pointer ("note-heads")))
48     warning (_ ("Putting slur over rest.  Ignoring."));
49   else
50     {
51       Pointer_group_interface (this, "note-columns").add_element (n);
52       add_dependency (n);
53     }
54 }
55
56 void
57 Slur::de_uglyfy (Slur_bezier_bow* bb, Real default_height)
58 {
59   Real length = bb->curve_.control_[3][X_AXIS] ; 
60   Real ff = bb->fit_factor ();
61   for (int i = 1; i < 3; i++)
62     {
63       Real ind = abs (bb->curve_.control_[(i-1)*3][X_AXIS]
64                       - bb->curve_.control_[i][X_AXIS]) / length;
65       Real h = bb->curve_.control_[i][Y_AXIS] * ff / length;
66
67       Real f = default_height / length;
68       Real c1 = paper_l ()->get_var ("bezier_control1");
69       Real c2 = paper_l ()->get_var ("bezier_control2");
70       Real c3 = paper_l ()->get_var ("bezier_control3");
71       if (h > c1 * f)
72         {
73           h = c1 * f; 
74         }
75       else if (h > c2 + c3 * ind)
76         {
77           h = c2 + c3 * ind; 
78         }
79       
80       bb->curve_.control_[i][Y_AXIS] = h * length;
81     } 
82
83   bb->curve_.assert_sanity ();
84 }
85
86 Direction
87 Slur::get_default_dir () const
88 {
89   Link_array<Note_column> encompass_arr =
90     Pointer_group_interface__extract_elements (this, (Note_column*)0, "note-columns");
91   
92   Direction d = DOWN;
93   for (int i=0; i < encompass_arr.size (); i ++) 
94     {
95       if (encompass_arr[i]->dir () < 0) 
96         {
97           d = UP;
98           break;
99         }
100     }
101   return d;
102 }
103
104 void
105 Slur::do_add_processing ()
106 {
107   Link_array<Note_column> encompass_arr =
108     Pointer_group_interface__extract_elements (this, (Note_column*)0, "note-columns");
109
110   if (encompass_arr.size ())
111     {
112       set_bound (LEFT, encompass_arr[0]);    
113       if (encompass_arr.size () > 1)
114         set_bound (RIGHT, encompass_arr.top ());
115     }
116 }
117
118
119
120 Offset
121 Slur::encompass_offset (Note_column const* col) const
122 {
123   Offset o;
124   Stem* stem_l = col->stem_l ();
125   Direction dir = directional_element (this).get ();
126   
127   if (!stem_l)
128     {
129       warning (_ ("Slur over rest?"));
130      o[X_AXIS] = col->relative_coordinate (0, X_AXIS);
131       o[Y_AXIS] = col->extent (Y_AXIS)[dir];
132       return o;  
133     }
134   Direction stem_dir = directional_element (stem_l).get ();
135   o[X_AXIS] = stem_l->relative_coordinate (0, X_AXIS);
136
137   /*
138     Simply set x to middle of notehead
139    */
140
141   o[X_AXIS] -= 0.5 * stem_dir * col->extent (X_AXIS).length ();
142
143   if ((stem_dir == dir)
144       && !stem_l->extent (Y_AXIS).empty_b ())
145     {
146       o[Y_AXIS] = stem_l->extent (Y_AXIS)[dir];
147     }
148   else
149     {
150       o[Y_AXIS] = col->extent (Y_AXIS)[dir];
151     }
152
153   /*
154    leave a gap: slur mustn't touch head/stem
155    */
156   o[Y_AXIS] += dir * paper_l ()->get_var ("slur_y_free");
157   o[Y_AXIS] -= calc_interstaff_dist (stem_l, this);
158   return o;
159 }
160
161 GLUE_SCORE_ELEMENT(Slur,after_line_breaking);
162
163 SCM
164 Slur::member_after_line_breaking ()
165 {
166   set_extremities ();
167   set_control_points ();
168   return SCM_UNDEFINED;
169
170
171 SCM
172 slur_get_bound (SCM slur, SCM dir)
173 {
174   return ((Slur*)unsmob_element (slur))->get_bound (to_dir (dir))->self_scm_;
175 }
176
177 SCM
178 score_element_get_pointer (SCM se, SCM name)
179 {
180   SCM s = scm_assq (name, unsmob_element (se)->pointer_alist_);
181   return (s == SCM_BOOL_F) ? SCM_UNDEFINED : gh_cdr (s); 
182 }
183
184 SCM
185 score_element_get_property (SCM se, SCM name)
186 {
187   SCM s = scm_assq (name, unsmob_element (se)->property_alist_);
188   return (s == SCM_BOOL_F) ? SCM_UNDEFINED : gh_cdr (s); 
189 }
190
191 void
192 init_score_elts ()
193 {
194   scm_make_gsubr ("get-pointer", 2 , 0, 0,  
195                   (SCM(*)(...)) score_element_get_pointer);
196   scm_make_gsubr ("get-property", 2 , 0, 0,  
197                   (SCM(*)(...)) score_element_get_property);
198   scm_make_gsubr ("get-bound", 2 , 0, 0,  
199                   (SCM(*)(...)) slur_get_bound);
200 }
201
202 ADD_SCM_INIT_FUNC (score_elt, init_score_elts);
203
204 void
205 Slur::set_extremities ()
206 {
207   if (!directional_element (this).get ())
208     directional_element (this).set (get_default_dir ());
209
210   Direction dir = LEFT;
211   do 
212     {
213       // for (SCM s = get_elt_property ("slur-extremity-rules"); s != SCM_EOL; s = gh_cdr (s))
214       for (SCM s = scm_eval (ly_symbol2scm ("slur-extremity-rules"));
215            s != SCM_EOL; s = gh_cdr (s))
216         {
217           SCM r = scm_eval (scm_listify (gh_caar (s),
218                                          this->self_scm_,
219                                          gh_int2scm ((int)dir),
220                                          SCM_UNDEFINED));
221           if (r != SCM_BOOL_F)
222             {
223               index_set_cell (get_elt_property ("attachment"), dir,
224                               gh_cdar (s));
225               break;
226             }
227         }
228     }
229   while (flip (&dir) != LEFT);
230 }
231
232 Offset
233 Slur::get_attachment (Direction dir) const
234 {
235   SCM s = get_elt_property ("attachment");
236   SCM a = dir == LEFT ? gh_car (s) : gh_cdr (s);
237   Real ss = Staff_symbol_referencer_interface (this).staff_space ();
238   Real hs = ss / 2.0;
239   Offset o;
240   if (Note_column* n = dynamic_cast<Note_column*> (get_bound (dir)))
241     {
242       if (Stem* st = dynamic_cast<Stem*> (n->stem_l ()))
243         {
244           String str = ly_symbol2string (a);
245           if (str == "head")
246             {
247               o = Offset (0, st->chord_start_f ());
248             }
249           else if (str == "alongside-stem")
250             {
251               o = Offset (0, st->chord_start_f ());
252             }
253           else if (str == "stem")
254             {
255               o = Offset (0, st->stem_end_position () * hs);
256             }
257           else if (str == "loose-end")
258             {
259               SCM other_a = dir == LEFT ? gh_cdr (s) : gh_car (s);
260               if (ly_symbol2string (other_a) != "loose-end")
261                 {
262                   o = Offset (0, get_attachment (-dir)[Y_AXIS]);
263                 }
264             }
265
266           o += Offset (0.5 * st->get_direction ()
267                        * n->extent (X_AXIS).length (), 0);
268           
269           SCM l = scm_assoc
270             (scm_listify (a,
271                           gh_int2scm (st->get_direction () * dir),
272                           gh_int2scm (directional_element (this).get () * dir),
273                           SCM_UNDEFINED),
274              scm_eval (ly_symbol2scm ("slur-extremity-offset-alist")));
275           
276           if (l != SCM_BOOL_F)
277             {
278               o += ly_scm2offset (gh_cdr (l)) * ss * dir;
279             }
280         }
281     }
282   
283   return o;
284 }
285
286 int
287 Slur::cross_staff_count ()const
288 {
289   Link_array<Note_column> encompass_arr =
290     Pointer_group_interface__extract_elements (this, (Note_column*)0, "note-columns");
291
292   int k=0;
293
294   for (int i = 0; i < encompass_arr.size (); i++)
295     {
296       if (calc_interstaff_dist (encompass_arr[i], this))
297         k++;
298     }
299   return k;
300 }
301
302
303 Array<Offset>
304 Slur::get_encompass_offset_arr () const
305 {
306   Link_array<Note_column> encompass_arr =
307     Pointer_group_interface__extract_elements (this, (Note_column*)0, "note-columns");
308   
309   Array<Offset> offset_arr;
310
311   Offset origin (relative_coordinate (0, X_AXIS), 0);
312
313   int first = 1;
314   int last = encompass_arr.size () - 2;
315
316   offset_arr.push (get_attachment (LEFT));
317
318   /*
319     left is broken edge
320   */
321   int cross_count  = cross_staff_count ();
322   bool cross_b = cross_count && cross_count < encompass_arr.size ();
323   if (encompass_arr[0] != get_bound (LEFT))
324     {
325       first--;
326       Real is   = calc_interstaff_dist (encompass_arr[0], this);
327       if (cross_b)
328         offset_arr[0][Y_AXIS] += is;
329     }
330
331   /*
332     right is broken edge
333   */
334   if (encompass_arr.top () != get_bound (RIGHT))
335     {
336       last++;
337     }
338
339   for (int i = first; i <= last; i++)
340     {
341       Offset o (encompass_offset (encompass_arr[i]));
342       offset_arr.push (o - origin);
343     }
344
345   offset_arr.push (Offset (spanner_length (), 0) + get_attachment (RIGHT));
346
347   return offset_arr;
348 }
349
350
351 Array<Rod>
352 Slur::get_rods () const
353 {
354   Array<Rod> a;
355   Rod r;
356   
357   r.item_l_drul_[LEFT] = get_bound (LEFT);
358   r.item_l_drul_[RIGHT] = get_bound (RIGHT);
359   r.distance_f_ = paper_l ()->get_var ("slur_x_minimum");
360
361   a.push (r);
362   return a;
363 }
364
365
366 /*
367   Ugh should have dash-length + dash-period
368  */
369 GLUE_SCORE_ELEMENT(Slur,brew_molecule);
370 SCM
371 Slur::member_brew_molecule () const
372 {
373   Real thick = paper_l ()->get_var ("slur_thickness");
374   Bezier one = get_curve ();
375
376   Molecule a;
377   SCM d =  get_elt_property ("dashed");
378   if (gh_number_p (d))
379     a = lookup_l ()->dashed_slur (one, thick, thick * gh_scm2double (d));
380   else
381     a = lookup_l ()->slur (one, directional_element (this).get () * thick, thick);
382
383   return a.create_scheme();
384 }
385
386 void
387 Slur::set_control_points ()
388 {
389   Slur_bezier_bow bb (get_encompass_offset_arr (),
390                       directional_element (this).get ());
391
392   Real staff_space = Staff_symbol_referencer_interface (this).staff_space ();
393   Real h_inf = paper_l ()->get_var ("slur_height_limit_factor") * staff_space;
394   Real r_0 = paper_l ()->get_var ("slur_ratio");
395
396   bb.set_default_bezier (h_inf, r_0);
397
398   if (bb.fit_factor () > 1.0)
399     {
400       Real length = bb.curve_.control_[3][X_AXIS]; 
401       Real default_height = bb.get_default_height (h_inf, r_0, length);
402       bb.minimise_enclosed_area (paper_l(), default_height);
403       
404       Real bff = paper_l ()->get_var ("slur_force_blowfit");
405       bb.curve_.control_[1][Y_AXIS] *= bff;
406       bb.curve_.control_[2][Y_AXIS] *= bff;
407       bb.blow_fit ();
408
409       Real sb = paper_l ()->get_var ("slur_beautiful");
410       Real beautiful = length * default_height * sb;
411       Real area = bb.enclosed_area_f ();
412       
413       /*
414         Slurs that fit beautifully are not ugly
415       */
416       if (area > beautiful)
417         de_uglyfy (&bb, default_height);
418     }
419
420   Bezier b = bb.get_bezier ();
421
422
423   SCM controls = SCM_EOL;
424   for (int i= 4; i--;)
425     controls = gh_cons ( ly_offset2scm (b.control_[i]), controls);
426
427   set_elt_property ("control-points", controls);
428 }
429   
430   
431 Bezier
432 Slur::get_curve () const
433 {
434   Bezier b;
435   int i = 0;
436
437   if (!directional_element (this).get ())
438     ((Slur*)this)->set_extremities ();
439   
440   if (!gh_pair_p (get_elt_property ("control-points")))
441     ((Slur*)this)->set_control_points ();
442   
443   
444   for (SCM s= get_elt_property ("control-points"); s != SCM_EOL; s = gh_cdr (s))
445     {
446       b.control_[i] = ly_scm2offset (gh_car (s));
447       i++;
448     }
449   
450   Array<Offset> enc (get_encompass_offset_arr ());
451   Direction dir = directional_element (this).get ();
452   
453   Real x1 = enc[0][X_AXIS];
454   Real x2 = enc.top ()[X_AXIS];
455   
456   Real off = 0.0;
457   for (int i=1; i < enc.size ()-1; i++)
458     {
459       Real x = enc[i][X_AXIS];
460       if (x > x1 && x <x2)
461         {
462           Real y = b.get_other_coordinate (X_AXIS, x);
463           off = off >? dir *  (enc[i][Y_AXIS] - y);
464         }
465     }
466   b.translate (Offset (0, dir * off));
467   return b;
468 }
469