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