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
54 int slur_region_size_;
55 Real head_encompass_penalty_;
56 Real stem_encompass_penalty_;
57 Real closeness_factor_;
58 Real edge_attraction_factor_;
59 Real same_slope_penalty_;
60 Real steeper_slope_factor_;
61 Real non_horizontal_penalty_;
62 Real head_strict_free_space_;
64 Real max_slope_factor_;
65 Real extra_object_collision_;
66 Real accidental_collision_;
67 Real free_head_distance_;
68 Real extra_encompass_free_distance_;
69 Slur_score_parameters (Grob*);
75 - curve around flag for y coordinate
77 - short-cut: try a smaller region first.
78 - collisions with accidentals
79 - collisions with articulations (staccato, portato, sforzato, ...)
80 - handle non-visible stems better.
104 Interval slur_head_extent_;
123 score_extra_encompass (Grob *me, Grob *common[],
124 Slur_score_parameters *score_param,
125 Drul_array<Bound_info> ,
127 Array<Slur_score> * scores);
128 static void score_slopes (Grob *me, Grob *common[],
129 Slur_score_parameters *score_param,
130 Drul_array<Bound_info>,
131 Drul_array<Offset> base_attach,
132 Array<Slur_score> *scores);
134 static void score_edges (Grob *me, Grob *common[],
135 Slur_score_parameters *score_param,
136 Drul_array<Bound_info> extremes,
137 Drul_array<Offset> base_attach,
138 Array<Slur_score> *scores);
139 static void score_encompass (Grob *me, Grob *common[],
140 Slur_score_parameters*,
141 Drul_array<Bound_info>,
142 Drul_array<Offset>, Array<Slur_score> *scores);
143 static Bezier avoid_staff_line (Grob *me, Grob **common,
144 Drul_array<Bound_info> extremes,
147 static Encompass_info get_encompass_info (Grob *me,
150 static Bezier get_bezier (Grob *me, Drul_array<Offset>, Real, Real);
151 static Direction get_default_dir (Grob *me);
153 static void set_end_points (Grob *);
154 static Real broken_trend_y (Grob *me, Grob **, Direction dir);
155 static Drul_array<Bound_info> get_bound_info (Spanner *me, Grob **common);
157 static void generate_curves (Grob *me,
159 Drul_array<Bound_info> extremes,
160 Drul_array<Offset> base_attach,
161 Array<Slur_score> *scores);
162 static Array<Slur_score> enumerate_attachments
163 (Grob *me, Grob *common[], Slur_score_parameters*,
164 Drul_array<Bound_info> extremes,
165 Drul_array<Offset> base_attachment, Drul_array<Real> end_ys);
166 static Drul_array<Offset> get_base_attachments
167 (Spanner *sp, Grob **common, Drul_array<Bound_info> extremes);
168 static Drul_array<Real> get_y_attachment_range
169 (Spanner *sp, Grob **common, Drul_array<Bound_info> extremes,
170 Drul_array<Offset> base_attachment);
174 get_detail (SCM alist, SCM sym)
176 SCM entry = scm_assq (sym, alist);
177 return robust_scm2double (ly_c_pair_p (entry)
184 init_score_param (Grob *me,
185 Slur_score_parameters *score_param)
187 SCM details = me->get_property ("slur-details");
189 score_param->slur_region_size_
190 = (int) get_detail (details, ly_symbol2scm ("slur-region-size"));
191 score_param->head_encompass_penalty_
192 = get_detail (details, ly_symbol2scm ("head-encompass-penalty"));
193 score_param->stem_encompass_penalty_
194 = get_detail (details, ly_symbol2scm ("stem-encompass-penalty"));
195 score_param->closeness_factor_
196 = get_detail (details, ly_symbol2scm ("closeness-factor"));
197 score_param->edge_attraction_factor_
198 = get_detail (details, ly_symbol2scm ("edge-attraction-factor"));
199 score_param->same_slope_penalty_
200 = get_detail (details, ly_symbol2scm ("same-slope-penalty"));
201 score_param->steeper_slope_factor_
202 = get_detail (details, ly_symbol2scm ("steeper-slope-factor"));
203 score_param->non_horizontal_penalty_
204 = get_detail (details, ly_symbol2scm ("non-horizontal-penalty"));
205 score_param->head_strict_free_space_
206 = get_detail (details, ly_symbol2scm ("head-strict-free-space"));
207 score_param->max_slope_
208 = get_detail (details, ly_symbol2scm ("max-slope"));
209 score_param->max_slope_factor_
210 = get_detail (details, ly_symbol2scm ("max-slope-factor"));
211 score_param->free_head_distance_
212 = get_detail (details, ly_symbol2scm ("free-head-distance"));
213 score_param->extra_object_collision_
214 = get_detail (details, ly_symbol2scm ("extra-object-collision"));
215 score_param->accidental_collision_
216 = get_detail (details, ly_symbol2scm ("accidental-collision"));
217 score_param->extra_encompass_free_distance_
218 = get_detail (details, ly_symbol2scm ("extra-encompass-free-distance"));
222 Slur_score_parameters::Slur_score_parameters(Grob *me)
224 init_score_param (me, this);
227 /* HDIR indicates the direction for the slur. */
229 broken_trend_y (Grob *me, Grob **common, Direction hdir)
231 /* A broken slur should maintain the same vertical trend
232 the unbroken slur would have had. */
234 if (Spanner *mother = dynamic_cast<Spanner*> (me->original_))
236 int k = broken_spanner_index (dynamic_cast<Spanner*> (me));
238 if (j < 0 || j >= mother->broken_intos_.size ())
241 Grob *neighbor = mother->broken_intos_[j];
243 neighbor->set_property ("direction",
244 me->get_property ("direction"));
246 Spanner *common_mother
247 = dynamic_cast<Spanner*> (common[Y_AXIS]->original_);
249 = broken_spanner_index (dynamic_cast<Spanner*> (common[Y_AXIS]));
250 int common_j = common_k + hdir;
252 if (common_j < 0 || common_j >= common_mother->broken_intos_.size())
255 Grob *common_next_system = common_mother->broken_intos_[common_j];
256 Link_array<Grob> neighbor_cols
257 = Pointer_group_interface__extract_grobs (neighbor, (Grob *)0,
261 = (hdir == RIGHT) ? neighbor_cols[0] : neighbor_cols.top ();
262 Grob *neighbor_common
263 = common_next_system->common_refpoint (neighbor_col, Y_AXIS);
265 Direction vdir = get_grob_direction (me);
267 = neighbor_col->extent (neighbor_common, Y_AXIS)
268 .linear_combination (int(neighbor_cols.size()==1 ? CENTER : vdir))
269 - common_next_system->relative_coordinate (neighbor_common, Y_AXIS);
271 Link_array<Grob> my_cols
272 = Pointer_group_interface__extract_grobs (me, (Grob *)0,
275 Grob *extreme_col = (hdir == RIGHT) ? my_cols.top() : my_cols[0];
276 Real y = extreme_col->extent (common[Y_AXIS], Y_AXIS)
277 .linear_combination (int ((my_cols.size() == 1) ? CENTER : vdir));
278 by = (y*neighbor_cols.size() + neighbor_y*my_cols.size()) /
279 (my_cols.size() + neighbor_cols.size());
285 get_encompass_info (Grob *me,
289 Grob *stem = unsmob_grob (col->get_property ("stem"));
291 Direction dir = get_grob_direction (me);
295 programming_error ("No stem for note column?");
296 ei.x_ = col->relative_coordinate (common[X_AXIS], X_AXIS);
297 ei.head_ = ei.stem_ = col->extent (common[Y_AXIS],
298 Y_AXIS)[get_grob_direction (me)];
301 Direction stem_dir = get_grob_direction (stem);
303 if (Grob *head = Note_column::first_head (col))
304 ei.x_ = head->extent (common[X_AXIS], X_AXIS).center ();
306 ei.x_ = col->extent (common[X_AXIS], X_AXIS).center ();
308 Grob *h = Stem::extremal_heads (stem)[Direction (dir)];
311 ei.head_ = ei.stem_ = col->extent (common[Y_AXIS], Y_AXIS)[dir];
315 ei.head_ = h->extent (common[Y_AXIS], Y_AXIS)[dir];
317 if ((stem_dir == dir)
318 && !stem->extent (stem, Y_AXIS).is_empty ())
320 ei.stem_ = stem->extent (common[Y_AXIS], Y_AXIS)[dir];
321 if (Grob *b = Stem::get_beam (stem))
322 ei.stem_ += stem_dir * 0.5 * Beam::get_thickness (b);
324 Interval x = stem->extent (common[X_AXIS], X_AXIS);
325 ei.x_ = x.is_empty ()
326 ? stem->relative_coordinate (common[X_AXIS], X_AXIS)
337 get_default_dir (Grob*me)
339 Link_array<Grob> encompasses
340 = Pointer_group_interface__extract_grobs (me, (Grob*) 0, "note-columns");
343 for (int i= 0; i < encompasses.size (); i ++)
345 if (Note_column::dir (encompasses[i]) < 0)
355 get_slur_dir (Grob *slur)
357 Direction d = get_grob_direction (slur);
359 d = get_default_dir (slur);
363 MAKE_SCHEME_CALLBACK (New_slur, after_line_breaking,1);
365 New_slur::after_line_breaking (SCM smob)
367 Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob));
368 if (!scm_ilength (me->get_property ("note-columns")))
371 return SCM_UNSPECIFIED;
374 if (!get_grob_direction (me))
375 set_grob_direction (me, get_default_dir (me));
379 return SCM_UNSPECIFIED;
383 get_bezier (Grob *me, Drul_array<Offset> extremes, Real r_0, Real h_inf)
385 Array<Offset> encompasses;
386 encompasses.push (extremes[LEFT]);
387 encompasses.push (extremes[RIGHT]);
389 Slur_bezier_bow bb (encompasses,
390 get_grob_direction (me), h_inf, r_0);
392 return bb.get_bezier ();
395 Drul_array<Bound_info>
396 get_bound_info (Spanner* me, Grob **common)
398 Drul_array<Bound_info> extremes;
400 Direction dir = get_grob_direction (me);
404 extremes[d].bound_ = me->get_bound (d);
405 if (Note_column::has_interface (extremes[d].bound_))
407 extremes[d].note_column_ = extremes[d].bound_;
408 extremes[d].stem_ = Note_column::get_stem (extremes[d].note_column_);
409 extremes[d].stem_dir_ = get_grob_direction (extremes[d].stem_);
410 extremes[d].stem_extent_[X_AXIS]
411 = extremes[d].stem_->extent (common[X_AXIS], X_AXIS);
412 extremes[d].stem_extent_[Y_AXIS]
413 = extremes[d].stem_->extent (common[Y_AXIS], Y_AXIS);
414 extremes[d].slur_head_
415 = Stem::extremal_heads (extremes[d].stem_)[dir];
416 if (!extremes[d].slur_head_
417 && Note_column::has_rests (extremes[d].bound_))
419 extremes[d].slur_head_ = Note_column::get_rest (extremes[d].bound_);
422 if (extremes[d].slur_head_)
423 extremes[d].slur_head_extent_
424 = extremes[d].slur_head_->extent (common[X_AXIS], X_AXIS);
426 extremes[d].staff_ = Staff_symbol_referencer
427 ::get_staff_symbol (extremes[d].stem_);
428 extremes[d].staff_space_ = Staff_symbol_referencer
429 ::staff_space (extremes[d].stem_);
432 extremes[d].neighbor_y_ = broken_trend_y (me, common, d);
434 while (flip (&d) != LEFT);
439 set_end_points (Grob *me)
441 Link_array<Grob> columns
442 = Pointer_group_interface__extract_grobs (me, (Grob *) 0, "note-columns");
444 if (columns.is_empty ())
450 SCM eltlist = me->get_property ("note-columns");
451 SCM extra_list = me->get_property ("encompass-objects");
452 Spanner *sp = dynamic_cast<Spanner*> (me);
454 Grob *common[] = {0, 0};
455 for (int i = X_AXIS; i < NO_AXES; i++)
458 common[a] = common_refpoint_of_list (eltlist, me, a);
459 common[a] = common_refpoint_of_list (extra_list, common[a], a);
462 common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (RIGHT),
464 common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (LEFT),
467 Drul_array<Bound_info> extremes = get_bound_info (sp, common);
468 Drul_array<Offset> base_attachment
469 = get_base_attachments (sp, common, extremes);
470 Drul_array<Real> end_ys
471 = get_y_attachment_range (sp, common, extremes, base_attachment);
472 Slur_score_parameters params (me);
473 Array<Slur_score> scores = enumerate_attachments (me, common, ¶ms,
474 extremes, base_attachment,
477 generate_curves (me, common, extremes, base_attachment, &scores);
478 score_edges (me, common, ¶ms,extremes, base_attachment, &scores);
479 score_slopes (me, common, ¶ms,extremes, base_attachment, &scores);
480 score_encompass (me, common, ¶ms,extremes, base_attachment, &scores);
481 score_extra_encompass (me, common, ¶ms,extremes, base_attachment,
487 for (int i = scores.size (); i--;)
489 if (scores[i].score_ < opt)
491 opt = scores[i].score_;
496 #if DEBUG_SLUR_QUANTING
497 SCM inspect_quants = me->get_property ("inspect-quants");
498 if (to_boolean (me->get_paper ()
499 ->lookup_variable (ly_symbol2scm ("debug-slur-quanting")))
500 && ly_c_pair_p (inspect_quants))
502 Drul_array<Real> ins = ly_scm2interval (inspect_quants);
504 for (int i = 0; i < scores.size (); i ++)
506 Real d =fabs (scores[i].attachment_[LEFT][Y_AXIS] - ins[LEFT])
507 + fabs (scores[i].attachment_[RIGHT][Y_AXIS] - ins[RIGHT]);
515 programming_error ("Could not find quant.");
517 scores[opt_idx].score_card_ += to_string ("i%d", opt_idx);
520 me->set_property ("quant-score",
521 scm_makfrom0str (scores[opt_idx].score_card_.to_str0 ()));
524 Bezier b = scores[opt_idx].curve_;
525 SCM controls = SCM_EOL;
526 for (int i = 4; i--;)
528 Offset o = b.control_[i]
529 - Offset (me->relative_coordinate (common[X_AXIS], X_AXIS),
530 me->relative_coordinate (common[Y_AXIS], Y_AXIS));
531 controls = scm_cons (ly_offset2scm (o), controls);
533 me->set_property ("control-points", controls);
538 get_y_attachment_range (Spanner*me,
540 Drul_array<Bound_info> extremes,
541 Drul_array<Offset> base_attachment)
543 Drul_array<Real> end_ys;
544 Direction dir = get_grob_direction (me);
548 if (extremes[d].note_column_)
551 * ((dir * (base_attachment[d][Y_AXIS] + 4.0 * dir))
552 >? (dir * (dir + extremes[d].note_column_->extent(common[Y_AXIS],
554 >? (dir * base_attachment[-d][Y_AXIS]));
557 end_ys[d] = extremes[d].neighbor_y_ + 4.0 * dir;
559 while (flip (&d) != LEFT);
565 spanner_less (Spanner *s1, Spanner* s2)
571 b1[d] = s1->get_bound (d)->get_column ()->rank_;
572 b2[d] = s2->get_bound (d)->get_column ()->rank_;
573 } while (flip (&d) != LEFT);
575 return b2[LEFT] <= b1[LEFT] && b2[RIGHT] >= b1[RIGHT]
576 && (b2[LEFT] != b1[LEFT] || b2[RIGHT] != b1[RIGHT]);
581 get_base_attachments (Spanner *me,
582 Grob **common, Drul_array<Bound_info> extremes)
584 Link_array<Grob> columns
585 = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
586 Drul_array<Offset> base_attachment;
587 Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
588 Direction dir = get_grob_direction (me);
592 Grob *stem = extremes[d].stem_;
593 Grob *head = extremes[d].slur_head_;
596 if (!extremes[d].note_column_)
598 y = extremes[d].neighbor_y_;
600 x = extremes[d].bound_->extent (common[X_AXIS], X_AXIS)[d];
602 x = me->get_broken_left_end_align ();
607 (extremes[d].stem_ && extremes[-d].stem_
608 && Stem::get_beam (extremes[d].stem_) == Stem::get_beam (extremes[-d].stem_));
611 fixme: X coord should also be set in this case.
614 && extremes[d].stem_dir_ == dir
615 && Stem::get_beaming (stem, -d)
616 && (!spanner_less (me, Stem::get_beam (stem))
618 y = extremes[d].stem_extent_[Y_AXIS][dir];
620 y = head->extent (common[Y_AXIS], Y_AXIS)[dir];
621 y += dir * 0.5 * staff_space;
624 = (y - extremes[d].staff_->relative_coordinate (common[Y_AXIS],
626 * 2.0 / Staff_symbol::staff_space (extremes[d].staff_);
628 /* start off staffline. */
629 if (fabs (pos - my_round (pos)) < 0.2
630 && Staff_symbol_referencer::on_staffline (head, (int) rint (pos))
631 && Staff_symbol_referencer::line_count (head) - 1 >= rint (pos)
633 // TODO: calc from slur thick & line thick, parameter.
634 y += 1.5 * staff_space * dir / 10;
636 Grob * fh = Note_column::first_head (extremes[d].note_column_);
638 (fh ? fh->extent (common[X_AXIS], X_AXIS)
639 : extremes[d].bound_->extent (common[X_AXIS], X_AXIS))
640 .linear_combination (CENTER);
642 base_attachment[d] = Offset (x, y);
644 } while (flip (&d) != LEFT);
646 return base_attachment;
650 generate_curves (Grob *me, Grob **common,
651 Drul_array<Bound_info> extremes,
653 Array<Slur_score> *scores)
657 Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
658 Real r_0 = robust_scm2double (me->get_property ("ratio"), 0.33);
659 Real h_inf = staff_space * ly_scm2double (me->get_property ("height-limit"));
660 for (int i = 0; i < scores->size(); i++)
662 Bezier bez= get_bezier (me, (*scores)[i].attachment_, r_0, h_inf);
663 bez = avoid_staff_line (me, common, extremes, bez);
664 (*scores)[i].attachment_[LEFT] = bez.control_[0];
665 (*scores)[i].attachment_[RIGHT] = bez.control_[3];
666 (*scores)[i].curve_ = bez;
671 avoid_staff_line (Grob *me, Grob **common,
672 Drul_array<Bound_info> extremes,
676 Array<Real> ts = bez.solve_derivative (horiz);
677 Real lt = me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
678 Real thick = robust_scm2double (me->get_property ("thickness"), 1.0) * lt;
680 /* TODO: handle case of broken slur. */
682 && (extremes[LEFT].staff_ == extremes[RIGHT].staff_)
683 && extremes[LEFT].staff_ && extremes[RIGHT].staff_)
685 Real y = bez.curve_point (ts[0])[Y_AXIS];
687 Grob *staff = extremes[LEFT].staff_;
689 Real staff_space = extremes[LEFT].staff_space_;
690 Real p = 2 * (y - staff->relative_coordinate (common[Y_AXIS], Y_AXIS))
693 Real distance = fabs (my_round (p) - p); // in halfspaces
694 if (distance < 4 * thick
695 && (int) fabs (my_round (p))
696 <= 2 * Staff_symbol_referencer::staff_radius (staff) + 0.1
697 && (int (fabs (my_round (p))) % 2
698 != Staff_symbol_referencer::line_count (staff) % 2))
700 Direction resolution_dir =
701 (distance ? get_grob_direction (me) : Direction (sign (p - my_round(p))));
704 Real newp = my_round (p) + resolution_dir
707 Real dy = (newp - p) * staff_space / 2.0;
709 bez.translate (Offset (0, dy));
711 bez.control_[1][Y_AXIS] += dy;
712 bez.control_[2][Y_AXIS] += dy;
721 enumerate_attachments (Grob *me, Grob *common[],
722 Slur_score_parameters *score_param,
723 Drul_array<Bound_info> extremes,
724 Drul_array<Offset> base_attachment,
725 Drul_array<Real> end_ys)
729 Array<Slur_score> scores;
731 Direction dir = get_grob_direction (me);
732 Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
734 Drul_array<Offset> os;
735 os[LEFT] = base_attachment[LEFT];
736 Real minimum_length = staff_space
737 * robust_scm2double (me->get_property ("minimum-length"), 2.0);
739 for (int i = 0; dir * os[LEFT][Y_AXIS] <= dir * end_ys[LEFT]; i++)
741 os[RIGHT] = base_attachment[RIGHT];
742 for (int j = 0; dir * os[RIGHT][Y_AXIS] <= dir * end_ys[RIGHT]; j++)
746 Drul_array<bool> attach_to_stem (false, false);
749 os[d][X_AXIS] = base_attachment[d][X_AXIS];
750 if (extremes[d].stem_
751 && !Stem::is_invisible (extremes[d].stem_)
752 && extremes[d].stem_dir_ == dir)
755 && extremes[d].stem_extent_[Y_AXIS].contains (os[d][Y_AXIS]))
757 os[d][X_AXIS] = extremes[d].slur_head_extent_[-d]
759 attach_to_stem[d] = true;
761 else if (dir *extremes[d].stem_extent_[Y_AXIS][dir]
762 < dir * os[d][Y_AXIS]
763 && !extremes[d].stem_extent_[X_AXIS].is_empty()
766 os[d][X_AXIS] = extremes[d].stem_extent_[X_AXIS].center();
769 while (flip (&d) != LEFT);
772 dz = os[RIGHT] - os[LEFT];
773 if (dz[X_AXIS] < minimum_length
774 || fabs (dz[Y_AXIS] / dz[X_AXIS]) > score_param->max_slope_
779 if (extremes[d].slur_head_)
781 os[d][X_AXIS] = extremes[d].slur_head_extent_.center ();
782 attach_to_stem[d] = false;
785 while (flip (&d) != LEFT);
788 dz = os[RIGHT] - os[LEFT];
791 if (extremes[d].slur_head_
792 && !attach_to_stem[d])
794 /* Horizontally move tilted slurs a little. Move
795 more for bigger tilts.
799 -= dir * extremes[d].slur_head_extent_.length ()
800 * sin (dz.arg ()) / 3;
803 while (flip (&d) != LEFT);
808 os[RIGHT][Y_AXIS] += dir * staff_space / 2;
811 os[LEFT][Y_AXIS] += dir * staff_space / 2;
817 score_encompass (Grob *me, Grob *common[],
818 Slur_score_parameters *score_param,
819 Drul_array<Bound_info> extremes,
820 Drul_array<Offset> base_attach,
821 Array<Slur_score> *scores)
826 Link_array<Grob> encompasses
827 = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
828 Direction dir = get_grob_direction (me);
830 Array<Encompass_info> infos;
832 for (int i = 0; i < encompasses.size(); i++)
833 infos.push (get_encompass_info (me, encompasses[i], common));
835 for (int i = 0; i < scores->size (); i++)
837 Bezier const &bez (scores->elem (i).curve_);
839 for (int j = 0; j < infos.size(); j++)
841 Real x = infos[j].x_;
844 bool r_edge = j==infos.size()-1;
845 bool edge = l_edge || r_edge;
847 if (!(x < scores->elem (i).attachment_[RIGHT][X_AXIS]
848 && x > scores->elem (i).attachment_[LEFT][X_AXIS]))
851 Real y = bez.get_other_coordinate (X_AXIS, x);
854 Real head_dy = (y - infos[j].head_);
855 if (dir * head_dy < 0)
856 demerit += score_param->head_encompass_penalty_;
860 ? (1 / fabs (head_dy) - 1 / score_param->free_head_distance_)
861 : score_param->head_encompass_penalty_;
862 hd = (hd >? 0)<? score_param->head_encompass_penalty_;
868 if (dir * (y - infos[j].stem_) < 0)
870 Real stem_dem =score_param->stem_encompass_penalty_ ;
871 if ((l_edge && dir == UP)
872 || (r_edge && dir == DOWN))
880 ext.add_point (infos[j].stem_);
881 ext.add_point (infos[j].head_);
884 demerit += -score_param->closeness_factor_
886 * (y - (ext[dir] + dir * score_param->free_head_distance_))
892 #if DEBUG_SLUR_QUANTING
893 (*scores)[i].score_card_ += to_string ("C%.2f", demerit);
896 (*scores)[i].score_ += demerit;
901 score_extra_encompass (Grob *me, Grob *common[],
902 Slur_score_parameters *score_param,
903 Drul_array<Bound_info> extremes,
904 Drul_array<Offset> base_attach,
905 Array<Slur_score> *scores)
910 Link_array<Grob> encompasses
911 = Pointer_group_interface__extract_grobs (me, (Grob *)0,
912 "encompass-objects");
913 Direction dir = get_grob_direction (me);
914 Real lt = me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
915 Real thick = robust_scm2double (me->get_property ("thickness"), 1.0) * lt;
918 Array<Interval> yexts;
919 Array<Interval> xexts;
921 for (int i = encompasses.size (); i--; )
923 if (New_slur::has_interface (encompasses[i]))
925 Grob * small_slur = encompasses[i];
926 Bezier b = New_slur::get_curve (small_slur);
928 Offset z = b.curve_point (0.5);
929 z += Offset (small_slur->relative_coordinate (common[X_AXIS], X_AXIS),
930 small_slur->relative_coordinate (common[Y_AXIS], Y_AXIS));
932 xexts.push (Interval (z[X_AXIS], z[X_AXIS]));
934 yexts.push (z[Y_AXIS] + thick * Interval (-0.5, 0.5));
938 Grob *g = encompasses [i];
939 Interval xe = g->extent (common[X_AXIS], X_AXIS);
940 Interval ye = g->extent (common[Y_AXIS], Y_AXIS);
944 if (Accidental_interface::has_interface (g))
946 /* Begin copy accidental.cc */
948 if (to_boolean (g->get_property ("cautionary")))
950 SCM cstyle = g->get_property ("cautionary-style");
951 parens = ly_c_equal_p (cstyle, ly_symbol2scm ("parentheses"));
954 SCM accs = g->get_property ("accidentals");
955 SCM scm_style = g->get_property ("style");
956 if (!ly_c_symbol_p (scm_style)
958 && scm_ilength (accs) == 1)
960 /* End copy accidental.cc */
961 switch (ly_scm2int (ly_car (accs)))
978 ye.widen (thick * 0.5);
983 for (int i = 0; i < scores->size (); i++)
986 for (int j = 0; j < xidxs.size(); j++)
988 Drul_array<Offset> at = scores->elem (i).attachment_;
989 Interval slur_wid (at[LEFT][X_AXIS], at[RIGHT][X_AXIS]);
992 to prevent numerical inaccuracies in
993 Bezier::get_other_coordinate().
995 slur_wid.widen (- 0.5 * thick);
996 Real x = xexts[j].linear_combination (xidxs[j]);
998 if (!slur_wid.contains (x))
1000 Direction contains_dir = CENTER;
1004 if (xexts[j].contains (at[d][X_AXIS]))
1007 while (flip (&d) != LEFT);
1012 y = at[contains_dir][Y_AXIS];
1016 y = scores->elem (i).curve_.get_other_coordinate (X_AXIS, x);
1019 Real collision_demerit =
1020 (Accidental_interface::has_interface (encompasses[j]))
1021 ? score_param->accidental_collision_
1022 : score_param->extra_object_collision_;
1024 Real dist = yexts[j].distance (y);
1026 fabs (0 >? (score_param->extra_encompass_free_distance_ - dist)) /
1027 score_param->extra_encompass_free_distance_ * collision_demerit;
1029 #if DEBUG_SLUR_QUANTING
1030 (*scores)[i].score_card_ += to_string ("X%.2f", demerit);
1032 (*scores)[i].score_ += demerit;
1037 score_edges (Grob *me, Grob *common[],
1038 Slur_score_parameters * score_param,
1039 Drul_array<Bound_info> extremes,
1040 Drul_array<Offset> base_attach,
1041 Array<Slur_score> *scores)
1044 Direction dir = get_grob_direction (me);
1046 for (int i = 0; i < scores->size (); i++)
1051 Real y = scores->elem (i).attachment_[d][Y_AXIS];
1052 Real dy = fabs (y - base_attach[d][Y_AXIS]);
1054 Real factor = score_param->edge_attraction_factor_;
1055 Real demerit = factor * dy;
1056 if (extremes[d].stem_
1057 && extremes[d].stem_dir_ == dir
1058 && !Stem::get_beaming (extremes[d].stem_, -d)
1062 (*scores)[i].score_ += demerit;
1063 #if DEBUG_SLUR_QUANTING
1064 (*scores)[i].score_card_ += to_string ("E%.2f", demerit);
1067 while (flip (&d) != LEFT);
1072 score_slopes (Grob *me, Grob *common[],
1073 Slur_score_parameters *score_param,
1074 Drul_array<Bound_info> extremes,
1075 Drul_array<Offset> base_attach,
1076 Array<Slur_score> * scores)
1081 Drul_array<Real> ys;
1085 if (extremes[d].slur_head_)
1086 ys[d] = extremes[d].slur_head_->relative_coordinate (common[Y_AXIS],
1089 ys[d] = extremes[d].neighbor_y_;
1091 while (flip (&d) != LEFT);
1094 = (extremes[LEFT].stem_ && Stem::get_beam (extremes[LEFT].stem_))
1095 || (extremes[RIGHT].stem_ && Stem::get_beam (extremes[RIGHT].stem_));
1097 Real dy = ys[RIGHT] - ys[LEFT];
1098 for (int i = 0; i < scores->size (); i++)
1100 Offset slur_dz = (*scores)[i].attachment_[RIGHT]
1101 - (*scores)[i].attachment_[LEFT];
1102 Real slur_dy = slur_dz[Y_AXIS];
1105 demerit += ((fabs (slur_dy / slur_dz[X_AXIS])
1106 - score_param->max_slope_) >? 0)
1107 * score_param->max_slope_factor_;
1109 /* 0.2: account for staffline offset. */
1110 Real max_dy = (fabs (dy) + 0.2);
1114 demerit += score_param->steeper_slope_factor_
1115 * ((fabs (slur_dy) -max_dy) >? 0);
1117 demerit += ((fabs (slur_dy/slur_dz[X_AXIS])
1118 - score_param->max_slope_) >? 0)
1119 * score_param->max_slope_factor_;
1122 && sign (slur_dy) != 0)
1123 demerit += score_param->non_horizontal_penalty_;
1127 && sign (slur_dy) != sign (dy))
1128 demerit += has_beams
1129 ? score_param->same_slope_penalty_ / 10
1130 : score_param->same_slope_penalty_;
1132 #if DEBUG_SLUR_QUANTING
1133 (*scores)[i].score_card_ += to_string ("S%.2f", d);
1135 (*scores)[i].score_ += demerit;