Bump version.
[lilypond.git] / lily / slur-scoring.cc
blobbb2b1f3b2bafed2593fd0ac688b3cecef440cb9e
1 /*
2 slur-scoring.cc -- Score based slur formatting
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
8 */
11 #include "slur-scoring.hh"
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"
32 TODO:
34 - curve around flag for y coordinate
36 - short-cut: try a smaller region first.
38 - handle non-visible stems better.
40 - try to prune number of scoring criteria
42 - take encompass-objects more into account when determining
43 slur shape
45 - calculate encompass scoring directly after determining slur shape.
47 - optimize.
49 struct Slur_score_state;
51 Slur_score_state::Slur_score_state ()
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;
64 Slur_score_state::~Slur_score_state ()
66 junk_pointers (configurations_);
70 copy slur dir forwards across line break.
72 void
73 Slur_score_state::set_next_direction ()
75 if (extremes_[RIGHT].note_column_)
76 return;
78 if (Grob *neighbor = slur_->broken_neighbor (RIGHT))
80 set_grob_direction (neighbor, dir_);
84 Encompass_info
85 Slur_score_state::get_encompass_info (Grob *col) const
87 Grob *stem = unsmob_grob (col->get_object ("stem"));
88 Encompass_info ei;
90 if (!stem)
92 programming_error ("no stem for note column");
93 ei.x_ = col->relative_coordinate (common_[X_AXIS], X_AXIS);
94 ei.head_ = ei.stem_ = col->extent (common_[Y_AXIS],
95 Y_AXIS)[dir_];
96 return ei;
98 Direction stem_dir = get_grob_direction (stem);
100 if (Grob *head = Note_column::first_head (col))
101 ei.x_ = head->extent (common_[X_AXIS], X_AXIS).center ();
102 else
103 ei.x_ = col->extent (common_[X_AXIS], X_AXIS).center ();
105 Grob *h = Stem::extremal_heads (stem)[Direction (dir_)];
106 if (!h)
108 ei.head_ = ei.stem_ = col->extent (common_[Y_AXIS], Y_AXIS)[dir_];
109 return ei;
112 ei.head_ = h->extent (common_[Y_AXIS], Y_AXIS)[dir_];
114 if ((stem_dir == dir_)
115 && !stem->extent (stem, Y_AXIS).is_empty ())
117 ei.stem_ = stem->extent (common_[Y_AXIS], Y_AXIS)[dir_];
118 if (Grob *b = Stem::get_beam (stem))
119 ei.stem_ += stem_dir * 0.5 * Beam::get_thickness (b);
121 Interval x = stem->extent (common_[X_AXIS], X_AXIS);
122 ei.x_ = x.is_empty ()
123 ? stem->relative_coordinate (common_[X_AXIS], X_AXIS)
124 : x.center ();
126 else
127 ei.stem_ = ei.head_;
129 return ei;
132 Drul_array<Bound_info>
133 Slur_score_state::get_bound_info () const
135 Drul_array<Bound_info> extremes;
137 Direction d = LEFT;
138 Direction dir = dir_;
142 extremes[d].bound_ = slur_->get_bound (d);
143 if (Note_column::has_interface (extremes[d].bound_))
145 extremes[d].note_column_ = extremes[d].bound_;
146 extremes[d].stem_ = Note_column::get_stem (extremes[d].note_column_);
147 if (extremes[d].stem_)
149 extremes[d].stem_dir_ = get_grob_direction (extremes[d].stem_);
151 for (int a = X_AXIS; a < NO_AXES; a++)
153 Axis ax = Axis (a);
154 Interval s = extremes[d].stem_->extent (common_[ax], ax);
155 if (s.is_empty ())
158 do not issue warning. This happens for rests and
159 whole notes.
161 s = Interval (0, 0)
162 + extremes[d].stem_->relative_coordinate (common_[ax], ax);
164 extremes[d].stem_extent_[ax] = s;
167 extremes[d].slur_head_
168 = Stem::extremal_heads (extremes[d].stem_)[dir];
169 if (!extremes[d].slur_head_
170 && Note_column::has_rests (extremes[d].bound_))
171 extremes[d].slur_head_ = Note_column::get_rest (extremes[d].bound_);
172 extremes[d].staff_ = Staff_symbol_referencer
173 ::get_staff_symbol (extremes[d].stem_);
174 extremes[d].staff_space_ = Staff_symbol_referencer
175 ::staff_space (extremes[d].stem_);
178 if (extremes[d].slur_head_)
179 extremes[d].slur_head_x_extent_
180 = extremes[d].slur_head_->extent (common_[X_AXIS], X_AXIS);
184 while (flip (&d) != LEFT);
186 return extremes;
189 void
190 Slur_score_state::fill (Grob *me)
192 slur_ = dynamic_cast<Spanner *> (me);
193 columns_
194 = internal_extract_grob_array (me, ly_symbol2scm ("note-columns"));
196 if (columns_.empty ())
198 me->suicide ();
199 return;
202 Slur::replace_breakable_encompass_objects (me);
203 staff_space_ = Staff_symbol_referencer::staff_space (me);
204 Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
205 thickness_ = robust_scm2double (me->get_property ("thickness"), 1.0) * lt;
207 dir_ = get_grob_direction (me);
208 parameters_.fill (me);
210 extract_grob_set (me, "note-columns", columns);
211 extract_grob_set (me, "encompass-objects", extra_objects);
213 Spanner *sp = dynamic_cast<Spanner *> (me);
215 for (int i = X_AXIS; i < NO_AXES; i++)
217 Axis a = (Axis)i;
218 common_[a] = common_refpoint_of_array (columns, me, a);
219 common_[a] = common_refpoint_of_array (extra_objects, common_[a], a);
221 Direction d = LEFT;
225 If bound is not in note-columns, we don't want to know about
226 its Y-position
228 if (a != Y_AXIS)
229 common_[a] = common_[a]->common_refpoint (sp->get_bound (d), a);
231 while (flip (&d) != LEFT);
234 extremes_ = get_bound_info ();
235 is_broken_ = (!extremes_[LEFT].note_column_
236 || !extremes_[RIGHT].note_column_);
238 has_same_beam_
239 = (extremes_[LEFT].stem_ && extremes_[RIGHT].stem_
240 && Stem::get_beam (extremes_[LEFT].stem_) == Stem::get_beam (extremes_[RIGHT].stem_));
242 base_attachments_ = get_base_attachments ();
244 Drul_array<Real> end_ys
245 = get_y_attachment_range ();
247 configurations_ = enumerate_attachments (end_ys);
248 for (vsize i = 0; i < columns_.size (); i++)
249 encompass_infos_.push_back (get_encompass_info (columns_[i]));
251 extra_encompass_infos_ = get_extra_encompass_infos ();
252 valid_ = true;
254 musical_dy_ = 0.0;
255 Direction d = LEFT;
258 if (!is_broken_
259 && extremes_[d].slur_head_)
260 musical_dy_ += d
261 * extremes_[d].slur_head_->relative_coordinate (common_[Y_AXIS], Y_AXIS);
263 while (flip (&d) != LEFT);
265 edge_has_beams_
266 = (extremes_[LEFT].stem_ && Stem::get_beam (extremes_[LEFT].stem_))
267 || (extremes_[RIGHT].stem_ && Stem::get_beam (extremes_[RIGHT].stem_));
269 set_next_direction ();
271 if (is_broken_)
272 musical_dy_ = 0.0;
276 MAKE_SCHEME_CALLBACK (Slur, calc_control_points, 1)
278 Slur::calc_control_points (SCM smob)
280 Spanner *me = unsmob_spanner (smob);
282 Slur_score_state state;
283 state.fill (me);
285 if (!state.valid_)
286 return SCM_EOL;
288 state.generate_curves ();
290 SCM end_ys = me->get_property ("positions");
291 Bezier best;
293 if (is_number_pair (end_ys))
294 best = state.configurations_[state.get_closest_index (end_ys)]->curve_;
295 else
296 best = state.get_best_curve ();
298 SCM controls = SCM_EOL;
299 for (int i = 4; i--;)
301 Offset o = best.control_[i]
302 - Offset (me->relative_coordinate (state.common_[X_AXIS], X_AXIS),
303 me->relative_coordinate (state.common_[Y_AXIS], Y_AXIS));
304 controls = scm_cons (ly_offset2scm (o), controls);
307 return controls;
310 Bezier
311 Slur_score_state::get_best_curve ()
313 int opt_idx = -1;
314 Real opt = 1e6;
316 #if DEBUG_SLUR_SCORING
317 bool debug_slurs = to_boolean (slur_->layout ()
318 ->lookup_variable (ly_symbol2scm ("debug-slur-scoring")));
319 SCM inspect_quants = slur_->get_property ("inspect-quants");
320 SCM inspect_index = slur_->get_property ("inspect-index");
321 if (debug_slurs
322 && scm_is_integer (inspect_index))
324 opt_idx = scm_to_int (inspect_index);
325 configurations_[opt_idx]->calculate_score (*this);
326 opt = configurations_[opt_idx]->score ();
328 else if (debug_slurs
329 && scm_is_pair (inspect_quants))
331 opt_idx = get_closest_index (inspect_quants);
332 configurations_[opt_idx]->calculate_score (*this);
333 opt = configurations_[opt_idx]->score ();
335 else
336 #endif
338 for (vsize i = 0; i < configurations_.size (); i++)
339 configurations_[i]->calculate_score (*this);
340 for (vsize i = 0; i < configurations_.size (); i++)
342 if (configurations_[i]->score () < opt)
344 opt = configurations_[i]->score ();
345 opt_idx = i;
350 #if DEBUG_SLUR_SCORING
351 if (debug_slurs)
353 string total;
354 if (opt_idx >= 0)
356 total = configurations_[opt_idx]->card ();
357 total += to_string (" TOTAL=%.2f idx=%d", configurations_[opt_idx]->score (), opt_idx);
359 else
361 total = "no sol?";
364 slur_->set_property ("quant-score",
365 ly_string2scm (total));
367 #endif
369 if (opt_idx < 0)
371 opt_idx = 0;
372 programming_error ("No optimal slur found. Guessing 0.");
375 return configurations_[opt_idx]->curve_;
378 Grob *
379 Slur_score_state::breakable_bound_item (Direction d) const
381 Grob *col = slur_->get_bound (d)->get_column ();
383 extract_grob_set (slur_, "encompass-objects", extra_encompasses);
385 for (vsize i = 0; i < extra_encompasses.size (); i++)
387 Item *item = dynamic_cast<Item*> (extra_encompasses[i]);
388 if (item && col == item->get_column ())
389 return item;
392 return 0;
396 Slur_score_state::get_closest_index (SCM inspect_quants) const
398 Drul_array<Real> ins = ly_scm2interval (inspect_quants);
400 int opt_idx = -1;
401 Real mindist = 1e6;
402 for (vsize i = 0; i < configurations_.size (); i++)
404 Real d = fabs (configurations_[i]->attachment_[LEFT][Y_AXIS] - ins[LEFT])
405 + fabs (configurations_[i]->attachment_[RIGHT][Y_AXIS] - ins[RIGHT]);
406 if (d < mindist)
408 opt_idx = i;
409 mindist = d;
412 if (mindist > 1e5)
413 programming_error ("cannot find quant");
414 return opt_idx;
418 TODO: should analyse encompasses to determine sensible region, and
419 should limit slopes available.
422 Drul_array<Real>
423 Slur_score_state::get_y_attachment_range () const
425 Drul_array<Real> end_ys;
426 Direction d = LEFT;
429 if (extremes_[d].note_column_)
431 end_ys[d] = dir_
432 * max (max (dir_ * (base_attachments_[d][Y_AXIS]
433 + parameters_.region_size_ * dir_),
434 dir_ * (dir_ + extremes_[d].note_column_->extent (common_[Y_AXIS], Y_AXIS)[dir_])),
435 dir_ * base_attachments_[-d][Y_AXIS]);
437 else
438 end_ys[d] = base_attachments_[d][Y_AXIS] + parameters_.region_size_ * dir_;
440 while (flip (&d) != LEFT);
442 return end_ys;
445 bool
446 spanner_less (Spanner *s1, Spanner *s2)
448 Slice b1, b2;
449 Direction d = LEFT;
452 b1[d] = s1->get_bound (d)->get_column ()->get_rank ();
453 b2[d] = s2->get_bound (d)->get_column ()->get_rank ();
455 while (flip (&d) != LEFT);
457 return b2[LEFT] <= b1[LEFT] && b2[RIGHT] >= b1[RIGHT]
458 && (b2[LEFT] != b1[LEFT] || b2[RIGHT] != b1[RIGHT]);
461 Drul_array<Offset>
462 Slur_score_state::get_base_attachments () const
464 Drul_array<Offset> base_attachment;
465 Direction d = LEFT;
468 Grob *stem = extremes_[d].stem_;
469 Grob *head = extremes_[d].slur_head_;
471 Real x = 0.0;
472 Real y = 0.0;
473 if (extremes_[d].note_column_)
477 fixme: X coord should also be set in this case.
479 if (stem
480 && !Stem::is_invisible (stem)
481 && extremes_[d].stem_dir_ == dir_
482 && Stem::get_beaming (stem, -d)
483 && Stem::get_beam (stem)
484 && (!spanner_less (slur_, Stem::get_beam (stem))
485 || has_same_beam_))
486 y = extremes_[d].stem_extent_[Y_AXIS][dir_];
487 else if (head)
488 y = head->extent (common_[Y_AXIS], Y_AXIS)[dir_];
489 y += dir_ * 0.5 * staff_space_;
491 y = move_away_from_staffline (y, head);
493 Grob *fh = Note_column::first_head (extremes_[d].note_column_);
495 = (fh ? fh->extent (common_[X_AXIS], X_AXIS)
496 : extremes_[d].bound_->extent (common_[X_AXIS], X_AXIS))
497 .linear_combination (CENTER);
499 base_attachment[d] = Offset (x, y);
501 while (flip (&d) != LEFT);
505 if (!extremes_[d].note_column_)
507 Real x = 0;
508 Real y = 0;
510 if (Grob *g = breakable_bound_item (d))
512 x = robust_relative_extent (g, common_[X_AXIS], X_AXIS)[RIGHT];
514 else if (d == RIGHT)
515 x = robust_relative_extent (extremes_[d].bound_, common_[X_AXIS], X_AXIS)[d];
516 else
517 x = slur_->get_broken_left_end_align ();
519 Grob *col = (d == LEFT) ? columns_[0] : columns_.back ();
521 if (extremes_[-d].bound_ != col)
523 y = robust_relative_extent (col, common_[Y_AXIS], Y_AXIS)[dir_];
524 y += dir_ * 0.5 * staff_space_;
526 if (get_grob_direction (col) == dir_
527 && Note_column::get_stem (col)
528 && !Stem::is_invisible (Note_column::get_stem (col)))
529 y -= dir_ * 1.5 * staff_space_;
531 else
532 y = base_attachment[-d][Y_AXIS];
534 y = move_away_from_staffline (y, col);
536 base_attachment[d] = Offset (x, y);
539 while (flip (&d) != LEFT);
543 for (int a = X_AXIS; a < NO_AXES; a++)
545 Real &b = base_attachment[d][Axis (a)];
547 if (isinf (b) || isnan (b))
549 programming_error ("slur attachment is inf/nan");
550 b = 0.0;
554 while (flip (&d) != LEFT);
556 return base_attachment;
559 Real
560 Slur_score_state::move_away_from_staffline (Real y,
561 Grob *on_staff) const
563 if (!on_staff)
564 return y;
566 Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (on_staff);
567 if (!staff_symbol)
568 return y;
570 Real pos
571 = (y - staff_symbol->relative_coordinate (common_[Y_AXIS],
572 Y_AXIS))
573 * 2.0 / staff_space_;
575 if (fabs (pos - my_round (pos)) < 0.2
576 && Staff_symbol_referencer::on_line (on_staff, (int) rint (pos))
577 && Staff_symbol_referencer::line_count (on_staff) - 1 >= rint (pos))
578 y += 1.5 * staff_space_ * dir_ / 10;
580 return y;
583 vector<Offset>
584 Slur_score_state::generate_avoid_offsets () const
586 vector<Offset> avoid;
587 vector<Grob*> encompasses = columns_;
589 for (vsize i = 0; i < encompasses.size (); i++)
591 if (extremes_[LEFT].note_column_ == encompasses[i]
592 || extremes_[RIGHT].note_column_ == encompasses[i])
593 continue;
595 Encompass_info inf (get_encompass_info (encompasses[i]));
596 Real y = dir_ * (max (dir_ * inf.head_, dir_ * inf.stem_));
598 avoid.push_back (Offset (inf.x_, y + dir_ * parameters_.free_head_distance_));
601 extract_grob_set (slur_, "encompass-objects", extra_encompasses);
602 for (vsize i = 0; i < extra_encompasses.size (); i++)
604 if (Slur::has_interface (extra_encompasses[i]))
606 Grob *small_slur = extra_encompasses[i];
607 Bezier b = Slur::get_curve (small_slur);
609 Offset z = b.curve_point (0.5);
610 z += Offset (small_slur->relative_coordinate (common_[X_AXIS], X_AXIS),
611 small_slur->relative_coordinate (common_[Y_AXIS], Y_AXIS));
613 z[Y_AXIS] += dir_ * parameters_.free_slur_distance_;
614 avoid.push_back (z);
616 else if (extra_encompasses[i]->get_property ("avoid-slur") == ly_symbol2scm ("inside"))
618 Grob *g = extra_encompasses [i];
619 Interval xe = g->extent (common_[X_AXIS], X_AXIS);
620 Interval ye = g->extent (common_[Y_AXIS], Y_AXIS);
622 if (!xe.is_empty ()
623 && !ye.is_empty ())
624 avoid.push_back (Offset (xe.center (), ye[dir_]));
627 return avoid;
630 void
631 Slur_score_state::generate_curves () const
633 Real r_0 = robust_scm2double (slur_->get_property ("ratio"), 0.33);
634 Real h_inf = staff_space_ * scm_to_double (slur_->get_property ("height-limit"));
636 vector<Offset> avoid = generate_avoid_offsets ();
637 for (vsize i = 0; i < configurations_.size (); i++)
638 configurations_[i]->generate_curve (*this, r_0, h_inf, avoid);
641 vector<Slur_configuration*>
642 Slur_score_state::enumerate_attachments (Drul_array<Real> end_ys) const
644 vector<Slur_configuration*> scores;
646 Drul_array<Offset> os;
647 os[LEFT] = base_attachments_[LEFT];
648 Real minimum_length = staff_space_
649 * robust_scm2double (slur_->get_property ("minimum-length"), 2.0);
651 for (int i = 0; dir_ * os[LEFT][Y_AXIS] <= dir_ * end_ys[LEFT]; i++)
653 os[RIGHT] = base_attachments_[RIGHT];
654 for (int j = 0; dir_ * os[RIGHT][Y_AXIS] <= dir_ * end_ys[RIGHT]; j++)
656 Slur_configuration s;
657 Direction d = LEFT;
658 Drul_array<bool> attach_to_stem (false, false);
661 os[d][X_AXIS] = base_attachments_[d][X_AXIS];
662 if (extremes_[d].stem_
663 && !Stem::is_invisible (extremes_[d].stem_)
664 && extremes_[d].stem_dir_ == dir_)
666 Interval stem_y = extremes_[d].stem_extent_[Y_AXIS];
667 stem_y.widen (0.25 * staff_space_);
668 if (stem_y.contains (os[d][Y_AXIS]))
670 os[d][X_AXIS] = extremes_[d].stem_extent_[X_AXIS][-d]
671 - d * 0.3;
672 attach_to_stem[d] = true;
674 else if (dir_ * extremes_[d].stem_extent_[Y_AXIS][dir_]
675 < dir_ * os[d][Y_AXIS]
676 && !extremes_[d].stem_extent_[X_AXIS].is_empty ())
678 os[d][X_AXIS] = extremes_[d].stem_extent_[X_AXIS].center ();
681 while (flip (&d) != LEFT);
683 Offset dz;
684 dz = os[RIGHT] - os[LEFT];
685 if (dz[X_AXIS] < minimum_length
686 || fabs (dz[Y_AXIS] / dz[X_AXIS]) > parameters_.max_slope_)
690 if (extremes_[d].slur_head_
691 && !extremes_[d].slur_head_x_extent_.is_empty ())
693 os[d][X_AXIS] = extremes_[d].slur_head_x_extent_.center ();
694 attach_to_stem[d] = false;
697 while (flip (&d) != LEFT);
700 dz = os[RIGHT] - os[LEFT];
703 if (extremes_[d].slur_head_
704 && !attach_to_stem[d])
706 /* Horizontally move tilted slurs a little. Move
707 more for bigger tilts.
709 TODO: parameter */
710 os[d][X_AXIS]
711 -= dir_ * extremes_[d].slur_head_x_extent_.length ()
712 * sin (dz.arg ()) / 3;
715 while (flip (&d) != LEFT);
717 s.attachment_ = os;
718 s.index_ = scores.size ();
720 scores.push_back (new Slur_configuration (s));
722 os[RIGHT][Y_AXIS] += dir_ * staff_space_ / 2;
725 os[LEFT][Y_AXIS] += dir_ * staff_space_ / 2;
728 assert (scores.size () > 0);
729 return scores;
732 vector<Extra_collision_info>
733 Slur_score_state::get_extra_encompass_infos () const
735 extract_grob_set (slur_, "encompass-objects", encompasses);
736 vector<Extra_collision_info> collision_infos;
737 for (vsize i = encompasses.size (); i--;)
739 if (Slur::has_interface (encompasses[i]))
741 Spanner *small_slur = dynamic_cast<Spanner *> (encompasses[i]);
742 Bezier b = Slur::get_curve (small_slur);
744 Offset relative (small_slur->relative_coordinate (common_[X_AXIS], X_AXIS),
745 small_slur->relative_coordinate (common_[Y_AXIS], Y_AXIS));
747 for (int k = 0; k < 3; k++)
749 Direction hdir = Direction (k - 1);
752 Only take bound into account if small slur starts
753 together with big slur.
755 if (hdir && small_slur->get_bound (hdir) != slur_->get_bound (hdir))
756 continue;
758 Offset z = b.curve_point (k / 2.0);
759 z += relative;
761 Interval yext;
762 yext.set_full ();
763 yext[dir_] = z[Y_AXIS] + dir_ * thickness_ * 1.0;
765 Interval xext (-1, 1);
766 xext = xext * (thickness_ * 2) + z[X_AXIS];
767 Extra_collision_info info (small_slur,
768 hdir,
769 xext,
770 yext,
771 parameters_.extra_object_collision_penalty_);
772 collision_infos.push_back (info);
775 else
777 Grob *g = encompasses [i];
778 Interval xe = g->extent (common_[X_AXIS], X_AXIS);
779 Interval ye = g->extent (common_[Y_AXIS], Y_AXIS);
781 Real xp = 0.0;
782 Real penalty = parameters_.extra_object_collision_penalty_;
783 if (Accidental_interface::has_interface (g))
785 penalty = parameters_.accidental_collision_;
787 Rational alt = ly_scm2rational (g->get_property ("alteration"));
788 SCM scm_style = g->get_property ("style");
789 if (!scm_is_symbol (scm_style)
790 && !to_boolean (g->get_property ("parenthesized"))
791 && !to_boolean (g->get_property ("restore-first")))
793 /* End copy accidental.cc */
794 if (alt == FLAT_ALTERATION
795 || alt == DOUBLE_FLAT_ALTERATION)
796 xp = LEFT;
797 else if (alt == SHARP_ALTERATION)
798 xp = 0.5 * dir_;
799 else if (alt == NATURAL_ALTERATION)
800 xp = -dir_;
804 ye.widen (thickness_ * 0.5);
805 xe.widen (thickness_ * 1.0);
806 Extra_collision_info info (g, xp, xe, ye, penalty);
807 collision_infos.push_back (info);
811 return collision_infos;
814 Extra_collision_info::Extra_collision_info (Grob *g, Real idx, Interval x, Interval y, Real p)
816 idx_ = idx;
817 extents_[X_AXIS] = x;
818 extents_[Y_AXIS] = y;
819 penalty_ = p;
820 grob_ = g;
821 type_ = g->get_property ("avoid-slur");
824 Extra_collision_info::Extra_collision_info ()
826 idx_ = 0.0;
827 penalty_ = 0.;
828 grob_ = 0;
829 type_ = SCM_EOL;