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
74 best_quant_score_idx (Array<Quant_score> const & qscores)
78 for (int i = qscores.size (); i--;)
80 if (qscores[i].demerits < best)
82 best = qscores [i].demerits ;
89 programming_error ("Huh? No best beam quant score?");
97 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
99 Beam::quanting (SCM smob)
101 Grob *me = unsmob_grob (smob);
103 SCM s = me->get_property ("positions");
104 Real yl = scm_to_double (ly_car (s));
105 Real yr = scm_to_double (ly_cdr (s));
109 Calculations are relative to a unit-scaled staff, i.e. the quants are
110 divided by the current staff_space.
113 Real ss = Staff_symbol_referencer::staff_space (me);
114 Real thickness = Beam::get_thickness (me) / ss ;
115 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
117 Real dy_mus = robust_scm2double (me->get_property ("least-squares-dy"), 0);
119 Real sit = (thickness - slt) / 2;
121 Real hang = 1.0 - (thickness - slt) / 2;
122 Real quants [] = {straddle, sit, inter, hang };
126 int num_quants = int (sizeof (quants)/sizeof (Real));
131 going to REGION_SIZE == 2, yields another 0.6 second with
135 (result indexes between 70 and 575) ? --hwn.
142 Do stem computations. These depend on YL and YR linearly, so we can
143 precompute for every stem 2 factors.
145 Link_array<Grob> stems=
146 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
147 Array<Stem_info> stem_infos;
148 Array<Real> base_lengths;
149 Array<Real> stem_xposns;
151 Drul_array<bool> dirs_found (0,0);
153 for (int a = 2; a--;)
154 common[a] = common_refpoint_of_array (stems, me, Axis (a));
156 Grob * fvs = first_visible_stem (me);
157 Grob *lvs = last_visible_stem (me);
158 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
159 Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
162 We store some info to quickly interpolate.
164 Sometimes my head is screwed on backwards. The stemlength are
165 AFFINE linear in YL and YR. If YL == YR == 0, then we might have
166 stem_y != 0.0, when we're cross staff.
169 for (int i= 0; i < stems.size (); i++)
173 Stem_info si (Stem::get_stem_info (s));
175 stem_infos.push (si);
176 dirs_found[stem_infos.top ().dir_] = true;
178 bool f = to_boolean (s->get_property ("french-beaming"))
179 && s != lvs && s != fvs;
181 base_lengths.push (calc_stem_y (me, s, common, xl, xr,
182 Interval (0,0), f) / ss);
183 stem_xposns.push (s->relative_coordinate (common[X_AXIS], X_AXIS));
189 Grob *commony = fvs->common_refpoint (lvs, Y_AXIS);
190 xstaff = Align_interface::has_interface (commony);
193 Direction ldir = Direction (stem_infos[0].dir_);
194 Direction rdir = Direction (stem_infos.top ().dir_);
195 bool is_knee = dirs_found[LEFT] && dirs_found[RIGHT];
198 int region_size = REGION_SIZE;
200 Knees are harder, lets try some more possibilities for knees.
206 Asymetry ? should run to <= region_size ?
208 for (int i = -region_size ; i < region_size; i++)
209 for (int j = 0; j < num_quants; j++)
211 quantsl.push (i + quants[j] + int (yl));
212 quantsr.push (i + quants[j] + int (yr));
215 Array<Quant_score> qscores;
217 for (int l =0; l < quantsl.size (); l++)
218 for (int r =0; r < quantsr.size (); r++)
228 /* This is a longish function, but we don't separate this out into
229 neat modular separate subfunctions, as the subfunctions would be
230 called for many values of YL, YR. By precomputing various
231 parameters outside of the loop, we can save a lot of time. */
232 for (int i = qscores.size (); i--;)
234 Real d = score_slopes_dy (qscores[i].yl, qscores[i].yr,
238 qscores[i].demerits += d;
241 qscores[i].score_card_ += to_string ("S%.2f",d);
245 Real rad = Staff_symbol_referencer::staff_radius (me);
249 Drul_array<int> edge_beam_counts
250 (Stem::beam_multiplicity (stems[0]).length () + 1,
251 Stem::beam_multiplicity (stems.top ()).length () + 1);
253 Real beam_translation = get_beam_translation (me) / ss;
255 Real reasonable_score = (is_knee) ? 200000 : 100;
256 for (int i = qscores.size (); i--;)
257 if (qscores[i].demerits < reasonable_score)
259 Real d = score_forbidden_quants (qscores[i].yl, qscores[i].yr,
260 rad, slt, thickness, beam_translation,
261 edge_beam_counts, ldir, rdir);
262 qscores[i].demerits += d;
265 qscores[i].score_card_ += to_string (" F %.2f", d);
269 for (int i = qscores.size (); i--;)
270 if (qscores[i].demerits < reasonable_score)
272 Real d=score_stem_lengths (stems, stem_infos,
273 base_lengths, stem_xposns,
276 qscores[i].yl, qscores[i].yr);
277 qscores[i].demerits += d;
280 qscores[i].score_card_ += to_string (" L %.2f", d);
284 int best_idx = best_quant_score_idx (qscores);
287 SCM inspect_quants = me->get_property ("inspect-quants");
288 if (to_boolean (me->get_paper ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting")))
289 && ly_c_pair_p (inspect_quants))
291 Drul_array<Real> ins = ly_scm2interval (inspect_quants);
296 for (; i < qscores.size (); i ++)
298 Real d =fabs (qscores[i].yl- ins[LEFT]) + fabs (qscores[i].yr - ins[RIGHT]);
306 programming_error ("Could not find quant.");
310 me->set_property ("positions",
311 ly_interval2scm (Drul_array<Real> (qscores[best_idx].yl,
312 qscores[best_idx].yr)));
314 if (to_boolean (me->get_paper ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting"))))
316 qscores[best_idx].score_card_ += to_string ("i%d", best_idx);
319 me->set_property ("quant-score",
320 scm_makfrom0str (qscores[best_idx].score_card_.to_str0 ()));
324 return SCM_UNSPECIFIED;
328 Beam::score_stem_lengths (Link_array<Grob> const &stems,
329 Array<Stem_info> const &stem_infos,
330 Array<Real> const &base_stem_ys,
331 Array<Real> const &stem_xs,
336 Real limit_penalty = STEM_LENGTH_LIMIT_PENALTY;
337 Drul_array<Real> score (0, 0);
338 Drul_array<int> count (0, 0);
340 for (int i=0; i < stems.size (); i++)
343 if (Stem::is_invisible (s))
348 Real beam_y = dx ? yr *(x - xl)/dx + yl * ( xr - x)/dx : (yr + yl)/2;
349 Real current_y = beam_y + base_stem_ys[i];
350 Real length_pen = STEM_LENGTH_DEMERIT_FACTOR;
352 Stem_info info = stem_infos[i];
353 Direction d = info.dir_;
355 score[d] += limit_penalty * (0 >? (d * (info.shortest_y_ - current_y)));
357 Real ideal_diff = d * (current_y - info.ideal_y_);
358 Real ideal_score = shrink_extra_weight (ideal_diff, 1.5);
360 /* We introduce a power, to make the scoring strictly
361 convex. Otherwise a symmetric knee beam (up/down/up/down)
362 does not have an optimum in the middle. */
364 ideal_score = pow (ideal_score, 1.1);
366 score[d] += length_pen * ideal_score;
374 score[d] /= (count[d] >? 1);
376 while (flip (&d) != DOWN);
378 return score[LEFT]+score[RIGHT];
382 Beam::score_slopes_dy (Real yl, Real yr,
383 Real dy_mus, Real dy_damp,
391 DAMPING_DIRECTION_PENALTY is a very harsh measure, while for
392 complex beaming patterns, horizontal is often a good choice.
394 TODO: find a way to incorporate the complexity of the beam in this
397 if (fabs (dy/dx) > ROUND_TO_ZERO_SLOPE
398 && sign (dy_damp) != sign (dy))
400 dem += DAMPING_DIRECTION_PENALTY;
403 dem += MUSICAL_DIRECTION_FACTOR * (0 >? (fabs (dy) - fabs (dy_mus)));
406 Real slope_penalty = IDEAL_SLOPE_FACTOR;
408 /* Xstaff beams tend to use extreme slopes to get short stems. We
409 put in a penalty here. */
413 /* Huh, why would a too steep beam be better than a too flat one ? */
414 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy), 1.5)
424 return x - floor (x);
429 TODO: The fixed value SECONDARY_BEAM_DEMERIT is probably flawed:
430 because for 32nd and 64th beams the forbidden quants are relatively
431 more important than stem lengths.
434 Beam::score_forbidden_quants (Real yl, Real yr,
437 Real thickness, Real beam_translation,
438 Drul_array<int> beam_counts,
439 Direction ldir, Direction rdir)
442 Drul_array<Real> y (yl,yr);
443 Drul_array<Direction> dirs (ldir,rdir);
445 Real extra_demerit = SECONDARY_BEAM_DEMERIT / (beam_counts[LEFT] >? beam_counts[RIGHT]);
453 for (int j = 1; j <= beam_counts[d]; j++)
455 Direction stem_dir = dirs[d];
458 The 2.2 factor is to provide a little leniency for
459 borderline cases. If we do 2.0, then the upper outer line
460 will be in the gap of the (2,sit) quant, leading to a
463 Real gap1 = y[d] - stem_dir * ((j-1) * beam_translation + thickness / 2 - slt/2.2 );
464 Real gap2 = y[d] - stem_dir * (j * beam_translation - thickness / 2 + slt/2.2);
467 gap.add_point (gap1);
468 gap.add_point (gap2);
470 for (Real k = - radius ;
471 k <= radius + BEAM_EPS; k += 1.0)
472 if (gap.contains (k))
474 Real dist = fabs (gap[UP]-k) <? fabs (gap[DOWN] - k);
477 this parameter is tuned to grace-stem-length.ly
479 Real fixed_demerit = 0.4;
483 (1-fixed_demerit) * (dist / gap.length())* 2);
487 while ((flip (&d))!= LEFT);
490 if ((beam_counts[LEFT] >? beam_counts[RIGHT]) >= 2)
493 Real sit = (thickness - slt) / 2;
495 Real hang = 1.0 - (thickness - slt) / 2;
501 if (beam_counts[d] >= 2
502 && fabs (y[d] - dirs[d] * beam_translation) < radius + inter)
504 if (dirs[d] == UP && dy <= BEAM_EPS
505 && fabs (my_modf (y[d]) - sit) < BEAM_EPS)
506 dem += extra_demerit;
508 if (dirs[d] == DOWN && dy >= BEAM_EPS
509 && fabs (my_modf (y[d]) - hang) < BEAM_EPS)
510 dem += extra_demerit;
513 if (beam_counts[d] >= 3
514 && fabs (y[d] - 2 * dirs[d] * beam_translation) < radius + inter)
516 if (dirs[d] == UP && dy <= BEAM_EPS
517 && fabs (my_modf (y[d]) - straddle) < BEAM_EPS)
518 dem += extra_demerit;
520 if (dirs[d] == DOWN && dy >= BEAM_EPS
521 && fabs (my_modf (y[d]) - straddle) < BEAM_EPS)
522 dem += extra_demerit;
525 while (flip (&d) != LEFT);