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