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