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>
18 #include "staff-symbol-referencer.hh"
21 #include "output-def.hh"
22 #include "group-interface.hh"
23 #include "align-interface.hh"
25 const int INTER_QUANT_PENALTY = 1000;
26 const Real SECONDARY_BEAM_DEMERIT = 10.0;
27 const int STEM_LENGTH_DEMERIT_FACTOR = 5;
30 threshold to combat rounding errors.
32 const Real BEAM_EPS = 1e-3;
34 // possibly ridiculous, but too short stems just won't do
35 const int STEM_LENGTH_LIMIT_PENALTY = 5000;
36 const int DAMPING_DIRECTION_PENALTY = 800;
37 const int MUSICAL_DIRECTION_FACTOR = 400;
38 const int IDEAL_SLOPE_FACTOR = 10;
39 const Real ROUND_TO_ZERO_SLOPE = 0.02;
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
73 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 ;
88 programming_error ("Huh? No best beam quant score?");
96 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
98 Beam::quanting (SCM smob)
100 Grob *me = unsmob_grob (smob);
102 SCM s = me->get_property ("positions");
103 Real yl = scm_to_double (scm_car (s));
104 Real yr = scm_to_double (scm_cdr (s));
108 Calculations are relative to a unit-scaled staff, i.e. the quants are
109 divided by the current staff_space.
112 Real ss = Staff_symbol_referencer::staff_space (me);
113 Real thickness = Beam::get_thickness (me) / ss ;
114 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
116 Real dy_mus = robust_scm2double (me->get_property ("least-squares-dy"), 0);
118 Real sit = (thickness - slt) / 2;
120 Real hang = 1.0 - (thickness - slt) / 2;
121 Real quants [] = {straddle, sit, inter, hang };
125 int num_quants = int (sizeof (quants)/sizeof (Real));
130 going to REGION_SIZE == 2, yields another 0.6 second with
134 (result indexes between 70 and 575) ? --hwn.
141 Do stem computations. These depend on YL and YR linearly, so we can
142 precompute for every stem 2 factors.
144 Link_array<Grob> stems=
145 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
146 Array<Stem_info> stem_infos;
147 Array<Real> base_lengths;
148 Array<Real> stem_xposns;
150 Drul_array<bool> dirs_found (0,0);
152 for (int a = 2; a--;)
153 common[a] = common_refpoint_of_array (stems, me, Axis (a));
155 Grob * fvs = first_visible_stem (me);
156 Grob *lvs = last_visible_stem (me);
157 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
158 Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
161 We store some info to quickly interpolate.
163 Sometimes my head is screwed on backwards. The stemlength are
164 AFFINE linear in YL and YR. If YL == YR == 0, then we might have
165 stem_y != 0.0, when we're cross staff.
168 for (int i= 0; i < stems.size (); i++)
172 Stem_info si (Stem::get_stem_info (s));
174 stem_infos.push (si);
175 dirs_found[stem_infos.top ().dir_] = true;
177 bool f = to_boolean (s->get_property ("french-beaming"))
178 && s != lvs && s != fvs;
180 base_lengths.push (calc_stem_y (me, s, common, xl, xr,
181 Interval (0,0), f) / ss);
182 stem_xposns.push (s->relative_coordinate (common[X_AXIS], X_AXIS));
188 Grob *commony = fvs->common_refpoint (lvs, Y_AXIS);
189 xstaff = Align_interface::has_interface (commony);
192 Direction ldir = Direction (stem_infos[0].dir_);
193 Direction rdir = Direction (stem_infos.top ().dir_);
194 bool is_knee = dirs_found[LEFT] && dirs_found[RIGHT];
197 int region_size = REGION_SIZE;
199 Knees are harder, lets try some more possibilities for knees.
205 Asymetry ? should run to <= region_size ?
207 for (int i = -region_size ; i < region_size; i++)
208 for (int j = 0; j < num_quants; j++)
210 quantsl.push (i + quants[j] + int (yl));
211 quantsr.push (i + quants[j] + int (yr));
214 Array<Quant_score> qscores;
216 for (int l = 0; l < quantsl.size (); l++)
217 for (int r = 0; r < quantsr.size (); r++)
227 /* This is a longish function, but we don't separate this out into
228 neat modular separate subfunctions, as the subfunctions would be
229 called for many values of YL, YR. By precomputing various
230 parameters outside of the loop, we can save a lot of time. */
231 for (int i = qscores.size (); i--;)
233 Real d = score_slopes_dy (qscores[i].yl, qscores[i].yr,
237 qscores[i].demerits += d;
240 qscores[i].score_card_ += to_string ("S%.2f",d);
244 Real rad = Staff_symbol_referencer::staff_radius (me);
248 Drul_array<int> edge_beam_counts
249 (Stem::beam_multiplicity (stems[0]).length () + 1,
250 Stem::beam_multiplicity (stems.top ()).length () + 1);
252 Real beam_translation = get_beam_translation (me) / ss;
254 Real reasonable_score = (is_knee) ? 200000 : 100;
255 for (int i = qscores.size (); i--;)
256 if (qscores[i].demerits < reasonable_score)
258 Real d = score_forbidden_quants (qscores[i].yl, qscores[i].yr,
259 rad, slt, thickness, beam_translation,
260 edge_beam_counts, ldir, rdir);
261 qscores[i].demerits += d;
264 qscores[i].score_card_ += to_string (" F %.2f", d);
268 for (int i = qscores.size (); i--;)
269 if (qscores[i].demerits < reasonable_score)
271 Real d=score_stem_lengths (stems, stem_infos,
272 base_lengths, stem_xposns,
275 qscores[i].yl, qscores[i].yr);
276 qscores[i].demerits += d;
279 qscores[i].score_card_ += to_string (" L %.2f", d);
283 int best_idx = best_quant_score_idx (qscores);
286 SCM inspect_quants = me->get_property ("inspect-quants");
287 if (to_boolean (me->get_layout ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting")))
288 && scm_is_pair (inspect_quants))
290 Drul_array<Real> ins = ly_scm2interval (inspect_quants);
295 for (; i < qscores.size (); i ++)
297 Real d =fabs (qscores[i].yl- ins[LEFT]) + fabs (qscores[i].yr - ins[RIGHT]);
305 programming_error ("Could not find quant.");
309 me->set_property ("positions",
310 ly_interval2scm (Drul_array<Real> (qscores[best_idx].yl,
311 qscores[best_idx].yr)));
313 if (to_boolean (me->get_layout ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting"))))
315 qscores[best_idx].score_card_ += to_string ("i%d", best_idx);
318 me->set_property ("quant-score",
319 scm_makfrom0str (qscores[best_idx].score_card_.to_str0 ()));
323 return SCM_UNSPECIFIED;
327 Beam::score_stem_lengths (Link_array<Grob> const &stems,
328 Array<Stem_info> const &stem_infos,
329 Array<Real> const &base_stem_ys,
330 Array<Real> const &stem_xs,
335 Real limit_penalty = STEM_LENGTH_LIMIT_PENALTY;
336 Drul_array<Real> score (0, 0);
337 Drul_array<int> count (0, 0);
339 for (int i= 0; i < stems.size (); i++)
342 if (Stem::is_invisible (s))
347 Real beam_y = dx ? yr *(x - xl)/dx + yl * ( xr - x)/dx : (yr + yl)/2;
348 Real current_y = beam_y + base_stem_ys[i];
349 Real length_pen = STEM_LENGTH_DEMERIT_FACTOR;
351 Stem_info info = stem_infos[i];
352 Direction d = info.dir_;
354 score[d] += limit_penalty * (0 >? (d * (info.shortest_y_ - current_y)));
356 Real ideal_diff = d * (current_y - info.ideal_y_);
357 Real ideal_score = shrink_extra_weight (ideal_diff, 1.5);
359 /* We introduce a power, to make the scoring strictly
360 convex. Otherwise a symmetric knee beam (up/down/up/down)
361 does not have an optimum in the middle. */
363 ideal_score = pow (ideal_score, 1.1);
365 score[d] += length_pen * ideal_score;
373 score[d] /= (count[d] >? 1);
375 while (flip (&d) != DOWN);
377 return score[LEFT]+score[RIGHT];
381 Beam::score_slopes_dy (Real yl, Real yr,
382 Real dy_mus, Real dy_damp,
390 DAMPING_DIRECTION_PENALTY is a very harsh measure, while for
391 complex beaming patterns, horizontal is often a good choice.
393 TODO: find a way to incorporate the complexity of the beam in this
396 if (fabs (dy/dx) > ROUND_TO_ZERO_SLOPE
397 && sign (dy_damp) != sign (dy))
399 dem += DAMPING_DIRECTION_PENALTY;
402 dem += MUSICAL_DIRECTION_FACTOR * (0 >? (fabs (dy) - fabs (dy_mus)));
405 Real slope_penalty = IDEAL_SLOPE_FACTOR;
407 /* Xstaff beams tend to use extreme slopes to get short stems. We
408 put in a penalty here. */
412 /* Huh, why would a too steep beam be better than a too flat one ? */
413 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy), 1.5)
423 return x - floor (x);
428 TODO: The fixed value SECONDARY_BEAM_DEMERIT is probably flawed:
429 because for 32nd and 64th beams the forbidden quants are relatively
430 more important than stem lengths.
433 Beam::score_forbidden_quants (Real yl, Real yr,
436 Real thickness, Real beam_translation,
437 Drul_array<int> beam_counts,
438 Direction ldir, Direction rdir)
441 Drul_array<Real> y (yl,yr);
442 Drul_array<Direction> dirs (ldir,rdir);
444 Real extra_demerit = SECONDARY_BEAM_DEMERIT / (beam_counts[LEFT] >? beam_counts[RIGHT]);
452 for (int j = 1; j <= beam_counts[d]; j++)
454 Direction stem_dir = dirs[d];
457 The 2.2 factor is to provide a little leniency for
458 borderline cases. If we do 2.0, then the upper outer line
459 will be in the gap of the (2,sit) quant, leading to a
462 Real gap1 = y[d] - stem_dir * ((j-1) * beam_translation + thickness / 2 - slt/2.2 );
463 Real gap2 = y[d] - stem_dir * (j * beam_translation - thickness / 2 + slt/2.2);
466 gap.add_point (gap1);
467 gap.add_point (gap2);
469 for (Real k = - radius ;
470 k <= radius + BEAM_EPS; k += 1.0)
471 if (gap.contains (k))
473 Real dist = fabs (gap[UP]-k) <? fabs (gap[DOWN] - k);
476 this parameter is tuned to grace-stem-length.ly
478 Real fixed_demerit = 0.4;
482 (1-fixed_demerit) * (dist / gap.length())* 2);
486 while ((flip (&d))!= LEFT);
489 if ((beam_counts[LEFT] >? beam_counts[RIGHT]) >= 2)
492 Real sit = (thickness - slt) / 2;
494 Real hang = 1.0 - (thickness - slt) / 2;
500 if (beam_counts[d] >= 2
501 && fabs (y[d] - dirs[d] * beam_translation) < radius + inter)
503 if (dirs[d] == UP && dy <= BEAM_EPS
504 && fabs (my_modf (y[d]) - sit) < BEAM_EPS)
505 dem += extra_demerit;
507 if (dirs[d] == DOWN && dy >= BEAM_EPS
508 && fabs (my_modf (y[d]) - hang) < BEAM_EPS)
509 dem += extra_demerit;
512 if (beam_counts[d] >= 3
513 && fabs (y[d] - 2 * dirs[d] * beam_translation) < radius + inter)
515 if (dirs[d] == UP && dy <= BEAM_EPS
516 && fabs (my_modf (y[d]) - straddle) < BEAM_EPS)
517 dem += extra_demerit;
519 if (dirs[d] == DOWN && dy >= BEAM_EPS
520 && fabs (my_modf (y[d]) - straddle) < BEAM_EPS)
521 dem += extra_demerit;
524 while (flip (&d) != LEFT);