2 slur-configuration.cc -- implement Slur_configuration
4 source file of the GNU LilyPond music typesetter
6 (c) 2004--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
9 #include "slur-configuration.hh"
13 #include "libc-extension.hh"
15 #include "pointer-group-interface.hh"
16 #include "slur-scoring.hh"
19 #include "staff-symbol-referencer.hh"
24 avoid_staff_line (Slur_score_state
const &state
,
28 vector
<Real
> ts
= bez
.solve_derivative (horiz
);
30 /* TODO: handle case of broken slur. */
32 && (state
.extremes_
[LEFT
].staff_
== state
.extremes_
[RIGHT
].staff_
)
33 && state
.extremes_
[LEFT
].staff_
&& state
.extremes_
[RIGHT
].staff_
)
35 Real y
= bez
.curve_point (ts
[0])[Y_AXIS
];
37 Grob
*staff
= state
.extremes_
[LEFT
].staff_
;
39 Real p
= 2 * (y
- staff
->relative_coordinate (state
.common_
[Y_AXIS
], Y_AXIS
))
42 Real distance
= fabs (my_round (p
) - p
); // in halfspaces
43 if (distance
< 4 * state
.thickness_
44 && (int) fabs (my_round (p
))
45 <= 2 * Staff_symbol_referencer::staff_radius (staff
) + 0.1
46 && (int (fabs (my_round (p
))) % 2
47 != Staff_symbol_referencer::line_count (staff
) % 2))
49 Direction resolution_dir
50 = (distance
? state
.dir_
: Direction (sign (p
- my_round (p
))));
53 Real newp
= my_round (p
) + resolution_dir
54 * 5 * state
.thickness_
;
56 Real dy
= (newp
- p
) * state
.staff_space_
/ 2.0;
58 bez
.control_
[1][Y_AXIS
] += dy
;
59 bez
.control_
[2][Y_AXIS
] += dy
;
66 fit_factor (Offset dz_unit
, Offset dz_perp
,
67 Bezier curve
, Direction d
, vector
<Offset
> const &avoid
)
69 Real fit_factor
= 0.0;
70 Offset x0
= curve
.control_
[0];
71 curve
.translate (-x0
);
72 curve
.rotate (-dz_unit
.arg ());
76 curve_xext
.add_point (curve
.control_
[0][X_AXIS
]);
77 curve_xext
.add_point (curve
.control_
[3][X_AXIS
]);
79 for (vsize i
= 0; i
< avoid
.size (); i
++)
81 Offset z
= (avoid
[i
] - x0
);
82 Offset
p (dot_product (z
, dz_unit
),
83 d
* dot_product (z
, dz_perp
));
86 Interval pext
= eps
* Interval (-1,1) + p
[X_AXIS
];
87 pext
.intersect (curve_xext
);
88 if (pext
.is_empty () || pext
.length () <= 1.999 * eps
)
91 Real y
= curve
.get_other_coordinate (X_AXIS
, p
[X_AXIS
]);
93 fit_factor
= max (fit_factor
, (p
[Y_AXIS
] / y
));
99 Slur_configuration::generate_curve (Slur_score_state
const &state
,
100 Real r_0
, Real h_inf
,
101 vector
<Offset
> const &avoid
)
103 Offset dz
= attachment_
[RIGHT
]- attachment_
[LEFT
];;
105 dz_unit
*= 1 / dz
.length ();
106 Offset dz_perp
= dz_unit
* Offset (0, 1);
109 get_slur_indent_height (&indent
, &height
, dz
.length (), h_inf
, r_0
);
111 Real len
= dz
.length ();
115 len^2 > 4h^2 + 3 (i + 1/3len)^2 - 1/3 len^2
119 |bez' (0)| < | bez' (.5)|
121 when (control2 - control1) has the same direction as
122 (control3 - control0). */
124 Real max_indent
= len
/ 3.1;
125 indent
= min (indent
, max_indent
);
127 Real a1
= sqr (len
) / 3.0;
128 Real a2
= 0.75 * sqr (indent
+ len
/ 3.0);
129 Real max_h
= a1
- a2
;
133 programming_error ("slur indent too small");
137 max_h
= sqrt (max_h
);
139 Real eccentricity
= robust_scm2double (state
.slur_
->get_property ("eccentricity"), 0);
141 Real x1
= (eccentricity
+ indent
);
142 Real x2
= (eccentricity
- indent
);
145 curve
.control_
[0] = attachment_
[LEFT
];
146 curve
.control_
[1] = attachment_
[LEFT
] + dz_perp
* height
* state
.dir_
148 curve
.control_
[2] = attachment_
[RIGHT
] + dz_perp
* height
* state
.dir_
150 curve
.control_
[3] = attachment_
[RIGHT
];
152 Real ff
= fit_factor (dz_unit
, dz_perp
, curve
, state
.dir_
, avoid
);
154 height
= max (height
, min (height
* ff
, max_h
));
156 curve
.control_
[0] = attachment_
[LEFT
];
157 curve
.control_
[1] = attachment_
[LEFT
] + dz_perp
* height
* state
.dir_
159 curve
.control_
[2] = attachment_
[RIGHT
] + dz_perp
* height
* state
.dir_
161 curve
.control_
[3] = attachment_
[RIGHT
];
163 curve_
= avoid_staff_line (state
, curve
);
167 Slur_configuration::Slur_configuration ()
176 Slur_configuration::add_score (Real s
, string desc
)
180 programming_error ("Negative demerits found for slur. Ignoring");
186 if (score_card_
.length () > 0)
188 score_card_
+= to_string ("%s=%.2f", desc
.c_str (), s
);
194 Slur_configuration::score_encompass (Slur_score_state
const &state
)
196 Bezier
const &bez (curve_
);
200 Distances for heads that are between slur and line between
203 vector
<Real
> convex_head_distances
;
204 for (vsize j
= 0; j
< state
.encompass_infos_
.size (); j
++)
206 Real x
= state
.encompass_infos_
[j
].x_
;
208 bool l_edge
= j
== 0;
209 bool r_edge
= j
== state
.encompass_infos_
.size () - 1;
210 bool edge
= l_edge
|| r_edge
;
212 if (! (x
< attachment_
[RIGHT
][X_AXIS
]
213 && x
> attachment_
[LEFT
][X_AXIS
]))
216 Real y
= bez
.get_other_coordinate (X_AXIS
, x
);
219 Real head_dy
= (y
- state
.encompass_infos_
[j
].head_
);
220 if (state
.dir_
* head_dy
< 0)
222 demerit
+= state
.parameters_
.head_encompass_penalty_
;
223 convex_head_distances
.push_back (0.0);
228 ? (1 / fabs (head_dy
) - 1 / state
.parameters_
.free_head_distance_
)
229 : state
.parameters_
.head_encompass_penalty_
;
230 hd
= min (max (hd
, 0.0), state
.parameters_
.head_encompass_penalty_
);
235 Real line_y
= linear_interpolate (x
,
236 attachment_
[RIGHT
][X_AXIS
],
237 attachment_
[LEFT
][X_AXIS
],
238 attachment_
[RIGHT
][Y_AXIS
],
239 attachment_
[LEFT
][Y_AXIS
]);
241 if (1) // state.dir_ * state.encompass_infos_[j].get_point (state.dir_) > state.dir_ *line_y )
245 = state
.dir_
* max (state
.dir_
* state
.encompass_infos_
[j
].get_point (state
.dir_
), state
.dir_
* line_y
);
246 Real d
= fabs (closest
- y
);
248 convex_head_distances
.push_back (d
);
252 if (state
.dir_
* (y
- state
.encompass_infos_
[j
].stem_
) < 0)
254 Real stem_dem
= state
.parameters_
.stem_encompass_penalty_
;
255 if ((l_edge
&& state
.dir_
== UP
)
256 || (r_edge
&& state
.dir_
== DOWN
))
264 ext
.add_point (state
.encompass_infos_
[j
].stem_
);
265 ext
.add_point (state
.encompass_infos_
[j
].head_
);
268 demerit
+= -state
.parameters_
.closeness_factor_
270 * (y
- (ext
[state
.dir_
] + state
.dir_
* state
.parameters_
.free_head_distance_
)), 0.0)
271 / state
.encompass_infos_
.size ();
274 add_score (demerit
, "encompass");
276 if (convex_head_distances
.size ())
278 Real avg_distance
= 0.0;
279 Real min_dist
= infinity_f
;
280 for (vsize j
= 0; j
< convex_head_distances
.size (); j
++)
282 min_dist
= min (min_dist
, convex_head_distances
[j
]);
283 avg_distance
+= convex_head_distances
[j
];
287 For slurs over 3 or 4 heads, the average distance is not a
290 Real n
= convex_head_distances
.size ();
294 avg_distance
+= height_
* fact
;
299 TODO: maybe it's better to use (avgdist - mindist)*factor
303 Real variance_penalty
= state
.parameters_
.head_slur_distance_max_ratio_
;
306 = min ((avg_distance
/ (min_dist
+ state
.parameters_
.absolute_closeness_measure_
) - 1.0), variance_penalty
);
308 variance_penalty
= max (variance_penalty
, 0.0);
309 variance_penalty
*= state
.parameters_
.head_slur_distance_factor_
;
311 add_score (variance_penalty
, "variance");
316 Slur_configuration::score_extra_encompass (Slur_score_state
const &state
)
318 for (vsize j
= 0; j
< state
.extra_encompass_infos_
.size (); j
++)
320 Drul_array
<Offset
> attachment
= attachment_
;
321 Extra_collision_info
const &info (state
.extra_encompass_infos_
[j
]);
323 Interval
slur_wid (attachment
[LEFT
][X_AXIS
], attachment
[RIGHT
][X_AXIS
]);
326 to prevent numerical inaccuracies in
327 Bezier::get_other_coordinate ().
336 We need to check for the bound explicitly, since the
337 slur-ending can be almost vertical, making the Y
338 coordinate a bad approximation of the object-slur
341 Item
*as_item
= dynamic_cast<Item
*> (state
.extra_encompass_infos_
[j
].grob_
);
345 Interval item_x
= as_item
->extent (state
.common_
[X_AXIS
], X_AXIS
);
346 item_x
.intersect (state
.extremes_
[d
].slur_head_x_extent_
);
347 if (!item_x
.is_empty ())
349 y
= attachment
[d
][Y_AXIS
];
354 while (flip (&d
) != LEFT
);
358 Real x
= info
.extents_
[X_AXIS
].linear_combination (info
.idx_
);
360 if (!slur_wid
.contains (x
))
363 y
= curve_
.get_other_coordinate (X_AXIS
, x
);
367 if (info
.type_
== ly_symbol2scm ("around"))
368 dist
= info
.extents_
[Y_AXIS
].distance (y
);
371 Have to score too: the curve enumeration is limited in its
372 shape, and may produce curves which collide anyway.
374 else if (info
.type_
== ly_symbol2scm ("inside"))
375 dist
= state
.dir_
* (y
- info
.extents_
[Y_AXIS
][state
.dir_
]);
377 programming_error ("unknown avoidance type");
379 dist
= max (dist
, 0.0);
381 Real penalty
= info
.penalty_
* peak_around (0.1 * state
.parameters_
.extra_encompass_free_distance_
,
382 state
.parameters_
.extra_encompass_free_distance_
,
385 add_score (penalty
, "extra");
390 Slur_configuration::score_edges (Slur_score_state
const &state
)
393 Offset dz
= attachment_
[RIGHT
]
395 Real slope
= dz
[Y_AXIS
] / dz
[X_AXIS
];
398 Real y
= attachment_
[d
][Y_AXIS
];
399 Real dy
= fabs (y
- state
.base_attachments_
[d
][Y_AXIS
]);
401 Real factor
= state
.parameters_
.edge_attraction_factor_
;
402 Real demerit
= factor
* dy
;
403 if (state
.extremes_
[d
].stem_
404 && state
.extremes_
[d
].stem_dir_
== state
.dir_
405 && !Stem::get_beaming (state
.extremes_
[d
].stem_
, -d
))
408 demerit
*= exp (state
.dir_
* d
* slope
409 * state
.parameters_
.edge_slope_exponent_
);
412 string dir_str
= d
== LEFT
? "L" : "R";
413 add_score (demerit
, dir_str
+ " edge");
415 while (flip (&d
) != LEFT
);
419 Slur_configuration ::score_slopes (Slur_score_state
const &state
)
421 Real dy
= state
.musical_dy_
;
422 Offset slur_dz
= attachment_
[RIGHT
] - attachment_
[LEFT
];
423 Real slur_dy
= slur_dz
[Y_AXIS
];
426 demerit
+= max ((fabs (slur_dy
/ slur_dz
[X_AXIS
])
427 - state
.parameters_
.max_slope_
), 0.0)
428 * state
.parameters_
.max_slope_factor_
;
430 /* 0.2: account for staffline offset. */
431 Real max_dy
= (fabs (dy
) + 0.2);
432 if (state
.edge_has_beams_
)
435 if (!state
.is_broken_
)
436 demerit
+= state
.parameters_
.steeper_slope_factor_
437 * (max (fabs (slur_dy
) -max_dy
, 0.0));
439 demerit
+= max ((fabs (slur_dy
/ slur_dz
[X_AXIS
])
440 - state
.parameters_
.max_slope_
), 0.0)
441 * state
.parameters_
.max_slope_factor_
;
444 && sign (slur_dy
) != 0
445 && !state
.is_broken_
)
446 demerit
+= state
.parameters_
.non_horizontal_penalty_
;
451 && sign (slur_dy
) != sign (dy
))
452 demerit
+= state
.edge_has_beams_
453 ? state
.parameters_
.same_slope_penalty_
/ 10
454 : state
.parameters_
.same_slope_penalty_
;
456 add_score (demerit
, "slope");
460 Slur_configuration::calculate_score (Slur_score_state
const &state
)
462 score_extra_encompass (state
);
463 score_slopes (state
);
465 score_encompass (state
);