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