2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2004--2010 Han-Wen Nienhuys <hanwen@xs4all.nl>
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
20 #include "slur-configuration.hh"
24 #include "libc-extension.hh"
26 #include "pointer-group-interface.hh"
27 #include "slur-scoring.hh"
30 #include "staff-symbol-referencer.hh"
35 avoid_staff_line (Slur_score_state
const &state
,
39 vector
<Real
> ts
= bez
.solve_derivative (horiz
);
41 /* TODO: handle case of broken slur. */
43 && (state
.extremes_
[LEFT
].staff_
== state
.extremes_
[RIGHT
].staff_
)
44 && state
.extremes_
[LEFT
].staff_
&& state
.extremes_
[RIGHT
].staff_
)
46 Real y
= bez
.curve_point (ts
[0])[Y_AXIS
];
48 Grob
*staff
= state
.extremes_
[LEFT
].staff_
;
50 Real p
= 2 * (y
- staff
->relative_coordinate (state
.common_
[Y_AXIS
], Y_AXIS
))
53 Real distance
= fabs (my_round (p
) - p
); // in halfspaces
54 if (distance
< 4 * state
.thickness_
55 && (int) fabs (my_round (p
))
56 <= 2 * Staff_symbol_referencer::staff_radius (staff
) + 0.1
57 && (int (fabs (my_round (p
))) % 2
58 != Staff_symbol_referencer::line_count (staff
) % 2))
60 Direction resolution_dir
61 = (distance
? state
.dir_
: Direction (sign (p
- my_round (p
))));
64 Real newp
= my_round (p
) + resolution_dir
65 * 5 * state
.thickness_
;
67 Real dy
= (newp
- p
) * state
.staff_space_
/ 2.0;
69 bez
.control_
[1][Y_AXIS
] += dy
;
70 bez
.control_
[2][Y_AXIS
] += dy
;
77 fit_factor (Offset dz_unit
, Offset dz_perp
,
78 Bezier curve
, Direction d
, vector
<Offset
> const &avoid
)
80 Real fit_factor
= 0.0;
81 Offset x0
= curve
.control_
[0];
82 curve
.translate (-x0
);
83 curve
.rotate (-dz_unit
.arg ());
87 curve_xext
.add_point (curve
.control_
[0][X_AXIS
]);
88 curve_xext
.add_point (curve
.control_
[3][X_AXIS
]);
90 for (vsize i
= 0; i
< avoid
.size (); i
++)
92 Offset z
= (avoid
[i
] - x0
);
93 Offset
p (dot_product (z
, dz_unit
),
94 d
* dot_product (z
, dz_perp
));
97 Interval pext
= eps
* Interval (-1,1) + p
[X_AXIS
];
98 pext
.intersect (curve_xext
);
99 if (pext
.is_empty () || pext
.length () <= 1.999 * eps
)
102 Real y
= curve
.get_other_coordinate (X_AXIS
, p
[X_AXIS
]);
104 fit_factor
= max (fit_factor
, (p
[Y_AXIS
] / y
));
110 Slur_configuration::generate_curve (Slur_score_state
const &state
,
111 Real r_0
, Real h_inf
,
112 vector
<Offset
> const &avoid
)
114 Offset dz
= attachment_
[RIGHT
]- attachment_
[LEFT
];;
116 dz_unit
*= 1 / dz
.length ();
117 Offset dz_perp
= dz_unit
* Offset (0, 1);
120 get_slur_indent_height (&indent
, &height
, dz
.length (), h_inf
, r_0
);
122 Real len
= dz
.length ();
126 len^2 > 4h^2 + 3 (i + 1/3len)^2 - 1/3 len^2
130 |bez' (0)| < | bez' (.5)|
132 when (control2 - control1) has the same direction as
133 (control3 - control0). */
135 Real max_indent
= len
/ 3.1;
136 indent
= min (indent
, max_indent
);
138 Real a1
= sqr (len
) / 3.0;
139 Real a2
= 0.75 * sqr (indent
+ len
/ 3.0);
140 Real max_h
= a1
- a2
;
144 programming_error ("slur indent too small");
148 max_h
= sqrt (max_h
);
150 Real eccentricity
= robust_scm2double (state
.slur_
->get_property ("eccentricity"), 0);
152 Real x1
= (eccentricity
+ indent
);
153 Real x2
= (eccentricity
- indent
);
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 Real ff
= fit_factor (dz_unit
, dz_perp
, curve
, state
.dir_
, avoid
);
165 height
= max (height
, min (height
* ff
, max_h
));
167 curve
.control_
[0] = attachment_
[LEFT
];
168 curve
.control_
[1] = attachment_
[LEFT
] + dz_perp
* height
* state
.dir_
170 curve
.control_
[2] = attachment_
[RIGHT
] + dz_perp
* height
* state
.dir_
172 curve
.control_
[3] = attachment_
[RIGHT
];
174 curve_
= avoid_staff_line (state
, curve
);
178 Slur_configuration::Slur_configuration ()
187 Slur_configuration::add_score (Real s
, string desc
)
191 programming_error ("Negative demerits found for slur. Ignoring");
197 if (score_card_
.length () > 0)
199 score_card_
+= to_string ("%s=%.2f", desc
.c_str (), s
);
205 Slur_configuration::score_encompass (Slur_score_state
const &state
)
207 Bezier
const &bez (curve_
);
211 Distances for heads that are between slur and line between
214 vector
<Real
> convex_head_distances
;
215 for (vsize j
= 0; j
< state
.encompass_infos_
.size (); j
++)
217 Real x
= state
.encompass_infos_
[j
].x_
;
219 bool l_edge
= j
== 0;
220 bool r_edge
= j
== state
.encompass_infos_
.size () - 1;
221 bool edge
= l_edge
|| r_edge
;
223 if (! (x
< attachment_
[RIGHT
][X_AXIS
]
224 && x
> attachment_
[LEFT
][X_AXIS
]))
227 Real y
= bez
.get_other_coordinate (X_AXIS
, x
);
230 Real head_dy
= (y
- state
.encompass_infos_
[j
].head_
);
231 if (state
.dir_
* head_dy
< 0)
233 demerit
+= state
.parameters_
.head_encompass_penalty_
;
234 convex_head_distances
.push_back (0.0);
239 ? (1 / fabs (head_dy
) - 1 / state
.parameters_
.free_head_distance_
)
240 : state
.parameters_
.head_encompass_penalty_
;
241 hd
= min (max (hd
, 0.0), state
.parameters_
.head_encompass_penalty_
);
246 Real line_y
= linear_interpolate (x
,
247 attachment_
[RIGHT
][X_AXIS
],
248 attachment_
[LEFT
][X_AXIS
],
249 attachment_
[RIGHT
][Y_AXIS
],
250 attachment_
[LEFT
][Y_AXIS
]);
252 if (1) // state.dir_ * state.encompass_infos_[j].get_point (state.dir_) > state.dir_ *line_y )
256 = state
.dir_
* max (state
.dir_
* state
.encompass_infos_
[j
].get_point (state
.dir_
), state
.dir_
* line_y
);
257 Real d
= fabs (closest
- y
);
259 convex_head_distances
.push_back (d
);
263 if (state
.dir_
* (y
- state
.encompass_infos_
[j
].stem_
) < 0)
265 Real stem_dem
= state
.parameters_
.stem_encompass_penalty_
;
266 if ((l_edge
&& state
.dir_
== UP
)
267 || (r_edge
&& state
.dir_
== DOWN
))
275 ext
.add_point (state
.encompass_infos_
[j
].stem_
);
276 ext
.add_point (state
.encompass_infos_
[j
].head_
);
279 demerit
+= -state
.parameters_
.closeness_factor_
281 * (y
- (ext
[state
.dir_
] + state
.dir_
* state
.parameters_
.free_head_distance_
)), 0.0)
282 / state
.encompass_infos_
.size ();
285 add_score (demerit
, "encompass");
287 if (convex_head_distances
.size ())
289 Real avg_distance
= 0.0;
290 Real min_dist
= infinity_f
;
291 for (vsize j
= 0; j
< convex_head_distances
.size (); j
++)
293 min_dist
= min (min_dist
, convex_head_distances
[j
]);
294 avg_distance
+= convex_head_distances
[j
];
298 For slurs over 3 or 4 heads, the average distance is not a
301 Real n
= convex_head_distances
.size ();
305 avg_distance
+= height_
* fact
;
310 TODO: maybe it's better to use (avgdist - mindist)*factor
314 Real variance_penalty
= state
.parameters_
.head_slur_distance_max_ratio_
;
317 = min ((avg_distance
/ (min_dist
+ state
.parameters_
.absolute_closeness_measure_
) - 1.0), variance_penalty
);
319 variance_penalty
= max (variance_penalty
, 0.0);
320 variance_penalty
*= state
.parameters_
.head_slur_distance_factor_
;
322 add_score (variance_penalty
, "variance");
327 Slur_configuration::score_extra_encompass (Slur_score_state
const &state
)
329 for (vsize j
= 0; j
< state
.extra_encompass_infos_
.size (); j
++)
331 Drul_array
<Offset
> attachment
= attachment_
;
332 Extra_collision_info
const &info (state
.extra_encompass_infos_
[j
]);
334 Interval
slur_wid (attachment
[LEFT
][X_AXIS
], attachment
[RIGHT
][X_AXIS
]);
337 to prevent numerical inaccuracies in
338 Bezier::get_other_coordinate ().
347 We need to check for the bound explicitly, since the
348 slur-ending can be almost vertical, making the Y
349 coordinate a bad approximation of the object-slur
352 Item
*as_item
= dynamic_cast<Item
*> (state
.extra_encompass_infos_
[j
].grob_
);
356 Interval item_x
= as_item
->extent (state
.common_
[X_AXIS
], X_AXIS
);
357 item_x
.intersect (state
.extremes_
[d
].slur_head_x_extent_
);
358 if (!item_x
.is_empty ())
360 y
= attachment
[d
][Y_AXIS
];
365 while (flip (&d
) != LEFT
);
369 Real x
= info
.extents_
[X_AXIS
].linear_combination (info
.idx_
);
371 if (!slur_wid
.contains (x
))
374 y
= curve_
.get_other_coordinate (X_AXIS
, x
);
378 if (info
.type_
== ly_symbol2scm ("around"))
379 dist
= info
.extents_
[Y_AXIS
].distance (y
);
382 Have to score too: the curve enumeration is limited in its
383 shape, and may produce curves which collide anyway.
385 else if (info
.type_
== ly_symbol2scm ("inside"))
386 dist
= state
.dir_
* (y
- info
.extents_
[Y_AXIS
][state
.dir_
]);
388 programming_error ("unknown avoidance type");
390 dist
= max (dist
, 0.0);
392 Real penalty
= info
.penalty_
* peak_around (0.1 * state
.parameters_
.extra_encompass_free_distance_
,
393 state
.parameters_
.extra_encompass_free_distance_
,
396 add_score (penalty
, "extra");
401 Slur_configuration::score_edges (Slur_score_state
const &state
)
404 Offset dz
= attachment_
[RIGHT
]
406 Real slope
= dz
[Y_AXIS
] / dz
[X_AXIS
];
409 Real y
= attachment_
[d
][Y_AXIS
];
410 Real dy
= fabs (y
- state
.base_attachments_
[d
][Y_AXIS
]);
412 Real factor
= state
.parameters_
.edge_attraction_factor_
;
413 Real demerit
= factor
* dy
;
414 if (state
.extremes_
[d
].stem_
415 && state
.extremes_
[d
].stem_dir_
== state
.dir_
416 && !Stem::get_beaming (state
.extremes_
[d
].stem_
, -d
))
419 demerit
*= exp (state
.dir_
* d
* slope
420 * state
.parameters_
.edge_slope_exponent_
);
423 string dir_str
= d
== LEFT
? "L" : "R";
424 add_score (demerit
, dir_str
+ " edge");
426 while (flip (&d
) != LEFT
);
430 Slur_configuration ::score_slopes (Slur_score_state
const &state
)
432 Real dy
= state
.musical_dy_
;
433 Offset slur_dz
= attachment_
[RIGHT
] - attachment_
[LEFT
];
434 Real slur_dy
= slur_dz
[Y_AXIS
];
437 demerit
+= max ((fabs (slur_dy
/ slur_dz
[X_AXIS
])
438 - state
.parameters_
.max_slope_
), 0.0)
439 * state
.parameters_
.max_slope_factor_
;
441 /* 0.2: account for staffline offset. */
442 Real max_dy
= (fabs (dy
) + 0.2);
443 if (state
.edge_has_beams_
)
446 if (!state
.is_broken_
)
447 demerit
+= state
.parameters_
.steeper_slope_factor_
448 * (max (fabs (slur_dy
) -max_dy
, 0.0));
450 demerit
+= max ((fabs (slur_dy
/ slur_dz
[X_AXIS
])
451 - state
.parameters_
.max_slope_
), 0.0)
452 * state
.parameters_
.max_slope_factor_
;
455 && sign (slur_dy
) != 0
456 && !state
.is_broken_
)
457 demerit
+= state
.parameters_
.non_horizontal_penalty_
;
462 && sign (slur_dy
) != sign (dy
))
463 demerit
+= state
.edge_has_beams_
464 ? state
.parameters_
.same_slope_penalty_
/ 10
465 : state
.parameters_
.same_slope_penalty_
;
467 add_score (demerit
, "slope");
471 Slur_configuration::calculate_score (Slur_score_state
const &state
)
473 score_extra_encompass (state
);
474 score_slopes (state
);
476 score_encompass (state
);