]> git.donarmstrong.com Git - lilypond.git/blob - lily/slur-quanting.cc
(EXTRA_DIST_FILES): Add SConstruct.
[lilypond.git] / lily / slur-quanting.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 "pitch.hh"
14 #include "accidental-interface.hh"
15 #include "directional-element-interface.hh"
16 #include "group-interface.hh"
17 #include "lily-guile.hh"
18 #include "note-column.hh"
19 #include "output-def.hh"
20 #include "slur-bezier-bow.hh"
21 #include "new-slur.hh"
22 #include "spanner.hh"
23 #include "staff-symbol-referencer.hh"
24 #include "staff-symbol.hh"
25 #include "stem.hh"
26 #include "warn.hh"
27 #include "beam.hh"
28
29 struct Slur_score
30 {
31   Drul_array<Offset> attachment_;
32   Real score_;
33   Bezier curve_;
34
35 #if DEBUG_SLUR_QUANTING
36   String score_card_;
37 #endif
38
39   Slur_score()
40   {
41     score_ = 0.0;
42   }
43 };
44
45 /*
46   TODO: put in details property.,
47
48   use lowercase.
49 */
50 struct Slur_score_parameters
51 {
52   int SLUR_REGION_SIZE;
53   Real HEAD_ENCOMPASS_PENALTY;
54   Real STEM_ENCOMPASS_PENALTY;
55   Real CLOSENESS_FACTOR;
56   Real EDGE_ATTRACTION_FACTOR;
57   Real SAME_SLOPE_PENALTY;
58   Real STEEPER_SLOPE_FACTOR;
59   Real NON_HORIZONTAL_PENALTY;
60   Real HEAD_STRICT_FREE_SPACE;
61   Real MAX_SLOPE;
62   Real MAX_SLOPE_FACTOR;
63   Real EXTRA_OBJECT_COLLISION;
64   Real ACCIDENTAL_COLLISION;
65   Real FREE_HEAD_DISTANCE;
66   Slur_score_parameters ();
67 };
68
69 /*
70   TODO:
71
72   - curve around flag for y coordinate
73   - better scoring.
74   - short-cut: try a smaller region first.
75   - collisions with accidentals
76   - collisions with articulations (staccato, portato, sforzato, ...)
77   - 
78 */
79 struct Encompass_info
80 {
81   Real x_;
82   Real stem_;
83   Real head_;
84   Encompass_info ()
85   {
86     x_ = 0.0;
87     stem_ = 0.0;
88     head_ = 0.0;
89   }
90 };
91
92 struct Bound_info
93 {
94   Box stem_extent_;
95   Direction stem_dir_;
96   Grob *bound_;
97   Grob *note_column_;
98   Grob *slur_head_;
99   Grob *staff_;
100   Grob *stem_;
101   Interval slur_head_extent_;
102   Real neighbor_y_;
103   Real staff_space_;
104   
105   Bound_info ()
106   {
107     stem_ = 0;
108     neighbor_y_ = 0;
109     staff_ = 0;
110     slur_head_ = 0;
111     stem_dir_ = CENTER;
112     note_column_ = 0;
113   }
114 };
115
116
117
118
119 static void
120 score_extra_encompass (Grob *me, Grob *common[],
121                        Slur_score_parameters *score_param,
122                        Drul_array<Bound_info> ,
123                        Drul_array<Offset> ,
124                        Array<Slur_score> * scores);
125 static void score_slopes (Grob *me, Grob *common[],
126                           Slur_score_parameters *score_param,
127                           Drul_array<Bound_info>,
128                           Drul_array<Offset> base_attach,
129                           Array<Slur_score> *scores);
130
131 static void score_edges (Grob *me, Grob *common[],
132                          Slur_score_parameters *score_param,
133                          Drul_array<Bound_info> extremes,
134                          Drul_array<Offset> base_attach,
135                          Array<Slur_score> *scores);
136 static void score_encompass (Grob *me, Grob *common[],
137                              Slur_score_parameters*,
138                              Drul_array<Bound_info>,
139                              Drul_array<Offset>, Array<Slur_score> *scores);
140 static Bezier avoid_staff_line (Grob *me, Grob **common,
141                                 Drul_array<Bound_info> extremes,
142                                 Bezier bez);
143
144 static Encompass_info get_encompass_info (Grob *me,
145                                           Grob *col,
146                                           Grob **common);
147 static Bezier get_bezier (Grob *me, Drul_array<Offset>, Real, Real);
148 static Direction get_default_dir (Grob *me);
149
150 static void set_end_points (Grob *);
151 static Real broken_trend_y (Grob *me, Grob **, Direction dir);
152 static Drul_array<Bound_info> get_bound_info (Spanner *me, Grob **common);
153
154 static void generate_curves (Grob *me,
155                              Grob *common[],
156                              Drul_array<Bound_info> extremes,
157                              Drul_array<Offset> base_attach,
158                              Array<Slur_score> *scores);
159 static Array<Slur_score> enumerate_attachments
160 (Grob *me, Grob *common[], Slur_score_parameters *score_param,
161  Drul_array<Bound_info> extremes,
162  Drul_array<Offset> base_attachment, Drul_array<Real> end_ys);
163 static Drul_array<Offset> get_base_attachments
164 (Spanner *sp, Grob **common, Drul_array<Bound_info> extremes);
165 static Drul_array<Real> get_y_attachment_range
166 (Spanner *sp, Grob **common, Drul_array<Bound_info> extremes,
167  Drul_array<Offset> base_attachment);
168
169 void
170 init_score_param (Slur_score_parameters *score_param)
171 {
172   score_param->SLUR_REGION_SIZE = 5;
173   score_param->HEAD_ENCOMPASS_PENALTY = 1000.0;
174   score_param->STEM_ENCOMPASS_PENALTY = 30.0;
175   score_param->CLOSENESS_FACTOR = 10;
176   score_param->EDGE_ATTRACTION_FACTOR = 4;
177   score_param->SAME_SLOPE_PENALTY = 20;
178   score_param->STEEPER_SLOPE_FACTOR = 50;
179   score_param->NON_HORIZONTAL_PENALTY = 15;
180   score_param->HEAD_STRICT_FREE_SPACE = 0.2;
181   score_param->MAX_SLOPE = 1.1;
182   score_param->MAX_SLOPE_FACTOR = 10;
183   score_param->FREE_HEAD_DISTANCE = 0.3;
184   score_param->EXTRA_OBJECT_COLLISION = 8;
185   score_param->ACCIDENTAL_COLLISION = 3;
186 }
187
188 Slur_score_parameters::Slur_score_parameters()
189 {
190   init_score_param (this);
191 }
192
193 /* HDIR indicates the direction for the slur.  */
194 Real
195 broken_trend_y (Grob *me, Grob **common, Direction hdir)
196 {
197   /* A broken slur should maintain the same vertical trend
198      the unbroken slur would have had.  */
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
213         = dynamic_cast<Spanner*> (common[Y_AXIS]->original_);
214       int common_k
215         = broken_spanner_index (dynamic_cast<Spanner*> (common[Y_AXIS]));
216       int common_j = common_k + hdir;
217
218       if (common_j < 0 || common_j >= common_mother->broken_intos_.size())
219         return by;
220
221       Grob *common_next_system = common_mother->broken_intos_[common_j];
222       Link_array<Grob> neighbor_cols
223         = Pointer_group_interface__extract_grobs (neighbor, (Grob *)0,
224                                                   "note-columns");
225
226       Grob *neighbor_col
227         = (hdir == RIGHT) ? neighbor_cols[0] : neighbor_cols.top ();
228       Grob *neighbor_common
229         = common_next_system->common_refpoint (neighbor_col, Y_AXIS);
230
231       Direction vdir = get_grob_direction (me);
232       Real neighbor_y
233         = neighbor_col->extent (neighbor_common, Y_AXIS)
234         .linear_combination (int(neighbor_cols.size()==1 ? CENTER : vdir))
235         - common_next_system->relative_coordinate (neighbor_common, Y_AXIS);
236
237       Link_array<Grob> my_cols
238         = Pointer_group_interface__extract_grobs (me, (Grob *)0,
239                                                   "note-columns");
240
241       Grob *extreme_col = (hdir == RIGHT) ? my_cols.top() : my_cols[0];
242       Real y = extreme_col->extent (common[Y_AXIS], Y_AXIS)
243         .linear_combination (int ((my_cols.size() == 1) ? CENTER : vdir));
244       by = (y*neighbor_cols.size() + neighbor_y*my_cols.size()) /
245         (my_cols.size() + neighbor_cols.size());
246     }
247   return by;
248 }
249
250
251 Encompass_info
252 get_encompass_info (Grob *me,
253                     Grob *col,
254                     Grob **common)
255 {
256   Grob* stem = unsmob_grob (col->get_property ("stem"));
257
258   Encompass_info ei;
259
260   Direction dir = get_grob_direction (me);
261
262   if (!stem)
263     {
264       programming_error ("No stem for note column?");
265       ei.x_ = col->relative_coordinate (common[X_AXIS], X_AXIS);
266       ei.head_ = ei.stem_ = col->extent (common[Y_AXIS],
267                                          Y_AXIS)[get_grob_direction (me)];
268       return ei;
269     }
270   Direction stem_dir = get_grob_direction (stem);
271
272   if (Grob *head = Note_column::first_head (col))
273     ei.x_ = head->extent (common[X_AXIS], X_AXIS).center ();
274   else
275     ei.x_ = col->extent (common[X_AXIS], X_AXIS).center ();
276
277   Grob * h = Stem::extremal_heads (stem)[Direction (dir)];
278   if (!h)
279     {
280       ei.head_ = ei.stem_ = col->extent (common[Y_AXIS], Y_AXIS)[dir];
281       return ei;
282     }
283
284   ei.head_ = h->extent (common[Y_AXIS], Y_AXIS)[dir];
285
286   if ((stem_dir == dir)
287       && !stem->extent (stem, Y_AXIS).is_empty ())
288     {
289       ei.stem_ = stem->extent (common[Y_AXIS], Y_AXIS)[dir];
290       if (Grob * b = Stem::get_beam (stem))
291         ei.stem_ += stem_dir * 0.5 * Beam::get_thickness (b);
292       ei.x_  = stem->extent (common[X_AXIS], X_AXIS).center ();
293     }
294   else
295     ei.stem_ = ei.head_;
296
297   return ei;
298 }
299
300
301 Direction
302 get_default_dir (Grob*me)
303 {
304   Link_array<Grob> encompasses
305     = Pointer_group_interface__extract_grobs (me, (Grob*) 0, "note-columns");
306
307   Direction d = DOWN;
308   for (int i= 0; i < encompasses.size (); i ++)
309     {
310       if (Note_column::dir (encompasses[i]) < 0)
311         {
312           d = UP;
313           break;
314         }
315     }
316   return d;
317 }
318
319 MAKE_SCHEME_CALLBACK (New_slur, after_line_breaking,1);
320 SCM
321 New_slur::after_line_breaking (SCM smob)
322 {
323   Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob));
324   if (!scm_ilength (me->get_property ("note-columns")))
325     {
326       me->suicide ();
327       return SCM_UNSPECIFIED;
328     }
329
330   if (!get_grob_direction (me))
331     set_grob_direction (me, get_default_dir (me));
332
333   set_end_points (me);
334
335   return SCM_UNSPECIFIED;
336 }
337
338
339 Bezier
340 get_bezier (Grob *me, Drul_array<Offset> extremes,
341             Real r_0,
342             Real h_inf)
343 {
344   Array<Offset> encompasses;
345   encompasses.push (extremes[LEFT]);
346   encompasses.push (extremes[RIGHT]);
347
348   Slur_bezier_bow bb (encompasses,
349                       get_grob_direction (me), h_inf, r_0);
350
351   return bb.get_bezier ();
352 }
353
354
355 Drul_array<Bound_info>
356 get_bound_info (Spanner* me,
357                 Grob **common)
358 {
359   Drul_array<Bound_info> extremes;
360   Direction d = LEFT;
361   Direction dir = get_grob_direction (me);
362
363   do {
364     extremes[d].bound_ = me->get_bound (d);
365
366     if (Note_column::has_interface (extremes[d].bound_))
367       {
368         extremes[d].note_column_ = extremes[d].bound_;
369         extremes[d].stem_ = Note_column::get_stem (extremes[d].note_column_);
370         extremes[d].stem_dir_ = get_grob_direction (extremes[d].stem_);
371         extremes[d].stem_extent_[X_AXIS] = extremes[d].stem_->extent (common[X_AXIS], X_AXIS);
372         extremes[d].stem_extent_[Y_AXIS] = extremes[d].stem_->extent (common[Y_AXIS], Y_AXIS);
373         extremes[d].slur_head_ = Stem::extremal_heads (extremes[d].stem_)[dir];
374         extremes[d].slur_head_extent_ = extremes[d].slur_head_->extent (common[X_AXIS], X_AXIS);
375         extremes[d].staff_ = Staff_symbol_referencer::get_staff_symbol (extremes[d].slur_head_);
376         extremes[d].staff_space_ = Staff_symbol_referencer::staff_space (extremes[d].slur_head_);
377       }
378     else
379       {
380         extremes[d].neighbor_y_ = broken_trend_y (me, common, d);
381       }
382   } while (flip (&d) != LEFT);
383
384   return extremes;
385 }
386
387 void
388 set_end_points (Grob *me)
389 {
390   Link_array<Grob> columns
391     = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
392   Slur_score_parameters params;
393   if (columns.is_empty ())
394     {
395       me->suicide ();
396       return;
397     }
398
399   SCM eltlist = me->get_property ("note-columns");
400
401   
402   SCM extra_list = me->get_property ("encompass-objects");
403   Spanner *sp = dynamic_cast<Spanner*> (me);
404
405   Grob *common[] = {0,0};
406   for (int i = X_AXIS; i < NO_AXES; i++)
407     {
408       Axis a = (Axis)i;
409       common[a] = common_refpoint_of_list (eltlist, me, a);
410       common[a] = common_refpoint_of_list (extra_list, common[a], a);
411     }
412   
413   common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (RIGHT),X_AXIS);
414   common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (LEFT),
415                                                     X_AXIS);
416   
417   Drul_array<Bound_info> extremes = get_bound_info (sp, common);
418   Drul_array<Offset> base_attachment
419     = get_base_attachments (sp, common, extremes);
420   Drul_array<Real> end_ys
421     = get_y_attachment_range (sp, common, extremes, base_attachment);
422   Array<Slur_score> scores = enumerate_attachments (me, common, &params,
423                                                     extremes, base_attachment,
424                                                     end_ys);
425
426   generate_curves (me, common, extremes, base_attachment, &scores);
427   score_edges (me, common, &params,extremes, base_attachment, &scores);
428   score_slopes (me, common, &params,extremes, base_attachment, &scores);
429   score_encompass (me, common, &params,extremes, base_attachment, &scores);
430   score_extra_encompass (me, common, &params,extremes, base_attachment, &scores);
431   
432   Real opt = 1e6;
433   int opt_idx = 0;
434   // why backwards?
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 ()
447                   ->lookup_variable (ly_symbol2scm ("debug-slur-quanting")))
448       && ly_c_pair_p (inspect_quants))
449     {
450       Drul_array<Real> ins = ly_scm2interval (inspect_quants);
451       int i = 0;
452
453       Real mindist = 1e6;
454       for (; i < scores.size (); i ++)
455         {
456           Real d =fabs (scores[i].attachment_[LEFT][Y_AXIS] - ins[LEFT])
457             + fabs (scores[i].attachment_[RIGHT][Y_AXIS] - ins[RIGHT]);
458           if (d < mindist)
459             {
460               opt_idx = i;
461               mindist= d;
462             }
463         }
464       if (mindist > 1e5)
465         programming_error ("Could not find quant.");
466     }
467   scores[opt_idx].score_card_ += to_string ("i%d", opt_idx);
468
469   // debug quanting
470   me->set_property ("quant-score",
471                     scm_makfrom0str (scores[opt_idx].score_card_.to_str0 ()));
472 #endif
473   
474
475   Bezier b = scores[opt_idx].curve_;
476   SCM controls = SCM_EOL;
477   for (int i = 4; i--;)
478     {
479       Offset o = b.control_[i] -
480         Offset (me->relative_coordinate (common[X_AXIS], X_AXIS),
481                 me->relative_coordinate (common[Y_AXIS], Y_AXIS));
482
483       controls = scm_cons (ly_offset2scm (o), controls);
484     }
485
486   me->set_property ("control-points", controls);
487 }
488
489
490 Drul_array<Real>
491 get_y_attachment_range (Spanner*me,
492                         Grob **common,
493                         Drul_array<Bound_info> extremes,
494                         Drul_array<Offset> base_attachment)
495 {
496   Drul_array<Real> end_ys;
497   Direction dir = get_grob_direction (me);
498   Direction d = LEFT;
499   do
500     {
501       if (extremes[d].note_column_)
502         {
503           end_ys[d] = dir
504             * ((dir * (base_attachment[d][Y_AXIS] + 4.0 * dir))
505                >? (dir * (dir + extremes[d].note_column_->extent(common[Y_AXIS],
506                                                                  Y_AXIS)[dir]))
507                >? (dir * base_attachment[-d][Y_AXIS]));
508         }
509       else
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 get_base_attachments (Spanner *me,
519                       Grob **common, Drul_array<Bound_info> extremes)
520 {
521   Link_array<Grob> columns
522     = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
523   Drul_array<Offset> base_attachment;
524   Slur_score_parameters params;
525   Real staff_space = Staff_symbol_referencer::staff_space ((Grob*)me);
526   Direction dir = get_grob_direction (me);
527   Direction d = LEFT;
528   do
529     {
530       Grob *stem = extremes[d].stem_;
531       Grob *head = extremes[d].slur_head_;
532
533       Real x, y;
534       if (!extremes[d].note_column_)
535         {
536           y = extremes[d].neighbor_y_;
537           if (d== RIGHT)
538             x = extremes[d].bound_->extent (common[X_AXIS], X_AXIS)[d];
539           else
540             x = me->get_broken_left_end_align ();
541         }
542       else
543         {
544           if (stem
545               && extremes[d].stem_dir_ == dir
546               && Stem::get_beaming (stem, -d)
547               && columns.size () > 2
548               )
549             y = extremes[d].stem_extent_[Y_AXIS][dir];
550           else if (head)
551             y = head->extent (common[Y_AXIS], Y_AXIS)[dir];
552           y += dir * 0.5 * staff_space;
553
554           if (stem)
555             {
556               SCM scripts = stem->get_property ((dir == UP
557                                                  ? "script-up"
558                                                  : "script-down"));
559
560               /* FIXME: number of scripts, height, priority?
561                  articulation scripts should be inside slur, other,
562                  such as dynamics, pedals, fingering, should be
563                  outside.  */
564               if (!SCM_NULLP (scripts))
565                 y += dir * staff_space;
566             }
567
568           Real pos
569             = (y - extremes[d].staff_->relative_coordinate (common[Y_AXIS],
570                                                             Y_AXIS))
571             * 2.0 / Staff_symbol::staff_space (extremes[d].staff_);
572
573           /* start off staffline. */
574           if (fabs (pos - round (pos)) < 0.2
575               && Staff_symbol_referencer::on_staffline (head, (int) rint (pos))
576               && Staff_symbol_referencer::line_count (head) - 1 >= rint (pos)
577               )
578             // TODO: calc from slur thick & line thick, parameter.          
579             y += 1.5 * staff_space * dir / 10;
580
581           Grob * fh = Note_column::first_head (extremes[d].note_column_);
582           x = fh->extent (common[X_AXIS], X_AXIS).linear_combination (CENTER);
583         }
584       base_attachment[d] = Offset (x, y);
585
586     } while (flip (&d) != LEFT);
587
588   return base_attachment;
589 }
590
591 void
592 generate_curves (Grob *me, Grob **common,
593                  Drul_array<Bound_info> extremes,
594                  Drul_array<Offset>,
595                  Array<Slur_score> *scores)
596 {
597   (void) common;
598   (void) extremes;
599   Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
600   Real r_0 = robust_scm2double (me->get_property ("ratio"), 0.33);
601   Real h_inf = staff_space * ly_scm2double (me->get_property ("height-limit"));
602   for (int i = 0; i < scores->size(); i++)
603     {
604       Bezier bez= get_bezier (me, (*scores)[i].attachment_, r_0, h_inf);
605       bez = avoid_staff_line (me, common, extremes, bez);
606       (*scores)[i].attachment_[LEFT] = bez.control_[0];
607       (*scores)[i].attachment_[RIGHT] = bez.control_[3];
608       (*scores)[i].curve_ = bez;
609     }
610 }
611
612 Bezier
613 avoid_staff_line (Grob *me, Grob **common,
614                   Drul_array<Bound_info> extremes,
615                   Bezier bez)
616 {
617   Offset horiz (1,0);
618   Array<Real> ts  = bez.solve_derivative (horiz);
619   Real lt =  me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
620   Real thick = robust_scm2double (me->get_property ("thickness"), 1.0) *  lt;
621
622   /* TODO: handle case of broken slur.  */
623   if (!ts.is_empty ()
624       && (extremes[LEFT].staff_ == extremes[RIGHT].staff_)
625       && extremes[LEFT].staff_ && extremes[RIGHT].staff_)
626     {
627       Real y = bez.curve_point (ts[0])[Y_AXIS];
628
629       Grob *staff = extremes[LEFT].staff_;
630
631       Real staff_space = extremes[LEFT].staff_space_;
632       Real p = 2 * (y - staff->relative_coordinate (common[Y_AXIS], Y_AXIS))
633         / staff_space;
634
635       Real distance = fabs (round (p) - p);     //  in halfspaces
636       if (distance < 4 * thick
637           && (int) fabs (round (p))
638           <= 2 * Staff_symbol_referencer::staff_radius (staff) + 0.1
639           && (int (fabs (round (p))) % 2
640               != Staff_symbol_referencer::line_count (staff) % 2))
641         {
642           Direction resolution_dir =  
643             (distance ?  get_grob_direction (me) : Direction (sign (p - round(p))));
644
645           // TODO: parameter
646           Real newp = round (p) + resolution_dir
647             * 5 * thick;
648           
649           Real dy = (newp - p) * staff_space / 2.0;
650 #if 0
651           bez.translate (Offset (0, dy));
652 #else
653           bez.control_[1][Y_AXIS] += dy; 
654           bez.control_[2][Y_AXIS] += dy; 
655           
656 #endif
657         }
658     }
659   return bez;
660 }
661
662 Array<Slur_score>
663 enumerate_attachments (Grob *me, Grob **,
664                        Slur_score_parameters *score_param,
665                        Drul_array<Bound_info> extremes,
666                        Drul_array<Offset> base_attachment,
667                        Drul_array<Real> end_ys)
668 {
669   /*ugh.   */
670   Array<Slur_score> scores;
671
672   Direction dir = get_grob_direction (me);
673   Real staff_space = Staff_symbol_referencer::staff_space ((Grob *)me);
674
675   Drul_array<Offset> os;
676   os[LEFT] = base_attachment[LEFT];
677   Real minimum_length = staff_space
678     * robust_scm2double (me->get_property ("minimum-length"), 2.0);
679
680   for (int i = 0; dir * os[LEFT][Y_AXIS] <= dir * end_ys[LEFT]; i++)
681     {
682       os[RIGHT] = base_attachment[RIGHT];
683       for (int j = 0; dir * os[RIGHT][Y_AXIS] <= dir * end_ys[RIGHT]; j++)
684         {
685           Slur_score s;
686           Direction d = LEFT;
687           Drul_array<bool> attach_to_stem (false, false);
688           do {  
689             os[d][X_AXIS] = base_attachment[d][X_AXIS];
690             if (extremes[d].stem_
691                 && !Stem::is_invisible (extremes[d].stem_)
692                 && extremes[d].stem_dir_ == dir
693                 && dir == -d)
694               {
695                 if (extremes[d].stem_extent_[Y_AXIS].contains (os[d][Y_AXIS]))
696                   {
697                     os[d][X_AXIS] =  extremes[d].slur_head_extent_[-d]
698                       - d * 0.3;
699                     attach_to_stem[d] = true;
700                   }
701                 else if (dir *extremes[d].stem_extent_[Y_AXIS][dir] < dir * os[d][Y_AXIS])
702                   {
703                     os[d][X_AXIS] = extremes[d].stem_extent_[X_AXIS].center();
704                   }
705               }
706           } while (flip (&d) != LEFT);
707
708           Offset dz;      
709           dz = os[RIGHT] - os[LEFT];
710           if (dz[X_AXIS] < minimum_length
711               || fabs (dz[Y_AXIS] / dz[X_AXIS]) > score_param->MAX_SLOPE
712               )
713             {
714               do
715                 {
716                   if (extremes[d].slur_head_)
717                     {
718                       os[d][X_AXIS] = extremes[d].slur_head_extent_.center ();
719                       attach_to_stem[d] = false;
720                     }
721                 }
722               while (flip (&d) != LEFT);
723             }
724
725           dz = os[RIGHT] - os[LEFT];
726           do {
727             if (extremes[d].slur_head_
728                 && !attach_to_stem[d])
729               {
730                 /*
731                   horizontally move tilted slurs a little. Move more
732                   for bigger tilts.
733
734                   TODO: parameter
735                 */
736                 os[d][X_AXIS] -=
737                   dir * extremes[d].slur_head_extent_.length () * sin (dz.arg  ()) / 3;
738               }
739           } while (flip (&d) != LEFT);
740           
741           s.attachment_ = os;
742           scores.push (s);
743
744           os[RIGHT][Y_AXIS] += dir * staff_space / 2;
745         }
746
747       os[LEFT][Y_AXIS] += dir * staff_space / 2;
748     }
749
750   return scores;
751 }
752
753 void
754 score_encompass (Grob *me, Grob *common[],
755                  Slur_score_parameters *score_param,
756                  Drul_array<Bound_info> ,
757                  Drul_array<Offset> ,
758                  Array<Slur_score> * scores)
759 {
760   Link_array<Grob> encompasses
761     = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
762   Direction dir = get_grob_direction (me);
763
764   Array<Encompass_info> infos;
765
766   for (int i = 0; i < encompasses.size(); i++)
767     infos.push (get_encompass_info (me, encompasses[i], common));
768
769   for (int i = 0; i < scores->size (); i++)
770     {
771       Bezier const &bez (scores->elem (i).curve_);
772       Real demerit = 0.0;
773       for (int j = 0; j < infos.size(); j++)
774         {
775           Real x = infos[j].x_;
776
777           bool l_edge = j==0;
778           bool r_edge = j==infos.size()-1;
779           bool edge =  l_edge || r_edge;
780   
781           if (!(x < scores->elem (i).attachment_[RIGHT][X_AXIS]
782                 && x > scores->elem (i).attachment_[LEFT][X_AXIS]))
783             continue;
784         
785           Real y = bez.get_other_coordinate (X_AXIS, x);
786           if (!edge)
787             {
788               Real head_dy = (y - infos[j].head_);
789               if (dir * head_dy < 0)
790                 demerit += score_param->HEAD_ENCOMPASS_PENALTY;
791               else
792                 {
793                   Real hd = (head_dy)
794                     ? (1 / fabs (head_dy) - 1 / score_param->FREE_HEAD_DISTANCE)
795                     : score_param->HEAD_ENCOMPASS_PENALTY;
796                   hd = (hd >? 0)<? score_param->HEAD_ENCOMPASS_PENALTY;
797
798                   demerit += hd;        
799                 }
800             }     
801
802           if (dir * (y - infos[j].stem_) < 0)
803             {
804               Real stem_dem =score_param->STEM_ENCOMPASS_PENALTY ; 
805               if ((l_edge && dir == UP)
806                   || (r_edge && dir == DOWN))
807                 {
808                   stem_dem /= 5;
809                 }
810
811               demerit +=  stem_dem;
812             }
813           else if (!edge)
814             {
815               Interval ext;
816               ext.add_point (infos[j].stem_);
817               ext.add_point (infos[j].head_);
818
819               demerit += - score_param->CLOSENESS_FACTOR * (dir * (y - (ext[dir] + dir * score_param->FREE_HEAD_DISTANCE)) <? 0) /
820                 infos.size ();
821             }
822         }
823
824 #if DEBUG_SLUR_QUANTING
825       (*scores)[i].score_card_ += to_string ("C%.2f", demerit);
826 #endif
827
828       (*scores)[i].score_ += demerit;
829     }
830 }
831
832
833
834 void
835 score_extra_encompass (Grob *me, Grob *common[],
836                  Slur_score_parameters *score_param,
837                  Drul_array<Bound_info> ,
838                  Drul_array<Offset> ,
839                  Array<Slur_score> * scores)
840 {
841   Link_array<Grob> encompasses
842     = Pointer_group_interface__extract_grobs (me, (Grob *)0, "encompass-objects");
843   Direction dir = get_grob_direction (me);
844  Real lt =  me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
845   Real thick = robust_scm2double (me->get_property ("thickness"), 1.0) *  lt;
846
847
848   Array<Real> xs;
849   Array<Interval> yexts;
850   for (int i = 0; i < encompasses.size(); i++)
851     {
852       Grob *g = encompasses [i];
853       Interval xe = g->extent (common[X_AXIS], X_AXIS);
854       Interval ye = g->extent (common[Y_AXIS], Y_AXIS);
855
856       Real xp = 0.0;
857       
858       if (Accidental_interface::has_interface (g))
859         {
860           /*
861             C&P accidental.cc
862            */
863           bool parens = false;
864           if (to_boolean (g->get_property ("cautionary")))
865             {
866               SCM cstyle = g->get_property ("cautionary-style");
867               parens = ly_c_equal_p (cstyle, ly_symbol2scm ("parentheses"));
868
869             }
870           
871           SCM accs = g->get_property ("accidentals");
872           SCM scm_style = g->get_property ("style");
873           if (!ly_c_symbol_p (scm_style)
874               && !parens
875               && scm_ilength (accs) == 1)
876             {
877               switch (ly_scm2int (ly_car (accs))) {
878               case FLAT:
879               case DOUBLE_FLAT:
880                 xp = LEFT;
881               case SHARP:
882                 xp = 0.5* dir;
883               case NATURAL:
884                 xp = -dir;
885               }
886             }
887                   
888         }
889       xs.push (xe.linear_combination (xp));
890       ye.widen (.5 * thick);
891       yexts.push (ye);
892     }
893   
894   for (int i = 0; i < scores->size (); i++)
895     {
896       Bezier const &bez (scores->elem (i).curve_);
897       Real demerit = 0.0;
898       for (int j = 0; j < xs.size(); j++)
899         {
900           Real x = xs[j];
901           if (!(x < scores->elem (i).attachment_[RIGHT][X_AXIS]
902                 && x > scores->elem (i).attachment_[LEFT][X_AXIS]))
903             continue;
904         
905           Real y = bez.get_other_coordinate (X_AXIS, x);
906           
907           if (yexts[j].contains (y))
908             {
909               if (Accidental_interface::has_interface (encompasses[j]))
910                 demerit += score_param->ACCIDENTAL_COLLISION;
911               else 
912                 demerit += score_param->EXTRA_OBJECT_COLLISION;
913             }
914         }
915
916 #if DEBUG_SLUR_QUANTING
917       (*scores)[i].score_card_ += to_string ("X%.2f", demerit);
918 #endif
919       (*scores)[i].score_ += demerit;
920     }
921 }
922
923
924 void
925 score_edges (Grob *me, Grob **,
926              Slur_score_parameters * score_param,
927              Drul_array<Bound_info> extremes,
928              Drul_array<Offset> base_attach,
929              Array<Slur_score> *scores)
930 {
931   Direction dir = get_grob_direction (me);
932
933   for (int i = 0; i < scores->size (); i++)
934     {
935
936       Direction d = LEFT;
937       do {
938         Real y = scores->elem (i).attachment_[d][Y_AXIS];
939         Real dy = fabs (y - base_attach[d][Y_AXIS]);
940         
941         Real factor = score_param->EDGE_ATTRACTION_FACTOR;
942         Real demerit = factor * dy;
943         if (extremes[d].stem_
944             && extremes[d].stem_dir_ == dir
945             && !Stem::get_beaming (extremes[d].stem_, -d)
946             )
947           demerit /= 5;
948         
949         (*scores)[i].score_ += demerit;
950 #if DEBUG_SLUR_QUANTING
951         (*scores)[i].score_card_ += to_string ("E%.2f", demerit);
952 #endif
953       } while (flip (&d) != LEFT);
954     }
955 }
956
957 void
958 score_slopes (Grob *me, Grob *common[],
959               Slur_score_parameters*score_param,
960               Drul_array<Bound_info> extremes,
961               Drul_array<Offset> ,
962               Array<Slur_score> * scores)
963 {
964   Drul_array<Real> ys;
965
966   Direction d = LEFT;
967   do {
968     if (extremes[d].slur_head_)
969       ys[d] = extremes[d].slur_head_ ->relative_coordinate (common[Y_AXIS],
970                                                             Y_AXIS);
971     else
972       ys[d] = extremes[d].neighbor_y_;
973   } while (flip (&d) != LEFT);
974
975   bool has_beams
976     = (extremes[LEFT].stem_ && Stem::get_beam (extremes[LEFT].stem_))
977     || (extremes[RIGHT].stem_ && Stem::get_beam (extremes[RIGHT].stem_));
978
979   Direction dir = get_grob_direction (me);
980   Real dy = ys[RIGHT] - ys[LEFT];
981   for (int i = 0; i < scores->size (); i++)
982     {
983       Offset slur_dz = (*scores)[i].attachment_[RIGHT]
984         -  (*scores)[i].attachment_[LEFT];
985       Real slur_dy = slur_dz[Y_AXIS]; 
986       Real demerit = 0.0;
987
988       demerit += ((fabs (slur_dy/slur_dz[X_AXIS])
989                    - score_param->MAX_SLOPE) >? 0)
990         * score_param->MAX_SLOPE_FACTOR;
991
992       /*
993         0.2: account for staffline offset.
994       */
995       Real max_dy = (fabs (dy) + 0.2);
996       if (has_beams)
997         max_dy += 1.0; 
998       
999       demerit += score_param->STEEPER_SLOPE_FACTOR * ((fabs (slur_dy) -max_dy) >? 0); 
1000
1001       
1002       demerit += ((fabs (slur_dy/slur_dz[X_AXIS]) - score_param->MAX_SLOPE)>?0)  * score_param->MAX_SLOPE_FACTOR;
1003       
1004       if (sign (dy) == 0 &&
1005           sign (slur_dy) != 0)
1006         demerit += score_param->NON_HORIZONTAL_PENALTY;
1007
1008       if (sign (dy)
1009           && sign (slur_dy)
1010           && sign (slur_dy) != sign (dy))
1011         demerit += has_beams
1012           ? score_param->SAME_SLOPE_PENALTY / 10
1013           : score_param->SAME_SLOPE_PENALTY;
1014
1015 #if DEBUG_SLUR_QUANTING
1016       (*scores)[i].score_card_ += to_string ("S%.2f",d);
1017 #endif
1018       (*scores)[i].score_ += demerit;
1019     }
1020 }
1021
1022