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 "output-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.
33 const Real BEAM_EPS = 1e-3;
35 // possibly ridiculous, but too short stems just won't do
36 const int STEM_LENGTH_LIMIT_PENALTY = 5000;
37 const int DAMPING_DIRECTION_PENALTY = 800;
38 const int MUSICAL_DIRECTION_FACTOR = 400;
39 const int IDEAL_SLOPE_FACTOR = 10;
40 const Real ROUND_TO_ZERO_SLOPE = 0.02;
43 shrink_extra_weight (Real x, Real fac)
45 return fabs (x) * ((x < 0) ? fac : 1.0);
64 - Make all demerits customisable
66 - One sensible check per demerit (what's this --hwn)
68 - Add demerits for quants per se, as to forbid a specific quant
73 int best_quant_score_idx (Array<Quant_score> const & qscores)
77 for (int i = qscores.size (); i--;)
79 if (qscores[i].demerits < best)
81 best = qscores [i].demerits ;
89 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
91 Beam::quanting (SCM smob)
93 Grob *me = unsmob_grob (smob);
95 SCM s = me->get_property ("positions");
96 Real yl = scm_to_double (ly_car (s));
97 Real yr = scm_to_double (ly_cdr (s));
101 Calculations are relative to a unit-scaled staff, i.e. the quants are
102 divided by the current staff_space.
105 Real ss = Staff_symbol_referencer::staff_space (me);
106 Real thickness = Beam::get_thickness (me) / ss ;
107 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
109 Real dy_mus = robust_scm2double (me->get_property ("least-squares-dy"), 0);
111 Real sit = (thickness - slt) / 2;
113 Real hang = 1.0 - (thickness - slt) / 2;
114 Real quants [] = {straddle, sit, inter, hang };
118 int num_quants = int (sizeof (quants)/sizeof (Real));
123 going to REGION_SIZE == 2, yields another 0.6 second with
127 (result indexes between 70 and 575) ? --hwn.
134 Do stem computations. These depend on YL and YR linearly, so we can
135 precompute for every stem 2 factors.
137 Link_array<Grob> stems=
138 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
139 Array<Stem_info> stem_infos;
140 Array<Real> base_lengths;
141 Array<Real> stem_xposns;
143 Drul_array<bool> dirs_found (0,0);
145 for (int a = 2; a--;)
146 common[a] = common_refpoint_of_array (stems, me, Axis (a));
148 Grob * fvs = first_visible_stem (me);
149 Grob *lvs = last_visible_stem (me);
150 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
151 Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
154 We store some info to quickly interpolate.
156 Sometimes my head is screwed on backwards. The stemlength are
157 AFFINE linear in YL and YR. If YL == YR == 0, then we might have
158 stem_y != 0.0, when we're cross staff.
161 for (int i= 0; i < stems.size (); i++)
165 Stem_info si (Stem::get_stem_info (s));
167 stem_infos.push (si);
168 dirs_found[stem_infos.top ().dir_] = true;
170 bool f = to_boolean (s->get_property ("french-beaming"))
171 && s != lvs && s != fvs;
173 base_lengths.push (calc_stem_y (me, s, common, xl, xr,
174 Interval (0,0), f) / ss);
175 stem_xposns.push (s->relative_coordinate (common[X_AXIS], X_AXIS));
181 Grob *commony = fvs->common_refpoint (lvs, Y_AXIS);
182 xstaff = Align_interface::has_interface (commony);
185 Direction ldir = Direction (stem_infos[0].dir_);
186 Direction rdir = Direction (stem_infos.top ().dir_);
187 bool is_knee = dirs_found[LEFT] && dirs_found[RIGHT];
190 int region_size = REGION_SIZE;
192 Knees are harder, lets try some more possibilities for knees.
198 Asymetry ? should run to <= region_size ?
200 for (int i = -region_size ; i < region_size; i++)
201 for (int j = 0; j < num_quants; j++)
203 quantsl.push (i + quants[j] + int (yl));
204 quantsr.push (i + quants[j] + int (yr));
207 Array<Quant_score> qscores;
209 for (int l =0; l < quantsl.size (); l++)
210 for (int r =0; r < quantsr.size (); r++)
220 /* This is a longish function, but we don't separate this out into
221 neat modular separate subfunctions, as the subfunctions would be
222 called for many values of YL, YR. By precomputing various
223 parameters outside of the loop, we can save a lot of time. */
224 for (int i = qscores.size (); i--;)
226 Real d = score_slopes_dy (qscores[i].yl, qscores[i].yr,
230 qscores[i].demerits += d;
233 qscores[i].score_card_ += to_string ("S%.2f",d);
237 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);
280 SCM inspect_quants = me->get_property ("inspect-quants");
281 if (to_boolean (me->get_paper ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting")))
282 && ly_c_pair_p (inspect_quants))
284 Drul_array<Real> ins = ly_scm2interval (inspect_quants);
289 for (; i < qscores.size (); i ++)
291 Real d =fabs (qscores[i].yl- ins[LEFT]) + fabs (qscores[i].yr - ins[RIGHT]);
299 programming_error ("Could not find quant.");
303 me->set_property ("positions",
304 ly_interval2scm (Drul_array<Real> (qscores[best_idx].yl,
305 qscores[best_idx].yr)));
307 if (to_boolean (me->get_paper ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting"))))
309 qscores[best_idx].score_card_ += to_string ("i%d", best_idx);
312 me->set_property ("quant-score",
313 scm_makfrom0str (qscores[best_idx].score_card_.to_str0 ()));
317 return SCM_UNSPECIFIED;
321 Beam::score_stem_lengths (Link_array<Grob> const &stems,
322 Array<Stem_info> const &stem_infos,
323 Array<Real> const &base_stem_ys,
324 Array<Real> const &stem_xs,
329 Real limit_penalty = STEM_LENGTH_LIMIT_PENALTY;
330 Drul_array<Real> score (0, 0);
331 Drul_array<int> count (0, 0);
333 for (int i=0; i < stems.size (); i++)
336 if (Stem::is_invisible (s))
341 Real beam_y = dx ? yr *(x - xl)/dx + yl * ( xr - x)/dx : (yr + yl)/2;
342 Real current_y = beam_y + base_stem_ys[i];
343 Real length_pen = STEM_LENGTH_DEMERIT_FACTOR;
345 Stem_info info = stem_infos[i];
346 Direction d = info.dir_;
348 score[d] += limit_penalty * (0 >? (d * (info.shortest_y_ - current_y)));
350 Real ideal_diff = d * (current_y - info.ideal_y_);
351 Real ideal_score = shrink_extra_weight (ideal_diff, 1.5);
353 /* We introduce a power, to make the scoring strictly
354 convex. Otherwise a symmetric knee beam (up/down/up/down)
355 does not have an optimum in the middle. */
357 ideal_score = pow (ideal_score, 1.1);
359 score[d] += length_pen * ideal_score;
367 score[d] /= (count[d] >? 1);
369 while (flip (&d) != DOWN);
371 return score[LEFT]+score[RIGHT];
375 Beam::score_slopes_dy (Real yl, Real yr,
376 Real dy_mus, Real dy_damp,
384 DAMPING_DIRECTION_PENALTY is a very harsh measure, while for
385 complex beaming patterns, horizontal is often a good choice.
387 TODO: find a way to incorporate the complexity of the beam in this
390 if (fabs (dy/dx) > ROUND_TO_ZERO_SLOPE
391 && sign (dy_damp) != sign (dy))
393 dem += DAMPING_DIRECTION_PENALTY;
396 dem += MUSICAL_DIRECTION_FACTOR * (0 >? (fabs (dy) - fabs (dy_mus)));
399 Real slope_penalty = IDEAL_SLOPE_FACTOR;
401 /* Xstaff beams tend to use extreme slopes to get short stems. We
402 put in a penalty here. */
406 /* Huh, why would a too steep beam be better than a too flat one ? */
407 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy), 1.5)
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,
431 Drul_array<int> beam_counts,
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_counts[LEFT] >? beam_counts[RIGHT]);
446 for (int j = 1; j <= beam_counts[d]; j++)
448 Direction stem_dir = dirs[d];
451 The 2.2 factor is to provide a little leniency for
452 borderline cases. If we do 2.0, then the upper outer line
453 will be in the gap of the (2,sit) quant, leading to a
456 Real gap1 = y[d] - stem_dir * ((j-1) * beam_translation + thickness / 2 - slt/2.2 );
457 Real gap2 = y[d] - stem_dir * (j * beam_translation - thickness / 2 + slt/2.2);
460 gap.add_point (gap1);
461 gap.add_point (gap2);
463 for (Real k = - radius ;
464 k <= radius + BEAM_EPS; k += 1.0)
465 if (gap.contains (k))
467 Real dist = fabs (gap[UP]-k) <? fabs (gap[DOWN] - k);
470 this parameter is tuned to grace-stem-length.ly
472 Real fixed_demerit = 0.4;
476 (1-fixed_demerit) * (dist / gap.length())* 2);
480 while ((flip (&d))!= LEFT);
483 if ((beam_counts[LEFT] >? beam_counts[RIGHT]) >= 2)
486 Real sit = (thickness - slt) / 2;
488 Real hang = 1.0 - (thickness - slt) / 2;
494 if (beam_counts[d] >= 2
495 && fabs (y[d] - dirs[d] * beam_translation) < radius + inter)
497 if (dirs[d] == UP && dy <= BEAM_EPS
498 && fabs (my_modf (y[d]) - sit) < BEAM_EPS)
499 dem += extra_demerit;
501 if (dirs[d] == DOWN && dy >= BEAM_EPS
502 && fabs (my_modf (y[d]) - hang) < BEAM_EPS)
503 dem += extra_demerit;
506 if (beam_counts[d] >= 3
507 && fabs (y[d] - 2 * dirs[d] * beam_translation) < radius + inter)
509 if (dirs[d] == UP && dy <= BEAM_EPS
510 && fabs (my_modf (y[d]) - straddle) < BEAM_EPS)
511 dem += extra_demerit;
513 if (dirs[d] == DOWN && dy >= BEAM_EPS
514 && fabs (my_modf (y[d]) - straddle) < BEAM_EPS)
515 dem += extra_demerit;
518 while (flip (&d) != LEFT);