2 beam-quanting.cc -- implement Beam quanting functions
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2003 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;
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;
36 const Real ROUND_TO_ZERO_SLOPE = 0.05;
37 const int ROUND_TO_ZERO_POINTS = 4;
39 extern bool debug_beam_quanting_flag;
42 shrink_extra_weight (Real x, Real fac)
44 return fabs (x) * ((x < 0) ? fac : 1.0);
63 - Make all demerits customisable
65 - One sensible check per demerit (what's this --hwn)
67 - Add demerits for quants per se, as to forbid a specific quant
72 int best_quant_score_idx (Array<Quant_score> const & qscores)
76 for (int i = qscores.size (); i--;)
78 if (qscores[i].demerits < best)
80 best = qscores [i].demerits ;
88 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
90 Beam::quanting (SCM smob)
92 Grob *me = unsmob_grob (smob);
94 SCM s = me->get_grob_property ("positions");
95 Real yl = gh_scm2double (gh_car (s));
96 Real yr = gh_scm2double (gh_cdr (s));
100 Calculations are relative to a unit-scaled staff, i.e. the quants are
101 divided by the current staff_space.
104 Real ss = Staff_symbol_referencer::staff_space (me);
105 Real thickness = Beam::get_thickness (me) / ss ;
106 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
108 SCM sdy = me->get_grob_property ("least-squares-dy");
109 Real dy_mus = gh_number_p (sdy) ? gh_scm2double (sdy) : 0.0;
112 Real sit = (thickness - slt) / 2;
114 Real hang = 1.0 - (thickness - slt) / 2;
115 Real quants [] = {straddle, sit, inter, hang };
117 int num_quants = int (sizeof (quants)/sizeof (Real));
122 going to REGION_SIZE == 2, yields another 0.6 second with
126 (result indexes between 70 and 575) ? --hwn.
133 Do stem computations. These depend on YL and YR linearly, so we can
134 precompute for every stem 2 factors.
136 Link_array<Grob> stems=
137 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
138 Array<Stem_info> stem_infos;
139 Array<Real> base_lengths;
140 Array<Real> stem_xposns;
142 Drul_array<bool> dirs_found(0,0);
144 for (int a = 2; a--;)
145 common[a] = common_refpoint_of_array (stems, me, Axis(a));
147 Grob * fvs = first_visible_stem (me);
148 Grob *lvs = last_visible_stem (me);
149 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
150 Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
153 We store some info to quickly interpolate.
155 Sometimes my head is screwed on backwards. The stemlength are
156 AFFINE linear in YL and YR. If YL == YR == 0, then we might have
157 stem_y != 0.0, when we're cross staff.
160 for (int i= 0; i < stems.size(); i++)
164 Stem_info si (Stem::get_stem_info (s));
166 stem_infos.push (si);
167 dirs_found[stem_infos.top ().dir_] = true;
169 bool f = to_boolean (s->get_grob_property ("french-beaming"))
170 && s != lvs && s != fvs;
172 base_lengths.push (calc_stem_y (me, s, common, xl, xr,
173 Interval (0,0), f) / ss);
174 stem_xposns.push (s->relative_coordinate (common[X_AXIS], X_AXIS));
180 Grob *commony = fvs->common_refpoint (lvs, Y_AXIS);
181 xstaff = Align_interface::has_interface (commony);
184 Direction ldir = Direction (stem_infos[0].dir_);
185 Direction rdir = Direction (stem_infos.top ().dir_);
186 bool knee_b = dirs_found[LEFT] && dirs_found[RIGHT];
189 int region_size = REGION_SIZE;
191 Knees are harder, lets try some more possibilities for knees.
197 Asymetry ? should run to <= region_size ?
199 for (int i = -region_size ; i < region_size; i++)
200 for (int j = 0; j < num_quants; j++)
202 quantsl.push (i + quants[j] + int (yl));
203 quantsr.push (i + quants[j] + int (yr));
206 Array<Quant_score> qscores;
208 for (int l =0; l < quantsl.size (); l++)
209 for (int r =0; r < quantsr.size (); r++)
219 /* This is a longish function, but we don't separate this out into
220 neat modular separate subfunctions, as the subfunctions would be
221 called for many values of YL, YR. By precomputing various
222 parameters outside of the loop, we can save a lot of time. */
223 for (int i = qscores.size (); i--;)
225 Real d = score_slopes_dy (qscores[i].yl, qscores[i].yr,
229 qscores[i].demerits += d;
232 qscores[i].score_card_ += to_string ("S%.2f",d);
236 Real rad = Staff_symbol_referencer::staff_radius (me);
237 int beam_count = get_beam_count (me);
238 Real beam_translation = get_beam_translation (me) / ss;
240 Real reasonable_score = (knee_b) ? 200000 : 100;
241 for (int i = qscores.size (); i--;)
242 if (qscores[i].demerits < reasonable_score)
244 Real d = score_forbidden_quants (qscores[i].yl, qscores[i].yr,
245 rad, slt, thickness, beam_translation,
246 beam_count, ldir, rdir);
247 qscores[i].demerits += d;
250 qscores[i].score_card_ += to_string (" F %.2f", d);
254 for (int i = qscores.size (); i--;)
255 if (qscores[i].demerits < reasonable_score)
257 Real d=score_stem_lengths (stems, stem_infos,
258 base_lengths, stem_xposns,
261 qscores[i].yl, qscores[i].yr);
262 qscores[i].demerits += d;
265 qscores[i].score_card_ += to_string (" L %.2f", d);
269 int best_idx = best_quant_score_idx (qscores);
273 SCM inspect_quants = me->get_grob_property ("inspect-quants");
274 if (debug_beam_quanting_flag
275 && gh_pair_p (inspect_quants))
277 Drul_array<Real> ins = ly_scm2interval (inspect_quants);
282 for (; i < qscores.size(); i ++)
284 Real d =fabs (qscores[i].yl- ins[LEFT]) + fabs (qscores[i].yr - ins[RIGHT]);
292 programming_error ("Could not find quant.");
296 me->set_grob_property ("positions",
297 ly_interval2scm (Drul_array<Real> (qscores[best_idx].yl,
298 qscores[best_idx].yr)));
300 if (debug_beam_quanting_flag)
302 qscores[best_idx].score_card_ += to_string ("i%d", best_idx);
305 me->set_grob_property ("quant-score",
306 scm_makfrom0str (qscores[best_idx].score_card_.to_str0 ()));
310 return SCM_UNSPECIFIED;
314 Beam::score_stem_lengths (Link_array<Grob> const &stems,
315 Array<Stem_info> const &stem_infos,
316 Array<Real> const &base_stem_ys,
317 Array<Real> const &stem_xs,
322 Real limit_penalty = STEM_LENGTH_LIMIT_PENALTY;
323 Drul_array<Real> score (0, 0);
324 Drul_array<int> count (0, 0);
326 for (int i=0; i < stems.size (); i++)
329 if (Stem::invisible_b (s))
334 Real beam_y = dx ? yr *(x - xl)/dx + yl * ( xr - x)/dx : (yr + yl)/2;
335 Real current_y = beam_y + base_stem_ys[i];
336 Real length_pen = STEM_LENGTH_DEMERIT_FACTOR;
338 Stem_info info = stem_infos[i];
339 Direction d = info.dir_;
341 score[d] += limit_penalty * (0 >? (d * (info.shortest_y_ - current_y)));
343 Real ideal_diff = d * (current_y - info.ideal_y_);
344 Real ideal_score = shrink_extra_weight (ideal_diff, 1.5);
346 /* We introduce a power, to make the scoring strictly
347 convex. Otherwise a symmetric knee beam (up/down/up/down)
348 does not have an optimum in the middle. */
350 ideal_score = pow (ideal_score, 1.1);
352 score[d] += length_pen * ideal_score;
360 score[d] /= (count[d] >? 1);
362 while (flip (&d) != DOWN);
364 return score[LEFT]+score[RIGHT];
368 Beam::score_slopes_dy (Real yl, Real yr,
369 Real dy_mus, Real dy_damp,
377 DAMPING_DIRECTION_PENALTY is a very harsh measure, while for
378 complex beaming patterns, horizontal is often a good choice.
380 TODO: find a way to incorporate the complexity of the beam in this
383 if (fabs (dy/dx) > ROUND_TO_ZERO_SLOPE
384 && sign (dy_damp) != sign (dy))
386 dem += DAMPING_DIRECTION_PENALTY;
389 dem += MUSICAL_DIRECTION_FACTOR * (0 >? (fabs (dy) - fabs (dy_mus)));
392 Real slope_penalty = IDEAL_SLOPE_FACTOR;
394 /* Xstaff beams tend to use extreme slopes to get short stems. We
395 put in a penalty here. */
399 /* Huh, why would a too steep beam be better than a too flat one ? */
400 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy), 1.5)
404 almost zero slopes look like errors in horizontal beams.
407 && fabs (dy / dx) < ROUND_TO_ZERO_SLOPE)
408 dem += ROUND_TO_ZERO_POINTS;
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,
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_count;
440 Inside the staff, inter quants are forbidden.
446 if (fabs (y[d]) <= (radius + 0.5) && fabs (my_modf (y[d]) - 0.5) < 1e-3)
447 dem += INTER_QUANT_PENALTY;
449 while ((flip (&d))!= LEFT);
452 for (int j = 1; j <= beam_count; j++)
457 see if the outer staffline falls in a beam-gap
459 This test is too weak; we should really check all lines.
461 Direction stem_dir = dirs[d];
462 Real gap1 = y[d] - stem_dir * ((j-1) * beam_translation + thickness / 2 - slt/2 );
463 Real gap2 = y[d] - stem_dir * (j * beam_translation - thickness / 2 + slt/2);
466 gap.add_point (gap1);
467 gap.add_point (gap2);
469 if (gap.contains (radius))
470 dem += extra_demerit;
472 while ((flip (&d))!= LEFT);
477 // todo: use beam_count of outer stems.
481 Real sit = (thickness - slt) / 2;
483 Real hang = 1.0 - (thickness - slt) / 2;
487 // hmm, without Interval/Drul_array, you get ~ 4x same code...
488 if (fabs (y[LEFT] - dirs[LEFT] * beam_translation) < radius + inter)
490 if (dirs[LEFT] == UP && dy <= eps
491 && fabs (my_modf (y[LEFT]) - sit) < eps)
492 dem += extra_demerit;
494 if (dirs[LEFT] == DOWN && dy >= eps
495 && fabs (my_modf (y[LEFT]) - hang) < eps)
496 dem += extra_demerit;
499 if (fabs (y[RIGHT] - dirs[RIGHT] * beam_translation) < radius + inter)
501 if (dirs[RIGHT] == UP && dy >= eps
502 && fabs (my_modf (y[RIGHT]) - sit) < eps)
503 dem += extra_demerit;
505 if (dirs[RIGHT] == DOWN && dy <= eps
506 && fabs (my_modf (y[RIGHT]) - hang) < eps)
507 dem += extra_demerit;
512 if (fabs (y[LEFT] - 2 * dirs[LEFT] * beam_translation) < radius + inter)
514 if (dirs[LEFT] == UP && dy <= eps
515 && fabs (my_modf (y[LEFT]) - straddle) < eps)
516 dem += extra_demerit;
518 if (dirs[LEFT] == DOWN && dy >= eps
519 && fabs (my_modf (y[LEFT]) - straddle) < eps)
520 dem += extra_demerit;
523 if (fabs (y[RIGHT] - 2 * dirs[RIGHT] * beam_translation) < radius + inter)
525 if (dirs[RIGHT] == UP && dy >= eps
526 && fabs (my_modf (y[RIGHT]) - straddle) < eps)
527 dem += extra_demerit;
529 if (dirs[RIGHT] == DOWN && dy <= eps
530 && fabs (my_modf (y[RIGHT]) - straddle) < eps)
531 dem += extra_demerit;