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