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