* lily/accidental-engraver.cc (process_acknowledged_grobs):
[lilypond.git] / lily / slur-quanting.cc
blobba4c56b8e59710e319ce9dc91d3137e5e2b19e49
1 /*
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>
8 */
11 #include <math.h>
13 #include "pitch.hh"
14 #include "accidental-interface.hh"
15 #include "directional-element-interface.hh"
16 #include "group-interface.hh"
17 #include "lily-guile.hh"
18 #include "note-column.hh"
19 #include "output-def.hh"
20 #include "slur-bezier-bow.hh"
21 #include "new-slur.hh"
22 #include "spanner.hh"
23 #include "staff-symbol-referencer.hh"
24 #include "staff-symbol.hh"
25 #include "stem.hh"
26 #include "warn.hh"
27 #include "beam.hh"
29 struct Slur_score
31 Drul_array<Offset> attachment_;
32 Real score_;
33 Bezier curve_;
35 #if DEBUG_SLUR_QUANTING
36 String score_card_;
37 #endif
39 Slur_score()
41 score_ = 0.0;
46 TODO: put in details property.,
48 use lowercase.
50 struct Slur_score_parameters
52 int SLUR_REGION_SIZE;
53 Real HEAD_ENCOMPASS_PENALTY;
54 Real STEM_ENCOMPASS_PENALTY;
55 Real CLOSENESS_FACTOR;
56 Real EDGE_ATTRACTION_FACTOR;
57 Real SAME_SLOPE_PENALTY;
58 Real STEEPER_SLOPE_FACTOR;
59 Real NON_HORIZONTAL_PENALTY;
60 Real HEAD_STRICT_FREE_SPACE;
61 Real MAX_SLOPE;
62 Real MAX_SLOPE_FACTOR;
63 Real EXTRA_OBJECT_COLLISION;
64 Real ACCIDENTAL_COLLISION;
65 Real FREE_HEAD_DISTANCE;
66 Slur_score_parameters ();
70 TODO:
72 - curve around flag for y coordinate
73 - better scoring.
74 - short-cut: try a smaller region first.
75 - collisions with accidentals
76 - collisions with scripts (staccato!)
79 struct Encompass_info
81 Real x_;
82 Real stem_;
83 Real head_;
84 Encompass_info ()
86 x_ = 0.0;
87 stem_ = 0.0;
88 head_ = 0.0;
92 struct Bound_info
94 Box stem_extent_;
95 Direction stem_dir_;
96 Grob *bound_;
97 Grob *note_column_;
98 Grob *slur_head_;
99 Grob *staff_;
100 Grob *stem_;
101 Interval slur_head_extent_;
102 Real neighbor_y_;
103 Real staff_space_;
105 Bound_info ()
107 stem_ = 0;
108 neighbor_y_ = 0;
109 staff_ = 0;
110 slur_head_ = 0;
111 stem_dir_ = CENTER;
112 note_column_ = 0;
119 static void
120 score_extra_encompass (Grob *me, Grob *common[],
121 Slur_score_parameters *score_param,
122 Drul_array<Bound_info> ,
123 Drul_array<Offset> ,
124 Array<Slur_score> * scores);
125 static void score_slopes (Grob *me, Grob *common[],
126 Slur_score_parameters *score_param,
127 Drul_array<Bound_info>,
128 Drul_array<Offset> base_attach,
129 Array<Slur_score> *scores);
131 static void score_edges (Grob *me, Grob *common[],
132 Slur_score_parameters *score_param,
133 Drul_array<Bound_info> extremes,
134 Drul_array<Offset> base_attach,
135 Array<Slur_score> *scores);
136 static void score_encompass (Grob *me, Grob *common[],
137 Slur_score_parameters*,
138 Drul_array<Bound_info>,
139 Drul_array<Offset>, Array<Slur_score> *scores);
140 static Bezier avoid_staff_line (Grob *me, Grob **common,
141 Drul_array<Bound_info> extremes,
142 Bezier bez);
144 static Encompass_info get_encompass_info (Grob *me,
145 Grob *col,
146 Grob **common);
147 static Bezier get_bezier (Grob *me, Drul_array<Offset>, Real, Real);
148 static Direction get_default_dir (Grob *me);
150 static void set_end_points (Grob *);
151 static Real broken_trend_y (Grob *me, Grob **, Direction dir);
152 static Drul_array<Bound_info> get_bound_info (Spanner *me, Grob **common);
154 static void generate_curves (Grob *me,
155 Grob *common[],
156 Drul_array<Bound_info> extremes,
157 Drul_array<Offset> base_attach,
158 Array<Slur_score> *scores);
159 static Array<Slur_score> enumerate_attachments
160 (Grob *me, Grob *common[], Slur_score_parameters *score_param,
161 Drul_array<Bound_info> extremes,
162 Drul_array<Offset> base_attachment, Drul_array<Real> end_ys);
163 static Drul_array<Offset> get_base_attachments
164 (Spanner *sp, Grob **common, Drul_array<Bound_info> extremes);
165 static Drul_array<Real> get_y_attachment_range
166 (Spanner *sp, Grob **common, Drul_array<Bound_info> extremes,
167 Drul_array<Offset> base_attachment);
169 void
170 init_score_param (Slur_score_parameters *score_param)
172 score_param->SLUR_REGION_SIZE = 5;
173 score_param->HEAD_ENCOMPASS_PENALTY = 1000.0;
174 score_param->STEM_ENCOMPASS_PENALTY = 30.0;
175 score_param->CLOSENESS_FACTOR = 10;
176 score_param->EDGE_ATTRACTION_FACTOR = 4;
177 score_param->SAME_SLOPE_PENALTY = 20;
178 score_param->STEEPER_SLOPE_FACTOR = 50;
179 score_param->NON_HORIZONTAL_PENALTY = 15;
180 score_param->HEAD_STRICT_FREE_SPACE = 0.2;
181 score_param->MAX_SLOPE = 1.1;
182 score_param->MAX_SLOPE_FACTOR = 10;
183 score_param->FREE_HEAD_DISTANCE = 0.3;
184 score_param->EXTRA_OBJECT_COLLISION = 8;
185 score_param->ACCIDENTAL_COLLISION = 3;
188 Slur_score_parameters::Slur_score_parameters()
190 init_score_param (this);
193 /* HDIR indicates the direction for the slur. */
194 Real
195 broken_trend_y (Grob *me, Grob **common, Direction hdir)
197 /* A broken slur should maintain the same vertical trend
198 the unbroken slur would have had. */
199 Real by = 0.0;
200 if (Spanner *mother = dynamic_cast<Spanner*> (me->original_))
202 int k = broken_spanner_index (dynamic_cast<Spanner*> (me));
203 int j = k + hdir;
204 if (j < 0 || j >= mother->broken_intos_.size ())
205 return by;
207 Grob *neighbor = mother->broken_intos_[j];
208 if (hdir == RIGHT)
209 neighbor->set_property ("direction",
210 me->get_property ("direction"));
212 Spanner *common_mother
213 = dynamic_cast<Spanner*> (common[Y_AXIS]->original_);
214 int common_k
215 = broken_spanner_index (dynamic_cast<Spanner*> (common[Y_AXIS]));
216 int common_j = common_k + hdir;
218 if (common_j < 0 || common_j >= common_mother->broken_intos_.size())
219 return by;
221 Grob *common_next_system = common_mother->broken_intos_[common_j];
222 Link_array<Grob> neighbor_cols
223 = Pointer_group_interface__extract_grobs (neighbor, (Grob *)0,
224 "note-columns");
226 Grob *neighbor_col
227 = (hdir == RIGHT) ? neighbor_cols[0] : neighbor_cols.top ();
228 Grob *neighbor_common
229 = common_next_system->common_refpoint (neighbor_col, Y_AXIS);
231 Direction vdir = get_grob_direction (me);
232 Real neighbor_y
233 = neighbor_col->extent (neighbor_common, Y_AXIS)
234 .linear_combination (int(neighbor_cols.size()==1 ? CENTER : vdir))
235 - common_next_system->relative_coordinate (neighbor_common, Y_AXIS);
237 Link_array<Grob> my_cols
238 = Pointer_group_interface__extract_grobs (me, (Grob *)0,
239 "note-columns");
241 Grob *extreme_col = (hdir == RIGHT) ? my_cols.top() : my_cols[0];
242 Real y = extreme_col->extent (common[Y_AXIS], Y_AXIS)
243 .linear_combination (int ((my_cols.size() == 1) ? CENTER : vdir));
244 by = (y*neighbor_cols.size() + neighbor_y*my_cols.size()) /
245 (my_cols.size() + neighbor_cols.size());
247 return by;
251 Encompass_info
252 get_encompass_info (Grob *me,
253 Grob *col,
254 Grob **common)
256 Grob* stem = unsmob_grob (col->get_property ("stem"));
258 Encompass_info ei;
260 Direction dir = get_grob_direction (me);
262 if (!stem)
264 programming_error ("No stem for note column?");
265 ei.x_ = col->relative_coordinate (common[X_AXIS], X_AXIS);
266 ei.head_ = ei.stem_ = col->extent (common[Y_AXIS],
267 Y_AXIS)[get_grob_direction (me)];
268 return ei;
270 Direction stem_dir = get_grob_direction (stem);
272 if (Grob *head = Note_column::first_head (col))
273 ei.x_ = head->extent (common[X_AXIS], X_AXIS).center ();
274 else
275 ei.x_ = col->extent (common[X_AXIS], X_AXIS).center ();
277 Grob * h = Stem::extremal_heads (stem)[Direction (dir)];
278 if (!h)
280 ei.head_ = ei.stem_ = col->extent (common[Y_AXIS], Y_AXIS)[dir];
281 return ei;
284 ei.head_ = h->extent (common[Y_AXIS], Y_AXIS)[dir];
286 if ((stem_dir == dir)
287 && !stem->extent (stem, Y_AXIS).is_empty ())
289 ei.stem_ = stem->extent (common[Y_AXIS], Y_AXIS)[dir];
290 if (Grob * b = Stem::get_beam (stem))
291 ei.stem_ += stem_dir * 0.5 * Beam::get_thickness (b);
292 ei.x_ = stem->extent (common[X_AXIS], X_AXIS).center ();
294 else
295 ei.stem_ = ei.head_;
297 return ei;
301 Direction
302 get_default_dir (Grob*me)
304 Link_array<Grob> encompasses
305 = Pointer_group_interface__extract_grobs (me, (Grob*) 0, "note-columns");
307 Direction d = DOWN;
308 for (int i= 0; i < encompasses.size (); i ++)
310 if (Note_column::dir (encompasses[i]) < 0)
312 d = UP;
313 break;
316 return d;
319 MAKE_SCHEME_CALLBACK (New_slur, after_line_breaking,1);
321 New_slur::after_line_breaking (SCM smob)
323 Spanner *me = dynamic_cast<Spanner*> (unsmob_grob (smob));
324 if (!scm_ilength (me->get_property ("note-columns")))
326 me->suicide ();
327 return SCM_UNSPECIFIED;
330 if (!get_grob_direction (me))
331 set_grob_direction (me, get_default_dir (me));
333 set_end_points (me);
335 return SCM_UNSPECIFIED;
339 Bezier
340 get_bezier (Grob *me, Drul_array<Offset> extremes,
341 Real r_0,
342 Real h_inf)
344 Array<Offset> encompasses;
345 encompasses.push (extremes[LEFT]);
346 encompasses.push (extremes[RIGHT]);
348 Slur_bezier_bow bb (encompasses,
349 get_grob_direction (me), h_inf, r_0);
351 return bb.get_bezier ();
355 Drul_array<Bound_info>
356 get_bound_info (Spanner* me,
357 Grob **common)
359 Drul_array<Bound_info> extremes;
360 Direction d = LEFT;
361 Direction dir = get_grob_direction (me);
363 do {
364 extremes[d].bound_ = me->get_bound (d);
366 if (Note_column::has_interface (extremes[d].bound_))
368 extremes[d].note_column_ = extremes[d].bound_;
369 extremes[d].stem_ = Note_column::get_stem (extremes[d].note_column_);
370 extremes[d].stem_dir_ = get_grob_direction (extremes[d].stem_);
371 extremes[d].stem_extent_[X_AXIS] = extremes[d].stem_->extent (common[X_AXIS], X_AXIS);
372 extremes[d].stem_extent_[Y_AXIS] = extremes[d].stem_->extent (common[Y_AXIS], Y_AXIS);
373 extremes[d].slur_head_ = Stem::extremal_heads (extremes[d].stem_)[dir];
374 extremes[d].slur_head_extent_ = extremes[d].slur_head_->extent (common[X_AXIS], X_AXIS);
375 extremes[d].staff_ = Staff_symbol_referencer::get_staff_symbol (extremes[d].slur_head_);
376 extremes[d].staff_space_ = Staff_symbol_referencer::staff_space (extremes[d].slur_head_);
378 else
380 extremes[d].neighbor_y_ = broken_trend_y (me, common, d);
382 } while (flip (&d) != LEFT);
384 return extremes;
387 void
388 set_end_points (Grob *me)
390 Link_array<Grob> columns
391 = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
392 Slur_score_parameters params;
393 if (columns.is_empty ())
395 me->suicide ();
396 return;
399 SCM eltlist = me->get_property ("note-columns");
402 SCM extra_list = me->get_property ("encompass-objects");
403 Spanner *sp = dynamic_cast<Spanner*> (me);
405 Grob *common[] = {0,0};
406 for (int i = X_AXIS; i < NO_AXES; i++)
408 Axis a = (Axis)i;
409 common[a] = common_refpoint_of_list (eltlist, me, a);
410 common[a] = common_refpoint_of_list (extra_list, common[a], a);
413 common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (RIGHT),X_AXIS);
414 common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (LEFT),
415 X_AXIS);
417 Drul_array<Bound_info> extremes = get_bound_info (sp, common);
418 Drul_array<Offset> base_attachment
419 = get_base_attachments (sp, common, extremes);
420 Drul_array<Real> end_ys
421 = get_y_attachment_range (sp, common, extremes, base_attachment);
422 Array<Slur_score> scores = enumerate_attachments (me, common, &params,
423 extremes, base_attachment,
424 end_ys);
426 generate_curves (me, common, extremes, base_attachment, &scores);
427 score_edges (me, common, &params,extremes, base_attachment, &scores);
428 score_slopes (me, common, &params,extremes, base_attachment, &scores);
429 score_encompass (me, common, &params,extremes, base_attachment, &scores);
430 score_extra_encompass (me, common, &params,extremes, base_attachment, &scores);
432 Real opt = 1e6;
433 int opt_idx = 0;
434 // why backwards?
435 for (int i = scores.size (); i--;)
437 if (scores[i].score_ < opt)
439 opt = scores[i].score_;
440 opt_idx = i;
444 #if DEBUG_SLUR_QUANTING
445 SCM inspect_quants = me->get_property ("inspect-quants");
446 if (to_boolean (me->get_paper ()
447 ->lookup_variable (ly_symbol2scm ("debug-slur-quanting")))
448 && ly_c_pair_p (inspect_quants))
450 Drul_array<Real> ins = ly_scm2interval (inspect_quants);
451 int i = 0;
453 Real mindist = 1e6;
454 for (; i < scores.size (); i ++)
456 Real d =fabs (scores[i].attachment_[LEFT][Y_AXIS] - ins[LEFT])
457 + fabs (scores[i].attachment_[RIGHT][Y_AXIS] - ins[RIGHT]);
458 if (d < mindist)
460 opt_idx = i;
461 mindist= d;
464 if (mindist > 1e5)
465 programming_error ("Could not find quant.");
467 scores[opt_idx].score_card_ += to_string ("i%d", opt_idx);
469 // debug quanting
470 me->set_property ("quant-score",
471 scm_makfrom0str (scores[opt_idx].score_card_.to_str0 ()));
472 #endif
475 Bezier b = scores[opt_idx].curve_;
476 SCM controls = SCM_EOL;
477 for (int i = 4; i--;)
479 Offset o = b.control_[i] -
480 Offset (me->relative_coordinate (common[X_AXIS], X_AXIS),
481 me->relative_coordinate (common[Y_AXIS], Y_AXIS));
483 controls = scm_cons (ly_offset2scm (o), controls);
486 me->set_property ("control-points", controls);
490 Drul_array<Real>
491 get_y_attachment_range (Spanner*me,
492 Grob **common,
493 Drul_array<Bound_info> extremes,
494 Drul_array<Offset> base_attachment)
496 Drul_array<Real> end_ys;
497 Direction dir = get_grob_direction (me);
498 Direction d = LEFT;
501 if (extremes[d].note_column_)
503 end_ys[d] = dir
504 * ((dir * (base_attachment[d][Y_AXIS] + 4.0 * dir))
505 >? (dir * (dir + extremes[d].note_column_->extent(common[Y_AXIS],
506 Y_AXIS)[dir]))
507 >? (dir * base_attachment[-d][Y_AXIS]));
509 else
510 end_ys[d] = extremes[d].neighbor_y_ + 4.0 * dir;
512 while (flip (&d) != LEFT);
514 return end_ys;
517 Drul_array<Offset>
518 get_base_attachments (Spanner *me,
519 Grob **common, Drul_array<Bound_info> extremes)
521 Link_array<Grob> columns
522 = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
523 Drul_array<Offset> base_attachment;
524 Slur_score_parameters params;
525 Real staff_space = Staff_symbol_referencer::staff_space ((Grob*)me);
526 Direction dir = get_grob_direction (me);
527 Direction d = LEFT;
530 Grob *stem = extremes[d].stem_;
531 Grob *head = extremes[d].slur_head_;
533 Real x, y;
534 if (!extremes[d].note_column_)
536 y = extremes[d].neighbor_y_;
537 if (d== RIGHT)
538 x = extremes[d].bound_->extent (common[X_AXIS], X_AXIS)[d];
539 else
540 x = me->get_broken_left_end_align ();
542 else
544 if (stem
545 && extremes[d].stem_dir_ == dir
546 && Stem::get_beaming (stem, -d)
547 && columns.size () > 2
549 y = extremes[d].stem_extent_[Y_AXIS][dir];
550 else if (head)
551 y = head->extent (common[Y_AXIS], Y_AXIS)[dir];
552 y += dir * 0.5 * staff_space;
554 if (stem)
556 SCM scripts = stem->get_property ((dir == UP
557 ? "script-up"
558 : "script-down"));
560 /* FIXME: number of scripts, height, priority?
561 articulation scripts should be inside slur, other,
562 such as dynamics, pedals, fingering, should be
563 outside. */
564 if (!SCM_NULLP (scripts))
565 y += dir * staff_space;
568 Real pos
569 = (y - extremes[d].staff_->relative_coordinate (common[Y_AXIS],
570 Y_AXIS))
571 * 2.0 / Staff_symbol::staff_space (extremes[d].staff_);
573 /* start off staffline. */
574 if (fabs (pos - round (pos)) < 0.2
575 && Staff_symbol_referencer::on_staffline (head, (int) rint (pos))
576 && Staff_symbol_referencer::line_count (head) - 1 >= rint (pos)
578 // TODO: calc from slur thick & line thick, parameter.
579 y += 1.5 * staff_space * dir / 10;
581 Grob * fh = Note_column::first_head (extremes[d].note_column_);
582 x = fh->extent (common[X_AXIS], X_AXIS).linear_combination (CENTER);
584 base_attachment[d] = Offset (x, y);
586 } while (flip (&d) != LEFT);
588 return base_attachment;
591 void
592 generate_curves (Grob *me, Grob **common,
593 Drul_array<Bound_info> extremes,
594 Drul_array<Offset>,
595 Array<Slur_score> *scores)
597 (void) common;
598 (void) extremes;
599 Real staff_space = Staff_symbol_referencer::staff_space ((Grob *) me);
600 Real r_0 = robust_scm2double (me->get_property ("ratio"), 0.33);
601 Real h_inf = staff_space * ly_scm2double (me->get_property ("height-limit"));
602 for (int i = 0; i < scores->size(); i++)
604 Bezier bez= get_bezier (me, (*scores)[i].attachment_, r_0, h_inf);
605 bez = avoid_staff_line (me, common, extremes, bez);
606 (*scores)[i].attachment_[LEFT] = bez.control_[0];
607 (*scores)[i].attachment_[RIGHT] = bez.control_[3];
608 (*scores)[i].curve_ = bez;
612 Bezier
613 avoid_staff_line (Grob *me, Grob **common,
614 Drul_array<Bound_info> extremes,
615 Bezier bez)
617 Offset horiz (1,0);
618 Array<Real> ts = bez.solve_derivative (horiz);
619 Real lt = me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
620 Real thick = robust_scm2double (me->get_property ("thickness"), 1.0) * lt;
622 /* TODO: handle case of broken slur. */
623 if (!ts.is_empty ()
624 && (extremes[LEFT].staff_ == extremes[RIGHT].staff_)
625 && extremes[LEFT].staff_ && extremes[RIGHT].staff_)
627 Real y = bez.curve_point (ts[0])[Y_AXIS];
629 Grob *staff = extremes[LEFT].staff_;
631 Real staff_space = extremes[LEFT].staff_space_;
632 Real p = 2 * (y - staff->relative_coordinate (common[Y_AXIS], Y_AXIS))
633 / staff_space;
635 Real distance = fabs (round (p) - p); // in halfspaces
636 if (distance < 4 * thick
637 && (int) fabs (round (p))
638 <= 2 * Staff_symbol_referencer::staff_radius (staff) + 0.1
639 && (int (fabs (round (p))) % 2
640 != Staff_symbol_referencer::line_count (staff) % 2))
642 Direction resolution_dir =
643 (distance ? get_grob_direction (me) : Direction (sign (p - round(p))));
645 // TODO: parameter
646 Real newp = round (p) + resolution_dir
647 * 5 * thick;
649 Real dy = (newp - p) * staff_space / 2.0;
650 #if 0
651 bez.translate (Offset (0, dy));
652 #else
653 bez.control_[1][Y_AXIS] += dy;
654 bez.control_[2][Y_AXIS] += dy;
656 #endif
659 return bez;
662 Array<Slur_score>
663 enumerate_attachments (Grob *me, Grob **,
664 Slur_score_parameters *score_param,
665 Drul_array<Bound_info> extremes,
666 Drul_array<Offset> base_attachment,
667 Drul_array<Real> end_ys)
669 /*ugh. */
670 Array<Slur_score> scores;
672 Direction dir = get_grob_direction (me);
673 Real staff_space = Staff_symbol_referencer::staff_space ((Grob *)me);
675 Drul_array<Offset> os;
676 os[LEFT] = base_attachment[LEFT];
677 Real minimum_length = staff_space
678 * robust_scm2double (me->get_property ("minimum-length"), 2.0);
680 for (int i = 0; dir * os[LEFT][Y_AXIS] <= dir * end_ys[LEFT]; i++)
682 os[RIGHT] = base_attachment[RIGHT];
683 for (int j = 0; dir * os[RIGHT][Y_AXIS] <= dir * end_ys[RIGHT]; j++)
685 Slur_score s;
686 Direction d = LEFT;
687 Drul_array<bool> attach_to_stem (false, false);
688 do {
689 os[d][X_AXIS] = base_attachment[d][X_AXIS];
690 if (extremes[d].stem_
691 && !Stem::is_invisible (extremes[d].stem_)
692 && extremes[d].stem_dir_ == dir
693 && dir == -d)
695 if (extremes[d].stem_extent_[Y_AXIS].contains (os[d][Y_AXIS]))
697 os[d][X_AXIS] = extremes[d].slur_head_extent_[-d]
698 - d * 0.3;
699 attach_to_stem[d] = true;
701 else if (dir *extremes[d].stem_extent_[Y_AXIS][dir] < dir * os[d][Y_AXIS])
703 os[d][X_AXIS] = extremes[d].stem_extent_[X_AXIS].center();
706 } while (flip (&d) != LEFT);
708 Offset dz;
709 dz = os[RIGHT] - os[LEFT];
710 if (dz[X_AXIS] < minimum_length
711 || fabs (dz[Y_AXIS] / dz[X_AXIS]) > score_param->MAX_SLOPE
716 if (extremes[d].slur_head_)
718 os[d][X_AXIS] = extremes[d].slur_head_extent_.center ();
719 attach_to_stem[d] = false;
722 while (flip (&d) != LEFT);
725 dz = os[RIGHT] - os[LEFT];
726 do {
727 if (extremes[d].slur_head_
728 && !attach_to_stem[d])
731 horizontally move tilted slurs a little. Move more
732 for bigger tilts.
734 TODO: parameter
736 os[d][X_AXIS] -=
737 dir * extremes[d].slur_head_extent_.length () * sin (dz.arg ()) / 3;
739 } while (flip (&d) != LEFT);
741 s.attachment_ = os;
742 scores.push (s);
744 os[RIGHT][Y_AXIS] += dir * staff_space / 2;
747 os[LEFT][Y_AXIS] += dir * staff_space / 2;
750 return scores;
753 void
754 score_encompass (Grob *me, Grob *common[],
755 Slur_score_parameters *score_param,
756 Drul_array<Bound_info> ,
757 Drul_array<Offset> ,
758 Array<Slur_score> * scores)
760 Link_array<Grob> encompasses
761 = Pointer_group_interface__extract_grobs (me, (Grob *)0, "note-columns");
762 Direction dir = get_grob_direction (me);
764 Array<Encompass_info> infos;
766 for (int i = 0; i < encompasses.size(); i++)
767 infos.push (get_encompass_info (me, encompasses[i], common));
769 for (int i = 0; i < scores->size (); i++)
771 Bezier const &bez (scores->elem (i).curve_);
772 Real demerit = 0.0;
773 for (int j = 0; j < infos.size(); j++)
775 Real x = infos[j].x_;
777 bool l_edge = j==0;
778 bool r_edge = j==infos.size()-1;
779 bool edge = l_edge || r_edge;
781 if (!(x < scores->elem (i).attachment_[RIGHT][X_AXIS]
782 && x > scores->elem (i).attachment_[LEFT][X_AXIS]))
783 continue;
785 Real y = bez.get_other_coordinate (X_AXIS, x);
786 if (!edge)
788 Real head_dy = (y - infos[j].head_);
789 if (dir * head_dy < 0)
790 demerit += score_param->HEAD_ENCOMPASS_PENALTY;
791 else
793 Real hd = (head_dy)
794 ? (1 / fabs (head_dy) - 1 / score_param->FREE_HEAD_DISTANCE)
795 : score_param->HEAD_ENCOMPASS_PENALTY;
796 hd = (hd >? 0)<? score_param->HEAD_ENCOMPASS_PENALTY;
798 demerit += hd;
802 if (dir * (y - infos[j].stem_) < 0)
804 Real stem_dem =score_param->STEM_ENCOMPASS_PENALTY ;
805 if ((l_edge && dir == UP)
806 || (r_edge && dir == DOWN))
808 stem_dem /= 5;
811 demerit += stem_dem;
813 else if (!edge)
815 Interval ext;
816 ext.add_point (infos[j].stem_);
817 ext.add_point (infos[j].head_);
819 demerit += - score_param->CLOSENESS_FACTOR * (dir * (y - (ext[dir] + dir * score_param->FREE_HEAD_DISTANCE)) <? 0) /
820 infos.size ();
824 #if DEBUG_SLUR_QUANTING
825 (*scores)[i].score_card_ += to_string ("C%.2f", demerit);
826 #endif
828 (*scores)[i].score_ += demerit;
834 void
835 score_extra_encompass (Grob *me, Grob *common[],
836 Slur_score_parameters *score_param,
837 Drul_array<Bound_info> ,
838 Drul_array<Offset> ,
839 Array<Slur_score> * scores)
841 Link_array<Grob> encompasses
842 = Pointer_group_interface__extract_grobs (me, (Grob *)0, "encompass-objects");
843 Direction dir = get_grob_direction (me);
844 Real lt = me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
845 Real thick = robust_scm2double (me->get_property ("thickness"), 1.0) * lt;
848 Array<Real> xs;
849 Array<Interval> yexts;
850 for (int i = 0; i < encompasses.size(); i++)
852 Grob *g = encompasses [i];
853 Interval xe = g->extent (common[X_AXIS], X_AXIS);
854 Interval ye = g->extent (common[Y_AXIS], Y_AXIS);
856 Real xp = 0.0;
858 if (Accidental_interface::has_interface (g))
861 C&P accidental.cc
863 bool parens = false;
864 if (to_boolean (g->get_property ("cautionary")))
866 SCM cstyle = g->get_property ("cautionary-style");
867 parens = ly_c_equal_p (cstyle, ly_symbol2scm ("parentheses"));
871 SCM accs = g->get_property ("accidentals");
872 SCM scm_style = g->get_property ("style");
873 if (!ly_c_symbol_p (scm_style)
874 && !parens
875 && scm_ilength (accs) == 1)
877 switch (ly_scm2int (ly_car (accs))) {
878 case FLAT:
879 case DOUBLE_FLAT:
880 xp = LEFT;
881 case SHARP:
882 xp = 0.5* dir;
883 case NATURAL:
884 xp = -dir;
889 xs.push (xe.linear_combination (xp));
890 ye.widen (.5 * thick);
891 yexts.push (ye);
894 for (int i = 0; i < scores->size (); i++)
896 Bezier const &bez (scores->elem (i).curve_);
897 Real demerit = 0.0;
898 for (int j = 0; j < xs.size(); j++)
900 Real x = xs[j];
901 if (!(x < scores->elem (i).attachment_[RIGHT][X_AXIS]
902 && x > scores->elem (i).attachment_[LEFT][X_AXIS]))
903 continue;
905 Real y = bez.get_other_coordinate (X_AXIS, x);
907 if (yexts[j].contains (y))
909 if (Accidental_interface::has_interface (encompasses[j]))
910 demerit += score_param->ACCIDENTAL_COLLISION;
911 else
912 demerit += score_param->EXTRA_OBJECT_COLLISION;
916 #if DEBUG_SLUR_QUANTING
917 (*scores)[i].score_card_ += to_string ("X%.2f", demerit);
918 #endif
919 (*scores)[i].score_ += demerit;
924 void
925 score_edges (Grob *me, Grob **,
926 Slur_score_parameters * score_param,
927 Drul_array<Bound_info> extremes,
928 Drul_array<Offset> base_attach,
929 Array<Slur_score> *scores)
931 Direction dir = get_grob_direction (me);
933 for (int i = 0; i < scores->size (); i++)
936 Direction d = LEFT;
937 do {
938 Real y = scores->elem (i).attachment_[d][Y_AXIS];
939 Real dy = fabs (y - base_attach[d][Y_AXIS]);
941 Real factor = score_param->EDGE_ATTRACTION_FACTOR;
942 Real demerit = factor * dy;
943 if (extremes[d].stem_
944 && extremes[d].stem_dir_ == dir
945 && !Stem::get_beaming (extremes[d].stem_, -d)
947 demerit /= 5;
949 (*scores)[i].score_ += demerit;
950 #if DEBUG_SLUR_QUANTING
951 (*scores)[i].score_card_ += to_string ("E%.2f", demerit);
952 #endif
953 } while (flip (&d) != LEFT);
957 void
958 score_slopes (Grob *me, Grob *common[],
959 Slur_score_parameters*score_param,
960 Drul_array<Bound_info> extremes,
961 Drul_array<Offset> ,
962 Array<Slur_score> * scores)
964 Drul_array<Real> ys;
966 Direction d = LEFT;
967 do {
968 if (extremes[d].slur_head_)
969 ys[d] = extremes[d].slur_head_ ->relative_coordinate (common[Y_AXIS],
970 Y_AXIS);
971 else
972 ys[d] = extremes[d].neighbor_y_;
973 } while (flip (&d) != LEFT);
975 bool has_beams
976 = (extremes[LEFT].stem_ && Stem::get_beam (extremes[LEFT].stem_))
977 || (extremes[RIGHT].stem_ && Stem::get_beam (extremes[RIGHT].stem_));
979 Direction dir = get_grob_direction (me);
980 Real dy = ys[RIGHT] - ys[LEFT];
981 for (int i = 0; i < scores->size (); i++)
983 Offset slur_dz = (*scores)[i].attachment_[RIGHT]
984 - (*scores)[i].attachment_[LEFT];
985 Real slur_dy = slur_dz[Y_AXIS];
986 Real demerit = 0.0;
988 demerit += ((fabs (slur_dy/slur_dz[X_AXIS])
989 - score_param->MAX_SLOPE) >? 0)
990 * score_param->MAX_SLOPE_FACTOR;
993 0.2: account for staffline offset.
995 Real max_dy = (fabs (dy) + 0.2);
996 if (has_beams)
997 max_dy += 1.0;
999 demerit += score_param->STEEPER_SLOPE_FACTOR * ((fabs (slur_dy) -max_dy) >? 0);
1002 demerit += ((fabs (slur_dy/slur_dz[X_AXIS]) - score_param->MAX_SLOPE)>?0) * score_param->MAX_SLOPE_FACTOR;
1004 if (sign (dy) == 0 &&
1005 sign (slur_dy) != 0)
1006 demerit += score_param->NON_HORIZONTAL_PENALTY;
1008 if (sign (dy)
1009 && sign (slur_dy)
1010 && sign (slur_dy) != sign (dy))
1011 demerit += has_beams
1012 ? score_param->SAME_SLOPE_PENALTY / 10
1013 : score_param->SAME_SLOPE_PENALTY;
1015 #if DEBUG_SLUR_QUANTING
1016 (*scores)[i].score_card_ += to_string ("S%.2f",d);
1017 #endif
1018 (*scores)[i].score_ += demerit;