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