2 slur-quanting.cc -- Score based slur formatting
4 source file of the GNU LilyPond music typesetter
6 (c) 1996--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
12 #include "accidental-interface.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"
22 #include "slur-bezier-bow.hh"
24 #include "staff-symbol-referencer.hh"
25 #include "staff-symbol.hh"
28 #include "paper-column.hh"
33 Drul_array<Offset> attachment_;
37 #if DEBUG_SLUR_QUANTING
48 TODO: put in details property.,
52 struct Slur_score_parameters
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;
64 Real MAX_SLOPE_FACTOR;
65 Real EXTRA_OBJECT_COLLISION;
66 Real ACCIDENTAL_COLLISION;
67 Real FREE_HEAD_DISTANCE;
68 Slur_score_parameters ();
74 - curve around flag for y coordinate
76 - short-cut: try a smaller region first.
77 - collisions with accidentals
78 - collisions with articulations (staccato, portato, sforzato, ...)
79 - handle non-visible stems better.
103 Interval slur_head_extent_;
122 score_extra_encompass (Grob *me, Grob *common[],
123 Slur_score_parameters *score_param,
124 Drul_array<Bound_info> ,
126 Array<Slur_score> * scores);
127 static void score_slopes (Grob *me, Grob *common[],
128 Slur_score_parameters *score_param,
129 Drul_array<Bound_info>,
130 Drul_array<Offset> base_attach,
131 Array<Slur_score> *scores);
133 static void score_edges (Grob *me, Grob *common[],
134 Slur_score_parameters *score_param,
135 Drul_array<Bound_info> extremes,
136 Drul_array<Offset> base_attach,
137 Array<Slur_score> *scores);
138 static void score_encompass (Grob *me, Grob *common[],
139 Slur_score_parameters*,
140 Drul_array<Bound_info>,
141 Drul_array<Offset>, Array<Slur_score> *scores);
142 static Bezier avoid_staff_line (Grob *me, Grob **common,
143 Drul_array<Bound_info> extremes,
146 static Encompass_info get_encompass_info (Grob *me,
149 static Bezier get_bezier (Grob *me, Drul_array<Offset>, Real, Real);
150 static Direction get_default_dir (Grob *me);
152 static void set_end_points (Grob *);
153 static Real broken_trend_y (Grob *me, Grob **, Direction dir);
154 static Drul_array<Bound_info> get_bound_info (Spanner *me, Grob **common);
156 static void generate_curves (Grob *me,
158 Drul_array<Bound_info> extremes,
159 Drul_array<Offset> base_attach,
160 Array<Slur_score> *scores);
161 static Array<Slur_score> enumerate_attachments
162 (Grob *me, Grob *common[], Slur_score_parameters *score_param,
163 Drul_array<Bound_info> extremes,
164 Drul_array<Offset> base_attachment, Drul_array<Real> end_ys);
165 static Drul_array<Offset> get_base_attachments
166 (Spanner *sp, Grob **common, Drul_array<Bound_info> extremes);
167 static Drul_array<Real> get_y_attachment_range
168 (Spanner *sp, Grob **common, Drul_array<Bound_info> extremes,
169 Drul_array<Offset> base_attachment);
172 init_score_param (Slur_score_parameters *score_param)
174 score_param->SLUR_REGION_SIZE = 5;
175 score_param->HEAD_ENCOMPASS_PENALTY = 1000.0;
176 score_param->STEM_ENCOMPASS_PENALTY = 30.0;
177 score_param->CLOSENESS_FACTOR = 10;
178 score_param->EDGE_ATTRACTION_FACTOR = 4;
179 score_param->SAME_SLOPE_PENALTY = 20;
180 score_param->STEEPER_SLOPE_FACTOR = 50;
181 score_param->NON_HORIZONTAL_PENALTY = 15;
182 score_param->HEAD_STRICT_FREE_SPACE = 0.2;
183 score_param->MAX_SLOPE = 1.1;
184 score_param->MAX_SLOPE_FACTOR = 10;
185 score_param->FREE_HEAD_DISTANCE = 0.3;
186 score_param->EXTRA_OBJECT_COLLISION = 50;
187 score_param->ACCIDENTAL_COLLISION = 3;
190 Slur_score_parameters::Slur_score_parameters()
192 init_score_param (this);
195 /* HDIR indicates the direction for the slur. */
197 broken_trend_y (Grob *me, Grob **common, Direction hdir)
199 /* A broken slur should maintain the same vertical trend
200 the unbroken slur would have had. */
202 if (Spanner *mother = dynamic_cast<Spanner*> (me->original_))
204 int k = broken_spanner_index (dynamic_cast<Spanner*> (me));
206 if (j < 0 || j >= mother->broken_intos_.size ())
209 Grob *neighbor = mother->broken_intos_[j];
211 neighbor->set_property ("direction",
212 me->get_property ("direction"));
214 Spanner *common_mother
215 = dynamic_cast<Spanner*> (common[Y_AXIS]->original_);
217 = broken_spanner_index (dynamic_cast<Spanner*> (common[Y_AXIS]));
218 int common_j = common_k + hdir;
220 if (common_j < 0 || common_j >= common_mother->broken_intos_.size())
223 Grob *common_next_system = common_mother->broken_intos_[common_j];
224 Link_array<Grob> neighbor_cols
225 = Pointer_group_interface__extract_grobs (neighbor, (Grob *)0,
229 = (hdir == RIGHT) ? neighbor_cols[0] : neighbor_cols.top ();
230 Grob *neighbor_common
231 = common_next_system->common_refpoint (neighbor_col, Y_AXIS);
233 Direction vdir = get_grob_direction (me);
235 = neighbor_col->extent (neighbor_common, Y_AXIS)
236 .linear_combination (int(neighbor_cols.size()==1 ? CENTER : vdir))
237 - common_next_system->relative_coordinate (neighbor_common, Y_AXIS);
239 Link_array<Grob> my_cols
240 = Pointer_group_interface__extract_grobs (me, (Grob *)0,
243 Grob *extreme_col = (hdir == RIGHT) ? my_cols.top() : my_cols[0];
244 Real y = extreme_col->extent (common[Y_AXIS], Y_AXIS)
245 .linear_combination (int ((my_cols.size() == 1) ? CENTER : vdir));
246 by = (y*neighbor_cols.size() + neighbor_y*my_cols.size()) /
247 (my_cols.size() + neighbor_cols.size());
253 get_encompass_info (Grob *me,
257 Grob *stem = unsmob_grob (col->get_property ("stem"));
259 Direction dir = get_grob_direction (me);
263 programming_error ("No stem for note column?");
264 ei.x_ = col->relative_coordinate (common[X_AXIS], X_AXIS);
265 ei.head_ = ei.stem_ = col->extent (common[Y_AXIS],
266 Y_AXIS)[get_grob_direction (me)];
269 Direction stem_dir = get_grob_direction (stem);
271 if (Grob *head = Note_column::first_head (col))
272 ei.x_ = head->extent (common[X_AXIS], X_AXIS).center ();
274 ei.x_ = col->extent (common[X_AXIS], X_AXIS).center ();
276 Grob *h = Stem::extremal_heads (stem)[Direction (dir)];
279 ei.head_ = ei.stem_ = col->extent (common[Y_AXIS], Y_AXIS)[dir];
283 ei.head_ = h->extent (common[Y_AXIS], Y_AXIS)[dir];
285 if ((stem_dir == dir)
286 && !stem->extent (stem, Y_AXIS).is_empty ())
288 ei.stem_ = stem->extent (common[Y_AXIS], Y_AXIS)[dir];
289 if (Grob *b = Stem::get_beam (stem))
290 ei.stem_ += stem_dir * 0.5 * Beam::get_thickness (b);
292 Interval x = stem->extent (common[X_AXIS], X_AXIS);
293 ei.x_ = x.is_empty ()
294 ? stem->relative_coordinate (common[X_AXIS], X_AXIS)
305 get_default_dir (Grob*me)
307 Link_array<Grob> encompasses
308 = Pointer_group_interface__extract_grobs (me, (Grob*) 0, "note-columns");
311 for (int i= 0; i < encompasses.size (); i ++)
313 if (Note_column::dir (encompasses[i]) < 0)
323 get_slur_dir (Grob *slur)
325 Direction d = get_grob_direction (slur);
327 d = get_default_dir (slur);
331 MAKE_SCHEME_CALLBACK (New_slur, after_line_breaking,1);
333 New_slur::after_line_breaking (SCM smob)
335 Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob));
336 if (!scm_ilength (me->get_property ("note-columns")))
339 return SCM_UNSPECIFIED;
342 if (!get_grob_direction (me))
343 set_grob_direction (me, get_default_dir (me));
347 return SCM_UNSPECIFIED;
351 get_bezier (Grob *me, Drul_array<Offset> extremes, Real r_0, Real h_inf)
353 Array<Offset> encompasses;
354 encompasses.push (extremes[LEFT]);
355 encompasses.push (extremes[RIGHT]);
357 Slur_bezier_bow bb (encompasses,
358 get_grob_direction (me), h_inf, r_0);
360 return bb.get_bezier ();
363 Drul_array<Bound_info>
364 get_bound_info (Spanner* me, Grob **common)
366 Drul_array<Bound_info> extremes;
368 Direction dir = get_grob_direction (me);
372 extremes[d].bound_ = me->get_bound (d);
373 if (Note_column::has_interface (extremes[d].bound_))
375 extremes[d].note_column_ = extremes[d].bound_;
376 extremes[d].stem_ = Note_column::get_stem (extremes[d].note_column_);
377 extremes[d].stem_dir_ = get_grob_direction (extremes[d].stem_);
378 extremes[d].stem_extent_[X_AXIS]
379 = extremes[d].stem_->extent (common[X_AXIS], X_AXIS);
380 extremes[d].stem_extent_[Y_AXIS]
381 = extremes[d].stem_->extent (common[Y_AXIS], Y_AXIS);
382 extremes[d].slur_head_
383 = Stem::extremal_heads (extremes[d].stem_)[dir];
384 extremes[d].slur_head_extent_
385 = extremes[d].slur_head_->extent (common[X_AXIS], X_AXIS);
386 extremes[d].staff_ = Staff_symbol_referencer
387 ::get_staff_symbol (extremes[d].slur_head_);
388 extremes[d].staff_space_ = Staff_symbol_referencer
389 ::staff_space (extremes[d].slur_head_);
392 extremes[d].neighbor_y_ = broken_trend_y (me, common, d);
394 while (flip (&d) != LEFT);
399 set_end_points (Grob *me)
401 Link_array<Grob> columns
402 = Pointer_group_interface__extract_grobs (me, (Grob *) 0, "note-columns");
403 Slur_score_parameters params;
404 if (columns.is_empty ())
410 SCM eltlist = me->get_property ("note-columns");
411 SCM extra_list = me->get_property ("encompass-objects");
412 Spanner *sp = dynamic_cast<Spanner*> (me);
414 Grob *common[] = {0, 0};
415 for (int i = X_AXIS; i < NO_AXES; i++)
418 common[a] = common_refpoint_of_list (eltlist, me, a);
419 common[a] = common_refpoint_of_list (extra_list, common[a], a);
422 common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (RIGHT),
424 common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (LEFT),
427 Drul_array<Bound_info> extremes = get_bound_info (sp, common);
428 Drul_array<Offset> base_attachment
429 = get_base_attachments (sp, common, extremes);
430 Drul_array<Real> end_ys
431 = get_y_attachment_range (sp, common, extremes, base_attachment);
432 Array<Slur_score> scores = enumerate_attachments (me, common, ¶ms,
433 extremes, base_attachment,
436 generate_curves (me, common, extremes, base_attachment, &scores);
437 score_edges (me, common, ¶ms,extremes, base_attachment, &scores);
438 score_slopes (me, common, ¶ms,extremes, base_attachment, &scores);
439 score_encompass (me, common, ¶ms,extremes, base_attachment, &scores);
440 score_extra_encompass (me, common, ¶ms,extremes, base_attachment,
446 for (int i = scores.size (); i--;)
448 if (scores[i].score_ < opt)
450 opt = scores[i].score_;
455 #if DEBUG_SLUR_QUANTING
456 SCM inspect_quants = me->get_property ("inspect-quants");
457 if (to_boolean (me->get_paper ()
458 ->lookup_variable (ly_symbol2scm ("debug-slur-quanting")))
459 && ly_c_pair_p (inspect_quants))
461 Drul_array<Real> ins = ly_scm2interval (inspect_quants);
463 for (int i = 0; i < scores.size (); i ++)
465 Real d =fabs (scores[i].attachment_[LEFT][Y_AXIS] - ins[LEFT])
466 + fabs (scores[i].attachment_[RIGHT][Y_AXIS] - ins[RIGHT]);
474 programming_error ("Could not find quant.");
476 scores[opt_idx].score_card_ += to_string ("i%d", opt_idx);
479 me->set_property ("quant-score",
480 scm_makfrom0str (scores[opt_idx].score_card_.to_str0 ()));
483 Bezier b = scores[opt_idx].curve_;
484 SCM controls = SCM_EOL;
485 for (int i = 4; i--;)
487 Offset o = b.control_[i]
488 - Offset (me->relative_coordinate (common[X_AXIS], X_AXIS),
489 me->relative_coordinate (common[Y_AXIS], Y_AXIS));
490 controls = scm_cons (ly_offset2scm (o), controls);
492 me->set_property ("control-points", controls);
497 get_y_attachment_range (Spanner*me,
499 Drul_array<Bound_info> extremes,
500 Drul_array<Offset> base_attachment)
502 Drul_array<Real> end_ys;
503 Direction dir = get_grob_direction (me);
507 if (extremes[d].note_column_)
510 * ((dir * (base_attachment[d][Y_AXIS] + 4.0 * dir))
511 >? (dir * (dir + extremes[d].note_column_->extent(common[Y_AXIS],
513 >? (dir * base_attachment[-d][Y_AXIS]));
516 end_ys[d] = extremes[d].neighbor_y_ + 4.0 * dir;
518 while (flip (&d) != LEFT);
524 spanner_less (Spanner *s1, Spanner* s2)
530 b1[d] = s1->get_bound (d)->get_column ()->rank_;
531 b2[d] = s2->get_bound (d)->get_column ()->rank_;
532 } while (flip (&d) != LEFT);
534 return b2[LEFT] <= b1[LEFT] && b2[RIGHT] >= b1[RIGHT]
535 && (b2[LEFT] != b1[LEFT] || b2[RIGHT] != b1[RIGHT]);
540 get_base_attachments (Spanner *me,
541 Grob **common, Drul_array<Bound_info> extremes)
543 Link_array<Grob> columns
544 = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
545 Drul_array<Offset> base_attachment;
546 Slur_score_parameters params;
547 Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
548 Direction dir = get_grob_direction (me);
552 Grob *stem = extremes[d].stem_;
553 Grob *head = extremes[d].slur_head_;
556 if (!extremes[d].note_column_)
558 y = extremes[d].neighbor_y_;
560 x = extremes[d].bound_->extent (common[X_AXIS], X_AXIS)[d];
562 x = me->get_broken_left_end_align ();
567 (extremes[d].stem_ && extremes[-d].stem_
568 && Stem::get_beam (extremes[d].stem_) == Stem::get_beam (extremes[-d].stem_));
571 fixme: X coord should also be set in this case.
574 && extremes[d].stem_dir_ == dir
575 && Stem::get_beaming (stem, -d)
576 && (!spanner_less (me, Stem::get_beam (stem))
578 y = extremes[d].stem_extent_[Y_AXIS][dir];
580 y = head->extent (common[Y_AXIS], Y_AXIS)[dir];
581 y += dir * 0.5 * staff_space;
584 = (y - extremes[d].staff_->relative_coordinate (common[Y_AXIS],
586 * 2.0 / Staff_symbol::staff_space (extremes[d].staff_);
588 /* start off staffline. */
589 if (fabs (pos - my_round (pos)) < 0.2
590 && Staff_symbol_referencer::on_staffline (head, (int) rint (pos))
591 && Staff_symbol_referencer::line_count (head) - 1 >= rint (pos)
593 // TODO: calc from slur thick & line thick, parameter.
594 y += 1.5 * staff_space * dir / 10;
596 Grob * fh = Note_column::first_head (extremes[d].note_column_);
597 x = fh->extent (common[X_AXIS], X_AXIS).linear_combination (CENTER);
599 base_attachment[d] = Offset (x, y);
601 } while (flip (&d) != LEFT);
603 return base_attachment;
607 generate_curves (Grob *me, Grob **common,
608 Drul_array<Bound_info> extremes,
610 Array<Slur_score> *scores)
614 Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
615 Real r_0 = robust_scm2double (me->get_property ("ratio"), 0.33);
616 Real h_inf = staff_space * ly_scm2double (me->get_property ("height-limit"));
617 for (int i = 0; i < scores->size(); i++)
619 Bezier bez= get_bezier (me, (*scores)[i].attachment_, r_0, h_inf);
620 bez = avoid_staff_line (me, common, extremes, bez);
621 (*scores)[i].attachment_[LEFT] = bez.control_[0];
622 (*scores)[i].attachment_[RIGHT] = bez.control_[3];
623 (*scores)[i].curve_ = bez;
628 avoid_staff_line (Grob *me, Grob **common,
629 Drul_array<Bound_info> extremes,
633 Array<Real> ts = bez.solve_derivative (horiz);
634 Real lt = me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
635 Real thick = robust_scm2double (me->get_property ("thickness"), 1.0) * lt;
637 /* TODO: handle case of broken slur. */
639 && (extremes[LEFT].staff_ == extremes[RIGHT].staff_)
640 && extremes[LEFT].staff_ && extremes[RIGHT].staff_)
642 Real y = bez.curve_point (ts[0])[Y_AXIS];
644 Grob *staff = extremes[LEFT].staff_;
646 Real staff_space = extremes[LEFT].staff_space_;
647 Real p = 2 * (y - staff->relative_coordinate (common[Y_AXIS], Y_AXIS))
650 Real distance = fabs (my_round (p) - p); // in halfspaces
651 if (distance < 4 * thick
652 && (int) fabs (my_round (p))
653 <= 2 * Staff_symbol_referencer::staff_radius (staff) + 0.1
654 && (int (fabs (my_round (p))) % 2
655 != Staff_symbol_referencer::line_count (staff) % 2))
657 Direction resolution_dir =
658 (distance ? get_grob_direction (me) : Direction (sign (p - my_round(p))));
661 Real newp = my_round (p) + resolution_dir
664 Real dy = (newp - p) * staff_space / 2.0;
666 bez.translate (Offset (0, dy));
668 bez.control_[1][Y_AXIS] += dy;
669 bez.control_[2][Y_AXIS] += dy;
678 enumerate_attachments (Grob *me, Grob *common[],
679 Slur_score_parameters *score_param,
680 Drul_array<Bound_info> extremes,
681 Drul_array<Offset> base_attachment,
682 Drul_array<Real> end_ys)
686 Array<Slur_score> scores;
688 Direction dir = get_grob_direction (me);
689 Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
691 Drul_array<Offset> os;
692 os[LEFT] = base_attachment[LEFT];
693 Real minimum_length = staff_space
694 * robust_scm2double (me->get_property ("minimum-length"), 2.0);
696 for (int i = 0; dir * os[LEFT][Y_AXIS] <= dir * end_ys[LEFT]; i++)
698 os[RIGHT] = base_attachment[RIGHT];
699 for (int j = 0; dir * os[RIGHT][Y_AXIS] <= dir * end_ys[RIGHT]; j++)
703 Drul_array<bool> attach_to_stem (false, false);
706 os[d][X_AXIS] = base_attachment[d][X_AXIS];
707 if (extremes[d].stem_
708 && !Stem::is_invisible (extremes[d].stem_)
709 && extremes[d].stem_dir_ == dir)
712 && extremes[d].stem_extent_[Y_AXIS].contains (os[d][Y_AXIS]))
714 os[d][X_AXIS] = extremes[d].slur_head_extent_[-d]
716 attach_to_stem[d] = true;
718 else if (dir *extremes[d].stem_extent_[Y_AXIS][dir]
719 < dir * os[d][Y_AXIS]
720 && !extremes[d].stem_extent_[X_AXIS].is_empty()
723 os[d][X_AXIS] = extremes[d].stem_extent_[X_AXIS].center();
726 while (flip (&d) != LEFT);
729 dz = os[RIGHT] - os[LEFT];
730 if (dz[X_AXIS] < minimum_length
731 || fabs (dz[Y_AXIS] / dz[X_AXIS]) > score_param->MAX_SLOPE
736 if (extremes[d].slur_head_)
738 os[d][X_AXIS] = extremes[d].slur_head_extent_.center ();
739 attach_to_stem[d] = false;
742 while (flip (&d) != LEFT);
745 dz = os[RIGHT] - os[LEFT];
748 if (extremes[d].slur_head_
749 && !attach_to_stem[d])
751 /* Horizontally move tilted slurs a little. Move
752 more for bigger tilts.
756 -= dir * extremes[d].slur_head_extent_.length ()
757 * sin (dz.arg ()) / 3;
760 while (flip (&d) != LEFT);
765 os[RIGHT][Y_AXIS] += dir * staff_space / 2;
768 os[LEFT][Y_AXIS] += dir * staff_space / 2;
774 score_encompass (Grob *me, Grob *common[],
775 Slur_score_parameters *score_param,
776 Drul_array<Bound_info> extremes,
777 Drul_array<Offset> base_attach,
778 Array<Slur_score> *scores)
783 Link_array<Grob> encompasses
784 = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
785 Direction dir = get_grob_direction (me);
787 Array<Encompass_info> infos;
789 for (int i = 0; i < encompasses.size(); i++)
790 infos.push (get_encompass_info (me, encompasses[i], common));
792 for (int i = 0; i < scores->size (); i++)
794 Bezier const &bez (scores->elem (i).curve_);
796 for (int j = 0; j < infos.size(); j++)
798 Real x = infos[j].x_;
801 bool r_edge = j==infos.size()-1;
802 bool edge = l_edge || r_edge;
804 if (!(x < scores->elem (i).attachment_[RIGHT][X_AXIS]
805 && x > scores->elem (i).attachment_[LEFT][X_AXIS]))
808 Real y = bez.get_other_coordinate (X_AXIS, x);
811 Real head_dy = (y - infos[j].head_);
812 if (dir * head_dy < 0)
813 demerit += score_param->HEAD_ENCOMPASS_PENALTY;
817 ? (1 / fabs (head_dy) - 1 / score_param->FREE_HEAD_DISTANCE)
818 : score_param->HEAD_ENCOMPASS_PENALTY;
819 hd = (hd >? 0)<? score_param->HEAD_ENCOMPASS_PENALTY;
825 if (dir * (y - infos[j].stem_) < 0)
827 Real stem_dem =score_param->STEM_ENCOMPASS_PENALTY ;
828 if ((l_edge && dir == UP)
829 || (r_edge && dir == DOWN))
837 ext.add_point (infos[j].stem_);
838 ext.add_point (infos[j].head_);
841 demerit += -score_param->CLOSENESS_FACTOR
843 * (y - (ext[dir] + dir * score_param->FREE_HEAD_DISTANCE))
849 #if DEBUG_SLUR_QUANTING
850 (*scores)[i].score_card_ += to_string ("C%.2f", demerit);
853 (*scores)[i].score_ += demerit;
858 score_extra_encompass (Grob *me, Grob *common[],
859 Slur_score_parameters *score_param,
860 Drul_array<Bound_info> extremes,
861 Drul_array<Offset> base_attach,
862 Array<Slur_score> *scores)
867 Link_array<Grob> encompasses
868 = Pointer_group_interface__extract_grobs (me, (Grob *)0,
869 "encompass-objects");
870 Direction dir = get_grob_direction (me);
871 Real lt = me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
872 Real thick = robust_scm2double (me->get_property ("thickness"), 1.0) * lt;
875 Array<Interval> yexts;
876 Array<Interval> xexts;
877 for (int i = 0; i < encompasses.size (); i++)
879 Grob *g = encompasses [i];
880 Interval xe = g->extent (common[X_AXIS], X_AXIS);
881 Interval ye = g->extent (common[Y_AXIS], Y_AXIS);
885 if (Accidental_interface::has_interface (g))
887 /* Begin copy accidental.cc */
889 if (to_boolean (g->get_property ("cautionary")))
891 SCM cstyle = g->get_property ("cautionary-style");
892 parens = ly_c_equal_p (cstyle, ly_symbol2scm ("parentheses"));
895 SCM accs = g->get_property ("accidentals");
896 SCM scm_style = g->get_property ("style");
897 if (!ly_c_symbol_p (scm_style)
899 && scm_ilength (accs) == 1)
901 /* End copy accidental.cc */
902 switch (ly_scm2int (ly_car (accs)))
919 ye.widen (thick * 0.5);
924 for (int i = 0; i < scores->size (); i++)
927 for (int j = 0; j < xidxs.size(); j++)
929 Drul_array<Offset> at = scores->elem (i).attachment_;
930 Interval slur_wid (at[LEFT][X_AXIS], at[RIGHT][X_AXIS]);
933 to prevent numerical inaccuracies in
934 Bezier::get_other_coordinate().
936 slur_wid.widen (- 0.5 * thick);
937 Real x = xexts[j].linear_combination (xidxs[j]);
939 if (!slur_wid.contains (x))
941 Direction contains_dir = CENTER;
945 if (xexts[j].contains (at[d][X_AXIS]))
948 while (flip (&d) != LEFT);
953 y = at[contains_dir][Y_AXIS];
957 y = scores->elem (i).curve_.get_other_coordinate (X_AXIS, x);
960 if (yexts[j].contains (y))
962 if (Accidental_interface::has_interface (encompasses[j]))
963 demerit += score_param->ACCIDENTAL_COLLISION;
965 demerit += score_param->EXTRA_OBJECT_COLLISION;
968 #if DEBUG_SLUR_QUANTING
969 (*scores)[i].score_card_ += to_string ("X%.2f", demerit);
971 (*scores)[i].score_ += demerit;
976 score_edges (Grob *me, Grob *common[],
977 Slur_score_parameters * score_param,
978 Drul_array<Bound_info> extremes,
979 Drul_array<Offset> base_attach,
980 Array<Slur_score> *scores)
983 Direction dir = get_grob_direction (me);
985 for (int i = 0; i < scores->size (); i++)
990 Real y = scores->elem (i).attachment_[d][Y_AXIS];
991 Real dy = fabs (y - base_attach[d][Y_AXIS]);
993 Real factor = score_param->EDGE_ATTRACTION_FACTOR;
994 Real demerit = factor * dy;
995 if (extremes[d].stem_
996 && extremes[d].stem_dir_ == dir
997 && !Stem::get_beaming (extremes[d].stem_, -d)
1001 (*scores)[i].score_ += demerit;
1002 #if DEBUG_SLUR_QUANTING
1003 (*scores)[i].score_card_ += to_string ("E%.2f", demerit);
1006 while (flip (&d) != LEFT);
1011 score_slopes (Grob *me, Grob *common[],
1012 Slur_score_parameters *score_param,
1013 Drul_array<Bound_info> extremes,
1014 Drul_array<Offset> base_attach,
1015 Array<Slur_score> * scores)
1020 Drul_array<Real> ys;
1024 if (extremes[d].slur_head_)
1025 ys[d] = extremes[d].slur_head_->relative_coordinate (common[Y_AXIS],
1028 ys[d] = extremes[d].neighbor_y_;
1030 while (flip (&d) != LEFT);
1033 = (extremes[LEFT].stem_ && Stem::get_beam (extremes[LEFT].stem_))
1034 || (extremes[RIGHT].stem_ && Stem::get_beam (extremes[RIGHT].stem_));
1036 Real dy = ys[RIGHT] - ys[LEFT];
1037 for (int i = 0; i < scores->size (); i++)
1039 Offset slur_dz = (*scores)[i].attachment_[RIGHT]
1040 - (*scores)[i].attachment_[LEFT];
1041 Real slur_dy = slur_dz[Y_AXIS];
1044 demerit += ((fabs (slur_dy / slur_dz[X_AXIS])
1045 - score_param->MAX_SLOPE) >? 0)
1046 * score_param->MAX_SLOPE_FACTOR;
1048 /* 0.2: account for staffline offset. */
1049 Real max_dy = (fabs (dy) + 0.2);
1053 demerit += score_param->STEEPER_SLOPE_FACTOR
1054 * ((fabs (slur_dy) -max_dy) >? 0);
1056 demerit += ((fabs (slur_dy/slur_dz[X_AXIS])
1057 - score_param->MAX_SLOPE) >? 0)
1058 * score_param->MAX_SLOPE_FACTOR;
1061 && sign (slur_dy) != 0)
1062 demerit += score_param->NON_HORIZONTAL_PENALTY;
1066 && sign (slur_dy) != sign (dy))
1067 demerit += has_beams
1068 ? score_param->SAME_SLOPE_PENALTY / 10
1069 : score_param->SAME_SLOPE_PENALTY;
1071 #if DEBUG_SLUR_QUANTING
1072 (*scores)[i].score_card_ += to_string ("S%.2f", d);
1074 (*scores)[i].score_ += demerit;