2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 1997--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
5 Jan Nieuwenhuizen <janneke@gnu.org>
7 LilyPond is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 LilyPond is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
27 #include "align-interface.hh"
28 #include "international.hh"
29 #include "output-def.hh"
30 #include "pointer-group-interface.hh"
31 #include "staff-symbol-referencer.hh"
37 get_detail (SCM alist, SCM sym, Real def)
39 SCM entry = scm_assq (sym, alist);
41 if (scm_is_pair (entry))
42 return robust_scm2double (scm_cdr (entry), def);
47 Beam_quant_parameters::fill (Grob *him)
49 SCM details = him->get_property ("details");
51 SECONDARY_BEAM_DEMERIT = get_detail (details, ly_symbol2scm ("secondary-beam-demerit"), 10.0);
52 STEM_LENGTH_DEMERIT_FACTOR = get_detail (details, ly_symbol2scm ("stem-length-demerit-factor"), 5);
53 REGION_SIZE = get_detail (details, ly_symbol2scm ("region-size"), 2);
54 BEAM_EPS = get_detail (details, ly_symbol2scm ("beam-eps"), 1e-3);
55 STEM_LENGTH_LIMIT_PENALTY = get_detail (details, ly_symbol2scm ("stem-length-limit-penalty"), 5000);
56 DAMPING_DIRECTION_PENALTY = get_detail (details, ly_symbol2scm ("damping-direction-penalty"), 800);
57 HINT_DIRECTION_PENALTY = get_detail (details, ly_symbol2scm ("hint-direction-penalty"), 20);
58 MUSICAL_DIRECTION_FACTOR = get_detail (details, ly_symbol2scm ("musical-direction-factor"), 400);
59 IDEAL_SLOPE_FACTOR = get_detail (details, ly_symbol2scm ("ideal-slope-factor"), 10);
60 ROUND_TO_ZERO_SLOPE = get_detail (details, ly_symbol2scm ("round-to-zero-slope"), 0.02);
64 shrink_extra_weight (Real x, Real fac)
66 return fabs (x) * ((x < 0) ? fac : 1.0);
75 #if DEBUG_BEAM_SCORING
83 - Make all demerits customisable
85 - One sensible check per demerit (what's this --hwn)
87 - Add demerits for quants per se, as to forbid a specific quant
92 best_quant_score_idx (vector<Quant_score> const &qscores)
96 for (vsize i = qscores.size (); i--;)
98 if (qscores[i].demerits < best)
100 best = qscores [i].demerits;
108 MAKE_SCHEME_CALLBACK (Beam, quanting, 2);
110 Beam::quanting (SCM smob, SCM posns)
112 Grob *me = unsmob_grob (smob);
114 Beam_quant_parameters parameters;
115 parameters.fill (me);
117 Real yl = scm_to_double (scm_car (posns));
118 Real yr = scm_to_double (scm_cdr (posns));
121 Calculations are relative to a unit-scaled staff, i.e. the quants are
122 divided by the current staff_space.
124 Real ss = Staff_symbol_referencer::staff_space (me);
125 Real beam_thickness = Beam::get_beam_thickness (me) / ss;
126 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
128 Real dy_mus = robust_scm2double (me->get_property ("least-squares-dy"), 0);
130 Real sit = (beam_thickness - slt) / 2;
132 Real hang = 1.0 - (beam_thickness - slt) / 2;
133 Real quants [] = {straddle, sit, inter, hang };
135 int num_quants = int (sizeof (quants) / sizeof (Real));
136 vector<Real> quantsl;
137 vector<Real> quantsr;
140 going to REGION_SIZE == 2, yields another 0.6 second with
143 (result indexes between 70 and 575) ? --hwn.
148 Do stem computations. These depend on YL and YR linearly, so we can
149 precompute for every stem 2 factors.
152 = extract_grob_array (me, "stems");
153 vector<Stem_info> stem_infos;
154 vector<Real> base_lengths;
155 vector<Real> stem_xposns;
157 Drul_array<bool> dirs_found (0, 0);
159 for (int a = 2; a--;)
160 common[a] = common_refpoint_of_array (stems, me, Axis (a));
162 Grob *fvs = first_normal_stem (me);
163 Grob *lvs = last_normal_stem (me);
164 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
165 Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
168 We store some info to quickly interpolate. The stemlength are
169 affine linear in YL and YR. If YL == YR == 0, then we might have
170 stem_y != 0.0, when we're cross staff.
173 for (vsize i = 0; i < stems.size (); i++)
177 Stem_info si (Stem::get_stem_info (s));
179 stem_infos.push_back (si);
180 dirs_found[stem_infos.back ().dir_] = true;
182 bool f = to_boolean (s->get_property ("french-beaming"))
183 && s != lvs && s != fvs;
185 if (Stem::is_normal_stem (s))
187 base_lengths.push_back (calc_stem_y (me, s, common, xl, xr, CENTER,
188 Interval (0, 0), f) / ss);
192 base_lengths.push_back (0);
195 stem_xposns.push_back (s->relative_coordinate (common[X_AXIS], X_AXIS));
197 bool xstaff = Align_interface::has_interface (common[Y_AXIS]);
199 Direction ldir = Direction (stem_infos[0].dir_);
200 Direction rdir = Direction (stem_infos.back ().dir_);
201 bool is_knee = dirs_found[LEFT] && dirs_found[RIGHT];
203 int region_size = (int) parameters.REGION_SIZE;
206 Knees are harder, lets try some more possibilities for knees.
212 Asymetry ? should run to <= region_size ?
214 for (int i = -region_size; i < region_size; i++)
215 for (int j = 0; j < num_quants; j++)
217 quantsl.push_back (i + quants[j] + int (yl));
218 quantsr.push_back (i + quants[j] + int (yr));
221 vector<Quant_score> qscores;
223 for (vsize l = 0; l < quantsl.size (); l++)
224 for (vsize r = 0; r < quantsr.size (); r++)
231 qscores.push_back (qs);
234 /* This is a longish function, but we don't separate this out into
235 neat modular separate subfunctions, as the subfunctions would be
236 called for many values of YL, YR. By precomputing various
237 parameters outside of the loop, we can save a lot of time. */
238 for (vsize i = qscores.size (); i--;)
240 Real d = score_slopes_dy (qscores[i].yl, qscores[i].yr,
243 xstaff, ¶meters);
244 qscores[i].demerits += d;
246 #if DEBUG_BEAM_SCORING
247 qscores[i].score_card_ += to_string ("S%.2f", d);
251 Real rad = Staff_symbol_referencer::staff_radius (me);
252 Drul_array<int> edge_beam_counts
253 (Stem::beam_multiplicity (stems[0]).length () + 1,
254 Stem::beam_multiplicity (stems.back ()).length () + 1);
256 Real beam_translation = get_beam_translation (me) / ss;
258 Real reasonable_score = (is_knee) ? 200000 : 100;
259 for (vsize i = qscores.size (); i--;)
260 if (qscores[i].demerits < reasonable_score)
262 Real d = score_forbidden_quants (qscores[i].yl, qscores[i].yr,
263 rad, slt, beam_thickness, beam_translation,
264 edge_beam_counts, ldir, rdir, ¶meters);
265 qscores[i].demerits += d;
267 #if DEBUG_BEAM_SCORING
268 qscores[i].score_card_ += to_string (" F %.2f", d);
272 for (vsize i = qscores.size (); i--;)
273 if (qscores[i].demerits < reasonable_score)
275 Real d = score_stem_lengths (stems, stem_infos,
276 base_lengths, stem_xposns,
279 qscores[i].yl, qscores[i].yr, ¶meters);
280 qscores[i].demerits += d;
282 #if DEBUG_BEAM_SCORING
283 qscores[i].score_card_ += to_string (" L %.2f", d);
287 int best_idx = best_quant_score_idx (qscores);
289 #if DEBUG_BEAM_SCORING
290 SCM inspect_quants = me->get_property ("inspect-quants");
291 if (to_boolean (me->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-scoring")))
292 && scm_is_pair (inspect_quants))
294 Drul_array<Real> ins = ly_scm2interval (inspect_quants);
297 for (vsize i = 0; i < qscores.size (); i++)
299 Real d = fabs (qscores[i].yl- ins[LEFT]) + fabs (qscores[i].yr - ins[RIGHT]);
307 programming_error ("cannot find quant");
311 Interval final_positions;
314 warning (_ ("no feasible beam position"));
315 final_positions = Interval (0, 0);
319 final_positions = Drul_array<Real> (qscores[best_idx].yl,
320 qscores[best_idx].yr);
323 #if DEBUG_BEAM_SCORING
325 && to_boolean (me->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-scoring"))))
327 qscores[best_idx].score_card_ += to_string ("i%d", best_idx);
330 me->set_property ("quant-score",
331 ly_string2scm (qscores[best_idx].score_card_));
335 return ly_interval2scm (final_positions);
339 Beam::score_stem_lengths (vector<Grob*> const &stems,
340 vector<Stem_info> const &stem_infos,
341 vector<Real> const &base_stem_ys,
342 vector<Real> const &stem_xs,
347 Beam_quant_parameters const *parameters)
349 Real limit_penalty = parameters->STEM_LENGTH_LIMIT_PENALTY;
350 Drul_array<Real> score (0, 0);
351 Drul_array<int> count (0, 0);
353 for (vsize i = 0; i < stems.size (); i++)
356 if (!Stem::is_normal_stem (s))
361 Real beam_y = dx ? yr * (x - xl) / dx + yl * (xr - x) / dx : (yr + yl) / 2;
362 Real current_y = beam_y + base_stem_ys[i];
363 Real length_pen = parameters->STEM_LENGTH_DEMERIT_FACTOR;
365 Stem_info info = stem_infos[i];
366 Direction d = info.dir_;
368 score[d] += limit_penalty * max (0.0, (d * (info.shortest_y_ - current_y)));
370 Real ideal_diff = d * (current_y - info.ideal_y_);
371 Real ideal_score = shrink_extra_weight (ideal_diff, 1.5);
373 /* We introduce a power, to make the scoring strictly
374 convex. Otherwise a symmetric knee beam (up/down/up/down)
375 does not have an optimum in the middle. */
377 ideal_score = pow (ideal_score, 1.1);
379 score[d] += length_pen * ideal_score;
386 score[d] /= max (count[d], 1);
387 while (flip (&d) != DOWN);
389 return score[LEFT] + score[RIGHT];
393 Beam::score_slopes_dy (Real yl, Real yr,
394 Real dy_mus, Real dy_damp,
398 Beam_quant_parameters const *parameters)
404 DAMPING_DIRECTION_PENALTY is a very harsh measure, while for
405 complex beaming patterns, horizontal is often a good choice.
407 TODO: find a way to incorporate the complexity of the beam in this
410 if (sign (dy_damp) != sign (dy))
414 if (fabs (dy_damp / dx) > parameters->ROUND_TO_ZERO_SLOPE)
415 dem += parameters->DAMPING_DIRECTION_PENALTY;
417 dem += parameters->HINT_DIRECTION_PENALTY;
420 dem += parameters->DAMPING_DIRECTION_PENALTY;
423 dem += parameters->MUSICAL_DIRECTION_FACTOR
424 * max (0.0, (fabs (dy) - fabs (dy_mus)));
426 Real slope_penalty = parameters->IDEAL_SLOPE_FACTOR;
428 /* Xstaff beams tend to use extreme slopes to get short stems. We
429 put in a penalty here. */
433 /* Huh, why would a too steep beam be better than a too flat one ? */
434 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy), 1.5)
443 return x - floor (x);
447 TODO: The fixed value SECONDARY_BEAM_DEMERIT is probably flawed:
448 because for 32nd and 64th beams the forbidden quants are relatively
449 more important than stem lengths.
452 Beam::score_forbidden_quants (Real yl, Real yr,
455 Real beam_thickness, Real beam_translation,
456 Drul_array<int> beam_counts,
457 Direction ldir, Direction rdir,
459 Beam_quant_parameters const *parameters)
462 Drul_array<Real> y (yl, yr);
463 Drul_array<Direction> dirs (ldir, rdir);
465 Real extra_demerit = parameters->SECONDARY_BEAM_DEMERIT / (max (beam_counts[LEFT], beam_counts[RIGHT]));
469 Real eps = parameters->BEAM_EPS;
473 for (int j = 1; j <= beam_counts[d]; j++)
475 Direction stem_dir = dirs[d];
478 The 2.2 factor is to provide a little leniency for
479 borderline cases. If we do 2.0, then the upper outer line
480 will be in the gap of the (2, sit) quant, leading to a
483 Real gap1 = y[d] - stem_dir * ((j - 1) * beam_translation + beam_thickness / 2 - slt / 2.2);
484 Real gap2 = y[d] - stem_dir * (j * beam_translation - beam_thickness / 2 + slt / 2.2);
487 gap.add_point (gap1);
488 gap.add_point (gap2);
490 for (Real k = -radius;
491 k <= radius + eps; k += 1.0)
492 if (gap.contains (k))
494 Real dist = min (fabs (gap[UP] - k), fabs (gap[DOWN] - k));
497 this parameter is tuned to grace-stem-length.ly
499 Real fixed_demerit = 0.4;
503 + (1 - fixed_demerit) * (dist / gap.length ()) * 2);
507 while ((flip (&d)) != LEFT);
509 if (max (beam_counts[LEFT], beam_counts[RIGHT]) >= 2)
512 Real sit = (beam_thickness - slt) / 2;
514 Real hang = 1.0 - (beam_thickness - slt) / 2;
519 if (beam_counts[d] >= 2
520 && fabs (y[d] - dirs[d] * beam_translation) < radius + inter)
522 if (dirs[d] == UP && dy <= eps
523 && fabs (my_modf (y[d]) - sit) < eps)
524 dem += extra_demerit;
526 if (dirs[d] == DOWN && dy >= eps
527 && fabs (my_modf (y[d]) - hang) < eps)
528 dem += extra_demerit;
531 if (beam_counts[d] >= 3
532 && fabs (y[d] - 2 * dirs[d] * beam_translation) < radius + inter)
534 if (dirs[d] == UP && dy <= eps
535 && fabs (my_modf (y[d]) - straddle) < eps)
536 dem += extra_demerit;
538 if (dirs[d] == DOWN && dy >= eps
539 && fabs (my_modf (y[d]) - straddle) < eps)
540 dem += extra_demerit;
543 while (flip (&d) != LEFT);