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 Real il = gh_scm2double (gh_car (inspect_quants));
278 Real ir = gh_scm2double (gh_cdr (inspect_quants));
283 for (; i < qscores.size(); i ++)
285 Real d =fabs (qscores[i].yl-il) + fabs (qscores[i].yr - ir);
293 programming_error ("Could not find quant.");
297 me->set_grob_property ("positions",
298 gh_cons (gh_double2scm (qscores[best_idx].yl),
299 gh_double2scm (qscores[best_idx].yr)));
301 if (debug_beam_quanting_flag)
303 qscores[best_idx].score_card_ += to_string ("i%d", best_idx);
306 me->set_grob_property ("quant-score",
307 scm_makfrom0str (qscores[best_idx].score_card_.to_str0 ()));
311 return SCM_UNSPECIFIED;
315 Beam::score_stem_lengths (Link_array<Grob> const &stems,
316 Array<Stem_info> const &stem_infos,
317 Array<Real> const &base_stem_ys,
318 Array<Real> const &stem_xs,
323 Real limit_penalty = STEM_LENGTH_LIMIT_PENALTY;
324 Drul_array<Real> score (0, 0);
325 Drul_array<int> count (0, 0);
327 for (int i=0; i < stems.size (); i++)
330 if (Stem::invisible_b (s))
335 Real beam_y = dx ? yr *(x - xl)/dx + yl * ( xr - x)/dx : (yr + yl)/2;
336 Real current_y = beam_y + base_stem_ys[i];
337 Real length_pen = STEM_LENGTH_DEMERIT_FACTOR;
339 Stem_info info = stem_infos[i];
340 Direction d = info.dir_;
342 score[d] += limit_penalty * (0 >? (d * (info.shortest_y_ - current_y)));
344 Real ideal_diff = d * (current_y - info.ideal_y_);
345 Real ideal_score = shrink_extra_weight (ideal_diff, 1.5);
347 /* We introduce a power, to make the scoring strictly
348 convex. Otherwise a symmetric knee beam (up/down/up/down)
349 does not have an optimum in the middle. */
351 ideal_score = pow (ideal_score, 1.1);
353 score[d] += length_pen * ideal_score;
361 score[d] /= (count[d] >? 1);
363 while (flip (&d) != DOWN);
365 return score[LEFT]+score[RIGHT];
369 Beam::score_slopes_dy (Real yl, Real yr,
370 Real dy_mus, Real dy_damp,
378 DAMPING_DIRECTION_PENALTY is a very harsh measure, while for
379 complex beaming patterns, horizontal is often a good choice.
381 TODO: find a way to incorporate the complexity of the beam in this
384 if (fabs (dy/dx) > ROUND_TO_ZERO_SLOPE
385 && sign (dy_damp) != sign (dy))
387 dem += DAMPING_DIRECTION_PENALTY;
390 dem += MUSICAL_DIRECTION_FACTOR * (0 >? (fabs (dy) - fabs (dy_mus)));
393 Real slope_penalty = IDEAL_SLOPE_FACTOR;
395 /* Xstaff beams tend to use extreme slopes to get short stems. We
396 put in a penalty here. */
400 /* Huh, why would a too steep beam be better than a too flat one ? */
401 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy), 1.5)
405 almost zero slopes look like errors in horizontal beams.
408 && fabs (dy / dx) < ROUND_TO_ZERO_SLOPE)
409 dem += ROUND_TO_ZERO_POINTS;
417 return x - floor (x);
422 TODO: The fixed value SECONDARY_BEAM_DEMERIT is probably flawed:
423 because for 32nd and 64th beams the forbidden quants are relatively
424 more important than stem lengths.
427 Beam::score_forbidden_quants (Real yl, Real yr,
430 Real thickness, Real beam_translation,
432 Direction ldir, Direction rdir)
435 Drul_array<Real> y(yl,yr);
436 Drul_array<Direction> dirs(ldir,rdir);
438 Real extra_demerit = SECONDARY_BEAM_DEMERIT / beam_count;
441 Inside the staff, inter quants are forbidden.
447 if (fabs (y[d]) <= (radius + 0.5) && fabs (my_modf (y[d]) - 0.5) < 1e-3)
448 dem += INTER_QUANT_PENALTY;
450 while ((flip (&d))!= LEFT);
453 for (int j = 1; j <= beam_count; j++)
458 see if the outer staffline falls in a beam-gap
460 This test is too weak; we should really check all lines.
462 Direction stem_dir = dirs[d];
463 Real gap1 = y[d] - stem_dir * ((j-1) * beam_translation + thickness / 2 - slt/2 );
464 Real gap2 = y[d] - stem_dir * (j * beam_translation - thickness / 2 + slt/2);
467 gap.add_point (gap1);
468 gap.add_point (gap2);
470 if (gap.contains (radius))
471 dem += extra_demerit;
473 while ((flip (&d))!= LEFT);
478 // todo: use beam_count of outer stems.
482 Real sit = (thickness - slt) / 2;
484 Real hang = 1.0 - (thickness - slt) / 2;
488 // hmm, without Interval/Drul_array, you get ~ 4x same code...
489 if (fabs (y[LEFT] - dirs[LEFT] * beam_translation) < radius + inter)
491 if (dirs[LEFT] == UP && dy <= eps
492 && fabs (my_modf (y[LEFT]) - sit) < eps)
493 dem += extra_demerit;
495 if (dirs[LEFT] == DOWN && dy >= eps
496 && fabs (my_modf (y[LEFT]) - hang) < eps)
497 dem += extra_demerit;
500 if (fabs (y[RIGHT] - dirs[RIGHT] * beam_translation) < radius + inter)
502 if (dirs[RIGHT] == UP && dy >= eps
503 && fabs (my_modf (y[RIGHT]) - sit) < eps)
504 dem += extra_demerit;
506 if (dirs[RIGHT] == DOWN && dy <= eps
507 && fabs (my_modf (y[RIGHT]) - hang) < eps)
508 dem += extra_demerit;
513 if (fabs (y[LEFT] - 2 * dirs[LEFT] * beam_translation) < radius + inter)
515 if (dirs[LEFT] == UP && dy <= eps
516 && fabs (my_modf (y[LEFT]) - straddle) < eps)
517 dem += extra_demerit;
519 if (dirs[LEFT] == DOWN && dy >= eps
520 && fabs (my_modf (y[LEFT]) - straddle) < eps)
521 dem += extra_demerit;
524 if (fabs (y[RIGHT] - 2 * dirs[RIGHT] * beam_translation) < radius + inter)
526 if (dirs[RIGHT] == UP && dy >= eps
527 && fabs (my_modf (y[RIGHT]) - straddle) < eps)
528 dem += extra_demerit;
530 if (dirs[RIGHT] == DOWN && dy <= eps
531 && fabs (my_modf (y[RIGHT]) - straddle) < eps)
532 dem += extra_demerit;