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>
16 #include "align-interface.hh"
17 #include "international.hh"
18 #include "output-def.hh"
19 #include "pointer-group-interface.hh"
20 #include "staff-symbol-referencer.hh"
26 get_detail (SCM alist, SCM sym, Real def)
28 SCM entry = scm_assq (sym, alist);
30 if (scm_is_pair (entry))
31 return robust_scm2double (scm_cdr (entry), def);
36 Beam_quant_parameters::fill (Grob *him)
38 SCM details = him->get_property ("details");
40 INTER_QUANT_PENALTY = get_detail (details, ly_symbol2scm ("inter-quant-penalty"), 1000.0);
41 SECONDARY_BEAM_DEMERIT = get_detail (details, ly_symbol2scm ("secondary-beam-demerit"), 10.0);
42 STEM_LENGTH_DEMERIT_FACTOR = get_detail (details, ly_symbol2scm ("stem-length-demerit-factor"), 5);
43 REGION_SIZE = get_detail (details, ly_symbol2scm ("region-size"), 2);
44 BEAM_EPS = get_detail (details, ly_symbol2scm ("beam-eps"), 1e-3);
46 // possibly ridiculous, but too short stems just won't do
47 STEM_LENGTH_LIMIT_PENALTY = get_detail (details, ly_symbol2scm ("stem-length-limit-penalty"), 5000);
48 DAMPING_DIRECTION_PENALTY = get_detail (details, ly_symbol2scm ("damping-direction-penalty"), 800);
49 MUSICAL_DIRECTION_FACTOR = get_detail (details, ly_symbol2scm ("musical-direction-factor"), 400);
50 IDEAL_SLOPE_FACTOR = get_detail (details, ly_symbol2scm ("ideal-slope-factor"), 10);
51 ROUND_TO_ZERO_SLOPE = get_detail (details, ly_symbol2scm ("round-to-zero-slope"), 0.02);
55 shrink_extra_weight (Real x, Real fac)
57 return fabs (x) * ((x < 0) ? fac : 1.0);
66 #if DEBUG_BEAM_SCORING
74 - Make all demerits customisable
76 - One sensible check per demerit (what's this --hwn)
78 - Add demerits for quants per se, as to forbid a specific quant
83 best_quant_score_idx (vector<Quant_score> const &qscores)
87 for (vsize i = qscores.size (); i--;)
89 if (qscores[i].demerits < best)
91 best = qscores [i].demerits;
99 MAKE_SCHEME_CALLBACK (Beam, quanting, 2);
101 Beam::quanting (SCM smob, SCM posns)
103 Grob *me = unsmob_grob (smob);
105 Beam_quant_parameters parameters;
106 parameters.fill (me);
108 Real yl = scm_to_double (scm_car (posns));
109 Real yr = scm_to_double (scm_cdr (posns));
112 Calculations are relative to a unit-scaled staff, i.e. the quants are
113 divided by the current staff_space.
116 Real ss = Staff_symbol_referencer::staff_space (me);
117 Real thickness = Beam::get_thickness (me) / ss;
118 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
120 Real dy_mus = robust_scm2double (me->get_property ("least-squares-dy"), 0);
122 Real sit = (thickness - slt) / 2;
124 Real hang = 1.0 - (thickness - slt) / 2;
125 Real quants [] = {straddle, sit, inter, hang };
127 int num_quants = int (sizeof (quants) / sizeof (Real));
128 vector<Real> quantsl;
129 vector<Real> quantsr;
132 going to REGION_SIZE == 2, yields another 0.6 second with
136 (result indexes between 70 and 575) ? --hwn.
141 Do stem computations. These depend on YL and YR linearly, so we can
142 precompute for every stem 2 factors.
145 = extract_grob_array (me, "stems");
146 vector<Stem_info> stem_infos;
147 vector<Real> base_lengths;
148 vector<Real> stem_xposns;
150 Drul_array<bool> dirs_found (0, 0);
152 for (int a = 2; a--;)
153 common[a] = common_refpoint_of_array (stems, me, Axis (a));
155 Grob *fvs = first_visible_stem (me);
156 Grob *lvs = last_visible_stem (me);
157 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
158 Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
161 We store some info to quickly interpolate.
163 Sometimes my head is screwed on backwards. The stemlength are
164 AFFINE linear in YL and YR. If YL == YR == 0, then we might have
165 stem_y != 0.0, when we're cross staff.
168 for (vsize i = 0; i < stems.size (); i++)
172 Stem_info si (Stem::get_stem_info (s));
174 stem_infos.push_back (si);
175 dirs_found[stem_infos.back ().dir_] = true;
177 bool f = to_boolean (s->get_property ("french-beaming"))
178 && s != lvs && s != fvs;
180 base_lengths.push_back (calc_stem_y (me, s, common, xl, xr,
181 Interval (0, 0), f) / ss);
182 stem_xposns.push_back (s->relative_coordinate (common[X_AXIS], X_AXIS));
188 Grob *commony = fvs->common_refpoint (lvs, Y_AXIS);
189 xstaff = Align_interface::has_interface (commony);
192 Direction ldir = Direction (stem_infos[0].dir_);
193 Direction rdir = Direction (stem_infos.back ().dir_);
194 bool is_knee = dirs_found[LEFT] && dirs_found[RIGHT];
196 int region_size = (int) parameters.REGION_SIZE;
199 Knees are harder, lets try some more possibilities for knees.
205 Asymetry ? should run to <= region_size ?
207 for (int i = -region_size; i < region_size; i++)
208 for (int j = 0; j < num_quants; j++)
210 quantsl.push_back (i + quants[j] + int (yl));
211 quantsr.push_back (i + quants[j] + int (yr));
214 vector<Quant_score> qscores;
216 for (vsize l = 0; l < quantsl.size (); l++)
217 for (vsize r = 0; r < quantsr.size (); r++)
224 qscores.push_back (qs);
227 /* This is a longish function, but we don't separate this out into
228 neat modular separate subfunctions, as the subfunctions would be
229 called for many values of YL, YR. By precomputing various
230 parameters outside of the loop, we can save a lot of time. */
231 for (vsize i = qscores.size (); i--;)
233 Real d = score_slopes_dy (qscores[i].yl, qscores[i].yr,
236 xstaff, ¶meters);
237 qscores[i].demerits += d;
239 #if DEBUG_BEAM_SCORING
240 qscores[i].score_card_ += to_string ("S%.2f", d);
244 Real rad = Staff_symbol_referencer::staff_radius (me);
245 Drul_array<int> edge_beam_counts
246 (Stem::beam_multiplicity (stems[0]).length () + 1,
247 Stem::beam_multiplicity (stems.back ()).length () + 1);
249 Real beam_translation = get_beam_translation (me) / ss;
251 Real reasonable_score = (is_knee) ? 200000 : 100;
252 for (vsize i = qscores.size (); i--;)
253 if (qscores[i].demerits < reasonable_score)
255 Real d = score_forbidden_quants (qscores[i].yl, qscores[i].yr,
256 rad, slt, thickness, beam_translation,
257 edge_beam_counts, ldir, rdir, ¶meters);
258 qscores[i].demerits += d;
260 #if DEBUG_BEAM_SCORING
261 qscores[i].score_card_ += to_string (" F %.2f", d);
265 for (vsize i = qscores.size (); i--;)
266 if (qscores[i].demerits < reasonable_score)
268 Real d = score_stem_lengths (stems, stem_infos,
269 base_lengths, stem_xposns,
272 qscores[i].yl, qscores[i].yr, ¶meters);
273 qscores[i].demerits += d;
275 #if DEBUG_BEAM_SCORING
276 qscores[i].score_card_ += to_string (" L %.2f", d);
280 int best_idx = best_quant_score_idx (qscores);
282 #if DEBUG_BEAM_SCORING
283 SCM inspect_quants = me->get_property ("inspect-quants");
284 if (to_boolean (me->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-scoring")))
285 && scm_is_pair (inspect_quants))
287 Drul_array<Real> ins = ly_scm2interval (inspect_quants);
290 for (vsize i = 0; 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);
316 #if DEBUG_BEAM_SCORING
318 && to_boolean (me->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-scoring"))))
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 (vector<Grob*> const &stems,
333 vector<Stem_info> const &stem_infos,
334 vector<Real> const &base_stem_ys,
335 vector<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 (vsize 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);