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>
16 #include "staff-symbol-referencer.hh"
18 #include "output-def.hh"
19 #include "group-interface.hh"
20 #include "align-interface.hh"
22 const int INTER_QUANT_PENALTY = 1000;
23 const Real SECONDARY_BEAM_DEMERIT = 10.0;
24 const int STEM_LENGTH_DEMERIT_FACTOR = 5;
27 threshold to combat rounding errors.
29 const Real BEAM_EPS = 1e-3;
31 // possibly ridiculous, but too short stems just won't do
32 const int STEM_LENGTH_LIMIT_PENALTY = 5000;
33 const int DAMPING_DIRECTION_PENALTY = 800;
34 const int MUSICAL_DIRECTION_FACTOR = 400;
35 const int IDEAL_SLOPE_FACTOR = 10;
36 const Real ROUND_TO_ZERO_SLOPE = 0.02;
39 shrink_extra_weight (Real x, Real fac)
41 return fabs (x) * ((x < 0) ? fac : 1.0);
58 - Make all demerits customisable
60 - One sensible check per demerit (what's this --hwn)
62 - Add demerits for quants per se, as to forbid a specific quant
67 best_quant_score_idx (Array<Quant_score> const &qscores)
71 for (int i = qscores.size (); i--;)
73 if (qscores[i].demerits < best)
75 best = qscores [i].demerits;
82 programming_error ("no best beam quant score");
89 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
91 Beam::quanting (SCM smob)
93 Grob *me = unsmob_grob (smob);
95 SCM s = me->get_property ("positions");
96 Real yl = scm_to_double (scm_car (s));
97 Real yr = scm_to_double (scm_cdr (s));
100 Calculations are relative to a unit-scaled staff, i.e. the quants are
101 divided by the current staff_space.
104 Real ss = Staff_symbol_referencer::staff_space (me);
105 Real thickness = Beam::get_thickness (me) / ss;
106 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
108 Real dy_mus = robust_scm2double (me->get_property ("least-squares-dy"), 0);
110 Real sit = (thickness - slt) / 2;
112 Real hang = 1.0 - (thickness - slt) / 2;
113 Real quants [] = {straddle, sit, inter, hang };
115 int num_quants = int (sizeof (quants) / sizeof (Real));
120 going to REGION_SIZE == 2, yields another 0.6 second with
124 (result indexes between 70 and 575) ? --hwn.
129 Do stem computations. These depend on YL and YR linearly, so we can
130 precompute for every stem 2 factors.
132 Link_array<Grob> stems
133 = extract_grob_array (me, ly_symbol2scm ("stems"));
134 Array<Stem_info> stem_infos;
135 Array<Real> base_lengths;
136 Array<Real> stem_xposns;
138 Drul_array<bool> dirs_found (0, 0);
140 for (int a = 2; a--;)
141 common[a] = common_refpoint_of_array (stems, me, Axis (a));
143 Grob *fvs = first_visible_stem (me);
144 Grob *lvs = last_visible_stem (me);
145 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
146 Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
149 We store some info to quickly interpolate.
151 Sometimes my head is screwed on backwards. The stemlength are
152 AFFINE linear in YL and YR. If YL == YR == 0, then we might have
153 stem_y != 0.0, when we're cross staff.
156 for (int i = 0; i < stems.size (); i++)
160 Stem_info si (Stem::get_stem_info (s));
162 stem_infos.push (si);
163 dirs_found[stem_infos.top ().dir_] = true;
165 bool f = to_boolean (s->get_property ("french-beaming"))
166 && s != lvs && s != fvs;
168 base_lengths.push (calc_stem_y (me, s, common, xl, xr,
169 Interval (0, 0), f) / ss);
170 stem_xposns.push (s->relative_coordinate (common[X_AXIS], X_AXIS));
176 Grob *commony = fvs->common_refpoint (lvs, Y_AXIS);
177 xstaff = Align_interface::has_interface (commony);
180 Direction ldir = Direction (stem_infos[0].dir_);
181 Direction rdir = Direction (stem_infos.top ().dir_);
182 bool is_knee = dirs_found[LEFT] && dirs_found[RIGHT];
184 int region_size = REGION_SIZE;
186 Knees are harder, lets try some more possibilities for knees.
192 Asymetry ? should run to <= region_size ?
194 for (int i = -region_size; i < region_size; i++)
195 for (int j = 0; j < num_quants; j++)
197 quantsl.push (i + quants[j] + int (yl));
198 quantsr.push (i + quants[j] + int (yr));
201 Array<Quant_score> qscores;
203 for (int l = 0; l < quantsl.size (); l++)
204 for (int r = 0; r < quantsr.size (); r++)
214 /* This is a longish function, but we don't separate this out into
215 neat modular separate subfunctions, as the subfunctions would be
216 called for many values of YL, YR. By precomputing various
217 parameters outside of the loop, we can save a lot of time. */
218 for (int i = qscores.size (); i--;)
220 Real d = score_slopes_dy (qscores[i].yl, qscores[i].yr,
224 qscores[i].demerits += d;
227 qscores[i].score_card_ += to_string ("S%.2f", d);
231 Real rad = Staff_symbol_referencer::staff_radius (me);
232 Drul_array<int> edge_beam_counts
233 (Stem::beam_multiplicity (stems[0]).length () + 1,
234 Stem::beam_multiplicity (stems.top ()).length () + 1);
236 Real beam_translation = get_beam_translation (me) / ss;
238 Real reasonable_score = (is_knee) ? 200000 : 100;
239 for (int i = qscores.size (); i--;)
240 if (qscores[i].demerits < reasonable_score)
242 Real d = score_forbidden_quants (qscores[i].yl, qscores[i].yr,
243 rad, slt, thickness, beam_translation,
244 edge_beam_counts, ldir, rdir);
245 qscores[i].demerits += d;
248 qscores[i].score_card_ += to_string (" F %.2f", d);
252 for (int i = qscores.size (); i--;)
253 if (qscores[i].demerits < reasonable_score)
255 Real d = score_stem_lengths (stems, stem_infos,
256 base_lengths, stem_xposns,
259 qscores[i].yl, qscores[i].yr);
260 qscores[i].demerits += d;
263 qscores[i].score_card_ += to_string (" L %.2f", d);
267 int best_idx = best_quant_score_idx (qscores);
270 SCM inspect_quants = me->get_property ("inspect-quants");
271 if (to_boolean (me->get_layout ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting")))
272 && scm_is_pair (inspect_quants))
274 Drul_array<Real> ins = ly_scm2interval (inspect_quants);
279 for (; i < qscores.size (); i++)
281 Real d = fabs (qscores[i].yl- ins[LEFT]) + fabs (qscores[i].yr - ins[RIGHT]);
289 programming_error ("can't find quant");
293 me->set_property ("positions",
294 ly_interval2scm (Drul_array<Real> (qscores[best_idx].yl,
295 qscores[best_idx].yr)));
297 if (to_boolean (me->get_layout ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting"))))
299 qscores[best_idx].score_card_ += to_string ("i%d", best_idx);
302 me->set_property ("quant-score",
303 scm_makfrom0str (qscores[best_idx].score_card_.to_str0 ()));
307 return SCM_UNSPECIFIED;
311 Beam::score_stem_lengths (Link_array<Grob> const &stems,
312 Array<Stem_info> const &stem_infos,
313 Array<Real> const &base_stem_ys,
314 Array<Real> const &stem_xs,
319 Real limit_penalty = STEM_LENGTH_LIMIT_PENALTY;
320 Drul_array<Real> score (0, 0);
321 Drul_array<int> count (0, 0);
323 for (int i = 0; i < stems.size (); i++)
326 if (Stem::is_invisible (s))
331 Real beam_y = dx ? yr * (x - xl) / dx + yl * (xr - x) / dx : (yr + yl) / 2;
332 Real current_y = beam_y + base_stem_ys[i];
333 Real length_pen = STEM_LENGTH_DEMERIT_FACTOR;
335 Stem_info info = stem_infos[i];
336 Direction d = info.dir_;
338 score[d] += limit_penalty * max (0.0, (d * (info.shortest_y_ - current_y)));
340 Real ideal_diff = d * (current_y - info.ideal_y_);
341 Real ideal_score = shrink_extra_weight (ideal_diff, 1.5);
343 /* We introduce a power, to make the scoring strictly
344 convex. Otherwise a symmetric knee beam (up/down/up/down)
345 does not have an optimum in the middle. */
347 ideal_score = pow (ideal_score, 1.1);
349 score[d] += length_pen * ideal_score;
357 score[d] /= max (count[d], 1);
359 while (flip (&d) != DOWN);
361 return score[LEFT] + score[RIGHT];
365 Beam::score_slopes_dy (Real yl, Real yr,
366 Real dy_mus, Real dy_damp,
374 DAMPING_DIRECTION_PENALTY is a very harsh measure, while for
375 complex beaming patterns, horizontal is often a good choice.
377 TODO: find a way to incorporate the complexity of the beam in this
380 if (fabs (dy / dx) > ROUND_TO_ZERO_SLOPE
381 && sign (dy_damp) != sign (dy))
383 dem += DAMPING_DIRECTION_PENALTY;
386 dem += MUSICAL_DIRECTION_FACTOR * max (0.0, (fabs (dy) - fabs (dy_mus)));
388 Real slope_penalty = IDEAL_SLOPE_FACTOR;
390 /* Xstaff beams tend to use extreme slopes to get short stems. We
391 put in a penalty here. */
395 /* Huh, why would a too steep beam be better than a too flat one ? */
396 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy), 1.5)
405 return x - floor (x);
409 TODO: The fixed value SECONDARY_BEAM_DEMERIT is probably flawed:
410 because for 32nd and 64th beams the forbidden quants are relatively
411 more important than stem lengths.
414 Beam::score_forbidden_quants (Real yl, Real yr,
417 Real thickness, Real beam_translation,
418 Drul_array<int> beam_counts,
419 Direction ldir, Direction rdir)
422 Drul_array<Real> y (yl, yr);
423 Drul_array<Direction> dirs (ldir, rdir);
425 Real extra_demerit = SECONDARY_BEAM_DEMERIT / (max (beam_counts[LEFT], beam_counts[RIGHT]));
432 for (int j = 1; j <= beam_counts[d]; j++)
434 Direction stem_dir = dirs[d];
437 The 2.2 factor is to provide a little leniency for
438 borderline cases. If we do 2.0, then the upper outer line
439 will be in the gap of the (2, sit) quant, leading to a
442 Real gap1 = y[d] - stem_dir * ((j - 1) * beam_translation + thickness / 2 - slt / 2.2);
443 Real gap2 = y[d] - stem_dir * (j * beam_translation - thickness / 2 + slt / 2.2);
446 gap.add_point (gap1);
447 gap.add_point (gap2);
449 for (Real k = -radius;
450 k <= radius + BEAM_EPS; k += 1.0)
451 if (gap.contains (k))
453 Real dist = min (fabs (gap[UP] - k), fabs (gap[DOWN] - k));
456 this parameter is tuned to grace-stem-length.ly
458 Real fixed_demerit = 0.4;
462 + (1 - fixed_demerit) * (dist / gap.length ()) * 2);
466 while ((flip (&d)) != LEFT);
468 if (max (beam_counts[LEFT], beam_counts[RIGHT]) >= 2)
471 Real sit = (thickness - slt) / 2;
473 Real hang = 1.0 - (thickness - slt) / 2;
478 if (beam_counts[d] >= 2
479 && fabs (y[d] - dirs[d] * beam_translation) < radius + inter)
481 if (dirs[d] == UP && dy <= BEAM_EPS
482 && fabs (my_modf (y[d]) - sit) < BEAM_EPS)
483 dem += extra_demerit;
485 if (dirs[d] == DOWN && dy >= BEAM_EPS
486 && fabs (my_modf (y[d]) - hang) < BEAM_EPS)
487 dem += extra_demerit;
490 if (beam_counts[d] >= 3
491 && fabs (y[d] - 2 * dirs[d] * beam_translation) < radius + inter)
493 if (dirs[d] == UP && dy <= BEAM_EPS
494 && fabs (my_modf (y[d]) - straddle) < BEAM_EPS)
495 dem += extra_demerit;
497 if (dirs[d] == DOWN && dy >= BEAM_EPS
498 && fabs (my_modf (y[d]) - straddle) < BEAM_EPS)
499 dem += extra_demerit;
502 while (flip (&d) != LEFT);