]> git.donarmstrong.com Git - lilypond.git/blob - lily/slur-quanting.cc
* lily/dynamic-engraver.cc (typeset_all):
[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 Direction
323 get_slur_dir (Grob *slur)
324 {
325   Direction d = get_grob_direction (slur);
326   if (d == CENTER)
327     d = get_default_dir (slur);
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   set_end_points (me);
346
347   return SCM_UNSPECIFIED;
348 }
349
350 Bezier
351 get_bezier (Grob *me, Drul_array<Offset> extremes, Real r_0, Real h_inf)
352 {
353   Array<Offset> encompasses;
354   encompasses.push (extremes[LEFT]);
355   encompasses.push (extremes[RIGHT]);
356
357   Slur_bezier_bow bb (encompasses,
358                       get_grob_direction (me), h_inf, r_0);
359
360   return bb.get_bezier ();
361 }
362
363 Drul_array<Bound_info>
364 get_bound_info (Spanner* me, Grob **common)
365 {
366   Drul_array<Bound_info> extremes;
367   Direction d = LEFT;
368   Direction dir = get_grob_direction (me);
369
370   do
371     {
372       extremes[d].bound_ = me->get_bound (d);
373       if (Note_column::has_interface (extremes[d].bound_))
374         {
375           extremes[d].note_column_ = extremes[d].bound_;
376           extremes[d].stem_ = Note_column::get_stem (extremes[d].note_column_);
377           extremes[d].stem_dir_ = get_grob_direction (extremes[d].stem_);
378           extremes[d].stem_extent_[X_AXIS]
379             = extremes[d].stem_->extent (common[X_AXIS], X_AXIS);
380           extremes[d].stem_extent_[Y_AXIS]
381             = extremes[d].stem_->extent (common[Y_AXIS], Y_AXIS);
382           extremes[d].slur_head_
383             = Stem::extremal_heads (extremes[d].stem_)[dir];
384           extremes[d].slur_head_extent_
385             = extremes[d].slur_head_->extent (common[X_AXIS], X_AXIS);
386           extremes[d].staff_ = Staff_symbol_referencer
387             ::get_staff_symbol (extremes[d].slur_head_);
388           extremes[d].staff_space_ = Staff_symbol_referencer
389             ::staff_space (extremes[d].slur_head_);
390         }
391       else
392         extremes[d].neighbor_y_ = broken_trend_y (me, common, d);
393     }
394   while (flip (&d) != LEFT);
395   return extremes;
396 }
397
398 void
399 set_end_points (Grob *me)
400 {
401   Link_array<Grob> columns
402     = Pointer_group_interface__extract_grobs (me, (Grob *) 0, "note-columns");
403   Slur_score_parameters params;
404   if (columns.is_empty ())
405     {
406       me->suicide ();
407       return;
408     }
409
410   SCM eltlist = me->get_property ("note-columns");
411   SCM extra_list = me->get_property ("encompass-objects");
412   Spanner *sp = dynamic_cast<Spanner*> (me);
413
414   Grob *common[] = {0, 0};
415   for (int i = X_AXIS; i < NO_AXES; i++)
416     {
417       Axis a = (Axis)i;
418       common[a] = common_refpoint_of_list (eltlist, me, a);
419       common[a] = common_refpoint_of_list (extra_list, common[a], a);
420     }
421
422   common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (RIGHT),
423                                                     X_AXIS);
424   common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (LEFT),
425                                                     X_AXIS);
426
427   Drul_array<Bound_info> extremes = get_bound_info (sp, common);
428   Drul_array<Offset> base_attachment
429     = get_base_attachments (sp, common, extremes);
430   Drul_array<Real> end_ys
431     = get_y_attachment_range (sp, common, extremes, base_attachment);
432   Array<Slur_score> scores = enumerate_attachments (me, common, &params,
433                                                     extremes, base_attachment,
434                                                     end_ys);
435
436   generate_curves (me, common, extremes, base_attachment, &scores);
437   score_edges (me, common, &params,extremes, base_attachment, &scores);
438   score_slopes (me, common, &params,extremes, base_attachment, &scores);
439   score_encompass (me, common, &params,extremes, base_attachment, &scores);
440   score_extra_encompass (me, common, &params,extremes, base_attachment,
441                          &scores);
442
443   Real opt = 1e6;
444   int opt_idx = 0;
445   // why backwards?
446   for (int i = scores.size (); i--;)
447     {
448       if (scores[i].score_ < opt)
449         {
450           opt = scores[i].score_;
451           opt_idx = i;
452         }
453     }
454
455 #if DEBUG_SLUR_QUANTING
456   SCM inspect_quants = me->get_property ("inspect-quants");
457   if (to_boolean (me->get_paper ()
458                   ->lookup_variable (ly_symbol2scm ("debug-slur-quanting")))
459       && ly_c_pair_p (inspect_quants))
460     {
461       Drul_array<Real> ins = ly_scm2interval (inspect_quants);
462       Real mindist = 1e6;
463       for (int i = 0; i < scores.size (); i ++)
464         {
465           Real d =fabs (scores[i].attachment_[LEFT][Y_AXIS] - ins[LEFT])
466             + fabs (scores[i].attachment_[RIGHT][Y_AXIS] - ins[RIGHT]);
467           if (d < mindist)
468             {
469               opt_idx = i;
470               mindist= d;
471             }
472         }
473       if (mindist > 1e5)
474         programming_error ("Could not find quant.");
475     }
476   scores[opt_idx].score_card_ += to_string ("i%d", opt_idx);
477
478   // debug quanting
479   me->set_property ("quant-score",
480                     scm_makfrom0str (scores[opt_idx].score_card_.to_str0 ()));
481 #endif
482
483   Bezier b = scores[opt_idx].curve_;
484   SCM controls = SCM_EOL;
485   for (int i = 4; i--;)
486     {
487       Offset o = b.control_[i]
488         - Offset (me->relative_coordinate (common[X_AXIS], X_AXIS),
489                   me->relative_coordinate (common[Y_AXIS], Y_AXIS));
490       controls = scm_cons (ly_offset2scm (o), controls);
491     }
492   me->set_property ("control-points", controls);
493 }
494
495
496 Drul_array<Real>
497 get_y_attachment_range (Spanner*me,
498                         Grob **common,
499                         Drul_array<Bound_info> extremes,
500                         Drul_array<Offset> base_attachment)
501 {
502   Drul_array<Real> end_ys;
503   Direction dir = get_grob_direction (me);
504   Direction d = LEFT;
505   do
506     {
507       if (extremes[d].note_column_)
508         {
509           end_ys[d] = dir
510             * ((dir * (base_attachment[d][Y_AXIS] + 4.0 * dir))
511                >? (dir * (dir + extremes[d].note_column_->extent(common[Y_AXIS],
512                                                                  Y_AXIS)[dir]))
513                >? (dir * base_attachment[-d][Y_AXIS]));
514         }
515       else
516         end_ys[d] = extremes[d].neighbor_y_ + 4.0 * dir;
517     }
518   while (flip (&d) != LEFT);
519
520   return end_ys;
521 }
522
523 bool
524 spanner_less (Spanner *s1, Spanner* s2)
525 {
526   Slice b1, b2;
527   Direction d  = LEFT;
528   do
529     {
530       b1[d] = s1->get_bound (d)->get_column ()->rank_;
531       b2[d] = s2->get_bound (d)->get_column ()->rank_;
532     } while (flip (&d) != LEFT);  
533
534   return b2[LEFT] <= b1[LEFT] && b2[RIGHT] >= b1[RIGHT]
535     && (b2[LEFT] != b1[LEFT] || b2[RIGHT] != b1[RIGHT]);
536 }
537
538
539 Drul_array<Offset>
540 get_base_attachments (Spanner *me,
541                       Grob **common, Drul_array<Bound_info> extremes)
542 {
543   Link_array<Grob> columns
544     = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
545   Drul_array<Offset> base_attachment;
546   Slur_score_parameters params;
547   Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
548   Direction dir = get_grob_direction (me);
549   Direction d = LEFT;
550   do
551     {
552       Grob *stem = extremes[d].stem_;
553       Grob *head = extremes[d].slur_head_;
554
555       Real x, y;
556       if (!extremes[d].note_column_)
557         {
558           y = extremes[d].neighbor_y_;
559           if (d== RIGHT)
560             x = extremes[d].bound_->extent (common[X_AXIS], X_AXIS)[d];
561           else
562             x = me->get_broken_left_end_align ();
563         }
564       else
565         {
566           bool same_beam =
567             (extremes[d].stem_ && extremes[-d].stem_
568              && Stem::get_beam (extremes[d].stem_) == Stem::get_beam (extremes[-d].stem_));
569
570           /*
571             fixme: X coord should also be set in this case.
572            */
573           if (stem
574               && extremes[d].stem_dir_ == dir
575               && Stem::get_beaming (stem, -d)
576               && (!spanner_less (me, Stem::get_beam (stem))
577                   || same_beam))
578             y = extremes[d].stem_extent_[Y_AXIS][dir];
579           else if (head)
580             y = head->extent (common[Y_AXIS], Y_AXIS)[dir];
581           y += dir * 0.5 * staff_space;
582
583           Real pos
584             = (y - extremes[d].staff_->relative_coordinate (common[Y_AXIS],
585                                                             Y_AXIS))
586             * 2.0 / Staff_symbol::staff_space (extremes[d].staff_);
587
588           /* start off staffline. */
589           if (fabs (pos - my_round (pos)) < 0.2
590               && Staff_symbol_referencer::on_staffline (head, (int) rint (pos))
591               && Staff_symbol_referencer::line_count (head) - 1 >= rint (pos)
592               )
593             // TODO: calc from slur thick & line thick, parameter.      
594             y += 1.5 * staff_space * dir / 10;
595
596           Grob * fh = Note_column::first_head (extremes[d].note_column_);
597           x = fh->extent (common[X_AXIS], X_AXIS).linear_combination (CENTER);
598         }
599       base_attachment[d] = Offset (x, y);
600
601     } while (flip (&d) != LEFT);
602
603   return base_attachment;
604 }
605
606 void
607 generate_curves (Grob *me, Grob **common,
608                  Drul_array<Bound_info> extremes,
609                  Drul_array<Offset>,
610                  Array<Slur_score> *scores)
611 {
612   (void) common;
613   (void) extremes;
614   Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
615   Real r_0 = robust_scm2double (me->get_property ("ratio"), 0.33);
616   Real h_inf = staff_space * ly_scm2double (me->get_property ("height-limit"));
617   for (int i = 0; i < scores->size(); i++)
618     {
619       Bezier bez= get_bezier (me, (*scores)[i].attachment_, r_0, h_inf);
620       bez = avoid_staff_line (me, common, extremes, bez);
621       (*scores)[i].attachment_[LEFT] = bez.control_[0];
622       (*scores)[i].attachment_[RIGHT] = bez.control_[3];
623       (*scores)[i].curve_ = bez;
624     }
625 }
626
627 Bezier
628 avoid_staff_line (Grob *me, Grob **common,
629                   Drul_array<Bound_info> extremes,
630                   Bezier bez)
631 {
632   Offset horiz (1,0);
633   Array<Real> ts = bez.solve_derivative (horiz);
634   Real lt = me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
635   Real thick = robust_scm2double (me->get_property ("thickness"), 1.0) *  lt;
636
637   /* TODO: handle case of broken slur.  */
638   if (!ts.is_empty ()
639       && (extremes[LEFT].staff_ == extremes[RIGHT].staff_)
640       && extremes[LEFT].staff_ && extremes[RIGHT].staff_)
641     {
642       Real y = bez.curve_point (ts[0])[Y_AXIS];
643
644       Grob *staff = extremes[LEFT].staff_;
645
646       Real staff_space = extremes[LEFT].staff_space_;
647       Real p = 2 * (y - staff->relative_coordinate (common[Y_AXIS], Y_AXIS))
648         / staff_space;
649
650       Real distance = fabs (my_round (p) - p);  //  in halfspaces
651       if (distance < 4 * thick
652           && (int) fabs (my_round (p))
653           <= 2 * Staff_symbol_referencer::staff_radius (staff) + 0.1
654           && (int (fabs (my_round (p))) % 2
655               != Staff_symbol_referencer::line_count (staff) % 2))
656         {
657           Direction resolution_dir =
658             (distance ?  get_grob_direction (me) : Direction (sign (p - my_round(p))));
659
660           // TODO: parameter
661           Real newp = my_round (p) + resolution_dir
662             * 5 * thick;
663         
664           Real dy = (newp - p) * staff_space / 2.0;
665 #if 0
666           bez.translate (Offset (0, dy));
667 #else
668           bez.control_[1][Y_AXIS] += dy;
669           bez.control_[2][Y_AXIS] += dy;
670         
671 #endif
672         }
673     }
674   return bez;
675 }
676
677 Array<Slur_score>
678 enumerate_attachments (Grob *me, Grob *common[],
679                        Slur_score_parameters *score_param,
680                        Drul_array<Bound_info> extremes,
681                        Drul_array<Offset> base_attachment,
682                        Drul_array<Real> end_ys)
683 {
684   (void) common;
685   /*ugh.   */
686   Array<Slur_score> scores;
687
688   Direction dir = get_grob_direction (me);
689   Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
690
691   Drul_array<Offset> os;
692   os[LEFT] = base_attachment[LEFT];
693   Real minimum_length = staff_space
694     * robust_scm2double (me->get_property ("minimum-length"), 2.0);
695
696   for (int i = 0; dir * os[LEFT][Y_AXIS] <= dir * end_ys[LEFT]; i++)
697     {
698       os[RIGHT] = base_attachment[RIGHT];
699       for (int j = 0; dir * os[RIGHT][Y_AXIS] <= dir * end_ys[RIGHT]; j++)
700         {
701           Slur_score s;
702           Direction d = LEFT;
703           Drul_array<bool> attach_to_stem (false, false);
704           do
705             {
706               os[d][X_AXIS] = base_attachment[d][X_AXIS];
707               if (extremes[d].stem_
708                   && !Stem::is_invisible (extremes[d].stem_)
709                   && extremes[d].stem_dir_ == dir)
710                 {
711                   if (dir == -d
712                       && extremes[d].stem_extent_[Y_AXIS].contains (os[d][Y_AXIS]))
713                     {
714                       os[d][X_AXIS] =  extremes[d].slur_head_extent_[-d]
715                         - d * 0.3;
716                       attach_to_stem[d] = true;
717                     }
718                   else if (dir *extremes[d].stem_extent_[Y_AXIS][dir]
719                              < dir * os[d][Y_AXIS]
720                            && !extremes[d].stem_extent_[X_AXIS].is_empty()
721                            )
722                     
723                     os[d][X_AXIS] = extremes[d].stem_extent_[X_AXIS].center();
724                 }
725             }
726           while (flip (&d) != LEFT);
727
728           Offset dz;    
729           dz = os[RIGHT] - os[LEFT];
730           if (dz[X_AXIS] < minimum_length
731               || fabs (dz[Y_AXIS] / dz[X_AXIS]) > score_param->MAX_SLOPE
732               )
733             {
734               do
735                 {
736                   if (extremes[d].slur_head_)
737                     {
738                       os[d][X_AXIS] = extremes[d].slur_head_extent_.center ();
739                       attach_to_stem[d] = false;
740                     }
741                 }
742               while (flip (&d) != LEFT);
743             }
744
745           dz = os[RIGHT] - os[LEFT];
746           do
747             {
748               if (extremes[d].slur_head_
749                   && !attach_to_stem[d])
750                 {
751                   /* Horizontally move tilted slurs a little.  Move
752                      more for bigger tilts.
753                     
754                      TODO: parameter */
755                   os[d][X_AXIS]
756                     -= dir * extremes[d].slur_head_extent_.length ()
757                     * sin (dz.arg  ()) / 3;
758                 }
759             }
760           while (flip (&d) != LEFT);
761           
762           s.attachment_ = os;
763           scores.push (s);
764           
765           os[RIGHT][Y_AXIS] += dir * staff_space / 2;
766         }
767       
768       os[LEFT][Y_AXIS] += dir * staff_space / 2;
769     }
770   return scores;
771 }
772
773 void
774 score_encompass (Grob *me, Grob *common[],
775                  Slur_score_parameters *score_param,
776                  Drul_array<Bound_info> extremes,
777                  Drul_array<Offset> base_attach,
778                  Array<Slur_score> *scores)
779 {
780   (void) extremes;
781   (void) base_attach;
782
783   Link_array<Grob> encompasses
784     = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
785   Direction dir = get_grob_direction (me);
786
787   Array<Encompass_info> infos;
788
789   for (int i = 0; i < encompasses.size(); i++)
790     infos.push (get_encompass_info (me, encompasses[i], common));
791
792   for (int i = 0; i < scores->size (); i++)
793     {
794       Bezier const &bez (scores->elem (i).curve_);
795       Real demerit = 0.0;
796       for (int j = 0; j < infos.size(); j++)
797         {
798           Real x = infos[j].x_;
799
800           bool l_edge = j==0;
801           bool r_edge = j==infos.size()-1;
802           bool edge =  l_edge || r_edge;
803
804           if (!(x < scores->elem (i).attachment_[RIGHT][X_AXIS]
805                 && x > scores->elem (i).attachment_[LEFT][X_AXIS]))
806             continue;
807         
808           Real y = bez.get_other_coordinate (X_AXIS, x);
809           if (!edge)
810             {
811               Real head_dy = (y - infos[j].head_);
812               if (dir * head_dy < 0)
813                 demerit += score_param->HEAD_ENCOMPASS_PENALTY;
814               else
815                 {
816                   Real hd = (head_dy)
817                     ? (1 / fabs (head_dy) - 1 / score_param->FREE_HEAD_DISTANCE)
818                     : score_param->HEAD_ENCOMPASS_PENALTY;
819                   hd = (hd >? 0)<? score_param->HEAD_ENCOMPASS_PENALTY;
820
821                   demerit += hd;        
822                 }
823             }   
824
825           if (dir * (y - infos[j].stem_) < 0)
826             {
827               Real stem_dem =score_param->STEM_ENCOMPASS_PENALTY ;
828               if ((l_edge && dir == UP)
829                   || (r_edge && dir == DOWN))
830                 stem_dem /= 5;
831
832               demerit +=  stem_dem;
833             }
834           else if (!edge)
835             {
836               Interval ext;
837               ext.add_point (infos[j].stem_);
838               ext.add_point (infos[j].head_);
839
840               // ?
841               demerit += -score_param->CLOSENESS_FACTOR
842                 * (dir
843                    * (y - (ext[dir] + dir * score_param->FREE_HEAD_DISTANCE))
844                    <? 0)
845                 / infos.size ();
846             }
847         }
848
849 #if DEBUG_SLUR_QUANTING
850       (*scores)[i].score_card_ += to_string ("C%.2f", demerit);
851 #endif
852
853       (*scores)[i].score_ += demerit;
854     }
855 }
856
857 void
858 score_extra_encompass (Grob *me, Grob *common[],
859                        Slur_score_parameters *score_param,
860                        Drul_array<Bound_info> extremes,
861                        Drul_array<Offset> base_attach,
862                        Array<Slur_score> *scores)
863 {
864   (void) base_attach;
865   (void) extremes;
866
867   Link_array<Grob> encompasses
868     = Pointer_group_interface__extract_grobs (me, (Grob *)0,
869                                               "encompass-objects");
870   Direction dir = get_grob_direction (me);
871   Real lt =  me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
872   Real thick = robust_scm2double (me->get_property ("thickness"), 1.0) * lt;
873
874   Array<Real> xidxs;
875   Array<Interval> yexts;
876   Array<Interval> xexts;
877   for (int i = 0; i < encompasses.size (); i++)
878     {
879       Grob *g = encompasses [i];
880       Interval xe = g->extent (common[X_AXIS], X_AXIS);
881       Interval ye = g->extent (common[Y_AXIS], Y_AXIS);
882
883       Real xp = 0.0;
884
885       if (Accidental_interface::has_interface (g))
886         {
887           /* Begin copy accidental.cc */
888           bool parens = false;
889           if (to_boolean (g->get_property ("cautionary")))
890             {
891               SCM cstyle = g->get_property ("cautionary-style");
892               parens = ly_c_equal_p (cstyle, ly_symbol2scm ("parentheses"));
893             }
894         
895           SCM accs = g->get_property ("accidentals");
896           SCM scm_style = g->get_property ("style");
897           if (!ly_c_symbol_p (scm_style)
898               && !parens
899               && scm_ilength (accs) == 1)
900             {
901               /* End copy accidental.cc */
902               switch (ly_scm2int (ly_car (accs)))
903                 {
904                 case FLAT:
905                 case DOUBLE_FLAT:
906                   xp = LEFT;
907                   break ;
908                 case SHARP:
909                   xp = 0.5 * dir;
910                   break ;
911                 case NATURAL:
912                   xp = -dir;
913                   break; 
914                 }
915             }
916         }
917
918       xidxs.push (xp);
919       ye.widen (thick * 0.5);
920       yexts.push (ye);
921       xexts.push (xe);
922     }
923
924   for (int i = 0; i < scores->size (); i++)
925     {
926       Real demerit = 0.0;
927       for (int j = 0; j < xidxs.size(); j++)
928         {
929           Drul_array<Offset> at = scores->elem (i).attachment_;
930           Interval slur_wid (at[LEFT][X_AXIS], at[RIGHT][X_AXIS]);
931
932           /*
933             to prevent numerical inaccuracies in
934             Bezier::get_other_coordinate().
935            */
936           slur_wid.widen (- 0.5 * thick);
937           Real x = xexts[j].linear_combination (xidxs[j]);
938           Real y = 0.0;
939           if (!slur_wid.contains (x))
940             {   
941               Direction contains_dir = CENTER;
942               Direction d = LEFT;
943               do
944                 {
945                   if (xexts[j].contains (at[d][X_AXIS]))
946                     contains_dir = d; 
947                 }
948               while (flip (&d) != LEFT);
949
950               if (!contains_dir)
951                 continue;
952               else
953                 y = at[contains_dir][Y_AXIS];
954             }
955           else
956             {
957               y = scores->elem (i).curve_.get_other_coordinate (X_AXIS, x);
958             }
959           
960           if (yexts[j].contains (y))
961             {
962               if (Accidental_interface::has_interface (encompasses[j]))
963                 demerit += score_param->ACCIDENTAL_COLLISION;
964               else
965                 demerit += score_param->EXTRA_OBJECT_COLLISION;
966             }
967         }
968 #if DEBUG_SLUR_QUANTING
969       (*scores)[i].score_card_ += to_string ("X%.2f", demerit);
970 #endif
971       (*scores)[i].score_ += demerit;
972     }
973 }
974
975 void
976 score_edges (Grob *me, Grob *common[],
977              Slur_score_parameters * score_param,
978              Drul_array<Bound_info> extremes,
979              Drul_array<Offset> base_attach,
980              Array<Slur_score> *scores)
981 {
982   (void) common;
983   Direction dir = get_grob_direction (me);
984
985   for (int i = 0; i < scores->size (); i++)
986     {
987       Direction d = LEFT;
988       do
989         {
990           Real y = scores->elem (i).attachment_[d][Y_AXIS];
991           Real dy = fabs (y - base_attach[d][Y_AXIS]);
992         
993           Real factor = score_param->EDGE_ATTRACTION_FACTOR;
994           Real demerit = factor * dy;
995           if (extremes[d].stem_
996               && extremes[d].stem_dir_ == dir
997               && !Stem::get_beaming (extremes[d].stem_, -d)
998               )
999             demerit /= 5;
1000         
1001           (*scores)[i].score_ += demerit;
1002 #if DEBUG_SLUR_QUANTING
1003           (*scores)[i].score_card_ += to_string ("E%.2f", demerit);
1004 #endif
1005         }
1006       while (flip (&d) != LEFT);
1007     }
1008 }
1009
1010 void
1011 score_slopes (Grob *me, Grob *common[],
1012               Slur_score_parameters *score_param,
1013               Drul_array<Bound_info> extremes,
1014               Drul_array<Offset> base_attach,
1015               Array<Slur_score> * scores)
1016 {
1017   (void) me;
1018   (void) base_attach;
1019
1020   Drul_array<Real> ys;
1021   Direction d = LEFT;
1022   do
1023     {
1024       if (extremes[d].slur_head_)
1025         ys[d] = extremes[d].slur_head_->relative_coordinate (common[Y_AXIS],
1026                                                               Y_AXIS);
1027       else
1028         ys[d] = extremes[d].neighbor_y_;
1029     }
1030   while (flip (&d) != LEFT);
1031
1032   bool has_beams
1033     = (extremes[LEFT].stem_ && Stem::get_beam (extremes[LEFT].stem_))
1034     || (extremes[RIGHT].stem_ && Stem::get_beam (extremes[RIGHT].stem_));
1035
1036   Real dy = ys[RIGHT] - ys[LEFT];
1037   for (int i = 0; i < scores->size (); i++)
1038     {
1039       Offset slur_dz = (*scores)[i].attachment_[RIGHT]
1040         - (*scores)[i].attachment_[LEFT];
1041       Real slur_dy = slur_dz[Y_AXIS];
1042       Real demerit = 0.0;
1043
1044       demerit += ((fabs (slur_dy / slur_dz[X_AXIS])
1045                    - score_param->MAX_SLOPE) >? 0)
1046         * score_param->MAX_SLOPE_FACTOR;
1047
1048       /* 0.2: account for staffline offset. */
1049       Real max_dy = (fabs (dy) + 0.2);
1050       if (has_beams)
1051         max_dy += 1.0;
1052
1053       demerit += score_param->STEEPER_SLOPE_FACTOR
1054         * ((fabs (slur_dy) -max_dy) >? 0);
1055
1056       demerit += ((fabs (slur_dy/slur_dz[X_AXIS])
1057                    - score_param->MAX_SLOPE) >? 0)
1058         * score_param->MAX_SLOPE_FACTOR;
1059
1060       if (sign (dy) == 0
1061           && sign (slur_dy) != 0)
1062         demerit += score_param->NON_HORIZONTAL_PENALTY;
1063
1064       if (sign (dy)
1065           && sign (slur_dy)
1066           && sign (slur_dy) != sign (dy))
1067         demerit += has_beams
1068           ? score_param->SAME_SLOPE_PENALTY / 10
1069           : score_param->SAME_SLOPE_PENALTY;
1070
1071 #if DEBUG_SLUR_QUANTING
1072       (*scores)[i].score_card_ += to_string ("S%.2f", d);
1073 #endif
1074       (*scores)[i].score_ += demerit;
1075     }
1076 }
1077
1078