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 "pointer-group-interface.hh"
20 #include "align-interface.hh"
23 get_detail (SCM alist, SCM sym, Real def)
25 SCM entry = scm_assq (sym, alist);
27 if (scm_is_pair (entry))
29 return robust_scm2double (scm_cdr (entry), def);
35 Beam_quant_parameters::fill (Grob *him)
37 SCM details = him->get_property ("details");
39 INTER_QUANT_PENALTY = get_detail (details, ly_symbol2scm ("inter-quant-penalty"), 1000.0);
40 SECONDARY_BEAM_DEMERIT = get_detail (details, ly_symbol2scm ("secondary-beam-demerit"), 10.0);
41 STEM_LENGTH_DEMERIT_FACTOR = get_detail (details, ly_symbol2scm ("stem-length-demerit-factor"), 5);
42 REGION_SIZE = get_detail (details, ly_symbol2scm ("region-size"), 2);
43 BEAM_EPS = get_detail (details, ly_symbol2scm ("beam-eps"), 1e-3);
45 // possibly ridiculous, but too short stems just won't do
46 STEM_LENGTH_LIMIT_PENALTY = get_detail (details, ly_symbol2scm ("stem-length-limit-penalty"), 5000);
47 DAMPING_DIRECTION_PENALTY = get_detail (details, ly_symbol2scm ("damping-direction-penalty"), 800);
48 MUSICAL_DIRECTION_FACTOR = get_detail (details, ly_symbol2scm ("musical-direction-factor"), 400);
49 IDEAL_SLOPE_FACTOR = get_detail (details, ly_symbol2scm ("ideal-slope-factor"), 10);
50 ROUND_TO_ZERO_SLOPE = get_detail (details, ly_symbol2scm ("round-to-zero-slope"), 0.02);
54 shrink_extra_weight (Real x, Real fac)
56 return fabs (x) * ((x < 0) ? fac : 1.0);
73 - Make all demerits customisable
75 - One sensible check per demerit (what's this --hwn)
77 - Add demerits for quants per se, as to forbid a specific quant
82 best_quant_score_idx (Array<Quant_score> const &qscores)
86 for (int i = qscores.size (); i--;)
88 if (qscores[i].demerits < best)
90 best = qscores [i].demerits;
98 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
100 Beam::quanting (SCM smob)
102 Grob *me = unsmob_grob (smob);
105 Beam_quant_parameters parameters;
106 parameters.fill (me);
108 SCM s = me->get_property ("positions");
109 Real yl = scm_to_double (scm_car (s));
110 Real yr = scm_to_double (scm_cdr (s));
113 Calculations are relative to a unit-scaled staff, i.e. the quants are
114 divided by the current staff_space.
117 Real ss = Staff_symbol_referencer::staff_space (me);
118 Real thickness = Beam::get_thickness (me) / ss;
119 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
121 Real dy_mus = robust_scm2double (me->get_property ("least-squares-dy"), 0);
123 Real sit = (thickness - slt) / 2;
125 Real hang = 1.0 - (thickness - slt) / 2;
126 Real quants [] = {straddle, sit, inter, hang };
128 int num_quants = int (sizeof (quants) / sizeof (Real));
133 going to REGION_SIZE == 2, yields another 0.6 second with
137 (result indexes between 70 and 575) ? --hwn.
142 Do stem computations. These depend on YL and YR linearly, so we can
143 precompute for every stem 2 factors.
145 Link_array<Grob> stems
146 = extract_grob_array (me, "stems");
147 Array<Stem_info> stem_infos;
148 Array<Real> base_lengths;
149 Array<Real> stem_xposns;
151 Drul_array<bool> dirs_found (0, 0);
153 for (int a = 2; a--;)
154 common[a] = common_refpoint_of_array (stems, me, Axis (a));
156 Grob *fvs = first_visible_stem (me);
157 Grob *lvs = last_visible_stem (me);
158 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
159 Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
162 We store some info to quickly interpolate.
164 Sometimes my head is screwed on backwards. The stemlength are
165 AFFINE linear in YL and YR. If YL == YR == 0, then we might have
166 stem_y != 0.0, when we're cross staff.
169 for (int i = 0; i < stems.size (); i++)
173 Stem_info si (Stem::get_stem_info (s));
175 stem_infos.push (si);
176 dirs_found[stem_infos.top ().dir_] = true;
178 bool f = to_boolean (s->get_property ("french-beaming"))
179 && s != lvs && s != fvs;
181 base_lengths.push (calc_stem_y (me, s, common, xl, xr,
182 Interval (0, 0), f) / ss);
183 stem_xposns.push (s->relative_coordinate (common[X_AXIS], X_AXIS));
189 Grob *commony = fvs->common_refpoint (lvs, Y_AXIS);
190 xstaff = Align_interface::has_interface (commony);
193 Direction ldir = Direction (stem_infos[0].dir_);
194 Direction rdir = Direction (stem_infos.top ().dir_);
195 bool is_knee = dirs_found[LEFT] && dirs_found[RIGHT];
197 int region_size = (int) parameters.REGION_SIZE;
200 Knees are harder, lets try some more possibilities for knees.
206 Asymetry ? should run to <= region_size ?
208 for (int i = -region_size; i < region_size; i++)
209 for (int j = 0; j < num_quants; j++)
211 quantsl.push (i + quants[j] + int (yl));
212 quantsr.push (i + quants[j] + int (yr));
215 Array<Quant_score> qscores;
217 for (int l = 0; l < quantsl.size (); l++)
218 for (int r = 0; r < quantsr.size (); r++)
228 /* This is a longish function, but we don't separate this out into
229 neat modular separate subfunctions, as the subfunctions would be
230 called for many values of YL, YR. By precomputing various
231 parameters outside of the loop, we can save a lot of time. */
232 for (int i = qscores.size (); i--;)
234 Real d = score_slopes_dy (qscores[i].yl, qscores[i].yr,
237 xstaff, ¶meters);
238 qscores[i].demerits += d;
241 qscores[i].score_card_ += to_string ("S%.2f", d);
245 Real rad = Staff_symbol_referencer::staff_radius (me);
246 Drul_array<int> edge_beam_counts
247 (Stem::beam_multiplicity (stems[0]).length () + 1,
248 Stem::beam_multiplicity (stems.top ()).length () + 1);
250 Real beam_translation = get_beam_translation (me) / ss;
252 Real reasonable_score = (is_knee) ? 200000 : 100;
253 for (int i = qscores.size (); i--;)
254 if (qscores[i].demerits < reasonable_score)
256 Real d = score_forbidden_quants (qscores[i].yl, qscores[i].yr,
257 rad, slt, thickness, beam_translation,
258 edge_beam_counts, ldir, rdir, ¶meters);
259 qscores[i].demerits += d;
262 qscores[i].score_card_ += to_string (" F %.2f", d);
266 for (int i = qscores.size (); i--;)
267 if (qscores[i].demerits < reasonable_score)
269 Real d = score_stem_lengths (stems, stem_infos,
270 base_lengths, stem_xposns,
273 qscores[i].yl, qscores[i].yr, ¶meters);
274 qscores[i].demerits += d;
277 qscores[i].score_card_ += to_string (" L %.2f", d);
281 int best_idx = best_quant_score_idx (qscores);
284 SCM inspect_quants = me->get_property ("inspect-quants");
285 if ( to_boolean (me->get_layout ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting")))
286 && scm_is_pair (inspect_quants))
288 Drul_array<Real> ins = ly_scm2interval (inspect_quants);
293 for (; i < qscores.size (); i++)
295 Real d = fabs (qscores[i].yl- ins[LEFT]) + fabs (qscores[i].yr - ins[RIGHT]);
303 programming_error ("can't find quant");
308 warning (_ ("no feasible beam position"));
309 me->set_property ("positions", ly_interval2scm (Interval (0,0)));
312 me->set_property ("positions",
313 ly_interval2scm (Drul_array<Real> (qscores[best_idx].yl,
314 qscores[best_idx].yr)));
317 && to_boolean (me->get_layout ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting"))))
319 qscores[best_idx].score_card_ += to_string ("i%d", best_idx);
322 me->set_property ("quant-score",
323 scm_makfrom0str (qscores[best_idx].score_card_.to_str0 ()));
327 return SCM_UNSPECIFIED;
331 Beam::score_stem_lengths (Link_array<Grob> const &stems,
332 Array<Stem_info> const &stem_infos,
333 Array<Real> const &base_stem_ys,
334 Array<Real> const &stem_xs,
339 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;
380 score[d] /= max (count[d], 1);
382 while (flip (&d) != DOWN);
384 return score[LEFT] + score[RIGHT];
388 Beam::score_slopes_dy (Real yl, Real yr,
389 Real dy_mus, Real dy_damp,
393 Beam_quant_parameters const*parameters)
399 DAMPING_DIRECTION_PENALTY is a very harsh measure, while for
400 complex beaming patterns, horizontal is often a good choice.
402 TODO: find a way to incorporate the complexity of the beam in this
405 if (fabs (dy / dx) > parameters->ROUND_TO_ZERO_SLOPE
406 && sign (dy_damp) != sign (dy))
408 dem += parameters->DAMPING_DIRECTION_PENALTY;
411 dem += parameters->MUSICAL_DIRECTION_FACTOR * max (0.0, (fabs (dy) - fabs (dy_mus)));
413 Real slope_penalty = parameters->IDEAL_SLOPE_FACTOR;
415 /* Xstaff beams tend to use extreme slopes to get short stems. We
416 put in a penalty here. */
420 /* Huh, why would a too steep beam be better than a too flat one ? */
421 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy), 1.5)
430 return x - floor (x);
434 TODO: The fixed value SECONDARY_BEAM_DEMERIT is probably flawed:
435 because for 32nd and 64th beams the forbidden quants are relatively
436 more important than stem lengths.
439 Beam::score_forbidden_quants (Real yl, Real yr,
442 Real thickness, Real beam_translation,
443 Drul_array<int> beam_counts,
444 Direction ldir, Direction rdir,
446 Beam_quant_parameters const*parameters)
449 Drul_array<Real> y (yl, yr);
450 Drul_array<Direction> dirs (ldir, rdir);
452 Real extra_demerit = parameters->SECONDARY_BEAM_DEMERIT / (max (beam_counts[LEFT], beam_counts[RIGHT]));
456 Real eps = parameters->BEAM_EPS;
460 for (int j = 1; j <= beam_counts[d]; j++)
462 Direction stem_dir = dirs[d];
465 The 2.2 factor is to provide a little leniency for
466 borderline cases. If we do 2.0, then the upper outer line
467 will be in the gap of the (2, sit) quant, leading to a
470 Real gap1 = y[d] - stem_dir * ((j - 1) * beam_translation + thickness / 2 - slt / 2.2);
471 Real gap2 = y[d] - stem_dir * (j * beam_translation - thickness / 2 + slt / 2.2);
474 gap.add_point (gap1);
475 gap.add_point (gap2);
477 for (Real k = -radius;
478 k <= radius + eps; k += 1.0)
479 if (gap.contains (k))
481 Real dist = min (fabs (gap[UP] - k), fabs (gap[DOWN] - k));
484 this parameter is tuned to grace-stem-length.ly
486 Real fixed_demerit = 0.4;
490 + (1 - fixed_demerit) * (dist / gap.length ()) * 2);
494 while ((flip (&d)) != LEFT);
496 if (max (beam_counts[LEFT], beam_counts[RIGHT]) >= 2)
499 Real sit = (thickness - slt) / 2;
501 Real hang = 1.0 - (thickness - slt) / 2;
506 if (beam_counts[d] >= 2
507 && fabs (y[d] - dirs[d] * beam_translation) < radius + inter)
509 if (dirs[d] == UP && dy <= eps
510 && fabs (my_modf (y[d]) - sit) < eps)
511 dem += extra_demerit;
513 if (dirs[d] == DOWN && dy >= eps
514 && fabs (my_modf (y[d]) - hang) < eps)
515 dem += extra_demerit;
518 if (beam_counts[d] >= 3
519 && fabs (y[d] - 2 * dirs[d] * beam_translation) < radius + inter)
521 if (dirs[d] == UP && dy <= eps
522 && fabs (my_modf (y[d]) - straddle) < eps)
523 dem += extra_demerit;
525 if (dirs[d] == DOWN && dy >= eps
526 && fabs (my_modf (y[d]) - straddle) < eps)
527 dem += extra_demerit;
530 while (flip (&d) != LEFT);