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>
17 #include "staff-symbol-referencer.hh"
20 #include "paper-def.hh"
21 #include "group-interface.hh"
22 #include "align-interface.hh"
24 const int INTER_QUANT_PENALTY = 1000;
25 const Real SECONDARY_BEAM_DEMERIT = 10.0;
26 const int STEM_LENGTH_DEMERIT_FACTOR = 5;
28 // possibly ridiculous, but too short stems just won't do
29 const int STEM_LENGTH_LIMIT_PENALTY = 5000;
30 const int DAMPING_DIRECTION_PENALTY = 800;
31 const int MUSICAL_DIRECTION_FACTOR = 400;
32 const int IDEAL_SLOPE_FACTOR = 10;
34 const Real ROUND_TO_ZERO_SLOPE = 0.05;
35 const int ROUND_TO_ZERO_POINTS = 4;
37 extern bool debug_beam_quanting_flag;
40 shrink_extra_weight (Real x, Real fac)
42 return fabs (x) * ((x < 0) ? fac : 1.0);
61 - Make all demerits customisable
63 - One sensible check per demerit (what's this --hwn)
65 - Add demerits for quants per se, as to forbid a specific quant
70 int best_quant_score_idx (Array<Quant_score> const & qscores)
74 for (int i = qscores.size (); i--;)
76 if (qscores[i].demerits < best)
78 best = qscores [i].demerits ;
86 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
88 Beam::quanting (SCM smob)
90 Grob *me = unsmob_grob (smob);
92 SCM s = me->get_grob_property ("positions");
93 Real yl = gh_scm2double (gh_car (s));
94 Real yr = gh_scm2double (gh_cdr (s));
96 Real ss = Staff_symbol_referencer::staff_space (me);
97 Real thickness = gh_scm2double (me->get_grob_property ("thickness")) / ss;
98 Real slt = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness")) / ss;
101 SCM sdy = me->get_grob_property ("least-squares-dy");
102 Real dy_mus = gh_number_p (sdy) ? gh_scm2double (sdy) : 0.0;
105 Real sit = (thickness - slt) / 2;
107 Real hang = 1.0 - (thickness - slt) / 2;
108 Real quants [] = {straddle, sit, inter, hang };
110 int num_quants = int (sizeof (quants)/sizeof (Real));
115 going to REGION_SIZE == 2, yields another 0.6 second with
119 (result indexes between 70 and 575) ? --hwn.
126 Do stem computations. These depend on YL and YR linearly, so we can
127 precompute for every stem 2 factors.
129 Link_array<Grob> stems=
130 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
131 Array<Stem_info> stem_infos;
132 Array<Real> base_lengths;
133 Array<Real> stem_xposns;
135 Drul_array<bool> dirs_found(0,0);
137 for (int a = 2; a--;)
138 common[a] = common_refpoint_of_array (stems, me, Axis(a));
140 Grob * fvs = first_visible_stem (me);
141 Grob *lvs = last_visible_stem (me);
142 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
143 Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
146 We store some info to quickly interpolate.
148 Sometimes my head is screwed on backwards. The stemlength are
149 AFFINE linear in YL and YR. If YL == YR == 0, then we might have
150 stem_y != 0.0, when we're cross staff.
153 for (int i= 0; i < stems.size(); i++)
156 stem_infos.push (Stem::get_stem_info (s));
157 dirs_found[stem_infos.top ().dir_] = true;
159 bool f = to_boolean (s->get_grob_property ("french-beaming"))
160 && s != lvs && s != fvs;
162 base_lengths.push (calc_stem_y (me, s, common, xl, xr,
164 stem_xposns.push (s->relative_coordinate (common[X_AXIS], X_AXIS));
170 Grob *commony = fvs->common_refpoint (lvs, Y_AXIS);
171 xstaff = Align_interface::has_interface (commony);
174 Direction ldir = Direction (stem_infos[0].dir_);
175 Direction rdir = Direction (stem_infos.top ().dir_);
176 bool knee_b = dirs_found[LEFT] && dirs_found[RIGHT];
179 int region_size = REGION_SIZE;
181 Knees are harder, lets try some more possibilities for knees.
187 Asymetry ? should run to <= region_size ?
189 for (int i = -region_size ; i < region_size; i++)
190 for (int j = 0; j < num_quants; j++)
192 quantsl.push (i + quants[j] + int (yl));
193 quantsr.push (i + quants[j] + int (yr));
196 Array<Quant_score> qscores;
198 for (int l =0; l < quantsl.size (); l++)
199 for (int r =0; r < quantsr.size (); r++)
209 /* This is a longish function, but we don't separate this out into
210 neat modular separate subfunctions, as the subfunctions would be
211 called for many values of YL, YR. By precomputing various
212 parameters outside of the loop, we can save a lot of time. */
213 for (int i = qscores.size (); i--;)
215 Real d = score_slopes_dy (qscores[i].yl, qscores[i].yr,
219 qscores[i].demerits += d;
222 qscores[i].score_card_ += to_string ("S%.2f",d);
226 Real rad = Staff_symbol_referencer::staff_radius (me);
227 int beam_count = get_beam_count (me);
228 Real beam_translation = get_beam_translation (me);
230 Real reasonable_score = (knee_b) ? 200000 : 100;
231 for (int i = qscores.size (); i--;)
232 if (qscores[i].demerits < reasonable_score)
234 Real d = score_forbidden_quants (qscores[i].yl, qscores[i].yr,
235 rad, slt, thickness, beam_translation,
236 beam_count, ldir, rdir);
237 qscores[i].demerits += d;
240 qscores[i].score_card_ += to_string (" F %.2f", d);
244 for (int i = qscores.size (); i--;)
245 if (qscores[i].demerits < reasonable_score)
247 Real d=score_stem_lengths (stems, stem_infos,
248 base_lengths, stem_xposns,
251 qscores[i].yl, qscores[i].yr);
252 qscores[i].demerits += d;
255 qscores[i].score_card_ += to_string (" L %.2f", d);
259 int best_idx = best_quant_score_idx (qscores);
263 SCM inspect_quants = me->get_grob_property ("inspect-quants");
264 if (debug_beam_quanting_flag
265 && gh_pair_p (inspect_quants))
267 Real il = gh_scm2double (gh_car (inspect_quants));
268 Real ir = gh_scm2double (gh_cdr (inspect_quants));
273 for (; i < qscores.size(); i ++)
275 Real d =fabs (qscores[i].yl-il) + fabs (qscores[i].yr - ir);
283 programming_error ("Could not find quant.");
287 me->set_grob_property ("positions",
288 gh_cons (gh_double2scm (qscores[best_idx].yl),
289 gh_double2scm (qscores[best_idx].yr)));
291 if (debug_beam_quanting_flag)
293 qscores[best_idx].score_card_ += to_string ("i%d", best_idx);
296 me->set_grob_property ("quant-score",
297 scm_makfrom0str (qscores[best_idx].score_card_.to_str0 ()));
301 return SCM_UNSPECIFIED;
305 Beam::score_stem_lengths (Link_array<Grob> const &stems,
306 Array<Stem_info> const &stem_infos,
307 Array<Real> const &base_stem_ys,
308 Array<Real> const &stem_xs,
313 Real limit_penalty = STEM_LENGTH_LIMIT_PENALTY;
314 Drul_array<Real> score (0, 0);
315 Drul_array<int> count (0, 0);
317 for (int i=0; i < stems.size (); i++)
320 if (Stem::invisible_b (s))
325 Real beam_y = dx ? yr *(x - xl)/dx + yl * ( xr - x)/dx : (yr + yl)/2;
326 Real current_y = beam_y + base_stem_ys[i];
327 Real length_pen = STEM_LENGTH_DEMERIT_FACTOR;
329 Stem_info info = stem_infos[i];
330 Direction d = info.dir_;
332 score[d] += limit_penalty * (0 >? (d * (info.shortest_y_ - current_y)));
334 Real ideal_diff = d * (current_y - info.ideal_y_);
335 Real ideal_score = shrink_extra_weight (ideal_diff, 1.5);
337 /* We introduce a power, to make the scoring strictly
338 convex. Otherwise a symmetric knee beam (up/down/up/down)
339 does not have an optimum in the middle. */
341 ideal_score = pow (ideal_score, 1.1);
343 score[d] += length_pen * ideal_score;
351 score[d] /= (count[d] >? 1);
353 while (flip (&d) != DOWN);
355 return score[LEFT]+score[RIGHT];
359 Beam::score_slopes_dy (Real yl, Real yr,
360 Real dy_mus, Real dy_damp,
368 DAMPING_DIRECTION_PENALTY is a very harsh measure, while for
369 complex beaming patterns, horizontal is often a good choice.
371 TODO: find a way to incorporate the complexity of the beam in this
374 if (fabs (dy/dx) > ROUND_TO_ZERO_SLOPE
375 && sign (dy_damp) != sign (dy))
377 dem += DAMPING_DIRECTION_PENALTY;
380 dem += MUSICAL_DIRECTION_FACTOR * (0 >? (fabs (dy) - fabs (dy_mus)));
383 Real slope_penalty = IDEAL_SLOPE_FACTOR;
385 /* Xstaff beams tend to use extreme slopes to get short stems. We
386 put in a penalty here. */
390 /* Huh, why would a too steep beam be better than a too flat one ? */
391 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy), 1.5)
395 almost zero slopes look like errors in horizontal beams.
398 && fabs (dy / dx) < ROUND_TO_ZERO_SLOPE)
399 dem += ROUND_TO_ZERO_POINTS;
407 return x - floor (x);
412 TODO: The fixed value SECONDARY_BEAM_DEMERIT is probably flawed:
413 because for 32nd and 64th beams the forbidden quants are relatively
414 more important than stem lengths.
417 Beam::score_forbidden_quants (Real yl, Real yr,
420 Real thickness, Real beam_translation,
422 Direction ldir, Direction rdir)
425 Drul_array<Real> y(yl,yr);
426 Drul_array<Direction> dirs(ldir,rdir);
428 Real extra_demerit = SECONDARY_BEAM_DEMERIT / beam_count;
431 Inside the staff, inter quants are forbidden.
437 if (fabs (y[d]) <= (radius + 0.5) && fabs (my_modf (y[d]) - 0.5) < 1e-3)
438 dem += INTER_QUANT_PENALTY;
440 while ((flip (&d))!= LEFT);
443 for (int j = 1; j <= beam_count; j++)
448 see if the outer staffline falls in a beam-gap
450 This test is too weak; we should really check all lines.
452 Direction stem_dir = dirs[d];
453 Real gap1 = y[d] - stem_dir * ((j-1) * beam_translation + thickness / 2 - slt/2 );
454 Real gap2 = y[d] - stem_dir * (j * beam_translation - thickness / 2 + slt/2);
457 gap.add_point (gap1);
458 gap.add_point (gap2);
460 if (gap.contains (radius))
461 dem += extra_demerit;
463 while ((flip (&d))!= LEFT);
468 // todo: use beam_count of outer stems.
472 Real sit = (thickness - slt) / 2;
474 Real hang = 1.0 - (thickness - slt) / 2;
478 // hmm, without Interval/Drul_array, you get ~ 4x same code...
479 if (fabs (y[LEFT] - dirs[LEFT] * beam_translation) < radius + inter)
481 if (dirs[LEFT] == UP && dy <= eps
482 && fabs (my_modf (y[LEFT]) - sit) < eps)
483 dem += extra_demerit;
485 if (dirs[LEFT] == DOWN && dy >= eps
486 && fabs (my_modf (y[LEFT]) - hang) < eps)
487 dem += extra_demerit;
490 if (fabs (y[RIGHT] - dirs[RIGHT] * beam_translation) < radius + inter)
492 if (dirs[RIGHT] == UP && dy >= eps
493 && fabs (my_modf (y[RIGHT]) - sit) < eps)
494 dem += extra_demerit;
496 if (dirs[RIGHT] == DOWN && dy <= eps
497 && fabs (my_modf (y[RIGHT]) - hang) < eps)
498 dem += extra_demerit;
503 if (fabs (y[LEFT] - 2 * dirs[LEFT] * beam_translation) < radius + inter)
505 if (dirs[LEFT] == UP && dy <= eps
506 && fabs (my_modf (y[LEFT]) - straddle) < eps)
507 dem += extra_demerit;
509 if (dirs[LEFT] == DOWN && dy >= eps
510 && fabs (my_modf (y[LEFT]) - straddle) < eps)
511 dem += extra_demerit;
514 if (fabs (y[RIGHT] - 2 * dirs[RIGHT] * beam_translation) < radius + inter)
516 if (dirs[RIGHT] == UP && dy >= eps
517 && fabs (my_modf (y[RIGHT]) - straddle) < eps)
518 dem += extra_demerit;
520 if (dirs[RIGHT] == DOWN && dy <= eps
521 && fabs (my_modf (y[RIGHT]) - straddle) < eps)
522 dem += extra_demerit;