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;
83 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
85 Beam::quanting (SCM smob)
87 Grob *me = unsmob_grob (smob);
89 SCM s = me->get_property ("positions");
90 Real yl = scm_to_double (scm_car (s));
91 Real yr = scm_to_double (scm_cdr (s));
94 Calculations are relative to a unit-scaled staff, i.e. the quants are
95 divided by the current staff_space.
98 Real ss = Staff_symbol_referencer::staff_space (me);
99 Real thickness = Beam::get_thickness (me) / ss;
100 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
102 Real dy_mus = robust_scm2double (me->get_property ("least-squares-dy"), 0);
104 Real sit = (thickness - slt) / 2;
106 Real hang = 1.0 - (thickness - slt) / 2;
107 Real quants [] = {straddle, sit, inter, hang };
109 int num_quants = int (sizeof (quants) / sizeof (Real));
114 going to REGION_SIZE == 2, yields another 0.6 second with
118 (result indexes between 70 and 575) ? --hwn.
123 Do stem computations. These depend on YL and YR linearly, so we can
124 precompute for every stem 2 factors.
126 Link_array<Grob> stems
127 = extract_grob_array (me, ly_symbol2scm ("stems"));
128 Array<Stem_info> stem_infos;
129 Array<Real> base_lengths;
130 Array<Real> stem_xposns;
132 Drul_array<bool> dirs_found (0, 0);
134 for (int a = 2; a--;)
135 common[a] = common_refpoint_of_array (stems, me, Axis (a));
137 Grob *fvs = first_visible_stem (me);
138 Grob *lvs = last_visible_stem (me);
139 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
140 Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
143 We store some info to quickly interpolate.
145 Sometimes my head is screwed on backwards. The stemlength are
146 AFFINE linear in YL and YR. If YL == YR == 0, then we might have
147 stem_y != 0.0, when we're cross staff.
150 for (int i = 0; i < stems.size (); i++)
154 Stem_info si (Stem::get_stem_info (s));
156 stem_infos.push (si);
157 dirs_found[stem_infos.top ().dir_] = true;
159 bool f = to_boolean (s->get_property ("french-beaming"))
160 && s != lvs && s != fvs;
162 base_lengths.push (calc_stem_y (me, s, common, xl, xr,
163 Interval (0, 0), f) / ss);
164 stem_xposns.push (s->relative_coordinate (common[X_AXIS], X_AXIS));
170 Grob *commony = fvs->common_refpoint (lvs, Y_AXIS);
171 xstaff = Align_interface::has_interface (commony);
174 Direction ldir = Direction (stem_infos[0].dir_);
175 Direction rdir = Direction (stem_infos.top ().dir_);
176 bool is_knee = dirs_found[LEFT] && dirs_found[RIGHT];
178 int region_size = REGION_SIZE;
180 Knees are harder, lets try some more possibilities for knees.
186 Asymetry ? should run to <= region_size ?
188 for (int i = -region_size; i < region_size; i++)
189 for (int j = 0; j < num_quants; j++)
191 quantsl.push (i + quants[j] + int (yl));
192 quantsr.push (i + quants[j] + int (yr));
195 Array<Quant_score> qscores;
197 for (int l = 0; l < quantsl.size (); l++)
198 for (int r = 0; r < quantsr.size (); r++)
208 /* This is a longish function, but we don't separate this out into
209 neat modular separate subfunctions, as the subfunctions would be
210 called for many values of YL, YR. By precomputing various
211 parameters outside of the loop, we can save a lot of time. */
212 for (int i = qscores.size (); i--;)
214 Real d = score_slopes_dy (qscores[i].yl, qscores[i].yr,
218 qscores[i].demerits += d;
221 qscores[i].score_card_ += to_string ("S%.2f", d);
225 Real rad = Staff_symbol_referencer::staff_radius (me);
226 Drul_array<int> edge_beam_counts
227 (Stem::beam_multiplicity (stems[0]).length () + 1,
228 Stem::beam_multiplicity (stems.top ()).length () + 1);
230 Real beam_translation = get_beam_translation (me) / ss;
232 Real reasonable_score = (is_knee) ? 200000 : 100;
233 for (int i = qscores.size (); i--;)
234 if (qscores[i].demerits < reasonable_score)
236 Real d = score_forbidden_quants (qscores[i].yl, qscores[i].yr,
237 rad, slt, thickness, beam_translation,
238 edge_beam_counts, ldir, rdir);
239 qscores[i].demerits += d;
242 qscores[i].score_card_ += to_string (" F %.2f", d);
246 for (int i = qscores.size (); i--;)
247 if (qscores[i].demerits < reasonable_score)
249 Real d = score_stem_lengths (stems, stem_infos,
250 base_lengths, stem_xposns,
253 qscores[i].yl, qscores[i].yr);
254 qscores[i].demerits += d;
257 qscores[i].score_card_ += to_string (" L %.2f", d);
261 int best_idx = best_quant_score_idx (qscores);
264 SCM inspect_quants = me->get_property ("inspect-quants");
265 if ( to_boolean (me->get_layout ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting")))
266 && scm_is_pair (inspect_quants))
268 Drul_array<Real> ins = ly_scm2interval (inspect_quants);
273 for (; i < qscores.size (); i++)
275 Real d = fabs (qscores[i].yl- ins[LEFT]) + fabs (qscores[i].yr - ins[RIGHT]);
283 programming_error ("can't find quant");
288 warning (_ ("no feasible beam position"));
289 me->set_property ("positions", ly_interval2scm (Interval (0,0)));
292 me->set_property ("positions",
293 ly_interval2scm (Drul_array<Real> (qscores[best_idx].yl,
294 qscores[best_idx].yr)));
297 && 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);