]> git.donarmstrong.com Git - lilypond.git/blob - lily/slur-quanting.cc
* lily/slur-quanting.cc (score_extra_encompass): avoid other slurs
[lilypond.git] / lily / slur-quanting.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 "new-slur.hh"
19 #include "note-column.hh"
20 #include "output-def.hh"
21 #include "pitch.hh"
22 #include "slur-bezier-bow.hh"
23 #include "spanner.hh"
24 #include "staff-symbol-referencer.hh"
25 #include "staff-symbol.hh"
26 #include "stem.hh"
27 #include "warn.hh"
28 #include "paper-column.hh"
29
30
31 struct Slur_score
32 {
33   Drul_array<Offset> attachment_;
34   Real score_;
35   Bezier curve_;
36
37 #if DEBUG_SLUR_QUANTING
38   String score_card_;
39 #endif
40
41   Slur_score()
42   {
43     score_ = 0.0;
44   }
45 };
46
47 /*
48   TODO: put in details property.,
49
50   use lowercase.
51 */
52 struct Slur_score_parameters
53 {
54   int SLUR_REGION_SIZE;
55   Real HEAD_ENCOMPASS_PENALTY;
56   Real STEM_ENCOMPASS_PENALTY;
57   Real CLOSENESS_FACTOR;
58   Real EDGE_ATTRACTION_FACTOR;
59   Real SAME_SLOPE_PENALTY;
60   Real STEEPER_SLOPE_FACTOR;
61   Real NON_HORIZONTAL_PENALTY;
62   Real HEAD_STRICT_FREE_SPACE;
63   Real MAX_SLOPE;
64   Real MAX_SLOPE_FACTOR;
65   Real EXTRA_OBJECT_COLLISION;
66   Real ACCIDENTAL_COLLISION;
67   Real FREE_HEAD_DISTANCE;
68   Real EXTRA_ENCOMPASS_FREE_DISTANCE;
69   Slur_score_parameters ();
70 };
71
72 /*
73   TODO:
74
75   - curve around flag for y coordinate
76   - better scoring.
77   - short-cut: try a smaller region first.
78   - collisions with accidentals
79   - collisions with articulations (staccato, portato, sforzato, ...)
80   - handle non-visible stems better.
81 */
82 struct Encompass_info
83 {
84   Real x_;
85   Real stem_;
86   Real head_;
87   Encompass_info ()
88   {
89     x_ = 0.0;
90     stem_ = 0.0;
91     head_ = 0.0;
92   }
93 };
94
95 struct Bound_info
96 {
97   Box stem_extent_;
98   Direction stem_dir_;
99   Item *bound_;
100   Grob *note_column_;
101   Grob *slur_head_;
102   Grob *staff_;
103   Grob *stem_;
104   Interval slur_head_extent_;
105   Real neighbor_y_;
106   Real staff_space_;
107
108   Bound_info ()
109   {
110     stem_ = 0;
111     neighbor_y_ = 0;
112     staff_ = 0;
113     slur_head_ = 0;
114     stem_dir_ = CENTER;
115     note_column_ = 0;
116   }
117 };
118
119
120
121
122 static void
123 score_extra_encompass (Grob *me, Grob *common[],
124                        Slur_score_parameters *score_param,
125                        Drul_array<Bound_info> ,
126                        Drul_array<Offset> ,
127                        Array<Slur_score> * scores);
128 static void score_slopes (Grob *me, Grob *common[],
129                           Slur_score_parameters *score_param,
130                           Drul_array<Bound_info>,
131                           Drul_array<Offset> base_attach,
132                           Array<Slur_score> *scores);
133
134 static void score_edges (Grob *me, Grob *common[],
135                          Slur_score_parameters *score_param,
136                          Drul_array<Bound_info> extremes,
137                          Drul_array<Offset> base_attach,
138                          Array<Slur_score> *scores);
139 static void score_encompass (Grob *me, Grob *common[],
140                              Slur_score_parameters*,
141                              Drul_array<Bound_info>,
142                              Drul_array<Offset>, Array<Slur_score> *scores);
143 static Bezier avoid_staff_line (Grob *me, Grob **common,
144                                 Drul_array<Bound_info> extremes,
145                                 Bezier bez);
146
147 static Encompass_info get_encompass_info (Grob *me,
148                                           Grob *col,
149                                           Grob **common);
150 static Bezier get_bezier (Grob *me, Drul_array<Offset>, Real, Real);
151 static Direction get_default_dir (Grob *me);
152
153 static void set_end_points (Grob *);
154 static Real broken_trend_y (Grob *me, Grob **, Direction dir);
155 static Drul_array<Bound_info> get_bound_info (Spanner *me, Grob **common);
156
157 static void generate_curves (Grob *me,
158                              Grob *common[],
159                              Drul_array<Bound_info> extremes,
160                              Drul_array<Offset> base_attach,
161                              Array<Slur_score> *scores);
162 static Array<Slur_score> enumerate_attachments
163 (Grob *me, Grob *common[], Slur_score_parameters *score_param,
164  Drul_array<Bound_info> extremes,
165  Drul_array<Offset> base_attachment, Drul_array<Real> end_ys);
166 static Drul_array<Offset> get_base_attachments
167 (Spanner *sp, Grob **common, Drul_array<Bound_info> extremes);
168 static Drul_array<Real> get_y_attachment_range
169 (Spanner *sp, Grob **common, Drul_array<Bound_info> extremes,
170  Drul_array<Offset> base_attachment);
171
172 void
173 init_score_param (Slur_score_parameters *score_param)
174 {
175   score_param->SLUR_REGION_SIZE = 5;
176   score_param->HEAD_ENCOMPASS_PENALTY = 1000.0;
177   score_param->STEM_ENCOMPASS_PENALTY = 30.0;
178   score_param->CLOSENESS_FACTOR = 10;
179   score_param->EDGE_ATTRACTION_FACTOR = 4;
180   score_param->SAME_SLOPE_PENALTY = 20;
181   score_param->STEEPER_SLOPE_FACTOR = 50;
182   score_param->NON_HORIZONTAL_PENALTY = 15;
183   score_param->HEAD_STRICT_FREE_SPACE = 0.2;
184   score_param->MAX_SLOPE = 1.1;
185   score_param->MAX_SLOPE_FACTOR = 10;
186   score_param->FREE_HEAD_DISTANCE = 0.3;
187   score_param->EXTRA_OBJECT_COLLISION = 50;
188   score_param->ACCIDENTAL_COLLISION = 3;
189   score_param->EXTRA_ENCOMPASS_FREE_DISTANCE = 0.5;
190 }
191
192 Slur_score_parameters::Slur_score_parameters()
193 {
194   init_score_param (this);
195 }
196
197 /* HDIR indicates the direction for the slur.  */
198 Real
199 broken_trend_y (Grob *me, Grob **common, Direction hdir)
200 {
201   /* A broken slur should maintain the same vertical trend
202      the unbroken slur would have had.  */
203   Real by = 0.0;
204   if (Spanner *mother = dynamic_cast<Spanner*> (me->original_))
205     {
206       int k = broken_spanner_index (dynamic_cast<Spanner*> (me));
207       int j = k + hdir;
208       if (j < 0 || j >= mother->broken_intos_.size ())
209         return by;
210
211       Grob *neighbor = mother->broken_intos_[j];
212       if (hdir == RIGHT)
213         neighbor->set_property ("direction",
214                                 me->get_property ("direction"));
215
216       Spanner *common_mother
217         = dynamic_cast<Spanner*> (common[Y_AXIS]->original_);
218       int common_k
219         = broken_spanner_index (dynamic_cast<Spanner*> (common[Y_AXIS]));
220       int common_j = common_k + hdir;
221
222       if (common_j < 0 || common_j >= common_mother->broken_intos_.size())
223         return by;
224
225       Grob *common_next_system = common_mother->broken_intos_[common_j];
226       Link_array<Grob> neighbor_cols
227         = Pointer_group_interface__extract_grobs (neighbor, (Grob *)0,
228                                                   "note-columns");
229
230       Grob *neighbor_col
231         = (hdir == RIGHT) ? neighbor_cols[0] : neighbor_cols.top ();
232       Grob *neighbor_common
233         = common_next_system->common_refpoint (neighbor_col, Y_AXIS);
234
235       Direction vdir = get_grob_direction (me);
236       Real neighbor_y
237         = neighbor_col->extent (neighbor_common, Y_AXIS)
238         .linear_combination (int(neighbor_cols.size()==1 ? CENTER : vdir))
239         - common_next_system->relative_coordinate (neighbor_common, Y_AXIS);
240
241       Link_array<Grob> my_cols
242         = Pointer_group_interface__extract_grobs (me, (Grob *)0,
243                                                   "note-columns");
244
245       Grob *extreme_col = (hdir == RIGHT) ? my_cols.top() : my_cols[0];
246       Real y = extreme_col->extent (common[Y_AXIS], Y_AXIS)
247         .linear_combination (int ((my_cols.size() == 1) ? CENTER : vdir));
248       by = (y*neighbor_cols.size() + neighbor_y*my_cols.size()) /
249         (my_cols.size() + neighbor_cols.size());
250     }
251   return by;
252 }
253
254 Encompass_info
255 get_encompass_info (Grob *me,
256                     Grob *col,
257                     Grob **common)
258 {
259   Grob *stem = unsmob_grob (col->get_property ("stem"));
260   Encompass_info ei;
261   Direction dir = get_grob_direction (me);
262
263   if (!stem)
264     {
265       programming_error ("No stem for note column?");
266       ei.x_ = col->relative_coordinate (common[X_AXIS], X_AXIS);
267       ei.head_ = ei.stem_ = col->extent (common[Y_AXIS],
268                                          Y_AXIS)[get_grob_direction (me)];
269       return ei;
270     }
271   Direction stem_dir = get_grob_direction (stem);
272
273   if (Grob *head = Note_column::first_head (col))
274     ei.x_ = head->extent (common[X_AXIS], X_AXIS).center ();
275   else
276     ei.x_ = col->extent (common[X_AXIS], X_AXIS).center ();
277
278   Grob *h = Stem::extremal_heads (stem)[Direction (dir)];
279   if (!h)
280     {
281       ei.head_ = ei.stem_ = col->extent (common[Y_AXIS], Y_AXIS)[dir];
282       return ei;
283     }
284
285   ei.head_ = h->extent (common[Y_AXIS], Y_AXIS)[dir];
286
287   if ((stem_dir == dir)
288       && !stem->extent (stem, Y_AXIS).is_empty ())
289     {
290       ei.stem_ = stem->extent (common[Y_AXIS], Y_AXIS)[dir];
291       if (Grob *b = Stem::get_beam (stem))
292         ei.stem_ += stem_dir * 0.5 * Beam::get_thickness (b);
293
294       Interval x = stem->extent (common[X_AXIS], X_AXIS);
295       ei.x_ = x.is_empty ()
296         ? stem->relative_coordinate (common[X_AXIS], X_AXIS)
297         : x.center ();
298     }
299   else
300     ei.stem_ = ei.head_;
301
302   return ei;
303 }
304
305
306 Direction
307 get_default_dir (Grob*me)
308 {
309   Link_array<Grob> encompasses
310     = Pointer_group_interface__extract_grobs (me, (Grob*) 0, "note-columns");
311
312   Direction d = DOWN;
313   for (int i= 0; i < encompasses.size (); i ++)
314     {
315       if (Note_column::dir (encompasses[i]) < 0)
316         {
317           d = UP;
318           break;
319         }
320     }
321   return d;
322 }
323
324 Direction
325 get_slur_dir (Grob *slur)
326 {
327   Direction d = get_grob_direction (slur);
328   if (d == CENTER)
329     d = get_default_dir (slur);
330   return d;
331 }
332
333 MAKE_SCHEME_CALLBACK (New_slur, after_line_breaking,1);
334 SCM
335 New_slur::after_line_breaking (SCM smob)
336 {
337   Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob));
338   if (!scm_ilength (me->get_property ("note-columns")))
339     {
340       me->suicide ();
341       return SCM_UNSPECIFIED;
342     }
343
344   if (!get_grob_direction (me))
345     set_grob_direction (me, get_default_dir (me));
346
347   set_end_points (me);
348
349   return SCM_UNSPECIFIED;
350 }
351
352 Bezier
353 get_bezier (Grob *me, Drul_array<Offset> extremes, Real r_0, Real h_inf)
354 {
355   Array<Offset> encompasses;
356   encompasses.push (extremes[LEFT]);
357   encompasses.push (extremes[RIGHT]);
358
359   Slur_bezier_bow bb (encompasses,
360                       get_grob_direction (me), h_inf, r_0);
361
362   return bb.get_bezier ();
363 }
364
365 Drul_array<Bound_info>
366 get_bound_info (Spanner* me, Grob **common)
367 {
368   Drul_array<Bound_info> extremes;
369   Direction d = LEFT;
370   Direction dir = get_grob_direction (me);
371
372   do
373     {
374       extremes[d].bound_ = me->get_bound (d);
375       if (Note_column::has_interface (extremes[d].bound_))
376         {
377           extremes[d].note_column_ = extremes[d].bound_;
378           extremes[d].stem_ = Note_column::get_stem (extremes[d].note_column_);
379           extremes[d].stem_dir_ = get_grob_direction (extremes[d].stem_);
380           extremes[d].stem_extent_[X_AXIS]
381             = extremes[d].stem_->extent (common[X_AXIS], X_AXIS);
382           extremes[d].stem_extent_[Y_AXIS]
383             = extremes[d].stem_->extent (common[Y_AXIS], Y_AXIS);
384           extremes[d].slur_head_
385             = Stem::extremal_heads (extremes[d].stem_)[dir];
386           if (!extremes[d].slur_head_
387               && Note_column::has_rests (extremes[d].bound_))
388             {
389               extremes[d].slur_head_ = Note_column::get_rest (extremes[d].bound_);
390             }
391
392           if (extremes[d].slur_head_)
393             extremes[d].slur_head_extent_
394               = extremes[d].slur_head_->extent (common[X_AXIS], X_AXIS);
395
396           extremes[d].staff_ = Staff_symbol_referencer
397             ::get_staff_symbol (extremes[d].stem_);
398           extremes[d].staff_space_ = Staff_symbol_referencer
399             ::staff_space (extremes[d].stem_);
400         }
401       else
402         extremes[d].neighbor_y_ = broken_trend_y (me, common, d);
403     }
404   while (flip (&d) != LEFT);
405   return extremes;
406 }
407
408 void
409 set_end_points (Grob *me)
410 {
411   Link_array<Grob> columns
412     = Pointer_group_interface__extract_grobs (me, (Grob *) 0, "note-columns");
413   Slur_score_parameters params;
414   if (columns.is_empty ())
415     {
416       me->suicide ();
417       return;
418     }
419
420   SCM eltlist = me->get_property ("note-columns");
421   SCM extra_list = me->get_property ("encompass-objects");
422   Spanner *sp = dynamic_cast<Spanner*> (me);
423
424   Grob *common[] = {0, 0};
425   for (int i = X_AXIS; i < NO_AXES; i++)
426     {
427       Axis a = (Axis)i;
428       common[a] = common_refpoint_of_list (eltlist, me, a);
429       common[a] = common_refpoint_of_list (extra_list, common[a], a);
430     }
431
432   common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (RIGHT),
433                                                     X_AXIS);
434   common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (LEFT),
435                                                     X_AXIS);
436
437   Drul_array<Bound_info> extremes = get_bound_info (sp, common);
438   Drul_array<Offset> base_attachment
439     = get_base_attachments (sp, common, extremes);
440   Drul_array<Real> end_ys
441     = get_y_attachment_range (sp, common, extremes, base_attachment);
442   Array<Slur_score> scores = enumerate_attachments (me, common, &params,
443                                                     extremes, base_attachment,
444                                                     end_ys);
445
446   generate_curves (me, common, extremes, base_attachment, &scores);
447   score_edges (me, common, &params,extremes, base_attachment, &scores);
448   score_slopes (me, common, &params,extremes, base_attachment, &scores);
449   score_encompass (me, common, &params,extremes, base_attachment, &scores);
450   score_extra_encompass (me, common, &params,extremes, base_attachment,
451                          &scores);
452
453   Real opt = 1e6;
454   int opt_idx = 0;
455   // why backwards?
456   for (int i = scores.size (); i--;)
457     {
458       if (scores[i].score_ < opt)
459         {
460           opt = scores[i].score_;
461           opt_idx = i;
462         }
463     }
464
465 #if DEBUG_SLUR_QUANTING
466   SCM inspect_quants = me->get_property ("inspect-quants");
467   if (to_boolean (me->get_paper ()
468                   ->lookup_variable (ly_symbol2scm ("debug-slur-quanting")))
469       && ly_c_pair_p (inspect_quants))
470     {
471       Drul_array<Real> ins = ly_scm2interval (inspect_quants);
472       Real mindist = 1e6;
473       for (int i = 0; i < scores.size (); i ++)
474         {
475           Real d =fabs (scores[i].attachment_[LEFT][Y_AXIS] - ins[LEFT])
476             + fabs (scores[i].attachment_[RIGHT][Y_AXIS] - ins[RIGHT]);
477           if (d < mindist)
478             {
479               opt_idx = i;
480               mindist= d;
481             }
482         }
483       if (mindist > 1e5)
484         programming_error ("Could not find quant.");
485     }
486   scores[opt_idx].score_card_ += to_string ("i%d", opt_idx);
487
488   // debug quanting
489   me->set_property ("quant-score",
490                     scm_makfrom0str (scores[opt_idx].score_card_.to_str0 ()));
491 #endif
492
493   Bezier b = scores[opt_idx].curve_;
494   SCM controls = SCM_EOL;
495   for (int i = 4; i--;)
496     {
497       Offset o = b.control_[i]
498         - Offset (me->relative_coordinate (common[X_AXIS], X_AXIS),
499                   me->relative_coordinate (common[Y_AXIS], Y_AXIS));
500       controls = scm_cons (ly_offset2scm (o), controls);
501     }
502   me->set_property ("control-points", controls);
503 }
504
505
506 Drul_array<Real>
507 get_y_attachment_range (Spanner*me,
508                         Grob **common,
509                         Drul_array<Bound_info> extremes,
510                         Drul_array<Offset> base_attachment)
511 {
512   Drul_array<Real> end_ys;
513   Direction dir = get_grob_direction (me);
514   Direction d = LEFT;
515   do
516     {
517       if (extremes[d].note_column_)
518         {
519           end_ys[d] = dir
520             * ((dir * (base_attachment[d][Y_AXIS] + 4.0 * dir))
521                >? (dir * (dir + extremes[d].note_column_->extent(common[Y_AXIS],
522                                                                  Y_AXIS)[dir]))
523                >? (dir * base_attachment[-d][Y_AXIS]));
524         }
525       else
526         end_ys[d] = extremes[d].neighbor_y_ + 4.0 * dir;
527     }
528   while (flip (&d) != LEFT);
529
530   return end_ys;
531 }
532
533 bool
534 spanner_less (Spanner *s1, Spanner* s2)
535 {
536   Slice b1, b2;
537   Direction d  = LEFT;
538   do
539     {
540       b1[d] = s1->get_bound (d)->get_column ()->rank_;
541       b2[d] = s2->get_bound (d)->get_column ()->rank_;
542     } while (flip (&d) != LEFT);  
543
544   return b2[LEFT] <= b1[LEFT] && b2[RIGHT] >= b1[RIGHT]
545     && (b2[LEFT] != b1[LEFT] || b2[RIGHT] != b1[RIGHT]);
546 }
547
548
549 Drul_array<Offset>
550 get_base_attachments (Spanner *me,
551                       Grob **common, Drul_array<Bound_info> extremes)
552 {
553   Link_array<Grob> columns
554     = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
555   Drul_array<Offset> base_attachment;
556   Slur_score_parameters params;
557   Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
558   Direction dir = get_grob_direction (me);
559   Direction d = LEFT;
560   do
561     {
562       Grob *stem = extremes[d].stem_;
563       Grob *head = extremes[d].slur_head_;
564
565       Real x, y;
566       if (!extremes[d].note_column_)
567         {
568           y = extremes[d].neighbor_y_;
569           if (d== RIGHT)
570             x = extremes[d].bound_->extent (common[X_AXIS], X_AXIS)[d];
571           else
572             x = me->get_broken_left_end_align ();
573         }
574       else
575         {
576           bool same_beam =
577             (extremes[d].stem_ && extremes[-d].stem_
578              && Stem::get_beam (extremes[d].stem_) == Stem::get_beam (extremes[-d].stem_));
579
580           /*
581             fixme: X coord should also be set in this case.
582            */
583           if (stem
584               && extremes[d].stem_dir_ == dir
585               && Stem::get_beaming (stem, -d)
586               && (!spanner_less (me, Stem::get_beam (stem))
587                   || same_beam))
588             y = extremes[d].stem_extent_[Y_AXIS][dir];
589           else if (head)
590             y = head->extent (common[Y_AXIS], Y_AXIS)[dir];
591           y += dir * 0.5 * staff_space;
592
593           Real pos
594             = (y - extremes[d].staff_->relative_coordinate (common[Y_AXIS],
595                                                             Y_AXIS))
596             * 2.0 / Staff_symbol::staff_space (extremes[d].staff_);
597
598           /* start off staffline. */
599           if (fabs (pos - my_round (pos)) < 0.2
600               && Staff_symbol_referencer::on_staffline (head, (int) rint (pos))
601               && Staff_symbol_referencer::line_count (head) - 1 >= rint (pos)
602               )
603             // TODO: calc from slur thick & line thick, parameter.      
604             y += 1.5 * staff_space * dir / 10;
605
606           Grob * fh = Note_column::first_head (extremes[d].note_column_);
607           x =
608             (fh ? fh->extent (common[X_AXIS], X_AXIS)
609              : extremes[d].bound_->extent (common[X_AXIS], X_AXIS))
610             .linear_combination (CENTER);
611         }
612       base_attachment[d] = Offset (x, y);
613
614     } while (flip (&d) != LEFT);
615
616   return base_attachment;
617 }
618
619 void
620 generate_curves (Grob *me, Grob **common,
621                  Drul_array<Bound_info> extremes,
622                  Drul_array<Offset>,
623                  Array<Slur_score> *scores)
624 {
625   (void) common;
626   (void) extremes;
627   Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
628   Real r_0 = robust_scm2double (me->get_property ("ratio"), 0.33);
629   Real h_inf = staff_space * ly_scm2double (me->get_property ("height-limit"));
630   for (int i = 0; i < scores->size(); i++)
631     {
632       Bezier bez= get_bezier (me, (*scores)[i].attachment_, r_0, h_inf);
633       bez = avoid_staff_line (me, common, extremes, bez);
634       (*scores)[i].attachment_[LEFT] = bez.control_[0];
635       (*scores)[i].attachment_[RIGHT] = bez.control_[3];
636       (*scores)[i].curve_ = bez;
637     }
638 }
639
640 Bezier
641 avoid_staff_line (Grob *me, Grob **common,
642                   Drul_array<Bound_info> extremes,
643                   Bezier bez)
644 {
645   Offset horiz (1,0);
646   Array<Real> ts = bez.solve_derivative (horiz);
647   Real lt = me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
648   Real thick = robust_scm2double (me->get_property ("thickness"), 1.0) *  lt;
649
650   /* TODO: handle case of broken slur.  */
651   if (!ts.is_empty ()
652       && (extremes[LEFT].staff_ == extremes[RIGHT].staff_)
653       && extremes[LEFT].staff_ && extremes[RIGHT].staff_)
654     {
655       Real y = bez.curve_point (ts[0])[Y_AXIS];
656
657       Grob *staff = extremes[LEFT].staff_;
658
659       Real staff_space = extremes[LEFT].staff_space_;
660       Real p = 2 * (y - staff->relative_coordinate (common[Y_AXIS], Y_AXIS))
661         / staff_space;
662
663       Real distance = fabs (my_round (p) - p);  //  in halfspaces
664       if (distance < 4 * thick
665           && (int) fabs (my_round (p))
666           <= 2 * Staff_symbol_referencer::staff_radius (staff) + 0.1
667           && (int (fabs (my_round (p))) % 2
668               != Staff_symbol_referencer::line_count (staff) % 2))
669         {
670           Direction resolution_dir =
671             (distance ?  get_grob_direction (me) : Direction (sign (p - my_round(p))));
672
673           // TODO: parameter
674           Real newp = my_round (p) + resolution_dir
675             * 5 * thick;
676         
677           Real dy = (newp - p) * staff_space / 2.0;
678 #if 0
679           bez.translate (Offset (0, dy));
680 #else
681           bez.control_[1][Y_AXIS] += dy;
682           bez.control_[2][Y_AXIS] += dy;
683         
684 #endif
685         }
686     }
687   return bez;
688 }
689
690 Array<Slur_score>
691 enumerate_attachments (Grob *me, Grob *common[],
692                        Slur_score_parameters *score_param,
693                        Drul_array<Bound_info> extremes,
694                        Drul_array<Offset> base_attachment,
695                        Drul_array<Real> end_ys)
696 {
697   (void) common;
698   /*ugh.   */
699   Array<Slur_score> scores;
700
701   Direction dir = get_grob_direction (me);
702   Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
703
704   Drul_array<Offset> os;
705   os[LEFT] = base_attachment[LEFT];
706   Real minimum_length = staff_space
707     * robust_scm2double (me->get_property ("minimum-length"), 2.0);
708
709   for (int i = 0; dir * os[LEFT][Y_AXIS] <= dir * end_ys[LEFT]; i++)
710     {
711       os[RIGHT] = base_attachment[RIGHT];
712       for (int j = 0; dir * os[RIGHT][Y_AXIS] <= dir * end_ys[RIGHT]; j++)
713         {
714           Slur_score s;
715           Direction d = LEFT;
716           Drul_array<bool> attach_to_stem (false, false);
717           do
718             {
719               os[d][X_AXIS] = base_attachment[d][X_AXIS];
720               if (extremes[d].stem_
721                   && !Stem::is_invisible (extremes[d].stem_)
722                   && extremes[d].stem_dir_ == dir)
723                 {
724                   if (dir == -d
725                       && extremes[d].stem_extent_[Y_AXIS].contains (os[d][Y_AXIS]))
726                     {
727                       os[d][X_AXIS] =  extremes[d].slur_head_extent_[-d]
728                         - d * 0.3;
729                       attach_to_stem[d] = true;
730                     }
731                   else if (dir *extremes[d].stem_extent_[Y_AXIS][dir]
732                              < dir * os[d][Y_AXIS]
733                            && !extremes[d].stem_extent_[X_AXIS].is_empty()
734                            )
735                     
736                     os[d][X_AXIS] = extremes[d].stem_extent_[X_AXIS].center();
737                 }
738             }
739           while (flip (&d) != LEFT);
740
741           Offset dz;    
742           dz = os[RIGHT] - os[LEFT];
743           if (dz[X_AXIS] < minimum_length
744               || fabs (dz[Y_AXIS] / dz[X_AXIS]) > score_param->MAX_SLOPE
745               )
746             {
747               do
748                 {
749                   if (extremes[d].slur_head_)
750                     {
751                       os[d][X_AXIS] = extremes[d].slur_head_extent_.center ();
752                       attach_to_stem[d] = false;
753                     }
754                 }
755               while (flip (&d) != LEFT);
756             }
757
758           dz = os[RIGHT] - os[LEFT];
759           do
760             {
761               if (extremes[d].slur_head_
762                   && !attach_to_stem[d])
763                 {
764                   /* Horizontally move tilted slurs a little.  Move
765                      more for bigger tilts.
766                     
767                      TODO: parameter */
768                   os[d][X_AXIS]
769                     -= dir * extremes[d].slur_head_extent_.length ()
770                     * sin (dz.arg  ()) / 3;
771                 }
772             }
773           while (flip (&d) != LEFT);
774           
775           s.attachment_ = os;
776           scores.push (s);
777           
778           os[RIGHT][Y_AXIS] += dir * staff_space / 2;
779         }
780       
781       os[LEFT][Y_AXIS] += dir * staff_space / 2;
782     }
783   return scores;
784 }
785
786 void
787 score_encompass (Grob *me, Grob *common[],
788                  Slur_score_parameters *score_param,
789                  Drul_array<Bound_info> extremes,
790                  Drul_array<Offset> base_attach,
791                  Array<Slur_score> *scores)
792 {
793   (void) extremes;
794   (void) base_attach;
795
796   Link_array<Grob> encompasses
797     = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
798   Direction dir = get_grob_direction (me);
799
800   Array<Encompass_info> infos;
801
802   for (int i = 0; i < encompasses.size(); i++)
803     infos.push (get_encompass_info (me, encompasses[i], common));
804
805   for (int i = 0; i < scores->size (); i++)
806     {
807       Bezier const &bez (scores->elem (i).curve_);
808       Real demerit = 0.0;
809       for (int j = 0; j < infos.size(); j++)
810         {
811           Real x = infos[j].x_;
812
813           bool l_edge = j==0;
814           bool r_edge = j==infos.size()-1;
815           bool edge =  l_edge || r_edge;
816
817           if (!(x < scores->elem (i).attachment_[RIGHT][X_AXIS]
818                 && x > scores->elem (i).attachment_[LEFT][X_AXIS]))
819             continue;
820         
821           Real y = bez.get_other_coordinate (X_AXIS, x);
822           if (!edge)
823             {
824               Real head_dy = (y - infos[j].head_);
825               if (dir * head_dy < 0)
826                 demerit += score_param->HEAD_ENCOMPASS_PENALTY;
827               else
828                 {
829                   Real hd = (head_dy)
830                     ? (1 / fabs (head_dy) - 1 / score_param->FREE_HEAD_DISTANCE)
831                     : score_param->HEAD_ENCOMPASS_PENALTY;
832                   hd = (hd >? 0)<? score_param->HEAD_ENCOMPASS_PENALTY;
833
834                   demerit += hd;        
835                 }
836             }   
837
838           if (dir * (y - infos[j].stem_) < 0)
839             {
840               Real stem_dem =score_param->STEM_ENCOMPASS_PENALTY ;
841               if ((l_edge && dir == UP)
842                   || (r_edge && dir == DOWN))
843                 stem_dem /= 5;
844
845               demerit +=  stem_dem;
846             }
847           else if (!edge)
848             {
849               Interval ext;
850               ext.add_point (infos[j].stem_);
851               ext.add_point (infos[j].head_);
852
853               // ?
854               demerit += -score_param->CLOSENESS_FACTOR
855                 * (dir
856                    * (y - (ext[dir] + dir * score_param->FREE_HEAD_DISTANCE))
857                    <? 0)
858                 / infos.size ();
859             }
860         }
861
862 #if DEBUG_SLUR_QUANTING
863       (*scores)[i].score_card_ += to_string ("C%.2f", demerit);
864 #endif
865
866       (*scores)[i].score_ += demerit;
867     }
868 }
869
870 void
871 score_extra_encompass (Grob *me, Grob *common[],
872                        Slur_score_parameters *score_param,
873                        Drul_array<Bound_info> extremes,
874                        Drul_array<Offset> base_attach,
875                        Array<Slur_score> *scores)
876 {
877   (void) base_attach;
878   (void) extremes;
879
880   Link_array<Grob> encompasses
881     = Pointer_group_interface__extract_grobs (me, (Grob *)0,
882                                               "encompass-objects");
883   Direction dir = get_grob_direction (me);
884   Real lt =  me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
885   Real thick = robust_scm2double (me->get_property ("thickness"), 1.0) * lt;
886
887   Array<Real> xidxs;
888   Array<Interval> yexts;
889   Array<Interval> xexts;
890
891   for (int i = encompasses.size (); i--; )
892     {
893       if (New_slur::has_interface (encompasses[i]))
894         {
895           Grob * small_slur = encompasses[i];
896           Bezier b = New_slur::get_curve (small_slur);
897
898           Offset z = b.curve_point (0.5);
899           z += Offset (small_slur->relative_coordinate (common[X_AXIS], X_AXIS),
900                        small_slur->relative_coordinate (common[Y_AXIS], Y_AXIS));
901
902           xexts.push (Interval (z[X_AXIS], z[X_AXIS]));
903           xidxs.push (0.0);
904           yexts.push (z[Y_AXIS] + thick * Interval (-0.5, 0.5));
905         }
906       else
907         {
908           Grob *g = encompasses [i];
909           Interval xe = g->extent (common[X_AXIS], X_AXIS);
910           Interval ye = g->extent (common[Y_AXIS], Y_AXIS);
911
912           Real xp = 0.0;
913
914           if (Accidental_interface::has_interface (g))
915             {
916               /* Begin copy accidental.cc */
917               bool parens = false;
918               if (to_boolean (g->get_property ("cautionary")))
919                 {
920                   SCM cstyle = g->get_property ("cautionary-style");
921                   parens = ly_c_equal_p (cstyle, ly_symbol2scm ("parentheses"));
922                 }
923         
924               SCM accs = g->get_property ("accidentals");
925               SCM scm_style = g->get_property ("style");
926               if (!ly_c_symbol_p (scm_style)
927                   && !parens
928                   && scm_ilength (accs) == 1)
929                 {
930                   /* End copy accidental.cc */
931                   switch (ly_scm2int (ly_car (accs)))
932                     {
933                     case FLAT:
934                     case DOUBLE_FLAT:
935                       xp = LEFT;
936                       break ;
937                     case SHARP:
938                       xp = 0.5 * dir;
939                       break ;
940                     case NATURAL:
941                       xp = -dir;
942                       break; 
943                     }
944                 }
945             }
946
947           xidxs.push (xp);
948           ye.widen (thick * 0.5);
949           yexts.push (ye);
950           xexts.push (xe);
951         }
952     }
953   for (int i = 0; i < scores->size (); i++)
954     {
955       Real demerit = 0.0;
956       for (int j = 0; j < xidxs.size(); j++)
957         {
958           Drul_array<Offset> at = scores->elem (i).attachment_;
959           Interval slur_wid (at[LEFT][X_AXIS], at[RIGHT][X_AXIS]);
960
961           /*
962             to prevent numerical inaccuracies in
963             Bezier::get_other_coordinate().
964            */
965           slur_wid.widen (- 0.5 * thick);
966           Real x = xexts[j].linear_combination (xidxs[j]);
967           Real y = 0.0;
968           if (!slur_wid.contains (x))
969             {   
970               Direction contains_dir = CENTER;
971               Direction d = LEFT;
972               do
973                 {
974                   if (xexts[j].contains (at[d][X_AXIS]))
975                     contains_dir = d; 
976                 }
977               while (flip (&d) != LEFT);
978
979               if (!contains_dir)
980                 continue;
981               else
982                 y = at[contains_dir][Y_AXIS];
983             }
984           else
985             {
986               y = scores->elem (i).curve_.get_other_coordinate (X_AXIS, x);
987             }
988
989           Real collision_demerit = 
990                (Accidental_interface::has_interface (encompasses[j]))
991             ? score_param->ACCIDENTAL_COLLISION
992             : score_param->EXTRA_OBJECT_COLLISION;
993           
994           Real dist = yexts[j].distance (y);
995           demerit +=
996             fabs (0 >? (score_param->EXTRA_ENCOMPASS_FREE_DISTANCE - dist)) /
997             score_param->EXTRA_ENCOMPASS_FREE_DISTANCE  * collision_demerit;
998         }
999 #if DEBUG_SLUR_QUANTING
1000       (*scores)[i].score_card_ += to_string ("X%.2f", demerit);
1001 #endif
1002       (*scores)[i].score_ += demerit;
1003     }
1004 }
1005
1006 void
1007 score_edges (Grob *me, Grob *common[],
1008              Slur_score_parameters * score_param,
1009              Drul_array<Bound_info> extremes,
1010              Drul_array<Offset> base_attach,
1011              Array<Slur_score> *scores)
1012 {
1013   (void) common;
1014   Direction dir = get_grob_direction (me);
1015
1016   for (int i = 0; i < scores->size (); i++)
1017     {
1018       Direction d = LEFT;
1019       do
1020         {
1021           Real y = scores->elem (i).attachment_[d][Y_AXIS];
1022           Real dy = fabs (y - base_attach[d][Y_AXIS]);
1023         
1024           Real factor = score_param->EDGE_ATTRACTION_FACTOR;
1025           Real demerit = factor * dy;
1026           if (extremes[d].stem_
1027               && extremes[d].stem_dir_ == dir
1028               && !Stem::get_beaming (extremes[d].stem_, -d)
1029               )
1030             demerit /= 5;
1031         
1032           (*scores)[i].score_ += demerit;
1033 #if DEBUG_SLUR_QUANTING
1034           (*scores)[i].score_card_ += to_string ("E%.2f", demerit);
1035 #endif
1036         }
1037       while (flip (&d) != LEFT);
1038     }
1039 }
1040
1041 void
1042 score_slopes (Grob *me, Grob *common[],
1043               Slur_score_parameters *score_param,
1044               Drul_array<Bound_info> extremes,
1045               Drul_array<Offset> base_attach,
1046               Array<Slur_score> * scores)
1047 {
1048   (void) me;
1049   (void) base_attach;
1050
1051   Drul_array<Real> ys;
1052   Direction d = LEFT;
1053   do
1054     {
1055       if (extremes[d].slur_head_)
1056         ys[d] = extremes[d].slur_head_->relative_coordinate (common[Y_AXIS],
1057                                                               Y_AXIS);
1058       else
1059         ys[d] = extremes[d].neighbor_y_;
1060     }
1061   while (flip (&d) != LEFT);
1062
1063   bool has_beams
1064     = (extremes[LEFT].stem_ && Stem::get_beam (extremes[LEFT].stem_))
1065     || (extremes[RIGHT].stem_ && Stem::get_beam (extremes[RIGHT].stem_));
1066
1067   Real dy = ys[RIGHT] - ys[LEFT];
1068   for (int i = 0; i < scores->size (); i++)
1069     {
1070       Offset slur_dz = (*scores)[i].attachment_[RIGHT]
1071         - (*scores)[i].attachment_[LEFT];
1072       Real slur_dy = slur_dz[Y_AXIS];
1073       Real demerit = 0.0;
1074
1075       demerit += ((fabs (slur_dy / slur_dz[X_AXIS])
1076                    - score_param->MAX_SLOPE) >? 0)
1077         * score_param->MAX_SLOPE_FACTOR;
1078
1079       /* 0.2: account for staffline offset. */
1080       Real max_dy = (fabs (dy) + 0.2);
1081       if (has_beams)
1082         max_dy += 1.0;
1083
1084       demerit += score_param->STEEPER_SLOPE_FACTOR
1085         * ((fabs (slur_dy) -max_dy) >? 0);
1086
1087       demerit += ((fabs (slur_dy/slur_dz[X_AXIS])
1088                    - score_param->MAX_SLOPE) >? 0)
1089         * score_param->MAX_SLOPE_FACTOR;
1090
1091       if (sign (dy) == 0
1092           && sign (slur_dy) != 0)
1093         demerit += score_param->NON_HORIZONTAL_PENALTY;
1094
1095       if (sign (dy)
1096           && sign (slur_dy)
1097           && sign (slur_dy) != sign (dy))
1098         demerit += has_beams
1099           ? score_param->SAME_SLOPE_PENALTY / 10
1100           : score_param->SAME_SLOPE_PENALTY;
1101
1102 #if DEBUG_SLUR_QUANTING
1103       (*scores)[i].score_card_ += to_string ("S%.2f", d);
1104 #endif
1105       (*scores)[i].score_ += demerit;
1106     }
1107
1108
1109   
1110 }
1111
1112