]> git.donarmstrong.com Git - lilypond.git/blob - lily/new-slur.cc
* lily/new-slur.cc: cleanup, split in functions.
[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 struct Encompass_info {
41   Real x_;
42   Real stem_;
43   Real head_;
44   Encompass_info ()
45   {
46     x_ = 0.0;
47     stem_ = 0.0;
48     head_ = 0.0;
49   }
50 };
51
52 struct Bound_info {
53
54   Box stem_extent_;
55   Direction stem_dir_;
56   Grob *bound_;
57   Grob *note_column_;
58   Grob *slur_head_;
59   Grob *staff_;
60   Grob *stem_;
61   Interval slur_head_extent_;
62   Real neighbor_y_;
63
64   Bound_info () {
65     stem_ = 0;
66     neighbor_y_ = 0;
67     staff_ = 0;
68     slur_head_ = 0;
69     stem_dir_ = CENTER;
70     note_column_ = 0;
71   }
72 };
73
74 /*
75   TODO: put in details property.
76 */
77 struct Slur_score_parameters
78 {
79   int SLUR_REGION_SIZE ;
80   Real HEAD_ENCOMPASS_PENALTY ;
81   Real STEM_ENCOMPASS_PENALTY ;
82   Real CLOSENESS_FACTOR ;
83   Real EDGE_ATTRACTION_FACTOR ; 
84   Real SAME_SLOPE_PENALTY ;
85   Real STEEPER_SLOPE_FACTOR ;
86   Real NON_HORIZONTAL_PENALTY ;
87   Real HEAD_STRICT_FREE_SPACE ;
88   Real MAX_SLOPE ;
89   Real MAX_SLOPE_FACTOR ;
90   Real FREE_HEAD_DISTANCE ;
91   Slur_score_parameters ();
92 };
93
94 void
95 init_score_param (Slur_score_parameters *score_param)
96 {
97   score_param->SLUR_REGION_SIZE = 5;
98   score_param->HEAD_ENCOMPASS_PENALTY = 1000.0;
99   score_param->STEM_ENCOMPASS_PENALTY = 30.0;
100   score_param->CLOSENESS_FACTOR = 10;
101   score_param->EDGE_ATTRACTION_FACTOR = 4; 
102   score_param->SAME_SLOPE_PENALTY = 20;
103   score_param->STEEPER_SLOPE_FACTOR = 50;
104   score_param->NON_HORIZONTAL_PENALTY = 15;
105   score_param->HEAD_STRICT_FREE_SPACE = 0.2;
106   score_param->MAX_SLOPE = 1.1;
107   score_param->MAX_SLOPE_FACTOR = 10;
108   score_param->FREE_HEAD_DISTANCE = 0.3 ;
109 }
110
111 Slur_score_parameters::Slur_score_parameters()
112 {
113   init_score_param (this);
114 }
115
116 #define DEBUG_SLUR_QUANTING 1
117
118 struct Slur_score {
119   Drul_array<Offset> attachment_;
120   Real score_;
121   Bezier curve_;
122
123 #if DEBUG_SLUR_QUANTING
124   String score_card_;
125 #endif
126
127   Slur_score() {
128     score_ = 0.0;
129   }
130 };
131
132 class New_slur
133 {
134 public:
135   static void add_column (Grob *me, Grob *col);
136   DECLARE_SCHEME_CALLBACK (print, (SCM));
137   static void score_slopes (Grob * me,  Grob *common[],
138                             Slur_score_parameters * score_param,
139                             
140                             Drul_array<Bound_info>,
141                             Drul_array<Offset> base_attach,
142                             Array<Slur_score> * scores);
143   
144   static void score_edges (Grob * me,  Grob *common[],
145                            Slur_score_parameters * score_param,
146                            
147                            Drul_array<Bound_info> extremes,
148                            Drul_array<Offset> base_attach,
149                            Array<Slur_score> * scores);
150   static void score_encompass (Grob * me,  Grob *common[],
151                                Slur_score_parameters*,
152                                Drul_array<Bound_info>,
153                                Drul_array<Offset>, Array<Slur_score> * scores);
154
155   static  Encompass_info get_encompass_info (Grob *me,
156                                              Grob *col,
157                                              Grob **common) ;
158   static void set_interface (Grob*);
159   static bool  has_interface (Grob*);
160   static Bezier get_curve (Grob *me);
161   static Bezier get_bezier (Grob *me, Drul_array<Offset>,Real,Real);
162   static Direction get_default_dir (Grob *me);
163   DECLARE_SCHEME_CALLBACK (after_line_breaking, (SCM));
164   DECLARE_SCHEME_CALLBACK (height, (SCM,SCM));
165
166   static void set_end_points (Grob*);
167   static Real get_boundary_notecolumn_y (Grob *me, Direction dir);
168   static Real broken_trend_y (Grob *me, Grob**, Direction dir);
169   static void set_control_points (Grob *me);
170   static Drul_array<Bound_info> get_bound_info (Spanner* me,
171                                                 Grob **common);
172
173   static void generate_curves (Grob*me, Array<Slur_score> *scores);
174   static Array<Slur_score> enumerate_attachments (Grob * me,  Grob *common[],
175                                                   Slur_score_parameters * score_param,
176                                                   Drul_array<Bound_info> extremes,
177                                                   Drul_array<Offset> base_attachment, Drul_array<Real> end_ys);
178   ;
179   static  Drul_array<Offset> get_base_attachments (Spanner*sp,
180                                                    Grob **common,
181                                                  
182                                                    Drul_array<Bound_info> extremes);
183
184   static  Drul_array<Real> get_y_attachment_range (Spanner*sp,
185                                                    Grob **common,
186                                                    
187                                                    Drul_array<Bound_info> extremes,
188                                                    Drul_array<Offset> base_attachment);
189 };
190
191 Real
192 New_slur::broken_trend_y (Grob *me, Grob**common, Direction hdir)
193 {
194   /*
195     A broken slur should maintain the same vertical trend
196     the unbroken slur would have had.
197   */
198   Real by = 0.0;
199   if (Spanner *mother =  dynamic_cast<Spanner*> (me->original_))
200     {
201       int k = broken_spanner_index (dynamic_cast<Spanner*> (me));
202       int j = k + hdir;
203       if (j < 0 || j >= mother->broken_intos_.size ())
204         return by;
205       
206       Grob *neighbor = mother->broken_intos_[j];      
207       if (hdir == RIGHT)
208         neighbor->set_property ("direction",
209                                 me->get_property ("direction"));
210
211       Spanner * common_mother = dynamic_cast<Spanner*> (common[Y_AXIS]->original_);
212       int common_k = broken_spanner_index (dynamic_cast<Spanner*> (common[Y_AXIS]));
213       int common_j = common_k + hdir;
214
215       if (common_j < 0 || common_j >= common_mother->broken_intos_.size())
216         return by;
217       
218
219       Grob *common_next_system = common_mother->broken_intos_[common_j];
220       Link_array<Grob> neighbor_cols = 
221         Pointer_group_interface__extract_grobs (neighbor, (Grob*)0, "note-columns");
222
223       Grob * neighbor_col = (hdir == RIGHT) ? neighbor_cols[0] : neighbor_cols.top ();
224       Grob * neighbor_common = common_next_system->common_refpoint (neighbor_col, Y_AXIS);
225
226       Direction vdir = get_grob_direction (me);
227       Real neighbor_y =
228         neighbor_col->extent (neighbor_common, Y_AXIS)
229         .linear_combination (int(neighbor_cols.size()==1 ? CENTER : vdir))
230         - common_next_system->relative_coordinate (neighbor_common, Y_AXIS);
231
232       Link_array<Grob> my_cols = 
233         Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-columns");
234       
235       Grob *extreme_col = (hdir == RIGHT) ? my_cols.top() : my_cols[0];
236       Real y = extreme_col->extent (common[Y_AXIS], Y_AXIS).linear_combination (vdir);
237       
238       by = (y*neighbor_cols.size() + neighbor_y*my_cols.size()) /
239         (my_cols.size() + neighbor_cols.size());
240     }
241   return by;
242 }
243
244 void
245 New_slur::set_interface (Grob*me)
246 {
247   /* Copy to mutable list. */
248   me->set_property ("attachment",
249                     ly_deep_copy (me->get_property ("attachment")));
250 }
251
252 void
253 New_slur::add_column (Grob*me, Grob*n)
254 {
255   Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-columns"), n);
256   me->add_dependency (n);
257
258   add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (n));
259 }
260
261 Encompass_info
262 New_slur::get_encompass_info (Grob *me,
263                               Grob *col,
264                               Grob **common) 
265 {
266   Grob* stem = unsmob_grob (col->get_property ("stem"));
267
268   Encompass_info ei;
269   
270   Direction dir = get_grob_direction (me);
271   
272   if (!stem)
273     {
274       programming_error ("No stem for note column?");
275       ei.x_ = col->relative_coordinate (common[X_AXIS], X_AXIS);
276       ei.head_ = ei.stem_ = col->extent (common[Y_AXIS], Y_AXIS)[get_grob_direction (me)];
277       return ei;  
278     }
279   Direction stem_dir = get_grob_direction (stem);
280
281   if (Grob *head = Note_column::first_head (col))
282     ei.x_ = head->extent (common[X_AXIS], X_AXIS).center ();
283   else
284     ei.x_ = col->extent (common[X_AXIS], X_AXIS).center ();
285
286   Grob * h = Stem::extremal_heads (stem)[Direction (dir)];
287   if (!h)
288     {
289       ei.head_ = ei.stem_ = col->extent (common[Y_AXIS], Y_AXIS)[dir];
290       return ei;  
291     }
292   
293   ei.head_ = h->extent (common[Y_AXIS], Y_AXIS)[dir];
294   
295   if ((stem_dir == dir)
296       && !stem->extent (stem, Y_AXIS).is_empty ())
297     {
298       ei.stem_ = stem->extent (common[Y_AXIS], Y_AXIS)[dir];
299     }
300   else
301     ei.stem_ = ei.head_;
302
303   return ei;
304 }
305
306
307 Direction
308 New_slur::get_default_dir (Grob*me) 
309 {
310   Link_array<Grob> encompasses =
311     Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-columns");
312   
313   Direction d = DOWN;
314   for (int i=0; i < encompasses.size (); i ++) 
315     {
316       if (Note_column::dir (encompasses[i]) < 0) 
317         {
318           d = UP;
319           break;
320         }
321     }
322   return d;
323 }
324
325 MAKE_SCHEME_CALLBACK (New_slur, after_line_breaking,1);
326 SCM
327 New_slur::after_line_breaking (SCM smob)
328 {
329   Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob));
330   if (!scm_ilength (me->get_property ("note-columns")))
331     {
332       me->suicide ();
333       return SCM_UNSPECIFIED;
334     }
335   
336   if (!get_grob_direction (me))
337     set_grob_direction (me, get_default_dir (me));
338
339   
340   set_end_points (me);
341
342   return SCM_UNSPECIFIED;
343 }
344
345 Bezier
346 New_slur::get_bezier (Grob *me, Drul_array<Offset> extremes,
347                       Real r_0,
348                       Real h_inf)
349 {
350   Array<Offset> encompasses;
351   encompasses.push (extremes[LEFT]);
352   encompasses.push (extremes[RIGHT]);
353   
354   Slur_bezier_bow bb (encompasses,
355                       get_grob_direction (me), h_inf, r_0);
356
357   return bb.get_bezier ();
358 }
359
360
361 Drul_array<Bound_info>
362 New_slur::get_bound_info (Spanner* me,
363                           Grob **common)
364 {
365   Drul_array<Bound_info> extremes;
366   Direction d = LEFT;
367   Direction dir = get_grob_direction (me);
368
369   do {
370     extremes[d].bound_ = me->get_bound (d);
371       
372     if (Note_column::has_interface (extremes[d].bound_))
373       {
374         extremes[d].note_column_ = extremes[d].bound_;
375         extremes[d].stem_ = Note_column::get_stem (extremes[d].note_column_);
376         extremes[d].stem_dir_ = get_grob_direction (extremes[d].stem_);
377         extremes[d].stem_extent_[X_AXIS] = extremes[d].stem_->extent (common[X_AXIS], X_AXIS);
378         extremes[d].stem_extent_[Y_AXIS] = extremes[d].stem_->extent (common[Y_AXIS], Y_AXIS);
379         extremes[d].slur_head_ = Stem::extremal_heads (extremes[d].stem_)[dir];
380         extremes[d].slur_head_extent_ = extremes[d].slur_head_->extent (common[X_AXIS], X_AXIS);
381         extremes[d].staff_ = Staff_symbol_referencer::get_staff_symbol (extremes[d].slur_head_);
382       }
383     else
384       {
385         extremes[d].neighbor_y_ = broken_trend_y (me, common, d);
386       }
387   } while (flip (&d) != LEFT);
388
389   return extremes;
390 }
391
392 void
393 New_slur::set_end_points (Grob *me)
394 {
395   Link_array<Grob> columns =
396     Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-columns");
397
398   Slur_score_parameters params;
399   
400   if (columns.is_empty ())
401     {
402       me->suicide ();
403       return ; 
404     }
405
406   SCM eltlist = me->get_property ("note-columns");
407   Grob *common[] = {common_refpoint_of_list (eltlist, me, X_AXIS),
408                     common_refpoint_of_list (eltlist, me, Y_AXIS)};
409
410
411   Spanner* sp = dynamic_cast<Spanner*> (me);
412   common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (RIGHT), X_AXIS);
413   common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (LEFT), X_AXIS);
414
415
416   Drul_array<Bound_info> extremes = get_bound_info (sp, common);
417   Drul_array<Offset> base_attachment =  get_base_attachments (sp, common, extremes);
418   Drul_array<Real> end_ys = get_y_attachment_range (sp, common, extremes, base_attachment);
419   Array<Slur_score> scores = enumerate_attachments (me, common,
420                                                     &params, extremes, base_attachment, end_ys);
421   generate_curves (me, &scores);
422
423   score_edges (me, common, &params,extremes, base_attachment, &scores);
424   score_slopes (me, common, &params,extremes, base_attachment, &scores);
425   score_encompass (me, common, &params,extremes, base_attachment, &scores);
426
427   Real opt = 1e6;
428   int opt_idx = 0;
429   for (int i = scores.size (); i--;)
430     {
431       if (scores[i].score_  < opt)
432         {
433           opt = scores[i].score_;
434           opt_idx = i;
435         }
436     }
437
438 #if DEBUG_SLUR_QUANTING
439   SCM inspect_quants = me->get_property ("inspect-quants");
440   if (to_boolean (me->get_paper ()->lookup_variable (ly_symbol2scm ("debug-slur-quanting")))
441       && ly_c_pair_p (inspect_quants))
442     {
443       Drul_array<Real> ins = ly_scm2interval (inspect_quants);
444       int i = 0;
445
446       Real mindist = 1e6;
447       for (; i < scores.size (); i ++)
448         {
449           Real d =fabs (scores[i].attachment_[LEFT][Y_AXIS] - ins[LEFT])
450             + fabs (scores[i].attachment_[RIGHT][Y_AXIS] - ins[RIGHT]);
451           if (d < mindist)
452             {
453               opt_idx = i;
454               mindist= d;
455             }
456         }
457       if (mindist > 1e5)
458         programming_error ("Could not find quant.");
459     }
460   
461   
462   scores[opt_idx].score_card_ += to_string ("i%d", opt_idx);
463
464   // debug quanting
465   me->set_property ("quant-score",
466                     scm_makfrom0str (scores[opt_idx].score_card_.to_str0 ()));
467 #endif
468   
469   Bezier const &b =  scores[opt_idx].curve_;
470   
471   SCM controls = SCM_EOL;
472   for (int i = 4; i--;)
473     {
474       Offset o = b.control_[i] -
475         Offset (me->relative_coordinate (common[X_AXIS], X_AXIS),
476                 me->relative_coordinate (common[Y_AXIS], Y_AXIS));
477       
478       controls = scm_cons (ly_offset2scm (o), controls);
479     }
480
481   me->set_property ("control-points", controls);
482 }
483
484
485 Drul_array<Real>
486 New_slur::get_y_attachment_range (Spanner*me,
487                                   Grob **common,
488                                   Drul_array<Bound_info> extremes,
489                                   Drul_array<Offset> base_attachment)
490 {
491   Drul_array<Real> end_ys;
492   Direction dir = get_grob_direction (me);
493   Direction d = LEFT;
494   do {
495     if (extremes[d].note_column_)
496       {
497         end_ys[d] =  dir * ((dir * (base_attachment[d][Y_AXIS] + 4.0 *dir))
498                             >? (dir * (dir + extremes[d].note_column_->extent(common[Y_AXIS],Y_AXIS)[dir]))
499                             >? (dir * base_attachment[-d][Y_AXIS])
500                             );
501       }
502     else
503       {
504         end_ys[d] = extremes[d].neighbor_y_ + 4.0 * dir ;
505       }
506   } while (flip (&d) != LEFT);
507
508   return end_ys;
509 }
510
511 Drul_array<Offset>
512 New_slur::get_base_attachments (Spanner*me,
513                                 Grob **common,
514                                 Drul_array<Bound_info> extremes)
515 {
516   Link_array<Grob> columns =
517     Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-columns");
518   Drul_array<Offset> base_attachment;
519     
520
521   Slur_score_parameters params;
522
523   Real staff_space = Staff_symbol_referencer::staff_space ((Grob*)me);
524
525   Direction dir = get_grob_direction (me);
526   Direction d = LEFT;  
527   do
528     {
529       Grob *stem = extremes[d].stem_;
530       Grob *head = extremes[d].slur_head_;
531       
532       Real x,y;
533       if (!extremes[d].note_column_)
534         {
535           y = extremes[d].neighbor_y_;
536           if (d== RIGHT)
537             x = extremes[d].bound_->extent (common[X_AXIS], X_AXIS)[d];
538           else
539             x = me->get_broken_left_end_align ();
540         }
541       else
542         {
543           if (stem
544               && extremes[d].stem_dir_ == dir
545               && Stem::get_beaming (stem, -d)
546               && columns.size () > 2
547               )
548             {
549               y = extremes[d].stem_extent_[Y_AXIS][dir];
550             }
551           else if (head) 
552             {
553               y = head->extent (common[Y_AXIS], Y_AXIS)[dir];
554             }
555           y += dir * 0.5 * staff_space;
556
557           Real pos = 2.0 * (y - extremes[d].staff_->relative_coordinate (common[Y_AXIS], Y_AXIS))
558             / Staff_symbol::staff_space (extremes[d].staff_);
559
560           /*
561             start off staffline.
562           */
563           if (fabs (pos - round (pos)) < 0.2
564               && Staff_symbol_referencer::on_staffline (head, (int) rint (pos))
565               && Staff_symbol_referencer::line_count (head) -1 >= rint (pos)
566               )
567             y += staff_space * dir / 10 ;         
568       
569           Grob * fh = Note_column::first_head (extremes[d].note_column_);
570           x = fh->extent (common[X_AXIS],  X_AXIS).linear_combination (CENTER);
571         }
572       base_attachment[d] = Offset (x, y);
573
574     } while (flip (&d) != LEFT);
575
576   return base_attachment;
577 }
578
579
580
581 void
582 New_slur::generate_curves (Grob*me,   Array<Slur_score> *scores)
583
584 {
585
586   Real staff_space = Staff_symbol_referencer::staff_space ((Grob*)me);
587
588   Real r_0 = robust_scm2double (me->get_property ("ratio"), 1);
589   Real h_inf = staff_space * ly_scm2double (me->get_property ("height-limit"));
590   for (int i = scores->size(); i-- ;)
591     {
592       (*scores)[i].curve_ = get_bezier (me, (*scores)[i].attachment_,
593                                         r_0, h_inf);
594     }
595 }
596
597 Array<Slur_score>
598 New_slur::enumerate_attachments (Grob * me,  Grob *common[],
599                                  Slur_score_parameters * score_param,
600                                  Drul_array<Bound_info> extremes,
601                                  Drul_array<Offset> base_attachment,
602                                  Drul_array<Real> end_ys)
603 {
604   /*ugh.   */
605   Array<Slur_score> scores;
606
607   Direction dir = get_grob_direction (me);
608   Real staff_space = Staff_symbol_referencer::staff_space ((Grob*)me);
609   
610   Drul_array<Offset> os;
611   os[LEFT] = base_attachment[LEFT];
612   Real minimum_length = staff_space * robust_scm2double (me->get_property ("minimum-length"),
613                                                          2.0);
614   
615   for (int i = 0; dir * os[LEFT][Y_AXIS] <= dir * end_ys[LEFT]; i++)
616     {
617       os[RIGHT] = base_attachment[RIGHT];
618       for (int j = 0; dir *os[RIGHT][Y_AXIS] <= dir * end_ys[RIGHT]; j++)
619         {
620           Slur_score s;
621           Direction d = LEFT;
622
623           
624           do {  
625             os[d][X_AXIS] = base_attachment[d][X_AXIS];
626             if (extremes[d].stem_
627                 && !Stem::is_invisible (extremes[d].stem_)
628                 && extremes[d].stem_dir_ == dir
629                 && dir == -d)
630               {
631                 if (extremes[d].stem_extent_[Y_AXIS].contains (os[d][Y_AXIS]))
632                   {
633                     os[d][X_AXIS] -=  d * extremes[d].slur_head_extent_.length ();
634                   }
635                 else if (dir *extremes[d].stem_extent_[Y_AXIS][dir] < dir * os[d][Y_AXIS])
636                   {
637                     os[d][X_AXIS] = extremes[d].stem_extent_[X_AXIS].center();
638                   }
639               }
640           } while (flip (&d) != LEFT);
641
642           Offset dz = os[RIGHT] - os[LEFT];
643           if (dz[X_AXIS] < minimum_length
644               || fabs (dz[Y_AXIS] / dz[X_AXIS])  > score_param->MAX_SLOPE
645               )
646             {
647               do {
648                 if (extremes[d].slur_head_)
649                   os[d][X_AXIS] = extremes[d].slur_head_extent_.center ();
650               } while (flip (&d) != LEFT);
651             }
652           
653           s.attachment_ = os;
654           scores.push (s);
655           
656           os[RIGHT][Y_AXIS] += dir * staff_space / 2;
657         }
658
659       os[LEFT][Y_AXIS] += dir * staff_space /2 ;
660     }
661
662   return scores;
663 }
664
665
666 void
667 New_slur::score_encompass (Grob * me,  Grob *common[],
668                            Slur_score_parameters * score_param,
669
670                            Drul_array<Bound_info> extremes,
671                            Drul_array<Offset> base_attach,
672                            Array<Slur_score> * scores)
673 {
674   Link_array<Grob> encompasses =
675     Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-columns");
676   Direction dir = get_grob_direction (me);
677  
678   Array<Encompass_info> infos;
679
680   for (int i = 0; i < encompasses.size(); i++)
681     infos.push (get_encompass_info (me, encompasses[i], common));
682
683   for (int i =0 ; i < scores->size (); i++)
684     {
685       Bezier const &bez (scores->elem (i).curve_);
686       Real demerit =0.;
687       for (int j = 0; j < infos.size(); j++)
688         {
689           Real x = infos[j].x_;
690
691           if (!(x < scores->elem (i).attachment_[RIGHT][X_AXIS]
692                 && x > scores->elem (i).attachment_[LEFT][X_AXIS]))
693             continue;
694           
695           Real y = bez.get_other_coordinate (X_AXIS, x);
696           Real head_dy = (y - infos[j].head_);
697           if (dir * head_dy < 0)
698             {
699               demerit += score_param->HEAD_ENCOMPASS_PENALTY;
700             }
701           else 
702             {
703               Real hd =  
704                 (head_dy) ? (1/(fabs (head_dy)  - 1/score_param->FREE_HEAD_DISTANCE))
705                 : score_param->HEAD_ENCOMPASS_PENALTY;
706               hd = (hd >? 0)<? score_param->HEAD_ENCOMPASS_PENALTY; 
707
708               demerit += hd;      
709             }
710           
711           if (dir * (y - infos[j].stem_) < 0)
712             demerit += score_param->STEM_ENCOMPASS_PENALTY;
713           else if (j && j < encompasses.size () - 1)  
714             {
715               Interval ext;
716               ext.add_point (infos[j].stem_);
717               ext.add_point (infos[j].head_);
718
719               demerit += - score_param->CLOSENESS_FACTOR * (dir * (y - (ext[dir] + dir * score_param->FREE_HEAD_DISTANCE)) <? 0) /
720                 infos.size ();
721             }
722         }
723
724 #if DEBUG_SLUR_QUANTING
725       (*scores)[i].score_card_ += to_string ("C%.2f", demerit);
726 #endif
727       
728       (*scores)[i].score_ += demerit;
729     }
730 }
731
732
733 void
734 New_slur::score_edges (Grob * me,  Grob *common[],
735                        Slur_score_parameters * score_param,
736
737                        Drul_array<Bound_info> extremes,
738                        Drul_array<Offset> base_attach,
739                        Array<Slur_score> * scores)
740 {
741   Direction dir = get_grob_direction (me);
742  
743   for (int i =0 ; i < scores->size (); i++)
744     {
745   
746       Direction d = LEFT;
747       do {
748         Real y = scores->elem (i).attachment_[d][Y_AXIS];
749         Real dy = fabs (y - base_attach[d][Y_AXIS]);
750         
751         Real factor = score_param->EDGE_ATTRACTION_FACTOR;
752
753         Real demerit =
754           score_param->EDGE_ATTRACTION_FACTOR
755           * fabs (scores->elem (i).attachment_[d][Y_AXIS] - base_attach[d][Y_AXIS]);
756  
757         if (extremes[d].stem_
758             && extremes[d].stem_dir_ == dir
759             && !Stem::get_beaming (extremes[d].stem_, -d)
760             )
761           demerit /= 5;
762         
763         (*scores)[i].score_ += demerit;
764 #if DEBUG_SLUR_QUANTING
765         (*scores)[i].score_card_ += to_string ("E%.2f", demerit);
766 #endif
767       } while (flip (&d) != LEFT);
768     }
769 }
770
771 void
772 New_slur::score_slopes (Grob * me,  Grob *common[],
773                         Slur_score_parameters*score_param,
774                         Drul_array<Bound_info> extremes,
775                         Drul_array<Offset> base_attach,
776                         Array<Slur_score> * scores)
777 {
778   Direction dir = get_grob_direction (me);
779   Drul_array<Real> ys;
780
781   Direction d = LEFT;
782   do {
783
784     if (extremes[d].slur_head_)
785       ys[d] = extremes[d].slur_head_ ->relative_coordinate (common[Y_AXIS], Y_AXIS);
786     else
787       ys[d] = extremes[d].neighbor_y_;
788   } while (flip (&d) != LEFT);
789
790   bool has_beams =
791     (extremes[LEFT].stem_ && Stem::get_beam (extremes[LEFT].stem_))
792     || (extremes[RIGHT].stem_ && Stem::get_beam (extremes[RIGHT].stem_));
793
794   Real dy = ys[RIGHT] - ys[LEFT];
795   for (int i =0 ; i < scores->size (); i++)
796     {
797       Offset slur_dz = (*scores)[i].attachment_[RIGHT]
798         -  (*scores)[i].attachment_[LEFT];
799
800       Real slur_dy = slur_dz[Y_AXIS]; 
801
802
803       Real demerit = 0.0;
804
805       if (!has_beams)
806         demerit += score_param->STEEPER_SLOPE_FACTOR *  (dir * (fabs (slur_dy) - fabs (dy)) >? 0);
807
808       demerit += ((fabs (slur_dy/slur_dz[X_AXIS]) - score_param->MAX_SLOPE)>?0)  * score_param->MAX_SLOPE_FACTOR;
809       
810       if (sign (dy) == 0 &&
811           sign (slur_dy) != 0)
812         demerit += score_param->NON_HORIZONTAL_PENALTY;
813
814       if (sign (dy)
815           && sign (slur_dy)
816           && sign (slur_dy) != sign (dy))
817         demerit +=
818           has_beams ? score_param->SAME_SLOPE_PENALTY/10 : score_param->SAME_SLOPE_PENALTY;
819       
820 #if DEBUG_SLUR_QUANTING
821       (*scores)[i].score_card_ += to_string ("S%.2f",d);
822 #endif
823       (*scores)[i].score_ += demerit;
824     }
825 }
826
827
828 Bezier
829 New_slur::get_curve (Grob*me) 
830 {
831   Bezier b;
832   int i = 0;
833   for (SCM s= me->get_property ("control-points"); s != SCM_EOL; s = ly_cdr (s))
834     {
835       b.control_[i++] = ly_scm2offset (ly_car (s));
836     }
837
838   return b;
839 }
840
841
842 MAKE_SCHEME_CALLBACK (New_slur, height, 2);
843 SCM
844 New_slur::height (SCM smob, SCM ax)
845 {
846   Axis a = (Axis)ly_scm2int (ax);
847   Grob * me = unsmob_grob (smob);
848   assert (a == Y_AXIS);
849
850   SCM mol = me->get_uncached_stencil ();
851   Interval ext;
852   if (Stencil * m = unsmob_stencil (mol))
853     ext = m->extent (a);
854   return ly_interval2scm (ext);
855 }
856
857 /*
858   Ugh should have dash-length + dash-period
859 */
860 MAKE_SCHEME_CALLBACK (New_slur, print,1);
861 SCM
862 New_slur::print (SCM smob)
863 {
864   Grob * me = unsmob_grob (smob);
865   if (!scm_ilength (me->get_property ("note-columns")))
866     {
867       me->suicide ();
868       return SCM_EOL;
869     }
870
871   Real base_thick = robust_scm2double (me->get_property ("thickness"), 1);
872   Real thick = base_thick * Staff_symbol_referencer::line_thickness (me);
873
874   Real ss = Staff_symbol_referencer::staff_space (me);
875   Bezier one = get_curve (me);
876
877   Stencil a;
878
879   /*
880     TODO: replace dashed with generic property.
881   */
882   SCM d =  me->get_property ("dashed");
883   if (ly_c_number_p (d))
884     a = Lookup::dashed_slur (one, thick, thick * robust_scm2double (d, 0));
885   else
886     a = Lookup::slur (one, get_grob_direction (me) * base_thick * ss / 10.0,
887                       thick);
888
889 #if DEBUG_SLUR_QUANTING
890   SCM quant_score = me->get_property ("quant-score");
891   
892   if (to_boolean (me->get_paper ()->lookup_variable (ly_symbol2scm ("debug-slur-quanting"))) &&
893       ly_c_string_p (quant_score))
894     {
895       String str;
896       SCM properties = Font_interface::text_font_alist_chain (me);
897
898       Stencil tm = *unsmob_stencil (Text_item::interpret_markup
899                                     (me->get_paper ()->self_scm (), properties, quant_score));
900       a.add_at_edge (Y_AXIS, get_grob_direction (me), tm, 1.0, 0);
901     }
902 #endif
903   
904   return a.smobbed_copy ();
905 }
906
907
908
909
910
911 ADD_INTERFACE (New_slur, "new-slur-interface",
912                "A slur",
913                "control-points dashed details direction height-limit note-columns ratio slope-limit thickness");