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 "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"
23 #include "staff-symbol-referencer.hh"
24 #include "staff-symbol.hh"
31 Drul_array
<Offset
> attachment_
;
35 #if DEBUG_SLUR_QUANTING
46 TODO: put in details property.,
50 struct Slur_score_parameters
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
;
62 Real MAX_SLOPE_FACTOR
;
63 Real EXTRA_OBJECT_COLLISION
;
64 Real ACCIDENTAL_COLLISION
;
65 Real FREE_HEAD_DISTANCE
;
66 Slur_score_parameters ();
72 - curve around flag for y coordinate
74 - short-cut: try a smaller region first.
75 - collisions with accidentals
76 - collisions with scripts (staccato!)
101 Interval slur_head_extent_
;
120 score_extra_encompass (Grob
*me
, Grob
*common
[],
121 Slur_score_parameters
*score_param
,
122 Drul_array
<Bound_info
> ,
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
,
144 static Encompass_info
get_encompass_info (Grob
*me
,
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
,
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
);
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. */
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. */
200 if (Spanner
*mother
= dynamic_cast<Spanner
*> (me
->original_
))
202 int k
= broken_spanner_index (dynamic_cast<Spanner
*> (me
));
204 if (j
< 0 || j
>= mother
->broken_intos_
.size ())
207 Grob
*neighbor
= mother
->broken_intos_
[j
];
209 neighbor
->set_property ("direction",
210 me
->get_property ("direction"));
212 Spanner
*common_mother
213 = dynamic_cast<Spanner
*> (common
[Y_AXIS
]->original_
);
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())
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,
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
);
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,
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());
252 get_encompass_info (Grob
*me
,
256 Grob
* stem
= unsmob_grob (col
->get_property ("stem"));
260 Direction dir
= get_grob_direction (me
);
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
)];
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 ();
275 ei
.x_
= col
->extent (common
[X_AXIS
], X_AXIS
).center ();
277 Grob
* h
= Stem::extremal_heads (stem
)[Direction (dir
)];
280 ei
.head_
= ei
.stem_
= col
->extent (common
[Y_AXIS
], Y_AXIS
)[dir
];
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 ();
302 get_default_dir (Grob
*me
)
304 Link_array
<Grob
> encompasses
305 = Pointer_group_interface__extract_grobs (me
, (Grob
*) 0, "note-columns");
308 for (int i
= 0; i
< encompasses
.size (); i
++)
310 if (Note_column::dir (encompasses
[i
]) < 0)
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")))
327 return SCM_UNSPECIFIED
;
330 if (!get_grob_direction (me
))
331 set_grob_direction (me
, get_default_dir (me
));
335 return SCM_UNSPECIFIED
;
340 get_bezier (Grob
*me
, Drul_array
<Offset
> extremes
,
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
,
359 Drul_array
<Bound_info
> extremes
;
361 Direction dir
= get_grob_direction (me
);
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_
);
380 extremes
[d
].neighbor_y_
= broken_trend_y (me
, common
, d
);
382 } while (flip (&d
) != LEFT
);
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 ())
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
++)
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
),
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
, ¶ms
,
423 extremes
, base_attachment
,
426 generate_curves (me
, common
, extremes
, base_attachment
, &scores
);
427 score_edges (me
, common
, ¶ms
,extremes
, base_attachment
, &scores
);
428 score_slopes (me
, common
, ¶ms
,extremes
, base_attachment
, &scores
);
429 score_encompass (me
, common
, ¶ms
,extremes
, base_attachment
, &scores
);
430 score_extra_encompass (me
, common
, ¶ms
,extremes
, base_attachment
, &scores
);
435 for (int i
= scores
.size (); i
--;)
437 if (scores
[i
].score_
< opt
)
439 opt
= scores
[i
].score_
;
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
);
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
]);
465 programming_error ("Could not find quant.");
467 scores
[opt_idx
].score_card_
+= to_string ("i%d", opt_idx
);
470 me
->set_property ("quant-score",
471 scm_makfrom0str (scores
[opt_idx
].score_card_
.to_str0 ()));
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
);
491 get_y_attachment_range (Spanner
*me
,
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
);
501 if (extremes
[d
].note_column_
)
504 * ((dir
* (base_attachment
[d
][Y_AXIS
] + 4.0 * dir
))
505 >? (dir
* (dir
+ extremes
[d
].note_column_
->extent(common
[Y_AXIS
],
507 >? (dir
* base_attachment
[-d
][Y_AXIS
]));
510 end_ys
[d
] = extremes
[d
].neighbor_y_
+ 4.0 * dir
;
512 while (flip (&d
) != LEFT
);
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
);
530 Grob
*stem
= extremes
[d
].stem_
;
531 Grob
*head
= extremes
[d
].slur_head_
;
534 if (!extremes
[d
].note_column_
)
536 y
= extremes
[d
].neighbor_y_
;
538 x
= extremes
[d
].bound_
->extent (common
[X_AXIS
], X_AXIS
)[d
];
540 x
= me
->get_broken_left_end_align ();
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
];
551 y
= head
->extent (common
[Y_AXIS
], Y_AXIS
)[dir
];
552 y
+= dir
* 0.5 * staff_space
;
556 SCM scripts
= stem
->get_property ((dir
== UP
560 /* FIXME: number of scripts, height, priority?
561 articulation scripts should be inside slur, other,
562 such as dynamics, pedals, fingering, should be
564 if (!SCM_NULLP (scripts
))
565 y
+= dir
* staff_space
;
569 = (y
- extremes
[d
].staff_
->relative_coordinate (common
[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
;
592 generate_curves (Grob
*me
, Grob
**common
,
593 Drul_array
<Bound_info
> extremes
,
595 Array
<Slur_score
> *scores
)
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
;
613 avoid_staff_line (Grob
*me
, Grob
**common
,
614 Drul_array
<Bound_info
> extremes
,
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. */
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
))
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
))));
646 Real newp
= round (p
) + resolution_dir
649 Real dy
= (newp
- p
) * staff_space
/ 2.0;
651 bez
.translate (Offset (0, dy
));
653 bez
.control_
[1][Y_AXIS
] += dy
;
654 bez
.control_
[2][Y_AXIS
] += dy
;
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
)
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
++)
687 Drul_array
<bool> attach_to_stem (false, false);
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
695 if (extremes
[d
].stem_extent_
[Y_AXIS
].contains (os
[d
][Y_AXIS
]))
697 os
[d
][X_AXIS
] = extremes
[d
].slur_head_extent_
[-d
]
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
);
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
];
727 if (extremes
[d
].slur_head_
728 && !attach_to_stem
[d
])
731 horizontally move tilted slurs a little. Move more
737 dir
* extremes
[d
].slur_head_extent_
.length () * sin (dz
.arg ()) / 3;
739 } while (flip (&d
) != LEFT
);
744 os
[RIGHT
][Y_AXIS
] += dir
* staff_space
/ 2;
747 os
[LEFT
][Y_AXIS
] += dir
* staff_space
/ 2;
754 score_encompass (Grob
*me
, Grob
*common
[],
755 Slur_score_parameters
*score_param
,
756 Drul_array
<Bound_info
> ,
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_
);
773 for (int j
= 0; j
< infos
.size(); j
++)
775 Real x
= infos
[j
].x_
;
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
]))
785 Real y
= bez
.get_other_coordinate (X_AXIS
, x
);
788 Real head_dy
= (y
- infos
[j
].head_
);
789 if (dir
* head_dy
< 0)
790 demerit
+= score_param
->HEAD_ENCOMPASS_PENALTY
;
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
;
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
))
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) /
824 #if DEBUG_SLUR_QUANTING
825 (*scores
)[i
].score_card_
+= to_string ("C%.2f", demerit
);
828 (*scores
)[i
].score_
+= demerit
;
835 score_extra_encompass (Grob
*me
, Grob
*common
[],
836 Slur_score_parameters
*score_param
,
837 Drul_array
<Bound_info
> ,
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
;
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
);
858 if (Accidental_interface::has_interface (g
))
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
)
875 && scm_ilength (accs
) == 1)
877 switch (ly_scm2int (ly_car (accs
))) {
889 xs
.push (xe
.linear_combination (xp
));
890 ye
.widen (.5 * thick
);
894 for (int i
= 0; i
< scores
->size (); i
++)
896 Bezier
const &bez (scores
->elem (i
).curve_
);
898 for (int j
= 0; j
< xs
.size(); j
++)
901 if (!(x
< scores
->elem (i
).attachment_
[RIGHT
][X_AXIS
]
902 && x
> scores
->elem (i
).attachment_
[LEFT
][X_AXIS
]))
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
;
912 demerit
+= score_param
->EXTRA_OBJECT_COLLISION
;
916 #if DEBUG_SLUR_QUANTING
917 (*scores
)[i
].score_card_
+= to_string ("X%.2f", demerit
);
919 (*scores
)[i
].score_
+= demerit
;
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
++)
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
)
949 (*scores
)[i
].score_
+= demerit
;
950 #if DEBUG_SLUR_QUANTING
951 (*scores
)[i
].score_card_
+= to_string ("E%.2f", demerit
);
953 } while (flip (&d
) != LEFT
);
958 score_slopes (Grob
*me
, Grob
*common
[],
959 Slur_score_parameters
*score_param
,
960 Drul_array
<Bound_info
> extremes
,
962 Array
<Slur_score
> * scores
)
968 if (extremes
[d
].slur_head_
)
969 ys
[d
] = extremes
[d
].slur_head_
->relative_coordinate (common
[Y_AXIS
],
972 ys
[d
] = extremes
[d
].neighbor_y_
;
973 } while (flip (&d
) != LEFT
);
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
];
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);
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
;
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
);
1018 (*scores
)[i
].score_
+= demerit
;