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_DIRECTIION_PENALTY = 800;
31 const int MUSICAL_DIRECTION_FACTOR = 400;
32 const int IDEAL_SLOPE_FACTOR = 10;
34 extern bool debug_beam_quanting_flag;
37 shrink_extra_weight (Real x, Real fac)
39 return fabs (x) * ((x < 0) ? fac : 1.0);
58 - Make all demerits customisable
60 - One sensible check per demerit (what's this --hwn)
62 - Add demerits for quants per se, as to forbid a specific quant
67 int best_quant_score_idx (Array<Quant_score> const & qscores)
71 for (int i = qscores.size (); i--;)
73 if (qscores[i].demerits < best)
75 best = qscores [i].demerits ;
83 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
85 Beam::quanting (SCM smob)
87 Grob *me = unsmob_grob (smob);
89 SCM s = me->get_grob_property ("positions");
90 Real yl = gh_scm2double (gh_car (s));
91 Real yr = gh_scm2double (gh_cdr (s));
93 Real ss = Staff_symbol_referencer::staff_space (me);
94 Real thickness = gh_scm2double (me->get_grob_property ("thickness")) / ss;
95 Real slt = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness")) / ss;
98 SCM sdy = me->get_grob_property ("least-squares-dy");
99 Real dy_mus = gh_number_p (sdy) ? gh_scm2double (sdy) : 0.0;
102 Real sit = (thickness - slt) / 2;
104 Real hang = 1.0 - (thickness - slt) / 2;
105 Real quants [] = {straddle, sit, inter, hang };
107 int num_quants = int (sizeof (quants)/sizeof (Real));
112 going to REGION_SIZE == 2, yields another 0.6 second with
116 (result indexes between 70 and 575) ? --hwn.
123 Do stem computations. These depend on YL and YR linearly, so we can
124 precompute for every stem 2 factors.
126 Link_array<Grob> stems=
127 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
128 Array<Stem_info> stem_infos;
129 Array<Real> base_lengths;
130 Array<Real> stem_xposns;
132 Drul_array<bool> dirs_found(0,0);
134 for (int a = 2; a--;)
135 common[a] = common_refpoint_of_array (stems, me, Axis(a));
137 Grob * fvs = first_visible_stem (me);
138 Grob *lvs = last_visible_stem (me);
139 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
140 Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
143 We store some info to quickly interpolate.
145 Sometimes my head is screwed on backwards. The stemlength are
146 AFFINE linear in YL and YR. If YL == YR == 0, then we might have
147 stem_y != 0.0, when we're cross staff.
150 for (int i= 0; i < stems.size(); i++)
153 stem_infos.push (Stem::get_stem_info (s));
154 dirs_found[stem_infos.top ().dir_] = true;
156 bool f = to_boolean (s->get_grob_property ("french-beaming"))
157 && s != lvs && s != fvs;
159 base_lengths.push (calc_stem_y (me, s, common, xl, xr,
161 stem_xposns.push (s->relative_coordinate (common[X_AXIS], X_AXIS));
167 Grob *commony = fvs->common_refpoint (lvs, Y_AXIS);
168 xstaff = Align_interface::has_interface (commony);
171 Direction ldir = Direction (stem_infos[0].dir_);
172 Direction rdir = Direction (stem_infos.top ().dir_);
173 bool knee_b = dirs_found[LEFT] && dirs_found[RIGHT];
176 int region_size = REGION_SIZE;
178 Knees are harder, lets try some more possibilities for knees.
184 Asymetry ? should run to <= region_size ?
186 for (int i = -region_size ; i < region_size; i++)
187 for (int j = 0; j < num_quants; j++)
189 quantsl.push (i + quants[j] + int (yl));
190 quantsr.push (i + quants[j] + int (yr));
193 Array<Quant_score> qscores;
195 for (int l =0; l < quantsl.size (); l++)
196 for (int r =0; r < quantsr.size (); r++)
206 /* This is a longish function, but we don't separate this out into
207 neat modular separate subfunctions, as the subfunctions would be
208 called for many values of YL, YR. By precomputing various
209 parameters outside of the loop, we can save a lot of time. */
210 for (int i = qscores.size (); i--;)
212 Real d = score_slopes_dy (qscores[i].yl, qscores[i].yr,
213 dy_mus, yr- yl, xstaff);
214 qscores[i].demerits += d;
217 qscores[i].score_card_ += to_string ("S%.2f",d);
221 Real rad = Staff_symbol_referencer::staff_radius (me);
222 int beam_count = get_beam_count (me);
223 Real beam_translation = get_beam_translation (me);
225 Real reasonable_score = (knee_b) ? 200000 : 100;
226 for (int i = qscores.size (); i--;)
227 if (qscores[i].demerits < reasonable_score)
229 Real d = score_forbidden_quants (qscores[i].yl, qscores[i].yr,
230 rad, slt, thickness, beam_translation,
231 beam_count, ldir, rdir);
232 qscores[i].demerits += d;
235 qscores[i].score_card_ += to_string (" F %.2f", d);
239 for (int i = qscores.size (); i--;)
240 if (qscores[i].demerits < reasonable_score)
242 Real d=score_stem_lengths (stems, stem_infos,
243 base_lengths, stem_xposns,
246 qscores[i].yl, qscores[i].yr);
247 qscores[i].demerits += d;
250 qscores[i].score_card_ += to_string (" L %.2f", d);
254 int best_idx = best_quant_score_idx (qscores);
258 SCM inspect_quants = me->get_grob_property ("inspect-quants");
259 if (debug_beam_quanting_flag
260 && gh_pair_p (inspect_quants))
262 Real il = gh_scm2double (gh_car (inspect_quants));
263 Real ir = gh_scm2double (gh_cdr (inspect_quants));
268 for (; i < qscores.size(); i ++)
270 Real d =fabs (qscores[i].yl-il) + fabs (qscores[i].yr - ir);
278 programming_error ("Could not find quant.");
282 me->set_grob_property ("positions",
283 gh_cons (gh_double2scm (qscores[best_idx].yl),
284 gh_double2scm (qscores[best_idx].yr)));
286 if (debug_beam_quanting_flag)
288 qscores[best_idx].score_card_ += to_string ("i%d", best_idx);
291 me->set_grob_property ("quant-score",
292 scm_makfrom0str (qscores[best_idx].score_card_.to_str0 ()));
296 return SCM_UNSPECIFIED;
300 Beam::score_stem_lengths (Link_array<Grob> const &stems,
301 Array<Stem_info> const &stem_infos,
302 Array<Real> const &base_stem_ys,
303 Array<Real> const &stem_xs,
308 Real limit_penalty = STEM_LENGTH_LIMIT_PENALTY;
309 Drul_array<Real> score (0, 0);
310 Drul_array<int> count (0, 0);
312 for (int i=0; i < stems.size (); i++)
315 if (Stem::invisible_b (s))
320 Real beam_y = dx ? yr *(x - xl)/dx + yl * ( xr - x)/dx : (yr + yl)/2;
321 Real current_y = beam_y + base_stem_ys[i];
322 Real length_pen = STEM_LENGTH_DEMERIT_FACTOR;
324 Stem_info info = stem_infos[i];
325 Direction d = info.dir_;
327 score[d] += limit_penalty * (0 >? (d * (info.shortest_y_ - current_y)));
329 Real ideal_diff = d * (current_y - info.ideal_y_);
330 Real ideal_score = shrink_extra_weight (ideal_diff, 1.5);
332 /* We introduce a power, to make the scoring strictly
333 convex. Otherwise a symmetric knee beam (up/down/up/down)
334 does not have an optimum in the middle. */
336 ideal_score = pow (ideal_score, 1.1);
338 score[d] += length_pen * ideal_score;
346 score[d] /= (count[d] >? 1);
348 while (flip (&d) != DOWN);
350 return score[LEFT]+score[RIGHT];
354 Beam::score_slopes_dy (Real yl, Real yr,
355 Real dy_mus, Real dy_damp,
361 if (sign (dy_damp) != sign (dy))
363 dem += DAMPING_DIRECTIION_PENALTY;
366 dem += MUSICAL_DIRECTION_FACTOR * (0 >? (fabs (dy) - fabs (dy_mus)));
369 Real slope_penalty = IDEAL_SLOPE_FACTOR;
371 /* Xstaff beams tend to use extreme slopes to get short stems. We
372 put in a penalty here. */
376 /* Huh, why would a too steep beam be better than a too flat one ? */
377 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy), 1.5)
385 return x - floor (x);
390 TODO: The fixed value SECONDARY_BEAM_DEMERIT is probably flawed:
391 because for 32nd and 64th beams the forbidden quants are relatively
392 more important than stem lengths.
395 Beam::score_forbidden_quants (Real yl, Real yr,
398 Real thickness, Real beam_translation,
400 Direction ldir, Direction rdir)
403 Drul_array<Real> y(yl,yr);
404 Drul_array<Direction> dirs(ldir,rdir);
406 Real extra_demerit = SECONDARY_BEAM_DEMERIT / beam_count;
409 Inside the staff, inter quants are forbidden.
415 if (fabs (y[d]) <= (radius + 0.5) && fabs (my_modf (y[d]) - 0.5) < 1e-3)
416 dem += INTER_QUANT_PENALTY;
418 while ((flip (&d))!= LEFT);
421 for (int j = 1; j <= beam_count; j++)
426 see if the outer staffline falls in a beam-gap
428 This test is too weak; we should really check all lines.
430 Direction stem_dir = dirs[d];
431 Real gap1 = y[d] - stem_dir * ((j-1) * beam_translation + thickness / 2 - slt/2 );
432 Real gap2 = y[d] - stem_dir * (j * beam_translation - thickness / 2 + slt/2);
435 gap.add_point (gap1);
436 gap.add_point (gap2);
438 if (gap.elem_b (radius))
439 dem += extra_demerit;
441 while ((flip (&d))!= LEFT);
446 // todo: use beam_count of outer stems.
450 Real sit = (thickness - slt) / 2;
452 Real hang = 1.0 - (thickness - slt) / 2;
456 // hmm, without Interval/Drul_array, you get ~ 4x same code...
457 if (fabs (y[LEFT] - dirs[LEFT] * beam_translation) < radius + inter)
459 if (dirs[LEFT] == UP && dy <= eps
460 && fabs (my_modf (y[LEFT]) - sit) < eps)
461 dem += extra_demerit;
463 if (dirs[LEFT] == DOWN && dy >= eps
464 && fabs (my_modf (y[LEFT]) - hang) < eps)
465 dem += extra_demerit;
468 if (fabs (y[RIGHT] - dirs[RIGHT] * beam_translation) < radius + inter)
470 if (dirs[RIGHT] == UP && dy >= eps
471 && fabs (my_modf (y[RIGHT]) - sit) < eps)
472 dem += extra_demerit;
474 if (dirs[RIGHT] == DOWN && dy <= eps
475 && fabs (my_modf (y[RIGHT]) - hang) < eps)
476 dem += extra_demerit;
481 if (fabs (y[LEFT] - 2 * dirs[LEFT] * beam_translation) < radius + inter)
483 if (dirs[LEFT] == UP && dy <= eps
484 && fabs (my_modf (y[LEFT]) - straddle) < eps)
485 dem += extra_demerit;
487 if (dirs[LEFT] == DOWN && dy >= eps
488 && fabs (my_modf (y[LEFT]) - straddle) < eps)
489 dem += extra_demerit;
492 if (fabs (y[RIGHT] - 2 * dirs[RIGHT] * beam_translation) < radius + inter)
494 if (dirs[RIGHT] == UP && dy >= eps
495 && fabs (my_modf (y[RIGHT]) - straddle) < eps)
496 dem += extra_demerit;
498 if (dirs[RIGHT] == DOWN && dy <= eps
499 && fabs (my_modf (y[RIGHT]) - straddle) < eps)
500 dem += extra_demerit;