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>
19 #include "staff-symbol-referencer.hh"
22 #include "paper-def.hh"
23 #include "group-interface.hh"
24 #include "align-interface.hh"
26 const int INTER_QUANT_PENALTY = 1000;
27 const Real SECONDARY_BEAM_DEMERIT = 10.0;
28 const int STEM_LENGTH_DEMERIT_FACTOR = 5;
31 threshold to combat rounding errors.
34 const Real BEAM_EPS = 1e-3;
36 // possibly ridiculous, but too short stems just won't do
37 const int STEM_LENGTH_LIMIT_PENALTY = 5000;
38 const int DAMPING_DIRECTION_PENALTY = 800;
39 const int MUSICAL_DIRECTION_FACTOR = 400;
40 const int IDEAL_SLOPE_FACTOR = 10;
42 const Real ROUND_TO_ZERO_SLOPE = 0.05;
43 const int ROUND_TO_ZERO_POINTS = 4;
45 extern bool debug_beam_quanting_flag;
48 shrink_extra_weight (Real x, Real fac)
50 return fabs (x) * ((x < 0) ? fac : 1.0);
69 - Make all demerits customisable
71 - One sensible check per demerit (what's this --hwn)
73 - Add demerits for quants per se, as to forbid a specific quant
78 int best_quant_score_idx (Array<Quant_score> const & qscores)
82 for (int i = qscores.size (); i--;)
84 if (qscores[i].demerits < best)
86 best = qscores [i].demerits ;
94 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
96 Beam::quanting (SCM smob)
98 Grob *me = unsmob_grob (smob);
100 SCM s = me->get_grob_property ("positions");
101 Real yl = gh_scm2double (gh_car (s));
102 Real yr = gh_scm2double (gh_cdr (s));
106 Calculations are relative to a unit-scaled staff, i.e. the quants are
107 divided by the current staff_space.
110 Real ss = Staff_symbol_referencer::staff_space (me);
111 Real thickness = Beam::get_thickness (me) / ss ;
112 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
114 SCM sdy = me->get_grob_property ("least-squares-dy");
115 Real dy_mus = gh_number_p (sdy) ? gh_scm2double (sdy) : 0.0;
118 Real sit = (thickness - slt) / 2;
120 Real hang = 1.0 - (thickness - slt) / 2;
121 Real quants [] = {straddle, sit, inter, hang };
123 int num_quants = int (sizeof (quants)/sizeof (Real));
128 going to REGION_SIZE == 2, yields another 0.6 second with
132 (result indexes between 70 and 575) ? --hwn.
139 Do stem computations. These depend on YL and YR linearly, so we can
140 precompute for every stem 2 factors.
142 Link_array<Grob> stems=
143 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
144 Array<Stem_info> stem_infos;
145 Array<Real> base_lengths;
146 Array<Real> stem_xposns;
148 Drul_array<bool> dirs_found(0,0);
150 for (int a = 2; a--;)
151 common[a] = common_refpoint_of_array (stems, me, Axis(a));
153 Grob * fvs = first_visible_stem (me);
154 Grob *lvs = last_visible_stem (me);
155 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
156 Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
159 We store some info to quickly interpolate.
161 Sometimes my head is screwed on backwards. The stemlength are
162 AFFINE linear in YL and YR. If YL == YR == 0, then we might have
163 stem_y != 0.0, when we're cross staff.
166 for (int i= 0; i < stems.size(); i++)
170 Stem_info si (Stem::get_stem_info (s));
172 stem_infos.push (si);
173 dirs_found[stem_infos.top ().dir_] = true;
175 bool f = to_boolean (s->get_grob_property ("french-beaming"))
176 && s != lvs && s != fvs;
178 base_lengths.push (calc_stem_y (me, s, common, xl, xr,
179 Interval (0,0), f) / ss);
180 stem_xposns.push (s->relative_coordinate (common[X_AXIS], X_AXIS));
186 Grob *commony = fvs->common_refpoint (lvs, Y_AXIS);
187 xstaff = Align_interface::has_interface (commony);
190 Direction ldir = Direction (stem_infos[0].dir_);
191 Direction rdir = Direction (stem_infos.top ().dir_);
192 bool is_knee = dirs_found[LEFT] && dirs_found[RIGHT];
195 int region_size = REGION_SIZE;
197 Knees are harder, lets try some more possibilities for knees.
203 Asymetry ? should run to <= region_size ?
205 for (int i = -region_size ; i < region_size; i++)
206 for (int j = 0; j < num_quants; j++)
208 quantsl.push (i + quants[j] + int (yl));
209 quantsr.push (i + quants[j] + int (yr));
212 Array<Quant_score> qscores;
214 for (int l =0; l < quantsl.size (); l++)
215 for (int r =0; r < quantsr.size (); r++)
225 /* This is a longish function, but we don't separate this out into
226 neat modular separate subfunctions, as the subfunctions would be
227 called for many values of YL, YR. By precomputing various
228 parameters outside of the loop, we can save a lot of time. */
229 for (int i = qscores.size (); i--;)
231 Real d = score_slopes_dy (qscores[i].yl, qscores[i].yr,
235 qscores[i].demerits += d;
238 qscores[i].score_card_ += to_string ("S%.2f",d);
242 Real rad = Staff_symbol_referencer::staff_radius (me);
243 int beam_count = get_beam_count (me);
244 Real beam_translation = get_beam_translation (me) / ss;
246 Real reasonable_score = (is_knee) ? 200000 : 100;
247 for (int i = qscores.size (); i--;)
248 if (qscores[i].demerits < reasonable_score)
250 Real d = score_forbidden_quants (qscores[i].yl, qscores[i].yr,
251 rad, slt, thickness, beam_translation,
252 beam_count, ldir, rdir);
253 qscores[i].demerits += d;
256 qscores[i].score_card_ += to_string (" F %.2f", d);
260 for (int i = qscores.size (); i--;)
261 if (qscores[i].demerits < reasonable_score)
263 Real d=score_stem_lengths (stems, stem_infos,
264 base_lengths, stem_xposns,
267 qscores[i].yl, qscores[i].yr);
268 qscores[i].demerits += d;
271 qscores[i].score_card_ += to_string (" L %.2f", d);
275 int best_idx = best_quant_score_idx (qscores);
279 SCM inspect_quants = me->get_grob_property ("inspect-quants");
280 if (debug_beam_quanting_flag
281 && gh_pair_p (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_grob_property ("positions",
303 ly_interval2scm (Drul_array<Real> (qscores[best_idx].yl,
304 qscores[best_idx].yr)));
306 if (debug_beam_quanting_flag)
308 qscores[best_idx].score_card_ += to_string ("i%d", best_idx);
311 me->set_grob_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)
410 almost zero slopes look like errors in horizontal beams.
413 && fabs (dy / dx) < ROUND_TO_ZERO_SLOPE)
414 dem += ROUND_TO_ZERO_POINTS;
422 return x - floor (x);
427 TODO: The fixed value SECONDARY_BEAM_DEMERIT is probably flawed:
428 because for 32nd and 64th beams the forbidden quants are relatively
429 more important than stem lengths.
432 Beam::score_forbidden_quants (Real yl, Real yr,
435 Real thickness, Real beam_translation,
437 Direction ldir, Direction rdir)
440 Drul_array<Real> y(yl,yr);
441 Drul_array<Direction> dirs(ldir,rdir);
443 Real extra_demerit = SECONDARY_BEAM_DEMERIT / beam_count;
446 Inside the staff, inter quants are forbidden.
452 if (fabs (y[d]) <= (radius + 0.5) && fabs (my_modf (y[d]) - 0.5) < BEAM_EPS)
453 dem += INTER_QUANT_PENALTY;
455 while ((flip (&d))!= LEFT);
458 for (int j = 1; j <= beam_count; j++)
463 see if the outer staffline falls in a beam-gap
465 This test is too weak; we should really check all lines.
467 Direction stem_dir = dirs[d];
468 Real gap1 = y[d] - stem_dir * ((j-1) * beam_translation + thickness / 2 - slt/2 );
469 Real gap2 = y[d] - stem_dir * (j * beam_translation - thickness / 2 + slt/2);
472 gap.add_point (gap1);
473 gap.add_point (gap2);
475 for (Real k = - radius ;
476 k <= radius + BEAM_EPS; k += 1.0)
477 if (gap.contains (k))
478 dem += extra_demerit;
480 while ((flip (&d))!= LEFT);
485 // todo: use beam_count of outer stems.
489 Real sit = (thickness - slt) / 2;
491 Real hang = 1.0 - (thickness - slt) / 2;
493 // hmm, without Interval/Drul_array, you get ~ 4x same code...
494 if (fabs (y[LEFT] - dirs[LEFT] * beam_translation) < radius + inter)
496 if (dirs[LEFT] == UP && dy <= BEAM_EPS
497 && fabs (my_modf (y[LEFT]) - sit) < BEAM_EPS)
498 dem += extra_demerit;
500 if (dirs[LEFT] == DOWN && dy >= BEAM_EPS
501 && fabs (my_modf (y[LEFT]) - hang) < BEAM_EPS)
502 dem += extra_demerit;
505 if (fabs (y[RIGHT] - dirs[RIGHT] * beam_translation) < radius + inter)
507 if (dirs[RIGHT] == UP && dy >= BEAM_EPS
508 && fabs (my_modf (y[RIGHT]) - sit) < BEAM_EPS)
509 dem += extra_demerit;
511 if (dirs[RIGHT] == DOWN && dy <= BEAM_EPS
512 && fabs (my_modf (y[RIGHT]) - hang) < BEAM_EPS)
513 dem += extra_demerit;
518 if (fabs (y[LEFT] - 2 * dirs[LEFT] * beam_translation) < radius + inter)
520 if (dirs[LEFT] == UP && dy <= BEAM_EPS
521 && fabs (my_modf (y[LEFT]) - straddle) < BEAM_EPS)
522 dem += extra_demerit;
524 if (dirs[LEFT] == DOWN && dy >= BEAM_EPS
525 && fabs (my_modf (y[LEFT]) - straddle) < BEAM_EPS)
526 dem += extra_demerit;
529 if (fabs (y[RIGHT] - 2 * dirs[RIGHT] * beam_translation) < radius + inter)
531 if (dirs[RIGHT] == UP && dy >= BEAM_EPS
532 && fabs (my_modf (y[RIGHT]) - straddle) < BEAM_EPS)
533 dem += extra_demerit;
535 if (dirs[RIGHT] == DOWN && dy <= BEAM_EPS
536 && fabs (my_modf (y[RIGHT]) - straddle) < BEAM_EPS)
537 dem += extra_demerit;