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