2 beam-quanting.cc -- implement Beam quanting functions
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
15 #include "align-interface.hh"
16 #include "international.hh"
17 #include "output-def.hh"
18 #include "pointer-group-interface.hh"
19 #include "staff-symbol-referencer.hh"
24 get_detail (SCM alist, SCM sym, Real def)
26 SCM entry = scm_assq (sym, alist);
28 if (scm_is_pair (entry))
29 return robust_scm2double (scm_cdr (entry), def);
34 Beam_quant_parameters::fill (Grob *him)
36 SCM details = him->get_property ("details");
38 INTER_QUANT_PENALTY = get_detail (details, ly_symbol2scm ("inter-quant-penalty"), 1000.0);
39 SECONDARY_BEAM_DEMERIT = get_detail (details, ly_symbol2scm ("secondary-beam-demerit"), 10.0);
40 STEM_LENGTH_DEMERIT_FACTOR = get_detail (details, ly_symbol2scm ("stem-length-demerit-factor"), 5);
41 REGION_SIZE = get_detail (details, ly_symbol2scm ("region-size"), 2);
42 BEAM_EPS = get_detail (details, ly_symbol2scm ("beam-eps"), 1e-3);
44 // possibly ridiculous, but too short stems just won't do
45 STEM_LENGTH_LIMIT_PENALTY = get_detail (details, ly_symbol2scm ("stem-length-limit-penalty"), 5000);
46 DAMPING_DIRECTION_PENALTY = get_detail (details, ly_symbol2scm ("damping-direction-penalty"), 800);
47 MUSICAL_DIRECTION_FACTOR = get_detail (details, ly_symbol2scm ("musical-direction-factor"), 400);
48 IDEAL_SLOPE_FACTOR = get_detail (details, ly_symbol2scm ("ideal-slope-factor"), 10);
49 ROUND_TO_ZERO_SLOPE = get_detail (details, ly_symbol2scm ("round-to-zero-slope"), 0.02);
53 shrink_extra_weight (Real x, Real fac)
55 return fabs (x) * ((x < 0) ? fac : 1.0);
65 std::string score_card_;
72 - Make all demerits customisable
74 - One sensible check per demerit (what's this --hwn)
76 - Add demerits for quants per se, as to forbid a specific quant
81 best_quant_score_idx (Array<Quant_score> const &qscores)
85 for (int i = qscores.size (); i--;)
87 if (qscores[i].demerits < best)
89 best = qscores [i].demerits;
97 MAKE_SCHEME_CALLBACK (Beam, quanting, 2);
99 Beam::quanting (SCM smob, SCM posns)
101 Grob *me = unsmob_grob (smob);
103 Beam_quant_parameters parameters;
104 parameters.fill (me);
106 Real yl = scm_to_double (scm_car (posns));
107 Real yr = scm_to_double (scm_cdr (posns));
110 Calculations are relative to a unit-scaled staff, i.e. the quants are
111 divided by the current staff_space.
114 Real ss = Staff_symbol_referencer::staff_space (me);
115 Real thickness = Beam::get_thickness (me) / ss;
116 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
118 Real dy_mus = robust_scm2double (me->get_property ("least-squares-dy"), 0);
120 Real sit = (thickness - slt) / 2;
122 Real hang = 1.0 - (thickness - slt) / 2;
123 Real quants [] = {straddle, sit, inter, hang };
125 int num_quants = int (sizeof (quants) / sizeof (Real));
130 going to REGION_SIZE == 2, yields another 0.6 second with
134 (result indexes between 70 and 575) ? --hwn.
139 Do stem computations. These depend on YL and YR linearly, so we can
140 precompute for every stem 2 factors.
142 Link_array<Grob> stems
143 = extract_grob_array (me, "stems");
144 Array<Stem_info> stem_infos;
145 Array<Real> base_lengths;
146 Array<Real> stem_xposns;
148 Drul_array<bool> dirs_found (0, 0);
150 for (int a = 2; a--;)
151 common[a] = common_refpoint_of_array (stems, me, Axis (a));
153 Grob *fvs = first_visible_stem (me);
154 Grob *lvs = last_visible_stem (me);
155 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
156 Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
159 We store some info to quickly interpolate.
161 Sometimes my head is screwed on backwards. The stemlength are
162 AFFINE linear in YL and YR. If YL == YR == 0, then we might have
163 stem_y != 0.0, when we're cross staff.
166 for (int i = 0; i < stems.size (); i++)
170 Stem_info si (Stem::get_stem_info (s));
172 stem_infos.push (si);
173 dirs_found[stem_infos.top ().dir_] = true;
175 bool f = to_boolean (s->get_property ("french-beaming"))
176 && s != lvs && s != fvs;
178 base_lengths.push (calc_stem_y (me, s, common, xl, xr,
179 Interval (0, 0), f) / ss);
180 stem_xposns.push (s->relative_coordinate (common[X_AXIS], X_AXIS));
186 Grob *commony = fvs->common_refpoint (lvs, Y_AXIS);
187 xstaff = Align_interface::has_interface (commony);
190 Direction ldir = Direction (stem_infos[0].dir_);
191 Direction rdir = Direction (stem_infos.top ().dir_);
192 bool is_knee = dirs_found[LEFT] && dirs_found[RIGHT];
194 int region_size = (int) parameters.REGION_SIZE;
197 Knees are harder, lets try some more possibilities for knees.
203 Asymetry ? should run to <= region_size ?
205 for (int i = -region_size; i < region_size; i++)
206 for (int j = 0; j < num_quants; j++)
208 quantsl.push (i + quants[j] + int (yl));
209 quantsr.push (i + quants[j] + int (yr));
212 Array<Quant_score> qscores;
214 for (int l = 0; l < quantsl.size (); l++)
215 for (int r = 0; r < quantsr.size (); r++)
225 /* This is a longish function, but we don't separate this out into
226 neat modular separate subfunctions, as the subfunctions would be
227 called for many values of YL, YR. By precomputing various
228 parameters outside of the loop, we can save a lot of time. */
229 for (int i = qscores.size (); i--;)
231 Real d = score_slopes_dy (qscores[i].yl, qscores[i].yr,
234 xstaff, ¶meters);
235 qscores[i].demerits += d;
238 qscores[i].score_card_ += to_string ("S%.2f", d);
242 Real rad = Staff_symbol_referencer::staff_radius (me);
243 Drul_array<int> edge_beam_counts
244 (Stem::beam_multiplicity (stems[0]).length () + 1,
245 Stem::beam_multiplicity (stems.top ()).length () + 1);
247 Real beam_translation = get_beam_translation (me) / ss;
249 Real reasonable_score = (is_knee) ? 200000 : 100;
250 for (int i = qscores.size (); i--;)
251 if (qscores[i].demerits < reasonable_score)
253 Real d = score_forbidden_quants (qscores[i].yl, qscores[i].yr,
254 rad, slt, thickness, beam_translation,
255 edge_beam_counts, ldir, rdir, ¶meters);
256 qscores[i].demerits += d;
259 qscores[i].score_card_ += to_string (" F %.2f", d);
263 for (int i = qscores.size (); i--;)
264 if (qscores[i].demerits < reasonable_score)
266 Real d = score_stem_lengths (stems, stem_infos,
267 base_lengths, stem_xposns,
270 qscores[i].yl, qscores[i].yr, ¶meters);
271 qscores[i].demerits += d;
274 qscores[i].score_card_ += to_string (" L %.2f", d);
278 int best_idx = best_quant_score_idx (qscores);
281 SCM inspect_quants = me->get_property ("inspect-quants");
282 if (to_boolean (me->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting")))
283 && scm_is_pair (inspect_quants))
285 Drul_array<Real> ins = ly_scm2interval (inspect_quants);
290 for (; i < qscores.size (); i++)
292 Real d = fabs (qscores[i].yl- ins[LEFT]) + fabs (qscores[i].yr - ins[RIGHT]);
300 programming_error ("can't find quant");
304 Interval final_positions;
307 warning (_ ("no feasible beam position"));
308 final_positions = Interval (0, 0);
312 final_positions = Drul_array<Real> (qscores[best_idx].yl,
313 qscores[best_idx].yr);
318 && to_boolean (me->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting"))))
320 qscores[best_idx].score_card_ += to_string ("i%d", best_idx);
323 me->set_property ("quant-score",
324 scm_makfrom0str (qscores[best_idx].score_card_.c_str ()));
328 return ly_interval2scm (final_positions);
332 Beam::score_stem_lengths (Link_array<Grob> const &stems,
333 Array<Stem_info> const &stem_infos,
334 Array<Real> const &base_stem_ys,
335 Array<Real> const &stem_xs,
340 Beam_quant_parameters const *parameters)
342 Real limit_penalty = parameters->STEM_LENGTH_LIMIT_PENALTY;
343 Drul_array<Real> score (0, 0);
344 Drul_array<int> count (0, 0);
346 for (int i = 0; i < stems.size (); i++)
349 if (Stem::is_invisible (s))
354 Real beam_y = dx ? yr * (x - xl) / dx + yl * (xr - x) / dx : (yr + yl) / 2;
355 Real current_y = beam_y + base_stem_ys[i];
356 Real length_pen = parameters->STEM_LENGTH_DEMERIT_FACTOR;
358 Stem_info info = stem_infos[i];
359 Direction d = info.dir_;
361 score[d] += limit_penalty * max (0.0, (d * (info.shortest_y_ - current_y)));
363 Real ideal_diff = d * (current_y - info.ideal_y_);
364 Real ideal_score = shrink_extra_weight (ideal_diff, 1.5);
366 /* We introduce a power, to make the scoring strictly
367 convex. Otherwise a symmetric knee beam (up/down/up/down)
368 does not have an optimum in the middle. */
370 ideal_score = pow (ideal_score, 1.1);
372 score[d] += length_pen * ideal_score;
379 score[d] /= max (count[d], 1);
380 while (flip (&d) != DOWN)
383 return score[LEFT] + score[RIGHT];
387 Beam::score_slopes_dy (Real yl, Real yr,
388 Real dy_mus, Real dy_damp,
392 Beam_quant_parameters const *parameters)
398 DAMPING_DIRECTION_PENALTY is a very harsh measure, while for
399 complex beaming patterns, horizontal is often a good choice.
401 TODO: find a way to incorporate the complexity of the beam in this
404 if (fabs (dy / dx) > parameters->ROUND_TO_ZERO_SLOPE
405 && sign (dy_damp) != sign (dy))
406 dem += parameters->DAMPING_DIRECTION_PENALTY;
408 dem += parameters->MUSICAL_DIRECTION_FACTOR
409 * max (0.0, (fabs (dy) - fabs (dy_mus)));
411 Real slope_penalty = parameters->IDEAL_SLOPE_FACTOR;
413 /* Xstaff beams tend to use extreme slopes to get short stems. We
414 put in a penalty here. */
418 /* Huh, why would a too steep beam be better than a too flat one ? */
419 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy), 1.5)
428 return x - floor (x);
432 TODO: The fixed value SECONDARY_BEAM_DEMERIT is probably flawed:
433 because for 32nd and 64th beams the forbidden quants are relatively
434 more important than stem lengths.
437 Beam::score_forbidden_quants (Real yl, Real yr,
440 Real thickness, Real beam_translation,
441 Drul_array<int> beam_counts,
442 Direction ldir, Direction rdir,
444 Beam_quant_parameters const *parameters)
447 Drul_array<Real> y (yl, yr);
448 Drul_array<Direction> dirs (ldir, rdir);
450 Real extra_demerit = parameters->SECONDARY_BEAM_DEMERIT / (max (beam_counts[LEFT], beam_counts[RIGHT]));
454 Real eps = parameters->BEAM_EPS;
458 for (int j = 1; j <= beam_counts[d]; j++)
460 Direction stem_dir = dirs[d];
463 The 2.2 factor is to provide a little leniency for
464 borderline cases. If we do 2.0, then the upper outer line
465 will be in the gap of the (2, sit) quant, leading to a
468 Real gap1 = y[d] - stem_dir * ((j - 1) * beam_translation + thickness / 2 - slt / 2.2);
469 Real gap2 = y[d] - stem_dir * (j * beam_translation - thickness / 2 + slt / 2.2);
472 gap.add_point (gap1);
473 gap.add_point (gap2);
475 for (Real k = -radius;
476 k <= radius + eps; k += 1.0)
477 if (gap.contains (k))
479 Real dist = min (fabs (gap[UP] - k), fabs (gap[DOWN] - k));
482 this parameter is tuned to grace-stem-length.ly
484 Real fixed_demerit = 0.4;
488 + (1 - fixed_demerit) * (dist / gap.length ()) * 2);
492 while ((flip (&d)) != LEFT);
494 if (max (beam_counts[LEFT], beam_counts[RIGHT]) >= 2)
497 Real sit = (thickness - slt) / 2;
499 Real hang = 1.0 - (thickness - slt) / 2;
504 if (beam_counts[d] >= 2
505 && fabs (y[d] - dirs[d] * beam_translation) < radius + inter)
507 if (dirs[d] == UP && dy <= eps
508 && fabs (my_modf (y[d]) - sit) < eps)
509 dem += extra_demerit;
511 if (dirs[d] == DOWN && dy >= eps
512 && fabs (my_modf (y[d]) - hang) < eps)
513 dem += extra_demerit;
516 if (beam_counts[d] >= 3
517 && fabs (y[d] - 2 * dirs[d] * beam_translation) < radius + inter)
519 if (dirs[d] == UP && dy <= eps
520 && fabs (my_modf (y[d]) - straddle) < eps)
521 dem += extra_demerit;
523 if (dirs[d] == DOWN && dy >= eps
524 && fabs (my_modf (y[d]) - straddle) < eps)
525 dem += extra_demerit;
528 while (flip (&d) != LEFT);