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