2 beam-quanting.cc -- implement Beam quanting functions
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
14 #include "staff-symbol-referencer.hh"
17 #include "output-def.hh"
18 #include "group-interface.hh"
19 #include "align-interface.hh"
21 const int INTER_QUANT_PENALTY = 1000;
22 const Real SECONDARY_BEAM_DEMERIT = 10.0;
23 const int STEM_LENGTH_DEMERIT_FACTOR = 5;
26 threshold to combat rounding errors.
28 const Real BEAM_EPS = 1e-3;
30 // possibly ridiculous, but too short stems just won't do
31 const int STEM_LENGTH_LIMIT_PENALTY = 5000;
32 const int DAMPING_DIRECTION_PENALTY = 800;
33 const int MUSICAL_DIRECTION_FACTOR = 400;
34 const int IDEAL_SLOPE_FACTOR = 10;
35 const Real ROUND_TO_ZERO_SLOPE = 0.02;
38 shrink_extra_weight (Real x, Real fac)
40 return fabs (x) * ((x < 0) ? fac : 1.0);
59 - Make all demerits customisable
61 - One sensible check per demerit (what's this --hwn)
63 - Add demerits for quants per se, as to forbid a specific quant
69 best_quant_score_idx (Array<Quant_score> const & qscores)
73 for (int i = qscores.size (); i--;)
75 if (qscores[i].demerits < best)
77 best = qscores [i].demerits ;
84 programming_error ("Huh? No best beam quant score?");
92 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
94 Beam::quanting (SCM smob)
96 Grob *me = unsmob_grob (smob);
98 SCM s = me->get_property ("positions");
99 Real yl = scm_to_double (scm_car (s));
100 Real yr = scm_to_double (scm_cdr (s));
104 Calculations are relative to a unit-scaled staff, i.e. the quants are
105 divided by the current staff_space.
108 Real ss = Staff_symbol_referencer::staff_space (me);
109 Real thickness = Beam::get_thickness (me) / ss ;
110 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
112 Real dy_mus = robust_scm2double (me->get_property ("least-squares-dy"), 0);
114 Real sit = (thickness - slt) / 2;
116 Real hang = 1.0 - (thickness - slt) / 2;
117 Real quants [] = {straddle, sit, inter, hang };
121 int num_quants = int (sizeof (quants)/sizeof (Real));
126 going to REGION_SIZE == 2, yields another 0.6 second with
130 (result indexes between 70 and 575) ? --hwn.
137 Do stem computations. These depend on YL and YR linearly, so we can
138 precompute for every stem 2 factors.
140 Link_array<Grob> stems =
141 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
142 Array<Stem_info> stem_infos;
143 Array<Real> base_lengths;
144 Array<Real> stem_xposns;
146 Drul_array<bool> dirs_found (0,0);
148 for (int a = 2; a--;)
149 common[a] = common_refpoint_of_array (stems, me, Axis (a));
151 Grob * fvs = first_visible_stem (me);
152 Grob *lvs = last_visible_stem (me);
153 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
154 Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
157 We store some info to quickly interpolate.
159 Sometimes my head is screwed on backwards. The stemlength are
160 AFFINE linear in YL and YR. If YL == YR == 0, then we might have
161 stem_y != 0.0, when we're cross staff.
164 for (int i = 0; i < stems.size (); i++)
168 Stem_info si (Stem::get_stem_info (s));
170 stem_infos.push (si);
171 dirs_found[stem_infos.top ().dir_] = true;
173 bool f = to_boolean (s->get_property ("french-beaming"))
174 && s != lvs && s != fvs;
176 base_lengths.push (calc_stem_y (me, s, common, xl, xr,
177 Interval (0,0), f) / ss);
178 stem_xposns.push (s->relative_coordinate (common[X_AXIS], X_AXIS));
184 Grob *commony = fvs->common_refpoint (lvs, Y_AXIS);
185 xstaff = Align_interface::has_interface (commony);
188 Direction ldir = Direction (stem_infos[0].dir_);
189 Direction rdir = Direction (stem_infos.top ().dir_);
190 bool is_knee = dirs_found[LEFT] && dirs_found[RIGHT];
193 int region_size = REGION_SIZE;
195 Knees are harder, lets try some more possibilities for knees.
201 Asymetry ? should run to <= region_size ?
203 for (int i = -region_size ; i < region_size; i++)
204 for (int j = 0; j < num_quants; j++)
206 quantsl.push (i + quants[j] + int (yl));
207 quantsr.push (i + quants[j] + int (yr));
210 Array<Quant_score> qscores;
212 for (int l = 0; l < quantsl.size (); l++)
213 for (int r = 0; r < quantsr.size (); r++)
223 /* This is a longish function, but we don't separate this out into
224 neat modular separate subfunctions, as the subfunctions would be
225 called for many values of YL, YR. By precomputing various
226 parameters outside of the loop, we can save a lot of time. */
227 for (int i = qscores.size (); i--;)
229 Real d = score_slopes_dy (qscores[i].yl, qscores[i].yr,
233 qscores[i].demerits += d;
236 qscores[i].score_card_ += to_string ("S%.2f",d);
240 Real rad = Staff_symbol_referencer::staff_radius (me);
241 Drul_array<int> edge_beam_counts
242 (Stem::beam_multiplicity (stems[0]).length () + 1,
243 Stem::beam_multiplicity (stems.top ()).length () + 1);
245 Real beam_translation = get_beam_translation (me) / ss;
247 Real reasonable_score = (is_knee) ? 200000 : 100;
248 for (int i = qscores.size (); i--;)
249 if (qscores[i].demerits < reasonable_score)
251 Real d = score_forbidden_quants (qscores[i].yl, qscores[i].yr,
252 rad, slt, thickness, beam_translation,
253 edge_beam_counts, ldir, rdir);
254 qscores[i].demerits += d;
257 qscores[i].score_card_ += to_string (" F %.2f", d);
261 for (int i = qscores.size (); i--;)
262 if (qscores[i].demerits < reasonable_score)
264 Real d = score_stem_lengths (stems, stem_infos,
265 base_lengths, stem_xposns,
268 qscores[i].yl, qscores[i].yr);
269 qscores[i].demerits += d;
272 qscores[i].score_card_ += to_string (" L %.2f", d);
276 int best_idx = best_quant_score_idx (qscores);
279 SCM inspect_quants = me->get_property ("inspect-quants");
280 if (to_boolean (me->get_layout ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting")))
281 && scm_is_pair (inspect_quants))
283 Drul_array<Real> ins = ly_scm2interval (inspect_quants);
288 for (; i < qscores.size (); i ++)
290 Real d = fabs (qscores[i].yl- ins[LEFT]) + fabs (qscores[i].yr - ins[RIGHT]);
298 programming_error ("Could not find quant.");
302 me->set_property ("positions",
303 ly_interval2scm (Drul_array<Real> (qscores[best_idx].yl,
304 qscores[best_idx].yr)));
306 if (to_boolean (me->get_layout ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting"))))
308 qscores[best_idx].score_card_ += to_string ("i%d", best_idx);
311 me->set_property ("quant-score",
312 scm_makfrom0str (qscores[best_idx].score_card_.to_str0 ()));
316 return SCM_UNSPECIFIED;
320 Beam::score_stem_lengths (Link_array<Grob> const &stems,
321 Array<Stem_info> const &stem_infos,
322 Array<Real> const &base_stem_ys,
323 Array<Real> const &stem_xs,
328 Real limit_penalty = STEM_LENGTH_LIMIT_PENALTY;
329 Drul_array<Real> score (0, 0);
330 Drul_array<int> count (0, 0);
332 for (int i = 0; i < stems.size (); i++)
335 if (Stem::is_invisible (s))
340 Real beam_y = dx ? yr *(x - xl)/dx + yl * ( xr - x)/dx : (yr + yl)/2;
341 Real current_y = beam_y + base_stem_ys[i];
342 Real length_pen = STEM_LENGTH_DEMERIT_FACTOR;
344 Stem_info info = stem_infos[i];
345 Direction d = info.dir_;
347 score[d] += limit_penalty * (0 >? (d * (info.shortest_y_ - current_y)));
349 Real ideal_diff = d * (current_y - info.ideal_y_);
350 Real ideal_score = shrink_extra_weight (ideal_diff, 1.5);
352 /* We introduce a power, to make the scoring strictly
353 convex. Otherwise a symmetric knee beam (up/down/up/down)
354 does not have an optimum in the middle. */
356 ideal_score = pow (ideal_score, 1.1);
358 score[d] += length_pen * ideal_score;
366 score[d] /= (count[d] >? 1);
368 while (flip (&d) != DOWN);
370 return score[LEFT]+score[RIGHT];
374 Beam::score_slopes_dy (Real yl, Real yr,
375 Real dy_mus, Real dy_damp,
383 DAMPING_DIRECTION_PENALTY is a very harsh measure, while for
384 complex beaming patterns, horizontal is often a good choice.
386 TODO: find a way to incorporate the complexity of the beam in this
389 if (fabs (dy/dx) > ROUND_TO_ZERO_SLOPE
390 && sign (dy_damp) != sign (dy))
392 dem += DAMPING_DIRECTION_PENALTY;
395 dem += MUSICAL_DIRECTION_FACTOR * (0 >? (fabs (dy) - fabs (dy_mus)));
398 Real slope_penalty = IDEAL_SLOPE_FACTOR;
400 /* Xstaff beams tend to use extreme slopes to get short stems. We
401 put in a penalty here. */
405 /* Huh, why would a too steep beam be better than a too flat one ? */
406 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy), 1.5)
416 return x - floor (x);
421 TODO: The fixed value SECONDARY_BEAM_DEMERIT is probably flawed:
422 because for 32nd and 64th beams the forbidden quants are relatively
423 more important than stem lengths.
426 Beam::score_forbidden_quants (Real yl, Real yr,
429 Real thickness, Real beam_translation,
430 Drul_array<int> beam_counts,
431 Direction ldir, Direction rdir)
434 Drul_array<Real> y (yl,yr);
435 Drul_array<Direction> dirs (ldir,rdir);
437 Real extra_demerit = SECONDARY_BEAM_DEMERIT / (beam_counts[LEFT] >? beam_counts[RIGHT]);
445 for (int j = 1; j <= beam_counts[d]; j++)
447 Direction stem_dir = dirs[d];
450 The 2.2 factor is to provide a little leniency for
451 borderline cases. If we do 2.0, then the upper outer line
452 will be in the gap of the (2,sit) quant, leading to a
455 Real gap1 = y[d] - stem_dir * ((j-1) * beam_translation + thickness / 2 - slt/2.2 );
456 Real gap2 = y[d] - stem_dir * (j * beam_translation - thickness / 2 + slt/2.2);
459 gap.add_point (gap1);
460 gap.add_point (gap2);
462 for (Real k = - radius ;
463 k <= radius + BEAM_EPS; k += 1.0)
464 if (gap.contains (k))
466 Real dist = fabs (gap[UP]-k) <? fabs (gap[DOWN] - k);
469 this parameter is tuned to grace-stem-length.ly
471 Real fixed_demerit = 0.4;
475 (1-fixed_demerit) * (dist / gap.length())* 2);
479 while ((flip (&d))!= LEFT);
482 if ((beam_counts[LEFT] >? beam_counts[RIGHT]) >= 2)
485 Real sit = (thickness - slt) / 2;
487 Real hang = 1.0 - (thickness - slt) / 2;
493 if (beam_counts[d] >= 2
494 && fabs (y[d] - dirs[d] * beam_translation) < radius + inter)
496 if (dirs[d] == UP && dy <= BEAM_EPS
497 && fabs (my_modf (y[d]) - sit) < BEAM_EPS)
498 dem += extra_demerit;
500 if (dirs[d] == DOWN && dy >= BEAM_EPS
501 && fabs (my_modf (y[d]) - hang) < BEAM_EPS)
502 dem += extra_demerit;
505 if (beam_counts[d] >= 3
506 && fabs (y[d] - 2 * dirs[d] * beam_translation) < radius + inter)
508 if (dirs[d] == UP && dy <= BEAM_EPS
509 && fabs (my_modf (y[d]) - straddle) < BEAM_EPS)
510 dem += extra_demerit;
512 if (dirs[d] == DOWN && dy >= BEAM_EPS
513 && fabs (my_modf (y[d]) - straddle) < BEAM_EPS)
514 dem += extra_demerit;
517 while (flip (&d) != LEFT);