]> git.donarmstrong.com Git - lilypond.git/blob - lily/new-slur.cc
* scm/define-grobs.scm: switch on new-slur by default.
[lilypond.git] / lily / new-slur.cc
1 /*
2   slur.cc -- implement score based Slur
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1996--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7     Jan Nieuwenhuizen <janneke@gnu.org>
8 */
9
10
11 #include <math.h>
12
13 #include "main.hh"
14 #include "font-interface.hh"
15 #include "text-item.hh"
16 #include "directional-element-interface.hh"
17 #include "group-interface.hh"
18 #include "lily-guile.hh"
19 #include "lookup.hh"
20 #include "note-column.hh"
21 #include "output-def.hh"
22 #include "rod.hh"
23 #include "slur-bezier-bow.hh"
24 #include "slur.hh"
25 #include "spanner.hh"
26 #include "staff-symbol-referencer.hh"
27 #include "staff-symbol.hh"
28 #include "stem.hh"
29 #include "stencil.hh"
30 #include "warn.hh"
31
32 /*
33   TODO:
34
35   - avoid collision with staff line
36   - curve around flag/stem for x coordinate
37   - better scoring.
38   
39  */
40
41 struct Encompass_info {
42   Real x_;
43   Real stem_;
44   Real head_;
45   Encompass_info ()
46   {
47     x_ = 0.0;
48     stem_ = 0.0;
49     head_ = 0.0;
50   }
51 };
52
53 struct Bound_info {
54
55   Box stem_extent_;
56   Direction stem_dir_;
57   Grob *bound_;
58   Grob *note_column_;
59   Grob *slur_head_;
60   Grob *staff_;
61   Grob *stem_;
62   Interval slur_head_extent_;
63   Real neighbor_y_;
64
65   Bound_info () {
66     stem_ = 0;
67     neighbor_y_ = 0;
68     staff_ = 0;
69     slur_head_ = 0;
70     stem_dir_ = CENTER;
71     note_column_ = 0;
72   }
73 };
74
75 /*
76   TODO: put in details list.
77   */
78 const int SLUR_REGION_SIZE = 5;
79 const Real HEAD_ENCOMPASS_PENALTY = 1000.0;
80 const Real STEM_ENCOMPASS_PENALTY = 30.0;
81 const Real CLOSENESS_FACTOR = 10;
82 const Real EDGE_ATTRACTION_FACTOR = 4; 
83 const Real HEAD_FREE_SPACE = 0.3;
84 const Real SAME_SLOPE_PENALTY = 20;
85 const Real STEEPER_SLOPE_FACTOR = 50;
86 const Real NON_HORIZONTAL_PENALTY = 15;
87 const Real HEAD_STRICT_FREE_SPACE = 0.2;
88 const Real MAX_SLOPE = 1.1;
89 const Real MAX_SLOPE_FACTOR = 10;
90
91
92 #define DEBUG_SLUR_QUANTING 1
93
94 struct Slur_score {
95   Drul_array<Offset> attachment_;
96   Real score_;
97   Bezier curve_;
98
99 #if DEBUG_SLUR_QUANTING
100   String score_card_;
101 #endif
102
103   Slur_score() {
104     score_ = 0.0;
105   }
106 };
107
108 class New_slur
109 {
110 public:
111   static void add_column (Grob *me, Grob *col);
112   DECLARE_SCHEME_CALLBACK (print, (SCM));
113   static void score_slopes (Grob * me,  Grob *common[],
114                             Drul_array<Bound_info>,
115                             Drul_array<Offset> base_attach,
116                             Array<Slur_score> * scores);
117   
118   static  void score_encompass (Grob * me,  Grob *common[],
119                                 Drul_array<Bound_info>,
120                                 Drul_array<Offset>, Array<Slur_score> * scores);
121   static void set_interface (Grob*);
122   static bool  has_interface (Grob*);
123   static Array<Offset> get_encompass_offsets (Grob *me);
124   static Bezier get_curve (Grob *me);
125   static Bezier get_bezier (Grob *me, Drul_array<Offset>,Real,Real);
126   static Direction get_default_dir (Grob *me);
127   DECLARE_SCHEME_CALLBACK (after_line_breaking, (SCM));
128   DECLARE_SCHEME_CALLBACK (height, (SCM,SCM));
129 private:
130   static void set_end_points (Grob*);
131   static Real get_boundary_notecolumn_y (Grob *me, Direction dir);
132   static Real broken_trend_y (Grob *me, Grob**, Direction dir);
133   static Offset get_attachment (Grob *me,Direction dir, Grob **common);
134   static void de_uglyfy (Grob *me,Slur_bezier_bow* bb, Real default_height);
135   static SCM set_extremities (Grob *me);
136   static void set_control_points (Grob *me);
137   static void check_slope (Grob *me);
138   static Encompass_info get_encompass_info (Grob *me, Grob *col, Grob **common);
139 };
140
141 Real
142 New_slur::broken_trend_y (Grob *me, Grob**common, Direction hdir)
143 {
144   /*
145     A broken slur should maintain the same vertical trend
146     the unbroken slur would have had.
147   */
148   Real by = 0.0;
149   if (Spanner *mother =  dynamic_cast<Spanner*> (me->original_))
150     {
151       int k = broken_spanner_index (dynamic_cast<Spanner*> (me));
152       int j = k + hdir;
153       if (j < 0 || j >= mother->broken_intos_.size ())
154         return by;
155       
156       Grob *neighbor = mother->broken_intos_[j];      
157       if (hdir == RIGHT)
158         neighbor->set_property ("direction",
159                                  me->get_property ("direction"));
160
161       Spanner * common_mother = dynamic_cast<Spanner*> (common[Y_AXIS]->original_);
162       int common_k = broken_spanner_index (dynamic_cast<Spanner*> (common[Y_AXIS]));
163       int common_j = common_k + hdir;
164
165       if (common_j < 0 || common_j >= common_mother->broken_intos_.size())
166         return by;
167       
168
169       Grob *common_next_system = common_mother->broken_intos_[common_j];
170       Link_array<Grob> neighbor_cols = 
171         Pointer_group_interface__extract_grobs (neighbor, (Grob*)0, "note-columns");
172
173       Grob * neighbor_col = (hdir == RIGHT) ? neighbor_cols[0] : neighbor_cols.top ();
174       Grob * neighbor_common = common_next_system->common_refpoint (neighbor_col, Y_AXIS);
175
176       Direction vdir = get_grob_direction (me);
177       Real neighbor_y =
178         neighbor_col->extent (neighbor_common, Y_AXIS)
179         .linear_combination (int(neighbor_cols.size()==1 ? CENTER : vdir))
180         - common_next_system->relative_coordinate (neighbor_common, Y_AXIS);
181
182       Link_array<Grob> my_cols = 
183         Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-columns");
184       
185       Grob *extreme_col = (hdir == RIGHT) ? my_cols.top() : my_cols[0];
186       Real y = extreme_col->extent (common[Y_AXIS], Y_AXIS).linear_combination (vdir);
187       
188       by = (y*neighbor_cols.size() + neighbor_y*my_cols.size()) /
189         (my_cols.size() + neighbor_cols.size());
190     }
191   return by;
192 }
193
194 void
195 New_slur::set_interface (Grob*me)
196 {
197   /* Copy to mutable list. */
198   me->set_property ("attachment",
199                     ly_deep_copy (me->get_property ("attachment")));
200 }
201
202 void
203 New_slur::add_column (Grob*me, Grob*n)
204 {
205   Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-columns"), n);
206   me->add_dependency (n);
207
208   add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (n));
209 }
210
211 Encompass_info
212 New_slur::get_encompass_info (Grob *me,
213                             Grob *col,
214                             Grob **common) 
215 {
216   Grob* stem = unsmob_grob (col->get_property ("stem"));
217
218   Encompass_info ei;
219   
220   Direction dir = get_grob_direction (me);
221   
222   if (!stem)
223     {
224       programming_error ("No stem for note column?");
225       ei.x_ = col->relative_coordinate (common[X_AXIS], X_AXIS);
226       ei.head_ = ei.stem_ = col->extent (common[Y_AXIS], Y_AXIS)[get_grob_direction (me)];
227       return ei;  
228     }
229   Direction stem_dir = get_grob_direction (stem);
230
231   if (Grob *head = Note_column::first_head (col))
232     ei.x_ = head->extent (common[X_AXIS], X_AXIS).center ();
233   else
234     ei.x_ = col->extent (common[X_AXIS], X_AXIS).center ();
235
236   Grob * h = Stem::extremal_heads (stem)[Direction (dir)];
237   if (!h)
238     {
239       ei.head_ = ei.stem_ = col->extent (common[Y_AXIS], Y_AXIS)[dir];
240       return ei;  
241     }
242   
243   ei.head_ = h->extent (common[Y_AXIS], Y_AXIS)[dir];
244   
245   if ((stem_dir == dir)
246       && !stem->extent (stem, Y_AXIS).is_empty ())
247     {
248       ei.stem_ = stem->extent (common[Y_AXIS], Y_AXIS)[dir];
249     }
250   else
251     ei.stem_ = ei.head_;
252
253   return ei;
254 }
255
256
257 Direction
258 New_slur::get_default_dir (Grob*me) 
259 {
260   Link_array<Grob> encompasses =
261     Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-columns");
262   
263   Direction d = DOWN;
264   for (int i=0; i < encompasses.size (); i ++) 
265     {
266       if (Note_column::dir (encompasses[i]) < 0) 
267         {
268           d = UP;
269           break;
270         }
271     }
272   return d;
273 }
274
275 MAKE_SCHEME_CALLBACK (New_slur, after_line_breaking,1);
276 SCM
277 New_slur::after_line_breaking (SCM smob)
278 {
279   Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob));
280   if (!scm_ilength (me->get_property ("note-columns")))
281     {
282       me->suicide ();
283       return SCM_UNSPECIFIED;
284     }
285   
286   if (!get_grob_direction (me))
287     set_grob_direction (me, get_default_dir (me));
288
289   
290   set_end_points (me);
291
292   return SCM_UNSPECIFIED;
293 }
294
295 Bezier
296 New_slur::get_bezier (Grob *me, Drul_array<Offset> extremes,
297                       Real r_0,
298                       Real h_inf)
299 {
300   Array<Offset> encompasses;
301   encompasses.push (extremes[LEFT]);
302   encompasses.push (extremes[RIGHT]);
303   
304   Slur_bezier_bow bb (encompasses,
305                       get_grob_direction (me), h_inf, r_0);
306
307   return bb.get_bezier ();
308 }
309
310 void
311 New_slur::set_end_points (Grob *me)
312 {
313   Link_array<Grob> columns =
314     Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-columns");
315   
316   if (columns.is_empty ())
317     {
318       me->suicide ();
319       return ; 
320     }
321   Real staff_space = Staff_symbol_referencer::staff_space ((Grob*)me);
322   Real minimum_length = staff_space * robust_scm2double (me->get_property ("minimum-length"),
323                                                          2.0);
324   
325   Drul_array<Offset> base_attachment;
326
327   SCM eltlist = me->get_property ("note-columns");
328   Grob *common[] = {common_refpoint_of_list (eltlist, me, X_AXIS),
329                     common_refpoint_of_list (eltlist, me, Y_AXIS)};
330
331
332   Spanner* sp = dynamic_cast<Spanner*> (me);
333   common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (RIGHT), X_AXIS);
334   common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (LEFT), X_AXIS);
335
336
337   Drul_array<Bound_info> extremes;
338   Direction dir = get_grob_direction (me);
339   
340   
341   Direction d = LEFT;
342   do {
343     extremes[d].bound_ = dynamic_cast<Spanner*> (me)->get_bound (d);
344       
345     if (Note_column::has_interface (extremes[d].bound_))
346       {
347         extremes[d].note_column_ = extremes[d].bound_;
348         extremes[d].stem_ = Note_column::get_stem (extremes[d].note_column_);
349         extremes[d].stem_dir_ = get_grob_direction (extremes[d].stem_);
350         extremes[d].stem_extent_[X_AXIS] = extremes[d].stem_->extent (common[X_AXIS], X_AXIS);
351         extremes[d].stem_extent_[Y_AXIS] = extremes[d].stem_->extent (common[Y_AXIS], Y_AXIS);
352         extremes[d].slur_head_ = Stem::extremal_heads (extremes[d].stem_)[dir];
353         extremes[d].slur_head_extent_ = extremes[d].slur_head_->extent (common[X_AXIS], X_AXIS);
354         extremes[d].staff_ = Staff_symbol_referencer::get_staff_symbol (extremes[d].slur_head_);
355       }
356     else
357       {
358         extremes[d].neighbor_y_ = broken_trend_y (me, common, d);
359       }
360   } while (flip (&d) != LEFT);
361   
362   do
363     {
364       Grob *stem = extremes[d].stem_;
365       Grob *head = extremes[d].slur_head_;
366       
367       Real x,y;
368       if (!extremes[d].note_column_)
369         {
370           y = extremes[d].neighbor_y_;
371           if (d== RIGHT)
372             x = extremes[d].bound_->extent (common[X_AXIS], X_AXIS)[d];
373           else
374             x = sp->get_broken_left_end_align ();
375         }
376       else
377         {
378           if (stem
379               && extremes[d].stem_dir_ == dir
380               && Stem::get_beaming (stem, -d)
381               && columns.size () > 2
382               )
383             {
384               y = extremes[d].stem_extent_[Y_AXIS][dir];
385             }
386           else if (head) 
387             {
388               y = head->extent (common[Y_AXIS], Y_AXIS)[dir];
389             }
390           y += dir * 0.5 * staff_space;
391
392           Real pos = 2.0 * (y - extremes[d].staff_->relative_coordinate (common[Y_AXIS], Y_AXIS))
393             / Staff_symbol::staff_space (extremes[d].staff_);
394
395           /*
396             start off staffline.
397           */
398           if (fabs (pos - round (pos)) < 0.2
399               && Staff_symbol_referencer::on_staffline (head, (int) rint (pos))
400               && Staff_symbol_referencer::line_count (head) -1 >= rint (pos)
401               )
402             y += staff_space * dir / 10 ;         
403       
404           Grob * fh = Note_column::first_head (extremes[d].note_column_);
405           x = fh->extent (common[X_AXIS],  X_AXIS).linear_combination (CENTER);
406         }
407       base_attachment[d] = Offset (x, y);
408
409     } while (flip (&d) != LEFT);
410
411  
412   Interval end_ys;
413   do {
414     if (extremes[d].note_column_)
415       {
416         end_ys[d] =  dir * ((dir * (base_attachment[d][Y_AXIS] + 4.0 *dir))
417                             >? (dir * (dir + extremes[d].note_column_->extent(common[Y_AXIS],Y_AXIS)[dir]))
418                             >? (dir * base_attachment[-d][Y_AXIS])
419                             );
420       }
421     else
422       {
423         end_ys[d] = extremes[d].neighbor_y_ + 4.0 * dir ;
424       }
425   } while (flip (&d) != LEFT);
426   
427   Array<Slur_score> scores;
428   
429   Drul_array<Offset> os;
430
431   /*ugh.   */
432   os[LEFT] = base_attachment[LEFT];
433     
434   for (int i = 0; dir * os[LEFT][Y_AXIS] <= dir * end_ys[LEFT]; i++)
435     {
436       os[RIGHT] = base_attachment[RIGHT];
437       for (int j = 0; dir *os[RIGHT][Y_AXIS] <= dir * end_ys[RIGHT]; j++)
438         {
439           Slur_score s;
440           Direction d = LEFT;
441
442           
443           do {  
444             os[d][X_AXIS] = base_attachment[d][X_AXIS];
445             if (extremes[d].stem_
446                 && !Stem::is_invisible (extremes[d].stem_)
447                 && extremes[d].stem_dir_ == dir
448                 && dir == -d)
449               {
450                 if (extremes[d].stem_extent_[Y_AXIS].contains (os[d][Y_AXIS]))
451                   {
452                     os[d][X_AXIS] -=  d * extremes[d].slur_head_extent_.length ();
453                   }
454                 else if (dir *extremes[d].stem_extent_[Y_AXIS][dir] < dir * os[d][Y_AXIS])
455                   {
456                     os[d][X_AXIS] = extremes[d].stem_extent_[X_AXIS].center();
457                   }
458               }
459           } while (flip (&d) != LEFT);
460
461           Offset dz = os[RIGHT] - os[LEFT];
462           if (dz[X_AXIS] < minimum_length
463               || fabs (dz[Y_AXIS] / dz[X_AXIS])  > MAX_SLOPE
464               )
465             {
466               do {
467                 if (extremes[d].slur_head_)
468                   os[d][X_AXIS] = extremes[d].slur_head_extent_.center ();
469               } while (flip (&d) != LEFT);
470             }
471           
472           s.attachment_ = os;
473           scores.push (s);
474           
475           os[RIGHT][Y_AXIS] += dir * staff_space / 2;
476         }
477
478       os[LEFT][Y_AXIS] += dir * staff_space /2 ;
479     }
480   
481   {
482     Real r_0 = robust_scm2double (me->get_property ("ratio"), 1);
483     Real h_inf = staff_space * ly_scm2double (me->get_property ("height-limit"));
484     for (int i = scores.size(); i-- ;)
485       {
486         scores[i].curve_ = get_bezier (me, scores[i].attachment_,
487                                        r_0, h_inf);
488       }
489   }
490   
491   score_encompass (me, common, extremes, base_attachment, &scores);
492   score_slopes (me, common, extremes, base_attachment, &scores);
493
494   Real opt = 1e6;
495   int opt_idx = 0;
496   for (int i = scores.size (); i--;)
497     {
498       if (scores[i].score_  < opt)
499         {
500           opt = scores[i].score_;
501           opt_idx = i;
502         }
503     }
504   
505   Bezier const &b =  scores[opt_idx].curve_;
506   
507   SCM controls = SCM_EOL;
508   for (int i = 4; i--;)
509     {
510       Offset o = b.control_[i] -
511         Offset (me->relative_coordinate (common[X_AXIS], X_AXIS),
512                 me->relative_coordinate (common[Y_AXIS], Y_AXIS));
513       
514       controls = scm_cons (ly_offset2scm (o), controls);
515     }
516
517   me->set_property ("control-points", controls);
518
519 #if DEBUG_SLUR_QUANTING
520  scores[opt_idx].score_card_ += to_string ("i%d", opt_idx);
521       
522  // debug quanting
523  me->set_property ("quant-score",
524                    scm_makfrom0str (scores[opt_idx].score_card_.to_str0 ()));
525 #endif
526   
527 }
528
529 void
530 New_slur::score_encompass (Grob * me,  Grob *common[],
531                            Drul_array<Bound_info> extremes,
532                            Drul_array<Offset> base_attach,
533                            Array<Slur_score> * scores)
534 {
535   Link_array<Grob> encompasses =
536     Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-columns");
537   Direction dir = get_grob_direction (me);
538  
539   Array<Encompass_info> infos;
540
541   for (int i = 0; i < encompasses.size(); i++)
542     infos.push (get_encompass_info (me, encompasses[i], common));
543
544   for (int i =0 ; i < scores->size (); i++)
545     {
546       Bezier const &bez (scores->elem (i).curve_);
547       Real demerit =0.;
548       for (int j = 0; j < infos.size(); j++)
549         {
550           Real x = infos[j].x_;
551
552           if (!(x < scores->elem (i).attachment_[RIGHT][X_AXIS]
553                 &&x > scores->elem (i).attachment_[LEFT][X_AXIS]))
554             continue;
555           
556           Real y = bez.get_other_coordinate (X_AXIS, x);
557
558           if (dir * (y - infos[j].head_) < 0)
559             demerit += HEAD_ENCOMPASS_PENALTY;
560           
561           if (dir * (y - infos[j].stem_) < 0)
562             demerit += STEM_ENCOMPASS_PENALTY;
563           else if (j && j < encompasses.size () - 1)  
564             {
565               Interval ext;
566               ext.add_point (infos[j].stem_);
567               ext.add_point (infos[j].head_);
568
569               demerit += - CLOSENESS_FACTOR * (dir * (y - (ext[dir] + dir * HEAD_FREE_SPACE)) <? 0) /
570                 infos.size ();
571             }
572         }
573
574       Direction d = LEFT;
575       do {
576         Real attr =
577           EDGE_ATTRACTION_FACTOR
578           * fabs (scores->elem (i).attachment_[d][Y_AXIS] - base_attach[d][Y_AXIS]);
579         if (extremes[d].stem_ && extremes[d].stem_dir_ == dir)
580           attr /= 5;
581         
582         demerit += attr;
583       } while (flip (&d) != LEFT);
584
585 #if DEBUG_SLUR_QUANTING
586       (*scores)[i].score_card_ += to_string ("E%.2f", demerit);
587 #endif
588       
589       (*scores)[i].score_ += demerit;
590     }
591 }
592
593
594 void
595 New_slur::score_slopes (Grob * me,  Grob *common[],
596                         Drul_array<Bound_info> extremes,
597                         Drul_array<Offset> base_attach,
598                         Array<Slur_score> * scores)
599 {
600   Direction dir = get_grob_direction (me);
601   Drul_array<Real> ys;
602
603   Direction d = LEFT;
604   do {
605
606     if (extremes[d].slur_head_)
607       ys[d] = extremes[d].slur_head_ ->relative_coordinate (common[Y_AXIS], Y_AXIS);
608     else
609       ys[d] = extremes[d].neighbor_y_;
610   } while (flip (&d) != LEFT);
611
612   bool has_beams =
613     (extremes[LEFT].stem_ && Stem::get_beam (extremes[LEFT].stem_))
614     || (extremes[RIGHT].stem_ && Stem::get_beam (extremes[RIGHT].stem_));
615
616   Real dy = ys[RIGHT] - ys[LEFT];
617   for (int i =0 ; i < scores->size (); i++)
618     {
619       Offset slur_dz = (*scores)[i].attachment_[RIGHT]
620         -  (*scores)[i].attachment_[LEFT];
621
622       Real slur_dy = slur_dz[Y_AXIS]; 
623
624
625       Real demerit = 0.0;
626
627       if (!has_beams)
628         demerit += STEEPER_SLOPE_FACTOR *  (dir * (fabs (slur_dy) - fabs (dy)) >? 0);
629
630       demerit += ((fabs (slur_dy/slur_dz[X_AXIS]) - MAX_SLOPE)>?0)  * MAX_SLOPE_FACTOR;
631       
632       if (sign (dy) == 0 &&
633           sign (slur_dy) != 0)
634         demerit += NON_HORIZONTAL_PENALTY;
635
636       if (sign (dy)
637           && sign (slur_dy)
638           && sign (slur_dy) != sign (dy))
639         demerit +=
640           has_beams ? SAME_SLOPE_PENALTY/10 : SAME_SLOPE_PENALTY;
641       
642 #if DEBUG_SLUR_QUANTING
643       (*scores)[i].score_card_ += to_string ("S%.2f",d);
644 #endif
645       (*scores)[i].score_ += demerit;
646     }
647 }
648
649
650 Bezier
651 New_slur::get_curve (Grob*me) 
652 {
653   Bezier b;
654   int i = 0;
655   for (SCM s= me->get_property ("control-points"); s != SCM_EOL; s = ly_cdr (s))
656     {
657       b.control_[i++] = ly_scm2offset (ly_car (s));
658     }
659
660   return b;
661 }
662
663
664 MAKE_SCHEME_CALLBACK (New_slur, height, 2);
665 SCM
666 New_slur::height (SCM smob, SCM ax)
667 {
668   Axis a = (Axis)ly_scm2int (ax);
669   Grob * me = unsmob_grob (smob);
670   assert (a == Y_AXIS);
671
672   SCM mol = me->get_uncached_stencil ();
673   Interval ext;
674   if (Stencil * m = unsmob_stencil (mol))
675     ext = m->extent (a);
676   return ly_interval2scm (ext);
677 }
678
679 /*
680   Ugh should have dash-length + dash-period
681  */
682 MAKE_SCHEME_CALLBACK (New_slur, print,1);
683 SCM
684 New_slur::print (SCM smob)
685 {
686   Grob * me = unsmob_grob (smob);
687   if (!scm_ilength (me->get_property ("note-columns")))
688     {
689       me->suicide ();
690       return SCM_EOL;
691     }
692
693   Real base_thick = robust_scm2double (me->get_property ("thickness"), 1);
694   Real thick = base_thick * Staff_symbol_referencer::line_thickness (me);
695
696   Real ss = Staff_symbol_referencer::staff_space (me);
697   Bezier one = get_curve (me);
698
699   // get_curve may suicide
700   if (!scm_ilength (me->get_property ("note-columns")))
701     return SCM_EOL;
702
703   Stencil a;
704
705   /*
706     TODO: replace dashed with generic property.
707    */
708   SCM d =  me->get_property ("dashed");
709   if (ly_c_number_p (d))
710     a = Lookup::dashed_slur (one, thick, thick * robust_scm2double (d, 0));
711   else
712     a = Lookup::slur (one, get_grob_direction (me) * base_thick * ss / 10.0,
713                       thick);
714
715 #if DEBUG_SLUR_QUANTING
716   SCM quant_score = me->get_property ("quant-score");
717   
718   if (debug_beam_quanting_flag      &&
719       ly_c_string_p (quant_score))
720     {
721       String str;
722       SCM properties = Font_interface::text_font_alist_chain (me);
723
724       Stencil tm = *unsmob_stencil (Text_item::interpret_markup
725          (me->get_paper ()->self_scm (), properties, quant_score));
726       a.add_at_edge (Y_AXIS, get_grob_direction (me), tm, 1.0, 0);
727     }
728 #endif
729   
730   return a.smobbed_copy ();
731 }
732
733
734
735
736
737 ADD_INTERFACE (New_slur, "new-slur-interface",
738   "A slur",
739   "attachment attachment-offset beautiful control-points dashed details de-uglify-parameters direction extremity-function extremity-offset-alist height-limit note-columns ratio slope-limit thickness y-free");