2 beam-quanting.cc -- implement Beam quanting functions
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2005 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
13 #include "staff-symbol-referencer.hh"
16 #include "output-def.hh"
17 #include "group-interface.hh"
18 #include "align-interface.hh"
20 const int INTER_QUANT_PENALTY = 1000;
21 const Real SECONDARY_BEAM_DEMERIT = 10.0;
22 const int STEM_LENGTH_DEMERIT_FACTOR = 5;
25 threshold to combat rounding errors.
27 const Real BEAM_EPS = 1e-3;
29 // possibly ridiculous, but too short stems just won't do
30 const int STEM_LENGTH_LIMIT_PENALTY = 5000;
31 const int DAMPING_DIRECTION_PENALTY = 800;
32 const int MUSICAL_DIRECTION_FACTOR = 400;
33 const int IDEAL_SLOPE_FACTOR = 10;
34 const Real ROUND_TO_ZERO_SLOPE = 0.02;
37 shrink_extra_weight (Real x, Real fac)
39 return fabs (x) * ((x < 0) ? fac : 1.0);
56 - Make all demerits customisable
58 - One sensible check per demerit (what's this --hwn)
60 - Add demerits for quants per se, as to forbid a specific quant
65 best_quant_score_idx (Array<Quant_score> const &qscores)
69 for (int i = qscores.size (); i--;)
71 if (qscores[i].demerits < best)
73 best = qscores [i].demerits;
80 programming_error ("Huh? No best beam quant score?");
87 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
89 Beam::quanting (SCM smob)
91 Grob *me = unsmob_grob (smob);
93 SCM s = me->get_property ("positions");
94 Real yl = scm_to_double (scm_car (s));
95 Real yr = scm_to_double (scm_cdr (s));
98 Calculations are relative to a unit-scaled staff, i.e. the quants are
99 divided by the current staff_space.
102 Real ss = Staff_symbol_referencer::staff_space (me);
103 Real thickness = Beam::get_thickness (me) / ss;
104 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
106 Real dy_mus = robust_scm2double (me->get_property ("least-squares-dy"), 0);
108 Real sit = (thickness - slt) / 2;
110 Real hang = 1.0 - (thickness - slt) / 2;
111 Real quants [] = {straddle, sit, inter, hang };
113 int num_quants = int (sizeof (quants) / sizeof (Real));
118 going to REGION_SIZE == 2, yields another 0.6 second with
122 (result indexes between 70 and 575) ? --hwn.
127 Do stem computations. These depend on YL and YR linearly, so we can
128 precompute for every stem 2 factors.
130 Link_array<Grob> stems
131 = extract_grob_array (me, ly_symbol2scm ("stems"));
132 Array<Stem_info> stem_infos;
133 Array<Real> base_lengths;
134 Array<Real> stem_xposns;
136 Drul_array<bool> dirs_found (0, 0);
138 for (int a = 2; a--;)
139 common[a] = common_refpoint_of_array (stems, me, Axis (a));
141 Grob *fvs = first_visible_stem (me);
142 Grob *lvs = last_visible_stem (me);
143 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
144 Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
147 We store some info to quickly interpolate.
149 Sometimes my head is screwed on backwards. The stemlength are
150 AFFINE linear in YL and YR. If YL == YR == 0, then we might have
151 stem_y != 0.0, when we're cross staff.
154 for (int i = 0; i < stems.size (); i++)
158 Stem_info si (Stem::get_stem_info (s));
160 stem_infos.push (si);
161 dirs_found[stem_infos.top ().dir_] = true;
163 bool f = to_boolean (s->get_property ("french-beaming"))
164 && s != lvs && s != fvs;
166 base_lengths.push (calc_stem_y (me, s, common, xl, xr,
167 Interval (0, 0), f) / ss);
168 stem_xposns.push (s->relative_coordinate (common[X_AXIS], X_AXIS));
174 Grob *commony = fvs->common_refpoint (lvs, Y_AXIS);
175 xstaff = Align_interface::has_interface (commony);
178 Direction ldir = Direction (stem_infos[0].dir_);
179 Direction rdir = Direction (stem_infos.top ().dir_);
180 bool is_knee = dirs_found[LEFT] && dirs_found[RIGHT];
182 int region_size = REGION_SIZE;
184 Knees are harder, lets try some more possibilities for knees.
190 Asymetry ? should run to <= region_size ?
192 for (int i = -region_size; i < region_size; i++)
193 for (int j = 0; j < num_quants; j++)
195 quantsl.push (i + quants[j] + int (yl));
196 quantsr.push (i + quants[j] + int (yr));
199 Array<Quant_score> qscores;
201 for (int l = 0; l < quantsl.size (); l++)
202 for (int r = 0; r < quantsr.size (); r++)
212 /* This is a longish function, but we don't separate this out into
213 neat modular separate subfunctions, as the subfunctions would be
214 called for many values of YL, YR. By precomputing various
215 parameters outside of the loop, we can save a lot of time. */
216 for (int i = qscores.size (); i--;)
218 Real d = score_slopes_dy (qscores[i].yl, qscores[i].yr,
222 qscores[i].demerits += d;
225 qscores[i].score_card_ += to_string ("S%.2f", d);
229 Real rad = Staff_symbol_referencer::staff_radius (me);
230 Drul_array<int> edge_beam_counts
231 (Stem::beam_multiplicity (stems[0]).length () + 1,
232 Stem::beam_multiplicity (stems.top ()).length () + 1);
234 Real beam_translation = get_beam_translation (me) / ss;
236 Real reasonable_score = (is_knee) ? 200000 : 100;
237 for (int i = qscores.size (); i--;)
238 if (qscores[i].demerits < reasonable_score)
240 Real d = score_forbidden_quants (qscores[i].yl, qscores[i].yr,
241 rad, slt, thickness, beam_translation,
242 edge_beam_counts, ldir, rdir);
243 qscores[i].demerits += d;
246 qscores[i].score_card_ += to_string (" F %.2f", d);
250 for (int i = qscores.size (); i--;)
251 if (qscores[i].demerits < reasonable_score)
253 Real d = score_stem_lengths (stems, stem_infos,
254 base_lengths, stem_xposns,
257 qscores[i].yl, qscores[i].yr);
258 qscores[i].demerits += d;
261 qscores[i].score_card_ += to_string (" L %.2f", d);
265 int best_idx = best_quant_score_idx (qscores);
268 SCM inspect_quants = me->get_property ("inspect-quants");
269 if (to_boolean (me->get_layout ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting")))
270 && scm_is_pair (inspect_quants))
272 Drul_array<Real> ins = ly_scm2interval (inspect_quants);
277 for (; i < qscores.size (); i++)
279 Real d = fabs (qscores[i].yl- ins[LEFT]) + fabs (qscores[i].yr - ins[RIGHT]);
287 programming_error ("Could not find quant.");
291 me->set_property ("positions",
292 ly_interval2scm (Drul_array<Real> (qscores[best_idx].yl,
293 qscores[best_idx].yr)));
295 if (to_boolean (me->get_layout ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting"))))
297 qscores[best_idx].score_card_ += to_string ("i%d", best_idx);
300 me->set_property ("quant-score",
301 scm_makfrom0str (qscores[best_idx].score_card_.to_str0 ()));
305 return SCM_UNSPECIFIED;
309 Beam::score_stem_lengths (Link_array<Grob> const &stems,
310 Array<Stem_info> const &stem_infos,
311 Array<Real> const &base_stem_ys,
312 Array<Real> const &stem_xs,
317 Real limit_penalty = STEM_LENGTH_LIMIT_PENALTY;
318 Drul_array<Real> score (0, 0);
319 Drul_array<int> count (0, 0);
321 for (int i = 0; i < stems.size (); i++)
324 if (Stem::is_invisible (s))
329 Real beam_y = dx ? yr * (x - xl) / dx + yl * (xr - x) / dx : (yr + yl) / 2;
330 Real current_y = beam_y + base_stem_ys[i];
331 Real length_pen = STEM_LENGTH_DEMERIT_FACTOR;
333 Stem_info info = stem_infos[i];
334 Direction d = info.dir_;
336 score[d] += limit_penalty * (0 >? (d * (info.shortest_y_ - current_y)));
338 Real ideal_diff = d * (current_y - info.ideal_y_);
339 Real ideal_score = shrink_extra_weight (ideal_diff, 1.5);
341 /* We introduce a power, to make the scoring strictly
342 convex. Otherwise a symmetric knee beam (up/down/up/down)
343 does not have an optimum in the middle. */
345 ideal_score = pow (ideal_score, 1.1);
347 score[d] += length_pen * ideal_score;
355 score[d] /= (count[d] >? 1);
357 while (flip (&d) != DOWN);
359 return score[LEFT] + score[RIGHT];
363 Beam::score_slopes_dy (Real yl, Real yr,
364 Real dy_mus, Real dy_damp,
372 DAMPING_DIRECTION_PENALTY is a very harsh measure, while for
373 complex beaming patterns, horizontal is often a good choice.
375 TODO: find a way to incorporate the complexity of the beam in this
378 if (fabs (dy / dx) > ROUND_TO_ZERO_SLOPE
379 && sign (dy_damp) != sign (dy))
381 dem += DAMPING_DIRECTION_PENALTY;
384 dem += MUSICAL_DIRECTION_FACTOR *(0 >? (fabs (dy) - fabs (dy_mus)));
386 Real slope_penalty = IDEAL_SLOPE_FACTOR;
388 /* Xstaff beams tend to use extreme slopes to get short stems. We
389 put in a penalty here. */
393 /* Huh, why would a too steep beam be better than a too flat one ? */
394 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy), 1.5)
403 return x - floor (x);
407 TODO: The fixed value SECONDARY_BEAM_DEMERIT is probably flawed:
408 because for 32nd and 64th beams the forbidden quants are relatively
409 more important than stem lengths.
412 Beam::score_forbidden_quants (Real yl, Real yr,
415 Real thickness, Real beam_translation,
416 Drul_array<int> beam_counts,
417 Direction ldir, Direction rdir)
420 Drul_array<Real> y (yl, yr);
421 Drul_array<Direction> dirs (ldir, rdir);
423 Real extra_demerit = SECONDARY_BEAM_DEMERIT / (beam_counts[LEFT] >? beam_counts[RIGHT]);
430 for (int j = 1; j <= beam_counts[d]; j++)
432 Direction stem_dir = dirs[d];
435 The 2.2 factor is to provide a little leniency for
436 borderline cases. If we do 2.0, then the upper outer line
437 will be in the gap of the (2, sit) quant, leading to a
440 Real gap1 = y[d] - stem_dir * ((j - 1) * beam_translation + thickness / 2 - slt / 2.2);
441 Real gap2 = y[d] - stem_dir * (j * beam_translation - thickness / 2 + slt / 2.2);
444 gap.add_point (gap1);
445 gap.add_point (gap2);
447 for (Real k = -radius;
448 k <= radius + BEAM_EPS; k += 1.0)
449 if (gap.contains (k))
451 Real dist = fabs (gap[UP] - k) <? fabs (gap[DOWN] - k);
454 this parameter is tuned to grace-stem-length.ly
456 Real fixed_demerit = 0.4;
460 + (1 - fixed_demerit) * (dist / gap.length ()) * 2);
464 while ((flip (&d)) != LEFT);
466 if ((beam_counts[LEFT] >? beam_counts[RIGHT]) >= 2)
469 Real sit = (thickness - slt) / 2;
471 Real hang = 1.0 - (thickness - slt) / 2;
476 if (beam_counts[d] >= 2
477 && fabs (y[d] - dirs[d] * beam_translation) < radius + inter)
479 if (dirs[d] == UP && dy <= BEAM_EPS
480 && fabs (my_modf (y[d]) - sit) < BEAM_EPS)
481 dem += extra_demerit;
483 if (dirs[d] == DOWN && dy >= BEAM_EPS
484 && fabs (my_modf (y[d]) - hang) < BEAM_EPS)
485 dem += extra_demerit;
488 if (beam_counts[d] >= 3
489 && fabs (y[d] - 2 * dirs[d] * beam_translation) < radius + inter)
491 if (dirs[d] == UP && dy <= BEAM_EPS
492 && fabs (my_modf (y[d]) - straddle) < BEAM_EPS)
493 dem += extra_demerit;
495 if (dirs[d] == DOWN && dy >= BEAM_EPS
496 && fabs (my_modf (y[d]) - straddle) < BEAM_EPS)
497 dem += extra_demerit;
500 while (flip (&d) != LEFT);