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"
19 #include "note-column.hh"
20 #include "output-def.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
47 struct Slur_score_parameters
50 Real head_encompass_penalty_;
51 Real stem_encompass_penalty_;
52 Real closeness_factor_;
53 Real edge_attraction_factor_;
54 Real same_slope_penalty_;
55 Real steeper_slope_factor_;
56 Real non_horizontal_penalty_;
58 Real max_slope_factor_;
59 Real extra_object_collision_;
60 Real accidental_collision_;
62 Real free_head_distance_;
63 Real extra_encompass_free_distance_;
65 Real head_slur_distance_max_ratio_;
66 Real head_slur_distance_factor_;
70 Slur_score_parameters (Grob*);
76 - curve around flag for y coordinate
78 - short-cut: try a smaller region first.
79 - handle non-visible stems better.
92 Real get_point (Direction dir)
110 Interval slur_head_extent_;
126 TODO: create one object for passing all parameters.
131 score_extra_encompass (Grob *me, Grob *common[],
132 Slur_score_parameters *score_param,
133 Drul_array<Bound_info> ,
135 Array<Slur_score> * scores);
136 static void score_slopes (Grob *me, Grob *common[],
137 Slur_score_parameters *score_param,
138 Drul_array<Bound_info>,
139 Drul_array<Offset> base_attach,
140 Array<Slur_score> *scores);
142 static void score_edges (Grob *me, Grob *common[],
143 Slur_score_parameters *score_param,
144 Drul_array<Bound_info> extremes,
145 Drul_array<Offset> base_attach,
146 Array<Slur_score> *scores);
147 static void score_encompass (Grob *me, Grob *common[],
148 Slur_score_parameters*,
149 Drul_array<Bound_info>,
150 Drul_array<Offset>, Array<Slur_score> *scores);
151 static Bezier avoid_staff_line (Grob *me, Grob **common,
152 Drul_array<Bound_info> extremes,
155 static Encompass_info get_encompass_info (Grob *me,
158 static Bezier get_bezier (Grob *me,
160 Slur_score_parameters*,
161 Drul_array<Bound_info> extremes,
162 Drul_array<Offset> attachments,
163 Real r_0, Real h_inf);
164 static Direction get_default_dir (Grob *me);
166 static void set_end_points (Grob *);
167 static Real broken_trend_y (Grob *me, Grob **, Direction dir);
168 static Drul_array<Bound_info> get_bound_info (Spanner *me, Grob **common);
170 static void generate_curves (Grob *me,
171 Grob *common[],Slur_score_parameters*,
172 Drul_array<Bound_info> extremes,
173 Drul_array<Offset> base_attach,
174 Array<Slur_score> *scores);
175 static Array<Slur_score> enumerate_attachments
176 (Grob *me, Grob *common[], Slur_score_parameters*,
177 Drul_array<Bound_info> extremes,
178 Drul_array<Offset> base_attachment, Drul_array<Real> end_ys);
179 static Drul_array<Offset> get_base_attachments
180 (Spanner *sp, Grob **common, Drul_array<Bound_info> extremes);
181 static Drul_array<Real> get_y_attachment_range
182 (Spanner *sp, Grob **common,
183 Slur_score_parameters*,
184 Drul_array<Bound_info> extremes,
185 Drul_array<Offset> base_attachment);
189 get_detail (SCM alist, SCM sym)
191 SCM entry = scm_assq (sym, alist);
192 return robust_scm2double (ly_c_pair_p (entry)
199 init_score_param (Grob *me,
200 Slur_score_parameters *score_param)
202 SCM details = me->get_property ("slur-details");
204 score_param->region_size_
205 = (int) get_detail (details, ly_symbol2scm ("region-size"));
206 score_param->head_encompass_penalty_
207 = get_detail (details, ly_symbol2scm ("head-encompass-penalty"));
208 score_param->stem_encompass_penalty_
209 = get_detail (details, ly_symbol2scm ("stem-encompass-penalty"));
210 score_param->closeness_factor_
211 = get_detail (details, ly_symbol2scm ("closeness-factor"));
212 score_param->edge_attraction_factor_
213 = get_detail (details, ly_symbol2scm ("edge-attraction-factor"));
214 score_param->same_slope_penalty_
215 = get_detail (details, ly_symbol2scm ("same-slope-penalty"));
216 score_param->steeper_slope_factor_
217 = get_detail (details, ly_symbol2scm ("steeper-slope-factor"));
218 score_param->non_horizontal_penalty_
219 = get_detail (details, ly_symbol2scm ("non-horizontal-penalty"));
220 score_param->max_slope_
221 = get_detail (details, ly_symbol2scm ("max-slope"));
222 score_param->max_slope_factor_
223 = get_detail (details, ly_symbol2scm ("max-slope-factor"));
224 score_param->free_head_distance_
225 = get_detail (details, ly_symbol2scm ("free-head-distance"));
226 score_param->extra_object_collision_
227 = get_detail (details, ly_symbol2scm ("extra-object-collision"));
228 score_param->accidental_collision_
229 = get_detail (details, ly_symbol2scm ("accidental-collision"));
230 score_param->extra_encompass_free_distance_
231 = get_detail (details, ly_symbol2scm ("extra-encompass-free-distance"));
232 score_param->head_slur_distance_factor_
233 = get_detail (details, ly_symbol2scm ("head-slur-distance-factor"));
234 score_param->head_slur_distance_max_ratio_
235 = get_detail (details, ly_symbol2scm ("head-slur-distance-max-ratio"));
239 Slur_score_parameters::Slur_score_parameters(Grob *me)
241 init_score_param (me, this);
244 /* HDIR indicates the direction for the slur. */
246 broken_trend_y (Grob *me, Grob **common, Direction hdir)
248 /* A broken slur should maintain the same vertical trend
249 the unbroken slur would have had. */
251 if (Spanner *mother = dynamic_cast<Spanner*> (me->original_))
253 int k = broken_spanner_index (dynamic_cast<Spanner*> (me));
255 if (j < 0 || j >= mother->broken_intos_.size ())
258 Grob *neighbor = mother->broken_intos_[j];
260 neighbor->set_property ("direction",
261 me->get_property ("direction"));
263 Spanner *common_mother
264 = dynamic_cast<Spanner*> (common[Y_AXIS]->original_);
266 = broken_spanner_index (dynamic_cast<Spanner*> (common[Y_AXIS]));
267 int common_j = common_k + hdir;
269 if (common_j < 0 || common_j >= common_mother->broken_intos_.size())
272 Grob *common_next_system = common_mother->broken_intos_[common_j];
273 Link_array<Grob> neighbor_cols
274 = Pointer_group_interface__extract_grobs (neighbor, (Grob *)0,
278 = (hdir == RIGHT) ? neighbor_cols[0] : neighbor_cols.top ();
279 Grob *neighbor_common
280 = common_next_system->common_refpoint (neighbor_col, Y_AXIS);
282 Direction vdir = get_grob_direction (me);
284 = neighbor_col->extent (neighbor_common, Y_AXIS)
285 .linear_combination (int(neighbor_cols.size()==1 ? CENTER : vdir))
286 - common_next_system->relative_coordinate (neighbor_common, Y_AXIS);
288 Link_array<Grob> my_cols
289 = Pointer_group_interface__extract_grobs (me, (Grob *)0,
292 Grob *extreme_col = (hdir == RIGHT) ? my_cols.top() : my_cols[0];
293 Real y = extreme_col->extent (common[Y_AXIS], Y_AXIS)
294 .linear_combination (int ((my_cols.size() == 1) ? CENTER : vdir));
295 by = (y*neighbor_cols.size() + neighbor_y*my_cols.size()) /
296 (my_cols.size() + neighbor_cols.size());
302 get_encompass_info (Grob *me,
306 Grob *stem = unsmob_grob (col->get_property ("stem"));
308 Direction dir = get_grob_direction (me);
312 programming_error ("No stem for note column?");
313 ei.x_ = col->relative_coordinate (common[X_AXIS], X_AXIS);
314 ei.head_ = ei.stem_ = col->extent (common[Y_AXIS],
315 Y_AXIS)[get_grob_direction (me)];
318 Direction stem_dir = get_grob_direction (stem);
320 if (Grob *head = Note_column::first_head (col))
321 ei.x_ = head->extent (common[X_AXIS], X_AXIS).center ();
323 ei.x_ = col->extent (common[X_AXIS], X_AXIS).center ();
325 Grob *h = Stem::extremal_heads (stem)[Direction (dir)];
328 ei.head_ = ei.stem_ = col->extent (common[Y_AXIS], Y_AXIS)[dir];
332 ei.head_ = h->extent (common[Y_AXIS], Y_AXIS)[dir];
334 if ((stem_dir == dir)
335 && !stem->extent (stem, Y_AXIS).is_empty ())
337 ei.stem_ = stem->extent (common[Y_AXIS], Y_AXIS)[dir];
338 if (Grob *b = Stem::get_beam (stem))
339 ei.stem_ += stem_dir * 0.5 * Beam::get_thickness (b);
341 Interval x = stem->extent (common[X_AXIS], X_AXIS);
342 ei.x_ = x.is_empty ()
343 ? stem->relative_coordinate (common[X_AXIS], X_AXIS)
354 get_default_dir (Grob*me)
356 Link_array<Grob> encompasses
357 = Pointer_group_interface__extract_grobs (me, (Grob*) 0, "note-columns");
360 for (int i= 0; i < encompasses.size (); i ++)
362 if (Note_column::dir (encompasses[i]) < 0)
373 MAKE_SCHEME_CALLBACK (Slur, after_line_breaking,1);
375 Slur::after_line_breaking (SCM smob)
377 Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob));
378 if (!scm_ilength (me->get_property ("note-columns")))
381 return SCM_UNSPECIFIED;
384 if (!get_grob_direction (me))
385 set_grob_direction (me, get_default_dir (me));
387 if (scm_ilength (me->get_property ("control-points")) < 4)
390 return SCM_UNSPECIFIED;
393 Drul_array<Bound_info>
394 get_bound_info (Spanner* me, Grob **common)
396 Drul_array<Bound_info> extremes;
398 Direction dir = get_grob_direction (me);
402 extremes[d].bound_ = me->get_bound (d);
403 if (Note_column::has_interface (extremes[d].bound_))
405 extremes[d].note_column_ = extremes[d].bound_;
406 extremes[d].stem_ = Note_column::get_stem (extremes[d].note_column_);
407 extremes[d].stem_dir_ = get_grob_direction (extremes[d].stem_);
408 extremes[d].stem_extent_[X_AXIS]
409 = extremes[d].stem_->extent (common[X_AXIS], X_AXIS);
410 extremes[d].stem_extent_[Y_AXIS]
411 = extremes[d].stem_->extent (common[Y_AXIS], Y_AXIS);
412 extremes[d].slur_head_
413 = Stem::extremal_heads (extremes[d].stem_)[dir];
414 if (!extremes[d].slur_head_
415 && Note_column::has_rests (extremes[d].bound_))
417 extremes[d].slur_head_ = Note_column::get_rest (extremes[d].bound_);
420 if (extremes[d].slur_head_)
421 extremes[d].slur_head_extent_
422 = extremes[d].slur_head_->extent (common[X_AXIS], X_AXIS);
424 extremes[d].staff_ = Staff_symbol_referencer
425 ::get_staff_symbol (extremes[d].stem_);
426 extremes[d].staff_space_ = Staff_symbol_referencer
427 ::staff_space (extremes[d].stem_);
430 extremes[d].neighbor_y_ = broken_trend_y (me, common, d);
432 while (flip (&d) != LEFT);
437 set_end_points (Grob *me)
439 Link_array<Grob> columns
440 = Pointer_group_interface__extract_grobs (me, (Grob *) 0, "note-columns");
442 if (columns.is_empty ())
448 SCM eltlist = me->get_property ("note-columns");
449 SCM extra_list = me->get_property ("encompass-objects");
450 Spanner *sp = dynamic_cast<Spanner*> (me);
452 Grob *common[] = {0, 0};
453 for (int i = X_AXIS; i < NO_AXES; i++)
456 common[a] = common_refpoint_of_list (eltlist, me, a);
457 common[a] = common_refpoint_of_list (extra_list, common[a], a);
460 common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (RIGHT),
462 common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (LEFT),
465 Drul_array<Bound_info> extremes = get_bound_info (sp, common);
466 Drul_array<Offset> base_attachment
467 = get_base_attachments (sp, common, extremes);
468 Slur_score_parameters params (me);
469 Drul_array<Real> end_ys
470 = get_y_attachment_range (sp, common, ¶ms, extremes, base_attachment);
471 Array<Slur_score> scores = enumerate_attachments (me, common, ¶ms,
472 extremes, base_attachment,
475 generate_curves (me, common, ¶ms, extremes, base_attachment, &scores);
476 score_edges (me, common, ¶ms,extremes, base_attachment, &scores);
477 score_slopes (me, common, ¶ms,extremes, base_attachment, &scores);
478 score_encompass (me, common, ¶ms,extremes, base_attachment, &scores);
479 score_extra_encompass (me, common, ¶ms,extremes, base_attachment,
485 for (int i = scores.size (); i--;)
487 if (scores[i].score_ < opt)
489 opt = scores[i].score_;
494 #if DEBUG_SLUR_QUANTING
495 SCM inspect_quants = me->get_property ("inspect-quants");
496 if (to_boolean (me->get_paper ()
497 ->lookup_variable (ly_symbol2scm ("debug-slur-scoring")))
498 && ly_c_pair_p (inspect_quants))
500 Drul_array<Real> ins = ly_scm2interval (inspect_quants);
502 for (int i = 0; i < scores.size (); i ++)
504 Real d =fabs (scores[i].attachment_[LEFT][Y_AXIS] - ins[LEFT])
505 + fabs (scores[i].attachment_[RIGHT][Y_AXIS] - ins[RIGHT]);
513 programming_error ("Could not find quant.");
515 scores[opt_idx].score_card_ += to_string ("i%d", opt_idx);
518 me->set_property ("quant-score",
519 scm_makfrom0str (scores[opt_idx].score_card_.to_str0 ()));
522 Bezier b = scores[opt_idx].curve_;
523 SCM controls = SCM_EOL;
524 for (int i = 4; i--;)
526 Offset o = b.control_[i]
527 - Offset (me->relative_coordinate (common[X_AXIS], X_AXIS),
528 me->relative_coordinate (common[Y_AXIS], Y_AXIS));
529 controls = scm_cons (ly_offset2scm (o), controls);
531 me->set_property ("control-points", controls);
535 TODO: should analyse encompasses to determine sensible region, and
536 should limit slopes available.
540 get_y_attachment_range (Spanner*me,
541 Grob **common, Slur_score_parameters *score_param,
542 Drul_array<Bound_info> extremes,
543 Drul_array<Offset> base_attachment)
545 Drul_array<Real> end_ys;
546 Direction dir = get_grob_direction (me);
550 if (extremes[d].note_column_)
553 * ((dir * (base_attachment[d][Y_AXIS] + score_param->region_size_* dir))
554 >? (dir * (dir + extremes[d].note_column_->extent(common[Y_AXIS],
556 >? (dir * base_attachment[-d][Y_AXIS]));
559 end_ys[d] = extremes[d].neighbor_y_ + score_param->region_size_ * dir;
561 while (flip (&d) != LEFT);
567 spanner_less (Spanner *s1, Spanner* s2)
573 b1[d] = s1->get_bound (d)->get_column ()->rank_;
574 b2[d] = s2->get_bound (d)->get_column ()->rank_;
575 } while (flip (&d) != LEFT);
577 return b2[LEFT] <= b1[LEFT] && b2[RIGHT] >= b1[RIGHT]
578 && (b2[LEFT] != b1[LEFT] || b2[RIGHT] != b1[RIGHT]);
583 get_base_attachments (Spanner *me,
584 Grob **common, Drul_array<Bound_info> extremes)
586 Link_array<Grob> columns
587 = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
588 Drul_array<Offset> base_attachment;
589 Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
590 Direction dir = get_grob_direction (me);
594 Grob *stem = extremes[d].stem_;
595 Grob *head = extremes[d].slur_head_;
598 if (!extremes[d].note_column_)
600 y = extremes[d].neighbor_y_;
602 x = extremes[d].bound_->extent (common[X_AXIS], X_AXIS)[d];
604 x = me->get_broken_left_end_align ();
609 (extremes[d].stem_ && extremes[-d].stem_
610 && Stem::get_beam (extremes[d].stem_) == Stem::get_beam (extremes[-d].stem_));
613 fixme: X coord should also be set in this case.
616 && extremes[d].stem_dir_ == dir
617 && Stem::get_beaming (stem, -d)
618 && (!spanner_less (me, Stem::get_beam (stem))
620 y = extremes[d].stem_extent_[Y_AXIS][dir];
622 y = head->extent (common[Y_AXIS], Y_AXIS)[dir];
623 y += dir * 0.5 * staff_space;
626 = (y - extremes[d].staff_->relative_coordinate (common[Y_AXIS],
628 * 2.0 / Staff_symbol::staff_space (extremes[d].staff_);
630 /* start off staffline. */
631 if (fabs (pos - my_round (pos)) < 0.2
632 && Staff_symbol_referencer::on_staffline (head, (int) rint (pos))
633 && Staff_symbol_referencer::line_count (head) - 1 >= rint (pos)
635 // TODO: calc from slur thick & line thick, parameter.
636 y += 1.5 * staff_space * dir / 10;
638 Grob * fh = Note_column::first_head (extremes[d].note_column_);
640 (fh ? fh->extent (common[X_AXIS], X_AXIS)
641 : extremes[d].bound_->extent (common[X_AXIS], X_AXIS))
642 .linear_combination (CENTER);
644 base_attachment[d] = Offset (x, y);
646 } while (flip (&d) != LEFT);
648 return base_attachment;
652 generate_curves (Grob *me, Grob **common,
653 Slur_score_parameters *score_param,
654 Drul_array<Bound_info> extremes,
656 Array<Slur_score> *scores)
660 Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
661 Real r_0 = robust_scm2double (me->get_property ("ratio"), 0.33);
662 Real h_inf = staff_space * scm_to_double (me->get_property ("height-limit"));
663 for (int i = 0; i < scores->size(); i++)
665 Bezier bez = get_bezier (me,
669 (*scores)[i].attachment_, r_0, h_inf);
671 bez = avoid_staff_line (me, common, extremes, bez);
672 (*scores)[i].attachment_[LEFT] = bez.control_[0];
673 (*scores)[i].attachment_[RIGHT] = bez.control_[3];
674 (*scores)[i].curve_ = bez;
679 avoid_staff_line (Grob *me, Grob **common,
680 Drul_array<Bound_info> extremes,
684 Array<Real> ts = bez.solve_derivative (horiz);
685 Real lt = me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
686 Real thick = robust_scm2double (me->get_property ("thickness"), 1.0) * lt;
688 /* TODO: handle case of broken slur. */
690 && (extremes[LEFT].staff_ == extremes[RIGHT].staff_)
691 && extremes[LEFT].staff_ && extremes[RIGHT].staff_)
693 Real y = bez.curve_point (ts[0])[Y_AXIS];
695 Grob *staff = extremes[LEFT].staff_;
697 Real staff_space = extremes[LEFT].staff_space_;
698 Real p = 2 * (y - staff->relative_coordinate (common[Y_AXIS], Y_AXIS))
701 Real distance = fabs (my_round (p) - p); // in halfspaces
702 if (distance < 4 * thick
703 && (int) fabs (my_round (p))
704 <= 2 * Staff_symbol_referencer::staff_radius (staff) + 0.1
705 && (int (fabs (my_round (p))) % 2
706 != Staff_symbol_referencer::line_count (staff) % 2))
708 Direction resolution_dir =
709 (distance ? get_grob_direction (me) : Direction (sign (p - my_round(p))));
712 Real newp = my_round (p) + resolution_dir
715 Real dy = (newp - p) * staff_space / 2.0;
717 bez.control_[1][Y_AXIS] += dy;
718 bez.control_[2][Y_AXIS] += dy;
725 enumerate_attachments (Grob *me, Grob *common[],
726 Slur_score_parameters *score_param,
727 Drul_array<Bound_info> extremes,
728 Drul_array<Offset> base_attachment,
729 Drul_array<Real> end_ys)
733 Array<Slur_score> scores;
735 Direction dir = get_grob_direction (me);
736 Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
738 Drul_array<Offset> os;
739 os[LEFT] = base_attachment[LEFT];
740 Real minimum_length = staff_space
741 * robust_scm2double (me->get_property ("minimum-length"), 2.0);
743 for (int i = 0; dir * os[LEFT][Y_AXIS] <= dir * end_ys[LEFT]; i++)
745 os[RIGHT] = base_attachment[RIGHT];
746 for (int j = 0; dir * os[RIGHT][Y_AXIS] <= dir * end_ys[RIGHT]; j++)
750 Drul_array<bool> attach_to_stem (false, false);
753 os[d][X_AXIS] = base_attachment[d][X_AXIS];
754 if (extremes[d].stem_
755 && !Stem::is_invisible (extremes[d].stem_)
756 && extremes[d].stem_dir_ == dir)
759 && extremes[d].stem_extent_[Y_AXIS].contains (os[d][Y_AXIS]))
761 os[d][X_AXIS] = extremes[d].slur_head_extent_[-d]
763 attach_to_stem[d] = true;
765 else if (dir *extremes[d].stem_extent_[Y_AXIS][dir]
766 < dir * os[d][Y_AXIS]
767 && !extremes[d].stem_extent_[X_AXIS].is_empty()
770 os[d][X_AXIS] = extremes[d].stem_extent_[X_AXIS].center();
773 while (flip (&d) != LEFT);
776 dz = os[RIGHT] - os[LEFT];
777 if (dz[X_AXIS] < minimum_length
778 || fabs (dz[Y_AXIS] / dz[X_AXIS]) > score_param->max_slope_
783 if (extremes[d].slur_head_)
785 os[d][X_AXIS] = extremes[d].slur_head_extent_.center ();
786 attach_to_stem[d] = false;
789 while (flip (&d) != LEFT);
792 dz = os[RIGHT] - os[LEFT];
795 if (extremes[d].slur_head_
796 && !attach_to_stem[d])
798 /* Horizontally move tilted slurs a little. Move
799 more for bigger tilts.
803 -= dir * extremes[d].slur_head_extent_.length ()
804 * sin (dz.arg ()) / 3;
807 while (flip (&d) != LEFT);
812 os[RIGHT][Y_AXIS] += dir * staff_space / 2;
815 os[LEFT][Y_AXIS] += dir * staff_space / 2;
821 linear_interpolate (Real x, Real x1, Real x2, Real y1, Real y2)
823 return (x2 - x) / (x2 - x1) * y1 +
824 (x - x1) / (x2 - x1) * y2 ;
829 score_encompass (Grob *me, Grob *common[],
830 Slur_score_parameters *score_param,
831 Drul_array<Bound_info> extremes,
832 Drul_array<Offset> base_attach,
833 Array<Slur_score> *scores)
838 Link_array<Grob> encompasses
839 = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
840 Direction dir = get_grob_direction (me);
842 Array<Encompass_info> infos;
844 for (int i = 0; i < encompasses.size(); i++)
845 infos.push (get_encompass_info (me, encompasses[i], common));
847 for (int i = 0; i < scores->size (); i++)
849 Slur_score &configuration = scores->elem_ref (i);
850 Bezier const &bez (configuration.curve_);
854 Distances for heads that are between slur and line between
857 Array<Real> convex_head_distances;
858 Array<Real> edge_distances;
859 for (int j = 0; j < infos.size(); j++)
861 Real x = infos[j].x_;
864 bool r_edge = j==infos.size()-1;
865 bool edge = l_edge || r_edge;
870 edge_distances.push (fabs (configuration.attachment_[l_edge ? LEFT : RIGHT][Y_AXIS]
871 - infos[j].get_point (dir)));
875 if (!(x < configuration.attachment_[RIGHT][X_AXIS]
876 && x > configuration.attachment_[LEFT][X_AXIS]))
879 Real y = bez.get_other_coordinate (X_AXIS, x);
882 Real head_dy = (y - infos[j].head_);
883 if (dir * head_dy < 0)
885 demerit += score_param->head_encompass_penalty_;
886 convex_head_distances.push (0.0);
891 ? (1 / fabs (head_dy) - 1 / score_param->free_head_distance_)
892 : score_param->head_encompass_penalty_;
893 hd = (hd >? 0)<? score_param->head_encompass_penalty_;
898 Real line_y = linear_interpolate (x,
899 configuration.attachment_[RIGHT][X_AXIS],
900 configuration.attachment_[LEFT][X_AXIS],
901 configuration.attachment_[RIGHT][Y_AXIS],
902 configuration.attachment_[LEFT][Y_AXIS]);
904 if (dir * (infos[j].get_point (dir) - line_y) > 0)
906 Real d = fabs (infos[j].get_point (dir) - y);
907 convex_head_distances.push (d);
914 if (dir * (y - infos[j].stem_) < 0)
916 Real stem_dem =score_param->stem_encompass_penalty_ ;
917 if ((l_edge && dir == UP)
918 || (r_edge && dir == DOWN))
926 ext.add_point (infos[j].stem_);
927 ext.add_point (infos[j].head_);
930 demerit += -score_param->closeness_factor_
932 * (y - (ext[dir] + dir * score_param->free_head_distance_))
938 Real variance_penalty = 0.0;
940 if (convex_head_distances.size())
942 Real avg_distance = 0.0;
943 Real min_dist = infinity_f;
944 for (int j = 0; j < convex_head_distances.size(); j++)
946 min_dist = min_dist <? convex_head_distances[j];
947 avg_distance += convex_head_distances[j];
951 For slurs over 3 or 4 heads, the average distance is not a
954 int n = convex_head_distances.size();
955 if (convex_head_distances.size() <= 2)
957 for (int j = 0; j < edge_distances.size(); j++)
959 avg_distance += edge_distances[j];
965 TODO: maybe it's better to use (avgdist - mindist)*factor
969 variance_penalty = score_param->head_slur_distance_max_ratio_;
971 variance_penalty = ((avg_distance / (min_dist +score_param->free_head_distance_)) - 1.0)
974 variance_penalty *= score_param->head_slur_distance_factor_;
976 #if DEBUG_SLUR_QUANTING
977 (*scores)[i].score_card_ += to_string ("C%.2f", demerit);
978 (*scores)[i].score_card_ += to_string ("D%.2f", variance_penalty);
981 (*scores)[i].score_ += demerit + variance_penalty;
986 score_extra_encompass (Grob *me, Grob *common[],
987 Slur_score_parameters *score_param,
988 Drul_array<Bound_info> extremes,
989 Drul_array<Offset> base_attach,
990 Array<Slur_score> *scores)
995 Link_array<Grob> encompasses
996 = Pointer_group_interface__extract_grobs (me, (Grob *)0,
997 "encompass-objects");
998 Direction dir = get_grob_direction (me);
999 Real lt = me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
1000 Real thick = robust_scm2double (me->get_property ("thickness"), 1.0) * lt;
1003 Array<Interval> yexts;
1004 Array<Interval> xexts;
1006 for (int i = encompasses.size (); i--; )
1008 if (Slur::has_interface (encompasses[i]))
1010 Grob * small_slur = encompasses[i];
1011 Bezier b = Slur::get_curve (small_slur);
1013 Offset z = b.curve_point (0.5);
1014 z += Offset (small_slur->relative_coordinate (common[X_AXIS], X_AXIS),
1015 small_slur->relative_coordinate (common[Y_AXIS], Y_AXIS));
1017 xexts.push (Interval (z[X_AXIS], z[X_AXIS]));
1019 yexts.push (z[Y_AXIS] + thick * Interval (-0.5, 0.5));
1023 Grob *g = encompasses [i];
1024 Interval xe = g->extent (common[X_AXIS], X_AXIS);
1025 Interval ye = g->extent (common[Y_AXIS], Y_AXIS);
1029 if (Accidental_interface::has_interface (g))
1031 /* Begin copy accidental.cc */
1032 bool parens = false;
1033 if (to_boolean (g->get_property ("cautionary")))
1035 SCM cstyle = g->get_property ("cautionary-style");
1036 parens = ly_c_equal_p (cstyle, ly_symbol2scm ("parentheses"));
1039 SCM accs = g->get_property ("accidentals");
1040 SCM scm_style = g->get_property ("style");
1041 if (!scm_is_symbol (scm_style)
1043 && scm_ilength (accs) == 1)
1045 /* End copy accidental.cc */
1046 switch (scm_to_int (ly_car (accs)))
1063 ye.widen (thick * 0.5);
1068 for (int i = 0; i < scores->size (); i++)
1071 for (int j = 0; j < xidxs.size(); j++)
1073 Drul_array<Offset> at = scores->elem (i).attachment_;
1074 Interval slur_wid (at[LEFT][X_AXIS], at[RIGHT][X_AXIS]);
1077 to prevent numerical inaccuracies in
1078 Bezier::get_other_coordinate().
1080 slur_wid.widen (- 0.5 * thick);
1081 Real x = xexts[j].linear_combination (xidxs[j]);
1083 if (!slur_wid.contains (x))
1085 Direction contains_dir = CENTER;
1089 if (xexts[j].contains (at[d][X_AXIS]))
1092 while (flip (&d) != LEFT);
1097 y = at[contains_dir][Y_AXIS];
1101 y = scores->elem (i).curve_.get_other_coordinate (X_AXIS, x);
1104 Real collision_demerit =
1105 (Accidental_interface::has_interface (encompasses[j]))
1106 ? score_param->accidental_collision_
1107 : score_param->extra_object_collision_;
1109 Real dist = yexts[j].distance (y);
1111 fabs (0 >? (score_param->extra_encompass_free_distance_ - dist)) /
1112 score_param->extra_encompass_free_distance_ * collision_demerit;
1114 #if DEBUG_SLUR_QUANTING
1115 (*scores)[i].score_card_ += to_string ("X%.2f", demerit);
1117 (*scores)[i].score_ += demerit;
1122 score_edges (Grob *me, Grob *common[],
1123 Slur_score_parameters * score_param,
1124 Drul_array<Bound_info> extremes,
1125 Drul_array<Offset> base_attach,
1126 Array<Slur_score> *scores)
1129 Direction dir = get_grob_direction (me);
1131 for (int i = 0; i < scores->size (); i++)
1136 Real y = scores->elem (i).attachment_[d][Y_AXIS];
1137 Real dy = fabs (y - base_attach[d][Y_AXIS]);
1139 Real factor = score_param->edge_attraction_factor_;
1140 Real demerit = factor * dy;
1141 if (extremes[d].stem_
1142 && extremes[d].stem_dir_ == dir
1143 && !Stem::get_beaming (extremes[d].stem_, -d)
1147 (*scores)[i].score_ += demerit;
1148 #if DEBUG_SLUR_QUANTING
1149 (*scores)[i].score_card_ += to_string ("E%.2f", demerit);
1152 while (flip (&d) != LEFT);
1157 score_slopes (Grob *me, Grob *common[],
1158 Slur_score_parameters *score_param,
1159 Drul_array<Bound_info> extremes,
1160 Drul_array<Offset> base_attach,
1161 Array<Slur_score> * scores)
1166 Drul_array<Real> ys;
1170 if (extremes[d].slur_head_)
1171 ys[d] = extremes[d].slur_head_->relative_coordinate (common[Y_AXIS],
1174 ys[d] = extremes[d].neighbor_y_;
1176 while (flip (&d) != LEFT);
1179 = (extremes[LEFT].stem_ && Stem::get_beam (extremes[LEFT].stem_))
1180 || (extremes[RIGHT].stem_ && Stem::get_beam (extremes[RIGHT].stem_));
1182 Real dy = ys[RIGHT] - ys[LEFT];
1183 for (int i = 0; i < scores->size (); i++)
1185 Offset slur_dz = (*scores)[i].attachment_[RIGHT]
1186 - (*scores)[i].attachment_[LEFT];
1187 Real slur_dy = slur_dz[Y_AXIS];
1190 demerit += ((fabs (slur_dy / slur_dz[X_AXIS])
1191 - score_param->max_slope_) >? 0)
1192 * score_param->max_slope_factor_;
1194 /* 0.2: account for staffline offset. */
1195 Real max_dy = (fabs (dy) + 0.2);
1199 demerit += score_param->steeper_slope_factor_
1200 * ((fabs (slur_dy) -max_dy) >? 0);
1202 demerit += ((fabs (slur_dy/slur_dz[X_AXIS])
1203 - score_param->max_slope_) >? 0)
1204 * score_param->max_slope_factor_;
1207 && sign (slur_dy) != 0)
1208 demerit += score_param->non_horizontal_penalty_;
1212 && sign (slur_dy) != sign (dy))
1213 demerit += has_beams
1214 ? score_param->same_slope_penalty_ / 10
1215 : score_param->same_slope_penalty_;
1217 #if DEBUG_SLUR_QUANTING
1218 (*scores)[i].score_card_ += to_string ("S%.2f", d);
1220 (*scores)[i].score_ += demerit;
1229 fit_factor (Offset dz_unit, Offset dz_perp,
1230 Bezier curve, Direction d, Array<Offset> const &avoid)
1232 Real fit_factor = 0.0;
1233 Offset x0 = curve.control_[0];
1234 curve.translate (-x0);
1235 curve.rotate (-dz_unit.arg ());
1238 Interval curve_xext;
1239 curve_xext.add_point (curve.control_[0][X_AXIS]);
1240 curve_xext.add_point (curve.control_[3][X_AXIS]);
1242 for (int i = 0; i < avoid.size (); i++)
1244 Offset z = (avoid[i] - x0) ;
1245 Offset p (dot_product (z, dz_unit),
1246 d* dot_product (z, dz_perp));
1247 if (!curve_xext.contains (p[X_AXIS]))
1250 Real y = curve.get_other_coordinate (X_AXIS, p[X_AXIS]);
1253 fit_factor = fit_factor >? (p[Y_AXIS] / y);
1261 get_bezier (Grob *me,
1263 Slur_score_parameters *score_param,
1264 Drul_array<Bound_info> extremes,
1265 Drul_array<Offset> attachments,
1266 Real r_0, Real h_inf
1269 Link_array<Grob> encompasses
1270 = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
1271 Direction dir = get_grob_direction (me);
1273 Array<Offset> avoid;
1274 for (int i = 0; i < encompasses.size(); i++)
1276 if (extremes[LEFT].note_column_ == encompasses[i]
1277 ||extremes[RIGHT].note_column_ == encompasses[i])
1280 Encompass_info inf (get_encompass_info (me, encompasses[i], common));
1282 Real y = dir*((dir * inf.head_) >? (dir *inf.stem_));
1284 avoid.push (Offset (inf.x_, y + dir * score_param->free_head_distance_));
1287 Offset dz = attachments[RIGHT]- attachments[LEFT];;
1288 Offset dz_unit = dz;
1289 dz_unit *= 1 / dz.length();
1290 Offset dz_perp = dz_unit * Offset(0,1);
1292 Real indent, height;
1293 get_slur_indent_height (&indent, &height, dz.length (), h_inf, r_0);
1295 Real excentricity = robust_scm2double (me->get_property ("excentricity"), 0.0);
1298 Real x1 = (excentricity + indent);
1299 Real x2 = (excentricity - indent);
1300 curve.control_[0] = attachments[LEFT];
1301 curve.control_[1] = attachments[LEFT] + dz_perp * height * dir + dz_unit * x1;
1302 curve.control_[2] = attachments[RIGHT] + dz_perp * height * dir + dz_unit * x2;
1303 curve.control_[3] = attachments[RIGHT];
1306 Real ff = fit_factor (dz_unit, dz_perp, curve, dir, avoid);
1307 Real l = dz.length ();
1312 l^2 > 4h^2 + 3(i 1/3l)^2 - 1/3 l^2
1316 |bez'(0)| < | bez'(.5)|
1318 when (control2-control1) has the same direction as (control3 -
1322 Real max_h = sqrt (sqr (l)/3 - .75 * sqr (indent + l / 3));
1323 height = height >? ((height * ff) <? max_h);
1325 curve.control_[0] = attachments[LEFT];
1326 curve.control_[1] = attachments[LEFT] + dz_perp * height * dir + dz_unit * x1;
1327 curve.control_[2] = attachments[RIGHT] + dz_perp * height * dir + dz_unit * x2;
1328 curve.control_[3] = attachments[RIGHT];