]> git.donarmstrong.com Git - lilypond.git/blob - lily/slur-scoring.cc
use Spanner::broken_neighbor() in slur and line spanner.
[lilypond.git] / lily / slur-scoring.cc
1 /*
2   slur-scoring.cc -- Score based slur formatting
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1996--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
7   Jan Nieuwenhuizen <janneke@gnu.org>
8 */
9
10
11 #include "slur-scoring.hh"
12
13 #include "accidental-interface.hh"
14 #include "beam.hh"
15 #include "directional-element-interface.hh"
16 #include "libc-extension.hh"
17 #include "main.hh"
18 #include "note-column.hh"
19 #include "output-def.hh"
20 #include "paper-column.hh"
21 #include "pitch.hh"
22 #include "pointer-group-interface.hh"
23 #include "slur-configuration.hh"
24 #include "slur.hh"
25 #include "spanner.hh"
26 #include "staff-symbol-referencer.hh"
27 #include "staff-symbol.hh"
28 #include "stem.hh"
29 #include "warn.hh"
30
31 /*
32   TODO:
33
34   - curve around flag for y coordinate
35
36   - short-cut: try a smaller region first.
37
38   - handle non-visible stems better.
39
40   - try to prune number of scoring criteria
41
42   - take encompass-objects more into account when determining
43   slur shape
44
45   - calculate encompass scoring directly after determining slur shape.
46
47   - optimize.
48 */
49 struct Slur_score_state;
50
51 Slur_score_state::Slur_score_state ()
52 {
53   musical_dy_ = 0.0;
54   valid_ = false;
55   edge_has_beams_ = false;
56   has_same_beam_ = false;
57   is_broken_ = false;
58   dir_ = CENTER;
59   slur_ = 0;
60   common_[X_AXIS] = 0;
61   common_[Y_AXIS] = 0;
62 }
63
64 Slur_score_state::~Slur_score_state ()
65 {
66   junk_pointers (configurations_);
67 }
68
69 Real
70 broken_trend_y (Slur_score_state const &state, Direction hdir)
71 {
72   /* A broken slur should maintain the same vertical trend
73      the unbroken slur would have had.  */
74   Real by = 0.0;
75   if (Spanner *mother = dynamic_cast<Spanner *> (state.slur_->original ()))
76     {
77       Grob *neighbor = mother->broken_neighbor (hdir);
78       if (!neighbor)
79         return by;
80
81       
82       Spanner *common_mother
83         = dynamic_cast<Spanner *> (state.common_[Y_AXIS]->original ());
84       int common_k
85         = broken_spanner_index (dynamic_cast<Spanner *> (state.common_[Y_AXIS]));
86       int common_j = common_k + hdir;
87
88       if (common_j < 0 || vsize (common_j) >= common_mother->broken_intos_.size ())
89         return by;
90
91       Grob *common_next_system = common_mother->broken_intos_[common_j];
92
93       SCM last_point = scm_car (scm_last_pair (neighbor->get_property ("control-points")));
94
95       return scm_to_double (scm_cdr (last_point))
96         + neighbor->relative_coordinate (common_next_system, Y_AXIS);
97     }
98   return by;
99 }
100
101 /*
102   copy slur dir forwards across line break.
103 */
104 void
105 Slur_score_state::set_next_direction ()
106 {
107   if (extremes_[RIGHT].note_column_)
108     return;
109
110   if (Grob *neighbor = slur_->broken_neighbor (RIGHT))
111     {
112       set_grob_direction (neighbor, dir_);
113     }
114 }
115
116 Encompass_info
117 Slur_score_state::get_encompass_info (Grob *col) const
118 {
119   Grob *stem = unsmob_grob (col->get_object ("stem"));
120   Encompass_info ei;
121
122   if (!stem)
123     {
124       programming_error ("no stem for note column");
125       ei.x_ = col->relative_coordinate (common_[X_AXIS], X_AXIS);
126       ei.head_ = ei.stem_ = col->extent (common_[Y_AXIS],
127                                          Y_AXIS)[dir_];
128       return ei;
129     }
130   Direction stem_dir = get_grob_direction (stem);
131
132   if (Grob *head = Note_column::first_head (col))
133     ei.x_ = head->extent (common_[X_AXIS], X_AXIS).center ();
134   else
135     ei.x_ = col->extent (common_[X_AXIS], X_AXIS).center ();
136
137   Grob *h = Stem::extremal_heads (stem)[Direction (dir_)];
138   if (!h)
139     {
140       ei.head_ = ei.stem_ = col->extent (common_[Y_AXIS], Y_AXIS)[dir_];
141       return ei;
142     }
143
144   ei.head_ = h->extent (common_[Y_AXIS], Y_AXIS)[dir_];
145
146   if ((stem_dir == dir_)
147       && !stem->extent (stem, Y_AXIS).is_empty ())
148     {
149       ei.stem_ = stem->extent (common_[Y_AXIS], Y_AXIS)[dir_];
150       if (Grob *b = Stem::get_beam (stem))
151         ei.stem_ += stem_dir * 0.5 * Beam::get_thickness (b);
152
153       Interval x = stem->extent (common_[X_AXIS], X_AXIS);
154       ei.x_ = x.is_empty ()
155         ? stem->relative_coordinate (common_[X_AXIS], X_AXIS)
156         : x.center ();
157     }
158   else
159     ei.stem_ = ei.head_;
160
161   return ei;
162 }
163
164 Drul_array<Bound_info>
165 Slur_score_state::get_bound_info () const
166 {
167   Drul_array<Bound_info> extremes;
168
169   Direction d = LEFT;
170   Direction dir = dir_;
171
172   do
173     {
174       extremes[d].bound_ = slur_->get_bound (d);
175       if (Note_column::has_interface (extremes[d].bound_))
176         {
177           extremes[d].note_column_ = extremes[d].bound_;
178           extremes[d].stem_ = Note_column::get_stem (extremes[d].note_column_);
179           if (extremes[d].stem_)
180             {
181               extremes[d].stem_dir_ = get_grob_direction (extremes[d].stem_);
182
183               for (int a = X_AXIS; a < NO_AXES; a++)
184                 {
185                   Axis ax = Axis (a);
186                   Interval s = extremes[d].stem_->extent (common_[ax], ax);
187                   if (s.is_empty ())
188                     {
189                       /*
190                         do not issue warning. This happens for rests and
191                         whole notes.
192                       */
193                       s = Interval (0, 0)
194                         + extremes[d].stem_->relative_coordinate (common_[ax], ax);
195                     }
196                   extremes[d].stem_extent_[ax] = s;
197                 }
198
199               extremes[d].slur_head_
200                 = Stem::extremal_heads (extremes[d].stem_)[dir];
201               if (!extremes[d].slur_head_
202                   && Note_column::has_rests (extremes[d].bound_))
203                 extremes[d].slur_head_ = Note_column::get_rest (extremes[d].bound_);
204               extremes[d].staff_ = Staff_symbol_referencer
205                 ::get_staff_symbol (extremes[d].stem_);
206               extremes[d].staff_space_ = Staff_symbol_referencer
207                 ::staff_space (extremes[d].stem_);
208             }
209
210           if (extremes[d].slur_head_)
211             extremes[d].slur_head_x_extent_
212               = extremes[d].slur_head_->extent (common_[X_AXIS], X_AXIS);
213
214         }
215     }
216   while (flip (&d) != LEFT);
217
218   return extremes;
219 }
220
221 void
222 Slur_score_state::fill (Grob *me)
223 {
224   slur_ = dynamic_cast<Spanner *> (me);
225   columns_
226     = internal_extract_grob_array (me, ly_symbol2scm ("note-columns"));
227
228   if (columns_.empty ())
229     {
230       me->suicide ();
231       return;
232     }
233
234   staff_space_ = Staff_symbol_referencer::staff_space (me);
235   Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
236   thickness_ = robust_scm2double (me->get_property ("thickness"), 1.0) * lt;
237
238   dir_ = get_grob_direction (me);
239   parameters_.fill (me);
240
241   extract_grob_set (me, "note-columns", columns);
242   extract_grob_set (me, "encompass-objects", extra_objects);
243
244   Spanner *sp = dynamic_cast<Spanner *> (me);
245
246   for (int i = X_AXIS; i < NO_AXES; i++)
247     {
248       Axis a = (Axis)i;
249       common_[a] = common_refpoint_of_array (columns, me, a);
250       common_[a] = common_refpoint_of_array (extra_objects, common_[a], a);
251
252       Direction d = LEFT;
253       do
254         {
255           /*
256             If bound is not in note-columns, we don't want to know about
257             its Y-position
258           */
259           if (a != Y_AXIS)
260             common_[a] = common_[a]->common_refpoint (sp->get_bound (d), a);
261         }
262       while (flip (&d) != LEFT);
263     }
264
265   extremes_ = get_bound_info ();
266   is_broken_ = (!extremes_[LEFT].note_column_
267                 || !extremes_[RIGHT].note_column_);
268
269   has_same_beam_
270     = (extremes_[LEFT].stem_ && extremes_[RIGHT].stem_
271        && Stem::get_beam (extremes_[LEFT].stem_) == Stem::get_beam (extremes_[RIGHT].stem_));
272
273   base_attachments_ = get_base_attachments ();
274
275   Drul_array<Real> end_ys
276     = get_y_attachment_range ();
277
278   configurations_ = enumerate_attachments (end_ys);
279   for (vsize i = 0; i < columns_.size (); i++)
280     encompass_infos_.push_back (get_encompass_info (columns_[i]));
281
282   extra_encompass_infos_ = get_extra_encompass_infos ();
283   valid_ = true;
284
285   musical_dy_ = 0.0;
286   Direction d = LEFT;
287   do
288     {
289       if (!is_broken_
290           && extremes_[d].slur_head_)
291         musical_dy_ += d
292           * extremes_[d].slur_head_->relative_coordinate (common_[Y_AXIS], Y_AXIS);
293     }
294   while (flip (&d) != LEFT);
295
296   edge_has_beams_
297     = (extremes_[LEFT].stem_ && Stem::get_beam (extremes_[LEFT].stem_))
298     || (extremes_[RIGHT].stem_ && Stem::get_beam (extremes_[RIGHT].stem_));
299
300   set_next_direction ();
301
302   if (is_broken_)
303     musical_dy_ = 0.0;
304 }
305
306
307 MAKE_SCHEME_CALLBACK(Slur, calc_control_points, 1)
308 SCM
309 Slur::calc_control_points (SCM smob)
310 {
311   Spanner *me = unsmob_spanner (smob);
312
313   Slur_score_state state;
314   state.fill (me);
315
316   if (!state.valid_)
317     return SCM_EOL;
318
319   state.generate_curves ();
320
321   SCM end_ys = me->get_property ("positions");
322   Bezier best;
323
324   if (is_number_pair (end_ys))
325     best = state.configurations_[state.get_closest_index (end_ys)]->curve_;
326   else
327     best = state.get_best_curve ();
328
329   SCM controls = SCM_EOL;
330   for (int i = 4; i--;)
331     {
332       Offset o = best.control_[i]
333         - Offset (me->relative_coordinate (state.common_[X_AXIS], X_AXIS),
334                   me->relative_coordinate (state.common_[Y_AXIS], Y_AXIS));
335       controls = scm_cons (ly_offset2scm (o), controls);
336     }
337
338   return controls;
339 }
340
341 Bezier
342 Slur_score_state::get_best_curve ()
343 {
344   int opt_idx = -1;
345   Real opt = 1e6;
346
347 #if DEBUG_SLUR_SCORING
348   bool debug_slurs = to_boolean (slur_->layout ()
349                                  ->lookup_variable (ly_symbol2scm ("debug-slur-scoring")));
350   SCM inspect_quants = slur_->get_property ("inspect-quants");
351   SCM inspect_index = slur_->get_property ("inspect-index");
352   if (debug_slurs
353       && scm_is_integer (inspect_index))
354     {
355       opt_idx = scm_to_int (inspect_index);
356       configurations_[opt_idx]->calculate_score (*this);
357       opt = configurations_[opt_idx]->score ();
358     }
359   else if (debug_slurs
360            && scm_is_pair (inspect_quants))
361     {
362       opt_idx = get_closest_index (inspect_quants);
363       configurations_[opt_idx]->calculate_score (*this);
364       opt = configurations_[opt_idx]->score ();
365     }
366   else
367 #endif
368     {
369       for (vsize i = 0; i < configurations_.size (); i++)
370         configurations_[i]->calculate_score (*this);
371       for (vsize i = 0; i < configurations_.size (); i++)
372         {
373           if (configurations_[i]->score () < opt)
374             {
375               opt = configurations_[i]->score ();
376               opt_idx = i;
377             }
378         }
379     }
380
381 #if DEBUG_SLUR_SCORING
382   if (debug_slurs)
383     {
384       string total;
385       if (opt_idx >= 0)
386         {
387           total = configurations_[opt_idx]->card ();
388           total += to_string (" TOTAL=%.2f idx=%d", configurations_[opt_idx]->score (), opt_idx); 
389         }
390       else
391         {
392           total = "no sol?";
393         }
394   
395       slur_->set_property ("quant-score",
396                            scm_makfrom0str (total.c_str ()));
397     }
398 #endif
399
400   if (opt_idx < 0)
401     {
402       opt_idx = 0;
403       programming_error ("No optimal slur found. Guessing 0.");
404     }
405   
406   return configurations_[opt_idx]->curve_;
407 }
408
409 Grob *
410 Slur_score_state::breakable_bound_item (Direction d) const
411 {
412   Grob *col = slur_->get_bound (d)->get_column ();
413
414   extract_grob_set (slur_, "encompass-objects", extra_encompasses);
415
416   for (vsize i = 0; i < extra_encompasses.size (); i++)
417     {
418       Item *item = dynamic_cast<Item*> (extra_encompasses[i]);
419       if (item && col == item->get_column ())
420         return item;
421     }
422
423   return 0;
424 }
425
426 int
427 Slur_score_state::get_closest_index (SCM inspect_quants) const
428 {
429   Drul_array<Real> ins = ly_scm2interval (inspect_quants);
430
431   int opt_idx = -1;
432   Real mindist = 1e6;
433   for (vsize i = 0; i < configurations_.size (); i++)
434     {
435       Real d = fabs (configurations_[i]->attachment_[LEFT][Y_AXIS] - ins[LEFT])
436         + fabs (configurations_[i]->attachment_[RIGHT][Y_AXIS] - ins[RIGHT]);
437       if (d < mindist)
438         {
439           opt_idx = i;
440           mindist = d;
441         }
442     }
443   if (mindist > 1e5)
444     programming_error ("can't not find quant");
445   return opt_idx;
446 }
447
448 /*
449   TODO: should analyse encompasses to determine sensible region, and
450   should limit slopes available.
451 */
452
453 Drul_array<Real>
454 Slur_score_state::get_y_attachment_range () const
455 {
456   Drul_array<Real> end_ys;
457   Direction d = LEFT;
458   do
459     {
460       if (extremes_[d].note_column_)
461         {
462           end_ys[d] = dir_
463             * max (max (dir_ * (base_attachments_[d][Y_AXIS]
464                                 + parameters_.region_size_ * dir_),
465                         dir_ * (dir_ + extremes_[d].note_column_->extent (common_[Y_AXIS], Y_AXIS)[dir_])),
466                    dir_ * base_attachments_[-d][Y_AXIS]);
467         }
468       else
469         end_ys[d] = base_attachments_[d][Y_AXIS] + parameters_.region_size_ * dir_;
470     }
471   while (flip (&d) != LEFT);
472
473   return end_ys;
474 }
475
476 bool
477 spanner_less (Spanner *s1, Spanner *s2)
478 {
479   Slice b1, b2;
480   Direction d = LEFT;
481   do
482     {
483       b1[d] = s1->get_bound (d)->get_column ()->get_rank ();
484       b2[d] = s2->get_bound (d)->get_column ()->get_rank ();
485     }
486   while (flip (&d) != LEFT);
487
488   return b2[LEFT] <= b1[LEFT] && b2[RIGHT] >= b1[RIGHT]
489     && (b2[LEFT] != b1[LEFT] || b2[RIGHT] != b1[RIGHT]);
490 }
491
492 Drul_array<Offset>
493 Slur_score_state::get_base_attachments () const
494 {
495   Drul_array<Offset> base_attachment;
496   Direction d = LEFT;
497   do
498     {
499       Grob *stem = extremes_[d].stem_;
500       Grob *head = extremes_[d].slur_head_;
501
502       Real x = 0.0;
503       Real y = 0.0;
504       if (extremes_[d].note_column_)
505         {
506
507           /*
508             fixme: X coord should also be set in this case.
509           */
510           if (stem
511               && !Stem::is_invisible (stem)
512               && extremes_[d].stem_dir_ == dir_
513               && Stem::get_beaming (stem, -d)
514               && Stem::get_beam (stem)
515               && (!spanner_less (slur_, Stem::get_beam (stem))
516                   || has_same_beam_))
517             y = extremes_[d].stem_extent_[Y_AXIS][dir_];
518           else if (head)
519             y = head->extent (common_[Y_AXIS], Y_AXIS)[dir_];
520           y += dir_ * 0.5 * staff_space_;
521
522           y = move_away_from_staffline (y, head);
523
524           Grob *fh = Note_column::first_head (extremes_[d].note_column_);
525           x
526             = (fh ? fh->extent (common_[X_AXIS], X_AXIS)
527                : extremes_[d].bound_->extent (common_[X_AXIS], X_AXIS))
528             .linear_combination (CENTER);
529         }
530       base_attachment[d] = Offset (x, y);
531     }
532   while (flip (&d) != LEFT);
533
534   do
535     {
536       if (!extremes_[d].note_column_)
537         {
538           Real x = 0;
539           Real y = 0;
540
541           if (Grob *g = breakable_bound_item (d))
542             {
543               x = robust_relative_extent (g, common_[X_AXIS], X_AXIS)[RIGHT];
544             }
545           else if (d == RIGHT)
546             x = robust_relative_extent (extremes_[d].bound_, common_[X_AXIS], X_AXIS)[d];
547           else
548             x = slur_->get_broken_left_end_align ();
549           
550           Grob *col = (d == LEFT) ? columns_[0] : columns_.back ();
551
552           if (extremes_[-d].bound_ != col)
553             {
554               y = robust_relative_extent (col, common_[Y_AXIS], Y_AXIS)[dir_];
555               y += dir_ * 0.5 * staff_space_;
556
557               if (get_grob_direction (col) == dir_
558                   && Note_column::get_stem (col)
559                   && !Stem::is_invisible (Note_column::get_stem (col)))
560                 y -= dir_ * 1.5 * staff_space_;
561             }
562           else
563             y = base_attachment[-d][Y_AXIS];
564
565           y = move_away_from_staffline (y, col);
566
567           base_attachment[d] = Offset (x, y);
568         }
569     }
570   while (flip (&d) != LEFT);
571
572   do
573     {
574       for (int a = X_AXIS; a < NO_AXES; a++)
575         {
576           Real &b = base_attachment[d][Axis (a)];
577
578           if (isinf (b) || isnan (b))
579             {
580               programming_error ("slur attachment is inf/nan");
581               b = 0.0;
582             }
583         }
584     }
585   while (flip (&d) != LEFT);
586
587   return base_attachment;
588 }
589
590 Real
591 Slur_score_state::move_away_from_staffline (Real y,
592                                             Grob *on_staff) const
593 {
594   if (!on_staff)
595     return y;
596   
597   Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (on_staff);
598   if (!staff_symbol)
599     return y;
600
601   Real pos
602     = (y - staff_symbol->relative_coordinate (common_[Y_AXIS],
603                                               Y_AXIS))
604     * 2.0 / staff_space_;
605
606   if (fabs (pos - my_round (pos)) < 0.2
607       && Staff_symbol_referencer::on_line (on_staff, (int) rint (pos))
608       && Staff_symbol_referencer::line_count (on_staff) - 1 >= rint (pos))
609     y += 1.5 * staff_space_ * dir_ / 10;
610
611   return y;
612 }
613
614 vector<Offset>
615 Slur_score_state::generate_avoid_offsets () const
616 {
617   vector<Offset> avoid;
618   vector<Grob*> encompasses = columns_;
619
620   for (vsize i = 0; i < encompasses.size (); i++)
621     {
622       if (extremes_[LEFT].note_column_ == encompasses[i]
623           || extremes_[RIGHT].note_column_ == encompasses[i])
624         continue;
625
626       Encompass_info inf (get_encompass_info (encompasses[i]));
627       Real y = dir_ * (max (dir_ * inf.head_, dir_ * inf.stem_));
628
629       avoid.push_back (Offset (inf.x_, y + dir_ * parameters_.free_head_distance_));
630     }
631
632   extract_grob_set (slur_, "encompass-objects", extra_encompasses);
633   for (vsize i = 0; i < extra_encompasses.size (); i++)
634     {
635       if (Slur::has_interface (extra_encompasses[i]))
636         {
637           Grob *small_slur = extra_encompasses[i];
638           Bezier b = Slur::get_curve (small_slur);
639
640           Offset z = b.curve_point (0.5);
641           z += Offset (small_slur->relative_coordinate (common_[X_AXIS], X_AXIS),
642                        small_slur->relative_coordinate (common_[Y_AXIS], Y_AXIS));
643
644           z[Y_AXIS] += dir_ * parameters_.free_slur_distance_;
645           avoid.push_back (z);
646         }
647       else if (extra_encompasses[i]->get_property ("avoid-slur") == ly_symbol2scm ("inside"))
648         {
649           Grob *g = extra_encompasses [i];
650           Interval xe = g->extent (common_[X_AXIS], X_AXIS);
651           Interval ye = g->extent (common_[Y_AXIS], Y_AXIS);
652
653           if (!xe.is_empty ()
654               && !ye.is_empty ())
655             avoid.push_back (Offset (xe.center(), ye[dir_]));
656         }
657     }  
658   return avoid;
659 }
660
661 void
662 Slur_score_state::generate_curves () const
663 {
664   Real r_0 = robust_scm2double (slur_->get_property ("ratio"), 0.33);
665   Real h_inf = staff_space_ * scm_to_double (slur_->get_property ("height-limit"));
666
667   vector<Offset> avoid = generate_avoid_offsets ();
668   for (vsize i = 0; i < configurations_.size (); i++)
669     configurations_[i]->generate_curve (*this, r_0, h_inf, avoid);
670 }
671
672 vector<Slur_configuration*>
673 Slur_score_state::enumerate_attachments (Drul_array<Real> end_ys) const
674 {
675   vector<Slur_configuration*> scores;
676
677   Drul_array<Offset> os;
678   os[LEFT] = base_attachments_[LEFT];
679   Real minimum_length = staff_space_
680     * robust_scm2double (slur_->get_property ("minimum-length"), 2.0);
681
682   for (int i = 0; dir_ * os[LEFT][Y_AXIS] <= dir_ * end_ys[LEFT]; i++)
683     {
684       os[RIGHT] = base_attachments_[RIGHT];
685       for (int j = 0; dir_ * os[RIGHT][Y_AXIS] <= dir_ * end_ys[RIGHT]; j++)
686         {
687           Slur_configuration s;
688           Direction d = LEFT;
689           Drul_array<bool> attach_to_stem (false, false);
690           do
691             {
692               os[d][X_AXIS] = base_attachments_[d][X_AXIS];
693               if (extremes_[d].stem_
694                   && !Stem::is_invisible (extremes_[d].stem_)
695                   && extremes_[d].stem_dir_ == dir_)
696                 {
697                   Interval stem_y = extremes_[d].stem_extent_[Y_AXIS];
698                   stem_y.widen (0.25 * staff_space_);
699                   if (stem_y.contains (os[d][Y_AXIS]))
700                     {
701                       os[d][X_AXIS] = extremes_[d].stem_extent_[X_AXIS][-d]
702                         - d * 0.3;
703                       attach_to_stem[d] = true;
704                     }
705                   else if (dir_ * extremes_[d].stem_extent_[Y_AXIS][dir_]
706                            < dir_ * os[d][Y_AXIS]
707                            && !extremes_[d].stem_extent_[X_AXIS].is_empty ())
708
709                     os[d][X_AXIS] = extremes_[d].stem_extent_[X_AXIS].center ();
710                 }
711             }
712           while (flip (&d) != LEFT);
713
714           Offset dz;
715           dz = os[RIGHT] - os[LEFT];
716           if (dz[X_AXIS] < minimum_length
717               || fabs (dz[Y_AXIS] / dz[X_AXIS]) > parameters_.max_slope_)
718             {
719               do
720                 {
721                   if (extremes_[d].slur_head_
722                       && !extremes_[d].slur_head_x_extent_.is_empty ())
723                     {
724                       os[d][X_AXIS] = extremes_[d].slur_head_x_extent_.center ();
725                       attach_to_stem[d] = false;
726                     }
727                 }
728               while (flip (&d) != LEFT);
729             }
730
731           dz = os[RIGHT] - os[LEFT];
732           do
733             {
734               if (extremes_[d].slur_head_
735                   && !attach_to_stem[d])
736                 {
737                   /* Horizontally move tilted slurs a little.  Move
738                      more for bigger tilts.
739
740                      TODO: parameter */
741                   os[d][X_AXIS]
742                     -= dir_ * extremes_[d].slur_head_x_extent_.length ()
743                     * sin (dz.arg ()) / 3;
744                 }
745             }
746           while (flip (&d) != LEFT);
747
748           s.attachment_ = os;
749           s.index_ = scores.size ();
750
751           scores.push_back (new Slur_configuration (s));
752
753           os[RIGHT][Y_AXIS] += dir_ * staff_space_ / 2;
754         }
755
756       os[LEFT][Y_AXIS] += dir_ * staff_space_ / 2;
757     }
758
759   assert (scores.size () > 0);
760   return scores;
761 }
762
763 vector<Extra_collision_info>
764 Slur_score_state::get_extra_encompass_infos () const
765 {
766   extract_grob_set (slur_, "encompass-objects", encompasses);
767   vector<Extra_collision_info> collision_infos;
768   for (vsize i = encompasses.size (); i--;)
769     {
770       if (Slur::has_interface (encompasses[i]))
771         {
772           Spanner *small_slur = dynamic_cast<Spanner *> (encompasses[i]);
773           Bezier b = Slur::get_curve (small_slur);
774
775           Offset relative (small_slur->relative_coordinate (common_[X_AXIS], X_AXIS),
776                            small_slur->relative_coordinate (common_[Y_AXIS], Y_AXIS));
777
778           for (int k = 0; k < 3; k++)
779             {
780               Direction hdir = Direction (k - 1);
781
782               /*
783                 Only take bound into account if small slur starts
784                 together with big slur.
785               */
786               if (hdir && small_slur->get_bound (hdir) != slur_->get_bound (hdir))
787                 continue;
788
789               Offset z = b.curve_point (k / 2.0);
790               z += relative;
791
792               Interval yext;
793               yext.set_full ();
794               yext[dir_] = z[Y_AXIS] + dir_ * thickness_ * 1.0;
795
796               Interval xext (-1, 1);
797               xext = xext * (thickness_ * 2) + z[X_AXIS];
798               Extra_collision_info info (small_slur,
799                                          hdir,
800                                          xext,
801                                          yext,
802                                          parameters_.extra_object_collision_penalty_);
803               collision_infos.push_back (info);
804             }
805         }
806       else
807         {
808           Grob *g = encompasses [i];
809           Interval xe = g->extent (common_[X_AXIS], X_AXIS);
810           Interval ye = g->extent (common_[Y_AXIS], Y_AXIS);
811
812           Real xp = 0.0;
813           Real penalty = parameters_.extra_object_collision_penalty_;
814           if (Accidental_interface::has_interface (g))
815             {
816               penalty = parameters_.accidental_collision_;
817               /* Begin copy accidental.cc */
818               bool parens = false;
819               if (to_boolean (g->get_property ("cautionary")))
820                 {
821                   SCM cstyle = g->get_property ("cautionary-style");
822                   parens = ly_is_equal (cstyle, ly_symbol2scm ("parentheses"));
823                 }
824
825               SCM accs = g->get_property ("accidentals");
826               SCM scm_style = g->get_property ("style");
827               if (!scm_is_symbol (scm_style)
828                   && !parens
829                   && scm_ilength (accs) == 1)
830                 {
831                   /* End copy accidental.cc */
832                   switch (scm_to_int (scm_car (accs)))
833                     {
834                     case FLAT:
835                     case DOUBLE_FLAT:
836                       xp = LEFT;
837                       break;
838                     case SHARP:
839                       xp = 0.5 * dir_;
840                       break;
841                     case NATURAL:
842                       xp = -dir_;
843                       break;
844                     }
845                 }
846             }
847
848           ye.widen (thickness_ * 0.5);
849           xe.widen (thickness_ * 1.0);
850           Extra_collision_info info (g, xp, xe, ye, penalty);
851           collision_infos.push_back (info);
852         }
853     }
854
855   return collision_infos;
856 }
857  
858 Extra_collision_info::Extra_collision_info (Grob *g, Real idx, Interval x, Interval y, Real p)
859 {
860   idx_ = idx;
861   extents_[X_AXIS] = x;
862   extents_[Y_AXIS] = y;
863   penalty_ = p;
864   grob_ = g;
865   type_ = g->get_property ("avoid-slur");
866 }
867
868 Extra_collision_info::Extra_collision_info ()
869 {
870   idx_ = 0.0;
871   penalty_ = 0.;
872   grob_ = 0;
873   type_ = SCM_EOL; 
874 }