2 slur.cc -- implement score based Slur
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>
14 #include "font-interface.hh"
15 #include "text-item.hh"
16 #include "directional-element-interface.hh"
17 #include "group-interface.hh"
18 #include "lily-guile.hh"
20 #include "note-column.hh"
21 #include "output-def.hh"
23 #include "slur-bezier-bow.hh"
26 #include "staff-symbol-referencer.hh"
27 #include "staff-symbol.hh"
36 - curve around flag for y coordinate
38 - short-cut: try a smaller region first.
39 - collisions with accidentals
40 - collisions with scripts (staccato!)
65 Interval slur_head_extent_;
81 TODO: put in details property.
83 struct Slur_score_parameters
86 Real HEAD_ENCOMPASS_PENALTY;
87 Real STEM_ENCOMPASS_PENALTY;
88 Real CLOSENESS_FACTOR;
89 Real EDGE_ATTRACTION_FACTOR;
90 Real SAME_SLOPE_PENALTY;
91 Real STEEPER_SLOPE_FACTOR;
92 Real NON_HORIZONTAL_PENALTY;
93 Real HEAD_STRICT_FREE_SPACE;
95 Real MAX_SLOPE_FACTOR;
96 Real FREE_HEAD_DISTANCE;
97 Slur_score_parameters ();
101 init_score_param (Slur_score_parameters *score_param)
103 score_param->SLUR_REGION_SIZE = 5;
104 score_param->HEAD_ENCOMPASS_PENALTY = 1000.0;
105 score_param->STEM_ENCOMPASS_PENALTY = 30.0;
106 score_param->CLOSENESS_FACTOR = 10;
107 score_param->EDGE_ATTRACTION_FACTOR = 4;
108 score_param->SAME_SLOPE_PENALTY = 20;
109 score_param->STEEPER_SLOPE_FACTOR = 50;
110 score_param->NON_HORIZONTAL_PENALTY = 15;
111 score_param->HEAD_STRICT_FREE_SPACE = 0.2;
112 score_param->MAX_SLOPE = 1.1;
113 score_param->MAX_SLOPE_FACTOR = 10;
114 score_param->FREE_HEAD_DISTANCE = 0.3;
117 Slur_score_parameters::Slur_score_parameters()
119 init_score_param (this);
122 #define DEBUG_SLUR_QUANTING 1
126 Drul_array<Offset> attachment_;
130 #if DEBUG_SLUR_QUANTING
143 static void add_column (Grob *me, Grob *col);
144 DECLARE_SCHEME_CALLBACK (print, (SCM));
145 static void score_slopes (Grob *me, Grob *common[],
146 Slur_score_parameters *score_param,
147 Drul_array<Bound_info>,
148 Drul_array<Offset> base_attach,
149 Array<Slur_score> *scores);
151 static void score_edges (Grob *me, Grob *common[],
152 Slur_score_parameters *score_param,
153 Drul_array<Bound_info> extremes,
154 Drul_array<Offset> base_attach,
155 Array<Slur_score> *scores);
156 static void score_encompass (Grob *me, Grob *common[],
157 Slur_score_parameters*,
158 Drul_array<Bound_info>,
159 Drul_array<Offset>, Array<Slur_score> *scores);
160 static Bezier avoid_staff_line (Grob *me, Grob **common,
161 Drul_array<Bound_info> extremes,
164 static Encompass_info get_encompass_info (Grob *me,
167 static void set_interface (Grob *);
168 static bool has_interface (Grob *);
169 static Bezier get_curve (Grob *me);
170 static Bezier get_bezier (Grob *me, Drul_array<Offset>, Real, Real);
171 static Direction get_default_dir (Grob *me);
172 DECLARE_SCHEME_CALLBACK (after_line_breaking, (SCM));
173 DECLARE_SCHEME_CALLBACK (height, (SCM,SCM));
175 static void set_end_points (Grob *);
176 static Real get_boundary_notecolumn_y (Grob *me, Direction dir);
177 static Real broken_trend_y (Grob *me, Grob **, Direction dir);
178 static void set_control_points (Grob *me);
179 static Drul_array<Bound_info> get_bound_info (Spanner *me, Grob **common);
181 static void generate_curves (Grob *me,
183 Drul_array<Bound_info> extremes,
184 Drul_array<Offset> base_attach,
185 Array<Slur_score> *scores);
186 static Array<Slur_score> enumerate_attachments
187 (Grob *me, Grob *common[], Slur_score_parameters *score_param,
188 Drul_array<Bound_info> extremes,
189 Drul_array<Offset> base_attachment, Drul_array<Real> end_ys);
190 static Drul_array<Offset> get_base_attachments
191 (Spanner *sp, Grob **common, Drul_array<Bound_info> extremes);
192 static Drul_array<Real> get_y_attachment_range
193 (Spanner *sp, Grob **common, Drul_array<Bound_info> extremes,
194 Drul_array<Offset> base_attachment);
197 /* HDIR indicates the direction for the slur. */
199 New_slur::broken_trend_y (Grob *me, Grob **common, Direction hdir)
201 /* A broken slur should maintain the same vertical trend
202 the unbroken slur would have had. */
204 if (Spanner *mother = dynamic_cast<Spanner*> (me->original_))
206 int k = broken_spanner_index (dynamic_cast<Spanner*> (me));
208 if (j < 0 || j >= mother->broken_intos_.size ())
211 Grob *neighbor = mother->broken_intos_[j];
213 neighbor->set_property ("direction",
214 me->get_property ("direction"));
216 Spanner *common_mother
217 = dynamic_cast<Spanner*> (common[Y_AXIS]->original_);
219 = broken_spanner_index (dynamic_cast<Spanner*> (common[Y_AXIS]));
220 int common_j = common_k + hdir;
222 if (common_j < 0 || common_j >= common_mother->broken_intos_.size())
225 Grob *common_next_system = common_mother->broken_intos_[common_j];
226 Link_array<Grob> neighbor_cols
227 = Pointer_group_interface__extract_grobs (neighbor, (Grob *)0,
231 = (hdir == RIGHT) ? neighbor_cols[0] : neighbor_cols.top ();
232 Grob *neighbor_common
233 = common_next_system->common_refpoint (neighbor_col, Y_AXIS);
235 Direction vdir = get_grob_direction (me);
237 = neighbor_col->extent (neighbor_common, Y_AXIS)
238 .linear_combination (int(neighbor_cols.size()==1 ? CENTER : vdir))
239 - common_next_system->relative_coordinate (neighbor_common, Y_AXIS);
241 Link_array<Grob> my_cols
242 = Pointer_group_interface__extract_grobs (me, (Grob *)0,
245 Grob *extreme_col = (hdir == RIGHT) ? my_cols.top() : my_cols[0];
246 Real y = extreme_col->extent (common[Y_AXIS], Y_AXIS)
247 .linear_combination (int ((my_cols.size() == 1) ? CENTER : vdir));
248 by = (y*neighbor_cols.size() + neighbor_y*my_cols.size()) /
249 (my_cols.size() + neighbor_cols.size());
255 New_slur::set_interface (Grob*me)
257 /* Copy to mutable list. */
258 me->set_property ("attachment",
259 ly_deep_copy (me->get_property ("attachment")));
263 New_slur::add_column (Grob*me, Grob*n)
265 Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-columns"), n);
266 me->add_dependency (n);
268 add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (n));
272 New_slur::get_encompass_info (Grob *me,
276 Grob* stem = unsmob_grob (col->get_property ("stem"));
280 Direction dir = get_grob_direction (me);
284 programming_error ("No stem for note column?");
285 ei.x_ = col->relative_coordinate (common[X_AXIS], X_AXIS);
286 ei.head_ = ei.stem_ = col->extent (common[Y_AXIS],
287 Y_AXIS)[get_grob_direction (me)];
290 Direction stem_dir = get_grob_direction (stem);
292 if (Grob *head = Note_column::first_head (col))
293 ei.x_ = head->extent (common[X_AXIS], X_AXIS).center ();
295 ei.x_ = col->extent (common[X_AXIS], X_AXIS).center ();
297 Grob * h = Stem::extremal_heads (stem)[Direction (dir)];
300 ei.head_ = ei.stem_ = col->extent (common[Y_AXIS], Y_AXIS)[dir];
304 ei.head_ = h->extent (common[Y_AXIS], Y_AXIS)[dir];
306 if ((stem_dir == dir)
307 && !stem->extent (stem, Y_AXIS).is_empty ())
309 ei.stem_ = stem->extent (common[Y_AXIS], Y_AXIS)[dir];
310 if (Grob * b = Stem::get_beam (stem))
311 ei.stem_ += stem_dir * 0.5 * Beam::get_thickness (b);
312 ei.x_ = stem->extent (common[X_AXIS], X_AXIS).center ();
322 New_slur::get_default_dir (Grob*me)
324 Link_array<Grob> encompasses
325 = Pointer_group_interface__extract_grobs (me, (Grob*) 0, "note-columns");
328 for (int i= 0; i < encompasses.size (); i ++)
330 if (Note_column::dir (encompasses[i]) < 0)
339 MAKE_SCHEME_CALLBACK (New_slur, after_line_breaking,1);
341 New_slur::after_line_breaking (SCM smob)
343 Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob));
344 if (!scm_ilength (me->get_property ("note-columns")))
347 return SCM_UNSPECIFIED;
350 if (!get_grob_direction (me))
351 set_grob_direction (me, get_default_dir (me));
355 return SCM_UNSPECIFIED;
359 New_slur::get_bezier (Grob *me, Drul_array<Offset> extremes,
363 Array<Offset> encompasses;
364 encompasses.push (extremes[LEFT]);
365 encompasses.push (extremes[RIGHT]);
367 Slur_bezier_bow bb (encompasses,
368 get_grob_direction (me), h_inf, r_0);
370 return bb.get_bezier ();
374 Drul_array<Bound_info>
375 New_slur::get_bound_info (Spanner* me,
378 Drul_array<Bound_info> extremes;
380 Direction dir = get_grob_direction (me);
383 extremes[d].bound_ = me->get_bound (d);
385 if (Note_column::has_interface (extremes[d].bound_))
387 extremes[d].note_column_ = extremes[d].bound_;
388 extremes[d].stem_ = Note_column::get_stem (extremes[d].note_column_);
389 extremes[d].stem_dir_ = get_grob_direction (extremes[d].stem_);
390 extremes[d].stem_extent_[X_AXIS] = extremes[d].stem_->extent (common[X_AXIS], X_AXIS);
391 extremes[d].stem_extent_[Y_AXIS] = extremes[d].stem_->extent (common[Y_AXIS], Y_AXIS);
392 extremes[d].slur_head_ = Stem::extremal_heads (extremes[d].stem_)[dir];
393 extremes[d].slur_head_extent_ = extremes[d].slur_head_->extent (common[X_AXIS], X_AXIS);
394 extremes[d].staff_ = Staff_symbol_referencer::get_staff_symbol (extremes[d].slur_head_);
395 extremes[d].staff_space_ = Staff_symbol_referencer::staff_space (extremes[d].slur_head_);
399 extremes[d].neighbor_y_ = broken_trend_y (me, common, d);
401 } while (flip (&d) != LEFT);
407 New_slur::set_end_points (Grob *me)
409 Link_array<Grob> columns
410 = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
411 Slur_score_parameters params;
412 if (columns.is_empty ())
418 SCM eltlist = me->get_property ("note-columns");
419 Grob *common[] = {common_refpoint_of_list (eltlist, me, X_AXIS),
420 common_refpoint_of_list (eltlist, me, Y_AXIS)};
423 Spanner *sp = dynamic_cast<Spanner*> (me);
424 common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (RIGHT),
426 common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (LEFT),
428 Drul_array<Bound_info> extremes = get_bound_info (sp, common);
429 Drul_array<Offset> base_attachment
430 = get_base_attachments (sp, common, extremes);
431 Drul_array<Real> end_ys
432 = get_y_attachment_range (sp, common, extremes, base_attachment);
433 Array<Slur_score> scores = enumerate_attachments (me, common, ¶ms,
434 extremes, base_attachment,
437 generate_curves (me, common, extremes, base_attachment, &scores);
438 score_edges (me, common, ¶ms,extremes, base_attachment, &scores);
439 score_slopes (me, common, ¶ms,extremes, base_attachment, &scores);
440 score_encompass (me, common, ¶ms,extremes, base_attachment, &scores);
445 for (int i = scores.size (); i--;)
447 if (scores[i].score_ < opt)
449 opt = scores[i].score_;
454 #if DEBUG_SLUR_QUANTING
455 SCM inspect_quants = me->get_property ("inspect-quants");
456 if (to_boolean (me->get_paper ()
457 ->lookup_variable (ly_symbol2scm ("debug-slur-quanting")))
458 && ly_c_pair_p (inspect_quants))
460 Drul_array<Real> ins = ly_scm2interval (inspect_quants);
464 for (; i < scores.size (); i ++)
466 Real d =fabs (scores[i].attachment_[LEFT][Y_AXIS] - ins[LEFT])
467 + fabs (scores[i].attachment_[RIGHT][Y_AXIS] - ins[RIGHT]);
475 programming_error ("Could not find quant.");
477 scores[opt_idx].score_card_ += to_string ("i%d", opt_idx);
480 me->set_property ("quant-score",
481 scm_makfrom0str (scores[opt_idx].score_card_.to_str0 ()));
485 Bezier b = scores[opt_idx].curve_;
486 SCM controls = SCM_EOL;
487 for (int i = 4; i--;)
489 Offset o = b.control_[i] -
490 Offset (me->relative_coordinate (common[X_AXIS], X_AXIS),
491 me->relative_coordinate (common[Y_AXIS], Y_AXIS));
493 controls = scm_cons (ly_offset2scm (o), controls);
496 me->set_property ("control-points", controls);
501 New_slur::get_y_attachment_range (Spanner*me,
503 Drul_array<Bound_info> extremes,
504 Drul_array<Offset> base_attachment)
506 Drul_array<Real> end_ys;
507 Direction dir = get_grob_direction (me);
511 if (extremes[d].note_column_)
514 * ((dir * (base_attachment[d][Y_AXIS] + 4.0 * dir))
515 >? (dir * (dir + extremes[d].note_column_->extent(common[Y_AXIS],
517 >? (dir * base_attachment[-d][Y_AXIS]));
520 end_ys[d] = extremes[d].neighbor_y_ + 4.0 * dir;
522 while (flip (&d) != LEFT);
528 New_slur::get_base_attachments (Spanner *me,
529 Grob **common, Drul_array<Bound_info> extremes)
531 Link_array<Grob> columns
532 = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
533 Drul_array<Offset> base_attachment;
534 Slur_score_parameters params;
535 Real staff_space = Staff_symbol_referencer::staff_space ((Grob*)me);
536 Direction dir = get_grob_direction (me);
540 Grob *stem = extremes[d].stem_;
541 Grob *head = extremes[d].slur_head_;
544 if (!extremes[d].note_column_)
546 y = extremes[d].neighbor_y_;
548 x = extremes[d].bound_->extent (common[X_AXIS], X_AXIS)[d];
550 x = me->get_broken_left_end_align ();
555 && extremes[d].stem_dir_ == dir
556 && Stem::get_beaming (stem, -d)
557 && columns.size () > 2
559 y = extremes[d].stem_extent_[Y_AXIS][dir];
561 y = head->extent (common[Y_AXIS], Y_AXIS)[dir];
562 y += dir * 0.5 * staff_space;
565 = (y - extremes[d].staff_->relative_coordinate (common[Y_AXIS],
567 * 2.0 / Staff_symbol::staff_space (extremes[d].staff_);
569 /* start off staffline. */
570 if (fabs (pos - round (pos)) < 0.2
571 && Staff_symbol_referencer::on_staffline (head, (int) rint (pos))
572 && Staff_symbol_referencer::line_count (head) - 1 >= rint (pos)
574 // TODO: calc from slur thick & line thick, parameter.
575 y += 1.5 * staff_space * dir / 10;
577 Grob * fh = Note_column::first_head (extremes[d].note_column_);
578 x = fh->extent (common[X_AXIS], X_AXIS).linear_combination (CENTER);
580 base_attachment[d] = Offset (x, y);
582 } while (flip (&d) != LEFT);
584 return base_attachment;
588 New_slur::generate_curves (Grob *me, Grob **common,
589 Drul_array<Bound_info> extremes,
591 Array<Slur_score> *scores)
595 Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
596 Real r_0 = robust_scm2double (me->get_property ("ratio"), 0.33);
597 Real h_inf = staff_space * ly_scm2double (me->get_property ("height-limit"));
598 for (int i = 0; i < scores->size(); i++)
600 Bezier bez= get_bezier (me, (*scores)[i].attachment_, r_0, h_inf);
601 bez = avoid_staff_line (me, common, extremes, bez);
602 (*scores)[i].attachment_[LEFT] = bez.control_[0];
603 (*scores)[i].attachment_[RIGHT] = bez.control_[3];
604 (*scores)[i].curve_ = bez;
609 New_slur::avoid_staff_line (Grob *me, Grob **common,
610 Drul_array<Bound_info> extremes,
614 Array<Real> ts = bez.solve_derivative (horiz);
615 Real lt = me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
616 Real thick = robust_scm2double (me->get_property ("thickness"), 1.0) * lt;
618 /* TODO: handle case of broken slur. */
620 && (extremes[LEFT].staff_ == extremes[RIGHT].staff_)
621 && extremes[LEFT].staff_ && extremes[RIGHT].staff_)
623 Real y = bez.curve_point (ts[0])[Y_AXIS];
625 Grob *staff = extremes[LEFT].staff_;
627 Real staff_space = extremes[LEFT].staff_space_;
628 Real p = 2 * (y - staff->relative_coordinate (common[Y_AXIS], Y_AXIS))
631 Real distance = fabs (round (p) - p); // in halfspaces
632 if (distance < 4 * thick
633 && (int) fabs (round (p))
634 <= 2 * Staff_symbol_referencer::staff_radius (staff) + 0.1
635 && (int (fabs (round (p))) % 2
636 != Staff_symbol_referencer::line_count (staff) % 2))
638 Direction resolution_dir = - get_grob_direction (me);
639 // (distance ? :sign (p - round(p)))
642 Real newp = round (p) + resolution_dir
644 Real dy = (newp - p) * staff_space / 2.0;
645 bez.translate (Offset (0, dy));
652 New_slur::enumerate_attachments (Grob *me, Grob **,
653 Slur_score_parameters *score_param,
654 Drul_array<Bound_info> extremes,
655 Drul_array<Offset> base_attachment,
656 Drul_array<Real> end_ys)
659 Array<Slur_score> scores;
661 Direction dir = get_grob_direction (me);
662 Real staff_space = Staff_symbol_referencer::staff_space ((Grob *)me);
664 Drul_array<Offset> os;
665 os[LEFT] = base_attachment[LEFT];
666 Real minimum_length = staff_space
667 * robust_scm2double (me->get_property ("minimum-length"), 2.0);
669 for (int i = 0; dir * os[LEFT][Y_AXIS] <= dir * end_ys[LEFT]; i++)
671 os[RIGHT] = base_attachment[RIGHT];
672 for (int j = 0; dir * os[RIGHT][Y_AXIS] <= dir * end_ys[RIGHT]; j++)
676 Drul_array<bool> attach_to_stem (false, false);
678 os[d][X_AXIS] = base_attachment[d][X_AXIS];
679 if (extremes[d].stem_
680 && !Stem::is_invisible (extremes[d].stem_)
681 && extremes[d].stem_dir_ == dir
684 if (extremes[d].stem_extent_[Y_AXIS].contains (os[d][Y_AXIS]))
686 os[d][X_AXIS] = extremes[d].slur_head_extent_[-d]
688 attach_to_stem[d] = true;
690 else if (dir *extremes[d].stem_extent_[Y_AXIS][dir] < dir * os[d][Y_AXIS])
692 os[d][X_AXIS] = extremes[d].stem_extent_[X_AXIS].center();
695 } while (flip (&d) != LEFT);
698 dz = os[RIGHT] - os[LEFT];
699 if (dz[X_AXIS] < minimum_length
700 || fabs (dz[Y_AXIS] / dz[X_AXIS]) > score_param->MAX_SLOPE
705 if (extremes[d].slur_head_)
707 os[d][X_AXIS] = extremes[d].slur_head_extent_.center ();
708 attach_to_stem[d] = false;
711 while (flip (&d) != LEFT);
714 dz = os[RIGHT] - os[LEFT];
716 if (extremes[d].slur_head_
717 && !attach_to_stem[d])
720 horizontally move tilted slurs a little. Move more
726 dir * extremes[d].slur_head_extent_.length () * sin (dz.arg ()) / 3;
728 } while (flip (&d) != LEFT);
733 os[RIGHT][Y_AXIS] += dir * staff_space / 2;
736 os[LEFT][Y_AXIS] += dir * staff_space / 2;
743 New_slur::score_encompass (Grob *me, Grob *common[],
744 Slur_score_parameters *score_param,
745 Drul_array<Bound_info> ,
747 Array<Slur_score> * scores)
749 Link_array<Grob> encompasses
750 = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
751 Direction dir = get_grob_direction (me);
753 Array<Encompass_info> infos;
755 for (int i = 0; i < encompasses.size(); i++)
756 infos.push (get_encompass_info (me, encompasses[i], common));
758 for (int i = 0; i < scores->size (); i++)
760 Bezier const &bez (scores->elem (i).curve_);
762 for (int j = 0; j < infos.size(); j++)
764 Real x = infos[j].x_;
767 bool r_edge = j==infos.size()-1;
768 bool edge = l_edge || r_edge;
770 if (!(x < scores->elem (i).attachment_[RIGHT][X_AXIS]
771 && x > scores->elem (i).attachment_[LEFT][X_AXIS]))
774 Real y = bez.get_other_coordinate (X_AXIS, x);
777 Real head_dy = (y - infos[j].head_);
778 if (dir * head_dy < 0)
779 demerit += score_param->HEAD_ENCOMPASS_PENALTY;
783 ? (1 / fabs (head_dy) - 1 / score_param->FREE_HEAD_DISTANCE)
784 : score_param->HEAD_ENCOMPASS_PENALTY;
785 hd = (hd >? 0)<? score_param->HEAD_ENCOMPASS_PENALTY;
791 if (dir * (y - infos[j].stem_) < 0)
793 Real stem_dem =score_param->STEM_ENCOMPASS_PENALTY ;
794 if ((l_edge && dir == UP)
795 || (r_edge && dir == DOWN))
805 ext.add_point (infos[j].stem_);
806 ext.add_point (infos[j].head_);
808 demerit += - score_param->CLOSENESS_FACTOR * (dir * (y - (ext[dir] + dir * score_param->FREE_HEAD_DISTANCE)) <? 0) /
813 #if DEBUG_SLUR_QUANTING
814 (*scores)[i].score_card_ += to_string ("C%.2f", demerit);
817 (*scores)[i].score_ += demerit;
822 New_slur::score_edges (Grob *me, Grob **,
823 Slur_score_parameters * score_param,
824 Drul_array<Bound_info> extremes,
825 Drul_array<Offset> base_attach,
826 Array<Slur_score> *scores)
828 Direction dir = get_grob_direction (me);
830 for (int i = 0; i < scores->size (); i++)
835 Real y = scores->elem (i).attachment_[d][Y_AXIS];
836 Real dy = fabs (y - base_attach[d][Y_AXIS]);
838 Real factor = score_param->EDGE_ATTRACTION_FACTOR;
839 Real demerit = factor * dy;
840 if (extremes[d].stem_
841 && extremes[d].stem_dir_ == dir
842 && !Stem::get_beaming (extremes[d].stem_, -d)
846 (*scores)[i].score_ += demerit;
847 #if DEBUG_SLUR_QUANTING
848 (*scores)[i].score_card_ += to_string ("E%.2f", demerit);
850 } while (flip (&d) != LEFT);
855 New_slur::score_slopes (Grob *me, Grob *common[],
856 Slur_score_parameters*score_param,
857 Drul_array<Bound_info> extremes,
858 Drul_array<Offset> base_attach,
859 Array<Slur_score> * scores)
865 if (extremes[d].slur_head_)
866 ys[d] = extremes[d].slur_head_ ->relative_coordinate (common[Y_AXIS],
869 ys[d] = extremes[d].neighbor_y_;
870 } while (flip (&d) != LEFT);
873 = (extremes[LEFT].stem_ && Stem::get_beam (extremes[LEFT].stem_))
874 || (extremes[RIGHT].stem_ && Stem::get_beam (extremes[RIGHT].stem_));
876 Direction dir = get_grob_direction (me);
877 Real dy = ys[RIGHT] - ys[LEFT];
878 for (int i = 0; i < scores->size (); i++)
880 Offset slur_dz = (*scores)[i].attachment_[RIGHT]
881 - (*scores)[i].attachment_[LEFT];
882 Real slur_dy = slur_dz[Y_AXIS];
885 demerit += ((fabs (slur_dy/slur_dz[X_AXIS])
886 - score_param->MAX_SLOPE) >? 0)
887 * score_param->MAX_SLOPE_FACTOR;
890 0.2: account for staffline offset.
892 Real max_dy = (fabs (dy) + 0.2);
896 demerit += score_param->STEEPER_SLOPE_FACTOR * ((fabs (slur_dy) -max_dy) >? 0);
899 demerit += ((fabs (slur_dy/slur_dz[X_AXIS]) - score_param->MAX_SLOPE)>?0) * score_param->MAX_SLOPE_FACTOR;
901 if (sign (dy) == 0 &&
903 demerit += score_param->NON_HORIZONTAL_PENALTY;
907 && sign (slur_dy) != sign (dy))
909 ? score_param->SAME_SLOPE_PENALTY / 10
910 : score_param->SAME_SLOPE_PENALTY;
912 #if DEBUG_SLUR_QUANTING
913 (*scores)[i].score_card_ += to_string ("S%.2f",d);
915 (*scores)[i].score_ += demerit;
921 New_slur::get_curve (Grob*me)
925 for (SCM s = me->get_property ("control-points"); s != SCM_EOL;
927 b.control_[i++] = ly_scm2offset (ly_car (s));
933 MAKE_SCHEME_CALLBACK (New_slur, height, 2);
935 New_slur::height (SCM smob, SCM ax)
937 Axis a = (Axis)ly_scm2int (ax);
938 Grob *me = unsmob_grob (smob);
939 assert (a == Y_AXIS);
941 SCM mol = me->get_uncached_stencil ();
943 if (Stencil *m = unsmob_stencil (mol))
945 return ly_interval2scm (ext);
949 Ugh should have dash-length + dash-period
951 MAKE_SCHEME_CALLBACK (New_slur, print,1);
953 New_slur::print (SCM smob)
955 Grob *me = unsmob_grob (smob);
956 if (!scm_ilength (me->get_property ("note-columns")))
962 Real base_thick = robust_scm2double (me->get_property ("thickness"), 1);
963 Real thick = base_thick * Staff_symbol_referencer::line_thickness (me);
965 Real ss = Staff_symbol_referencer::staff_space (me);
966 Bezier one = get_curve (me);
971 TODO: replace dashed with generic property.
973 SCM d = me->get_property ("dashed");
974 if (ly_c_number_p (d))
975 a = Lookup::dashed_slur (one, thick, thick * robust_scm2double (d, 0));
977 a = Lookup::slur (one, get_grob_direction (me) * base_thick * ss / 10.0,
980 #if DEBUG_SLUR_QUANTING
981 SCM quant_score = me->get_property ("quant-score");
983 if (to_boolean (me->get_paper ()
984 ->lookup_variable (ly_symbol2scm ("debug-slur-quanting")))
985 && ly_c_string_p (quant_score))
988 SCM properties = Font_interface::text_font_alist_chain (me);
990 Stencil tm = *unsmob_stencil (Text_item::interpret_markup
991 (me->get_paper ()->self_scm (), properties,
993 a.add_at_edge (Y_AXIS, get_grob_direction (me), tm, 1.0, 0);
997 return a.smobbed_copy ();
1001 ADD_INTERFACE (New_slur, "new-slur-interface",
1003 "control-points dashed details direction height-limit note-columns ratio slope-limit thickness");