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 "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;
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;
41 const int ROUND_TO_ZERO_POINTS = 4;
43 extern bool debug_beam_quanting_flag;
46 shrink_extra_weight (Real x, Real fac)
48 return fabs (x) * ((x < 0) ? fac : 1.0);
67 - Make all demerits customisable
69 - One sensible check per demerit (what's this --hwn)
71 - Add demerits for quants per se, as to forbid a specific quant
76 int best_quant_score_idx (Array<Quant_score> const & qscores)
80 for (int i = qscores.size (); i--;)
82 if (qscores[i].demerits < best)
84 best = qscores [i].demerits ;
92 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
94 Beam::quanting (SCM smob)
96 Grob *me = unsmob_grob (smob);
98 SCM s = me->get_property ("positions");
99 Real yl = gh_scm2double (gh_car (s));
100 Real yr = gh_scm2double (gh_cdr (s));
104 Calculations are relative to a unit-scaled staff, i.e. the quants are
105 divided by the current staff_space.
108 Real ss = Staff_symbol_referencer::staff_space (me);
109 Real thickness = Beam::get_thickness (me) / ss ;
110 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
112 SCM sdy = me->get_property ("least-squares-dy");
113 Real dy_mus = gh_number_p (sdy) ? gh_scm2double (sdy) : 0.0;
116 Real sit = (thickness - slt) / 2;
118 Real hang = 1.0 - (thickness - slt) / 2;
119 Real quants [] = {straddle, sit, inter, hang };
121 int num_quants = int (sizeof (quants)/sizeof (Real));
126 going to REGION_SIZE == 2, yields another 0.6 second with
130 (result indexes between 70 and 575) ? --hwn.
137 Do stem computations. These depend on YL and YR linearly, so we can
138 precompute for every stem 2 factors.
140 Link_array<Grob> stems=
141 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
142 Array<Stem_info> stem_infos;
143 Array<Real> base_lengths;
144 Array<Real> stem_xposns;
146 Drul_array<bool> dirs_found(0,0);
148 for (int a = 2; a--;)
149 common[a] = common_refpoint_of_array (stems, me, Axis(a));
151 Grob * fvs = first_visible_stem (me);
152 Grob *lvs = last_visible_stem (me);
153 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
154 Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
157 We store some info to quickly interpolate.
159 Sometimes my head is screwed on backwards. The stemlength are
160 AFFINE linear in YL and YR. If YL == YR == 0, then we might have
161 stem_y != 0.0, when we're cross staff.
164 for (int i= 0; i < stems.size(); i++)
168 Stem_info si (Stem::get_stem_info (s));
170 stem_infos.push (si);
171 dirs_found[stem_infos.top ().dir_] = true;
173 bool f = to_boolean (s->get_property ("french-beaming"))
174 && s != lvs && s != fvs;
176 base_lengths.push (calc_stem_y (me, s, common, xl, xr,
177 Interval (0,0), f) / ss);
178 stem_xposns.push (s->relative_coordinate (common[X_AXIS], X_AXIS));
184 Grob *commony = fvs->common_refpoint (lvs, Y_AXIS);
185 xstaff = Align_interface::has_interface (commony);
188 Direction ldir = Direction (stem_infos[0].dir_);
189 Direction rdir = Direction (stem_infos.top ().dir_);
190 bool is_knee = dirs_found[LEFT] && dirs_found[RIGHT];
193 int region_size = REGION_SIZE;
195 Knees are harder, lets try some more possibilities for knees.
201 Asymetry ? should run to <= region_size ?
203 for (int i = -region_size ; i < region_size; i++)
204 for (int j = 0; j < num_quants; j++)
206 quantsl.push (i + quants[j] + int (yl));
207 quantsr.push (i + quants[j] + int (yr));
210 Array<Quant_score> qscores;
212 for (int l =0; l < quantsl.size (); l++)
213 for (int r =0; r < quantsr.size (); r++)
223 /* This is a longish function, but we don't separate this out into
224 neat modular separate subfunctions, as the subfunctions would be
225 called for many values of YL, YR. By precomputing various
226 parameters outside of the loop, we can save a lot of time. */
227 for (int i = qscores.size (); i--;)
229 Real d = score_slopes_dy (qscores[i].yl, qscores[i].yr,
233 qscores[i].demerits += d;
236 qscores[i].score_card_ += to_string ("S%.2f",d);
240 Real rad = Staff_symbol_referencer::staff_radius (me);
241 int beam_count = get_beam_count (me);
242 Real beam_translation = get_beam_translation (me) / ss;
244 Real reasonable_score = (is_knee) ? 200000 : 100;
245 for (int i = qscores.size (); i--;)
246 if (qscores[i].demerits < reasonable_score)
248 Real d = score_forbidden_quants (qscores[i].yl, qscores[i].yr,
249 rad, slt, thickness, beam_translation,
250 beam_count, ldir, rdir);
251 qscores[i].demerits += d;
254 qscores[i].score_card_ += to_string (" F %.2f", d);
258 for (int i = qscores.size (); i--;)
259 if (qscores[i].demerits < reasonable_score)
261 Real d=score_stem_lengths (stems, stem_infos,
262 base_lengths, stem_xposns,
265 qscores[i].yl, qscores[i].yr);
266 qscores[i].demerits += d;
269 qscores[i].score_card_ += to_string (" L %.2f", d);
273 int best_idx = best_quant_score_idx (qscores);
277 SCM inspect_quants = me->get_property ("inspect-quants");
278 if (debug_beam_quanting_flag
279 && gh_pair_p (inspect_quants))
281 Drul_array<Real> ins = ly_scm2interval (inspect_quants);
286 for (; i < qscores.size(); i ++)
288 Real d =fabs (qscores[i].yl- ins[LEFT]) + fabs (qscores[i].yr - ins[RIGHT]);
296 programming_error ("Could not find quant.");
300 me->set_property ("positions",
301 ly_interval2scm (Drul_array<Real> (qscores[best_idx].yl,
302 qscores[best_idx].yr)));
304 if (debug_beam_quanting_flag)
306 qscores[best_idx].score_card_ += to_string ("i%d", best_idx);
309 me->set_property ("quant-score",
310 scm_makfrom0str (qscores[best_idx].score_card_.to_str0 ()));
314 return SCM_UNSPECIFIED;
318 Beam::score_stem_lengths (Link_array<Grob> const &stems,
319 Array<Stem_info> const &stem_infos,
320 Array<Real> const &base_stem_ys,
321 Array<Real> const &stem_xs,
326 Real limit_penalty = STEM_LENGTH_LIMIT_PENALTY;
327 Drul_array<Real> score (0, 0);
328 Drul_array<int> count (0, 0);
330 for (int i=0; i < stems.size (); i++)
333 if (Stem::is_invisible (s))
338 Real beam_y = dx ? yr *(x - xl)/dx + yl * ( xr - x)/dx : (yr + yl)/2;
339 Real current_y = beam_y + base_stem_ys[i];
340 Real length_pen = STEM_LENGTH_DEMERIT_FACTOR;
342 Stem_info info = stem_infos[i];
343 Direction d = info.dir_;
345 score[d] += limit_penalty * (0 >? (d * (info.shortest_y_ - current_y)));
347 Real ideal_diff = d * (current_y - info.ideal_y_);
348 Real ideal_score = shrink_extra_weight (ideal_diff, 1.5);
350 /* We introduce a power, to make the scoring strictly
351 convex. Otherwise a symmetric knee beam (up/down/up/down)
352 does not have an optimum in the middle. */
354 ideal_score = pow (ideal_score, 1.1);
356 score[d] += length_pen * ideal_score;
364 score[d] /= (count[d] >? 1);
366 while (flip (&d) != DOWN);
368 return score[LEFT]+score[RIGHT];
372 Beam::score_slopes_dy (Real yl, Real yr,
373 Real dy_mus, Real dy_damp,
381 DAMPING_DIRECTION_PENALTY is a very harsh measure, while for
382 complex beaming patterns, horizontal is often a good choice.
384 TODO: find a way to incorporate the complexity of the beam in this
387 if (fabs (dy/dx) > ROUND_TO_ZERO_SLOPE
388 && sign (dy_damp) != sign (dy))
390 dem += DAMPING_DIRECTION_PENALTY;
393 dem += MUSICAL_DIRECTION_FACTOR * (0 >? (fabs (dy) - fabs (dy_mus)));
396 Real slope_penalty = IDEAL_SLOPE_FACTOR;
398 /* Xstaff beams tend to use extreme slopes to get short stems. We
399 put in a penalty here. */
403 /* Huh, why would a too steep beam be better than a too flat one ? */
404 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy), 1.5)
409 almost zero slopes look like errors in horizontal beams.
412 This causes too much problems, because horizontal depends on
413 horizontal spacing details. These errors should be dealt with
414 through concaveness. --hwn.
417 && fabs (dy / dx) < ROUND_TO_ZERO_SLOPE)
418 dem += ROUND_TO_ZERO_POINTS;
427 return x - floor (x);
432 TODO: The fixed value SECONDARY_BEAM_DEMERIT is probably flawed:
433 because for 32nd and 64th beams the forbidden quants are relatively
434 more important than stem lengths.
437 Beam::score_forbidden_quants (Real yl, Real yr,
440 Real thickness, Real beam_translation,
442 Direction ldir, Direction rdir)
445 Drul_array<Real> y(yl,yr);
446 Drul_array<Direction> dirs(ldir,rdir);
448 Real extra_demerit = SECONDARY_BEAM_DEMERIT / beam_count;
451 Inside the staff, inter quants are forbidden.
457 if (fabs (y[d]) <= (radius + 0.5) && fabs (my_modf (y[d]) - 0.5) < BEAM_EPS)
458 dem += INTER_QUANT_PENALTY;
460 while ((flip (&d))!= LEFT);
463 for (int j = 1; j <= beam_count; j++)
468 see if the outer staffline falls in a beam-gap
470 This test is too weak; we should really check all lines.
472 Direction stem_dir = dirs[d];
473 Real gap1 = y[d] - stem_dir * ((j-1) * beam_translation + thickness / 2 - slt/2 );
474 Real gap2 = y[d] - stem_dir * (j * beam_translation - thickness / 2 + slt/2);
477 gap.add_point (gap1);
478 gap.add_point (gap2);
480 for (Real k = - radius ;
481 k <= radius + BEAM_EPS; k += 1.0)
482 if (gap.contains (k))
483 dem += extra_demerit;
485 while ((flip (&d))!= LEFT);
490 // todo: use beam_count of outer stems.
494 Real sit = (thickness - slt) / 2;
496 Real hang = 1.0 - (thickness - slt) / 2;
498 // hmm, without Interval/Drul_array, you get ~ 4x same code...
499 if (fabs (y[LEFT] - dirs[LEFT] * beam_translation) < radius + inter)
501 if (dirs[LEFT] == UP && dy <= BEAM_EPS
502 && fabs (my_modf (y[LEFT]) - sit) < BEAM_EPS)
503 dem += extra_demerit;
505 if (dirs[LEFT] == DOWN && dy >= BEAM_EPS
506 && fabs (my_modf (y[LEFT]) - hang) < BEAM_EPS)
507 dem += extra_demerit;
510 if (fabs (y[RIGHT] - dirs[RIGHT] * beam_translation) < radius + inter)
512 if (dirs[RIGHT] == UP && dy >= BEAM_EPS
513 && fabs (my_modf (y[RIGHT]) - sit) < BEAM_EPS)
514 dem += extra_demerit;
516 if (dirs[RIGHT] == DOWN && dy <= BEAM_EPS
517 && fabs (my_modf (y[RIGHT]) - hang) < BEAM_EPS)
518 dem += extra_demerit;
523 if (fabs (y[LEFT] - 2 * dirs[LEFT] * beam_translation) < radius + inter)
525 if (dirs[LEFT] == UP && dy <= BEAM_EPS
526 && fabs (my_modf (y[LEFT]) - straddle) < BEAM_EPS)
527 dem += extra_demerit;
529 if (dirs[LEFT] == DOWN && dy >= BEAM_EPS
530 && fabs (my_modf (y[LEFT]) - straddle) < BEAM_EPS)
531 dem += extra_demerit;
534 if (fabs (y[RIGHT] - 2 * dirs[RIGHT] * beam_translation) < radius + inter)
536 if (dirs[RIGHT] == UP && dy >= BEAM_EPS
537 && fabs (my_modf (y[RIGHT]) - straddle) < BEAM_EPS)
538 dem += extra_demerit;
540 if (dirs[RIGHT] == DOWN && dy <= BEAM_EPS
541 && fabs (my_modf (y[RIGHT]) - straddle) < BEAM_EPS)
542 dem += extra_demerit;