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