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 // #define DEBUG_QUANTING 1
35 extern bool debug_beam_quanting_flag;
38 shrink_extra_weight (Real x, Real fac)
40 return fabs (x) * ((x < 0) ? fac : 1.0);
59 - Make all demerits customisable
61 - One sensible check per demerit (what's this --hwn)
63 - Add demerits for quants per se, as to forbid a specific quant
68 int best_quant_score_idx (Array<Quant_score> const & qscores)
72 for (int i = qscores.size (); i--;)
74 if (qscores[i].demerits < best)
76 best = qscores [i].demerits ;
84 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
86 Beam::quanting (SCM smob)
88 Grob *me = unsmob_grob (smob);
90 SCM s = me->get_grob_property ("positions");
91 Real yl = gh_scm2double (gh_car (s));
92 Real yr = gh_scm2double (gh_cdr (s));
94 Real ss = Staff_symbol_referencer::staff_space (me);
95 Real thickness = gh_scm2double (me->get_grob_property ("thickness")) / ss;
96 Real slt = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness")) / ss;
99 SCM sdy = me->get_grob_property ("least-squares-dy");
100 Real dy_mus = gh_number_p (sdy) ? gh_scm2double (sdy) : 0.0;
103 Real sit = (thickness - slt) / 2;
105 Real hang = 1.0 - (thickness - slt) / 2;
106 Real quants [] = {straddle, sit, inter, hang };
108 int num_quants = int (sizeof (quants)/sizeof (Real));
113 going to REGION_SIZE == 2, yields another 0.6 second with
117 (result indexes between 70 and 575) ? --hwn.
124 Do stem computations. These depend on YL and YR linearly, so we can
125 precompute for every stem 2 factors.
127 Link_array<Grob> stems=
128 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
129 Array<Stem_info> stem_infos;
130 Array<Real> base_lengths;
131 Array<Real> stem_xposns;
133 Drul_array<bool> dirs_found(0,0);
135 for (int a = 2; a--;)
136 common[a] = common_refpoint_of_array (stems, me, Axis(a));
138 Grob * fvs = first_visible_stem (me);
139 Grob *lvs = last_visible_stem (me);
140 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
141 Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
144 We store some info to quickly interpolate.
146 Sometimes my head is screwed on backwards. The stemlength are
147 AFFINE linear in YL and YR. If YL == YR == 0, then we might have
148 stem_y != 0.0, when we're cross staff.
151 for (int i= 0; i < stems.size(); i++)
154 stem_infos.push (Stem::get_stem_info (s));
155 dirs_found[stem_infos.top ().dir_] = true;
157 bool f = to_boolean (s->get_grob_property ("french-beaming"))
158 && s != lvs && s != fvs;
160 base_lengths.push (calc_stem_y (me, s, common, xl, xr,
162 stem_xposns.push (s->relative_coordinate (common[X_AXIS], X_AXIS));
168 Grob *commony = fvs->common_refpoint (lvs, Y_AXIS);
169 xstaff = Align_interface::has_interface (commony);
172 Direction ldir = Direction (stem_infos[0].dir_);
173 Direction rdir = Direction (stem_infos.top ().dir_);
174 bool knee_b = dirs_found[LEFT] && dirs_found[RIGHT];
177 int region_size = REGION_SIZE;
179 Knees are harder, lets try some more possibilities for knees.
185 Asymetry ? should run to <= region_size ?
187 for (int i = -region_size ; i < region_size; i++)
188 for (int j = 0; j < num_quants; j++)
190 quantsl.push (i + quants[j] + int (yl));
191 quantsr.push (i + quants[j] + int (yr));
194 Array<Quant_score> qscores;
196 for (int l =0; l < quantsl.size (); l++)
197 for (int r =0; r < quantsr.size (); r++)
207 /* This is a longish function, but we don't separate this out into
208 neat modular separate subfunctions, as the subfunctions would be
209 called for many values of YL, YR. By precomputing various
210 parameters outside of the loop, we can save a lot of time. */
211 for (int i = qscores.size (); i--;)
213 Real d = score_slopes_dy (qscores[i].yl, qscores[i].yr,
214 dy_mus, yr- yl, xstaff);
215 qscores[i].demerits += d;
218 qscores[i].score_card_ += to_string ("S%.2f",d);
222 Real rad = Staff_symbol_referencer::staff_radius (me);
223 int beam_count = get_beam_count (me);
224 Real beam_translation = get_beam_translation (me);
226 Real reasonable_score = (knee_b) ? 200000 : 100;
227 for (int i = qscores.size (); i--;)
228 if (qscores[i].demerits < reasonable_score)
230 Real d = score_forbidden_quants (qscores[i].yl, qscores[i].yr,
231 rad, slt, thickness, beam_translation,
232 beam_count, ldir, rdir);
233 qscores[i].demerits += d;
236 qscores[i].score_card_ += to_string (" F %.2f", d);
240 for (int i = qscores.size (); i--;)
241 if (qscores[i].demerits < reasonable_score)
243 Real d=score_stem_lengths (stems, stem_infos,
244 base_lengths, stem_xposns,
247 qscores[i].yl, qscores[i].yr);
248 qscores[i].demerits += d;
251 qscores[i].score_card_ += to_string (" L %.2f", d);
255 int best_idx = best_quant_score_idx (qscores);
259 SCM inspect_quants = me->get_grob_property ("inspect-quants");
260 if (debug_beam_quanting_flag
261 && gh_pair_p (inspect_quants))
263 Real il = gh_scm2double (gh_car (inspect_quants));
264 Real ir = gh_scm2double (gh_cdr (inspect_quants));
269 for (; i < qscores.size(); i ++)
271 Real d =fabs (qscores[i].yl-il) + fabs (qscores[i].yr - ir);
279 programming_error ("Could not find quant.");
281 qscores[best_idx].score_card_ += to_string ("i%d", best_idx);
285 me->set_grob_property ("positions",
286 gh_cons (gh_double2scm (qscores[best_idx].yl),
287 gh_double2scm (qscores[best_idx].yr)));
289 if (debug_beam_quanting_flag)
292 me->set_grob_property ("quant-score",
293 scm_makfrom0str (qscores[best_idx].score_card_.to_str0 ()));
297 return SCM_UNSPECIFIED;
301 Beam::score_stem_lengths (Link_array<Grob> const &stems,
302 Array<Stem_info> const &stem_infos,
303 Array<Real> const &base_stem_ys,
304 Array<Real> const &stem_xs,
309 Real limit_penalty = STEM_LENGTH_LIMIT_PENALTY;
310 Drul_array<Real> score (0, 0);
311 Drul_array<int> count (0, 0);
313 for (int i=0; i < stems.size (); i++)
316 if (Stem::invisible_b (s))
321 Real beam_y = dx ? yr *(x - xl)/dx + yl * ( xr - x)/dx : (yr + yl)/2;
322 Real current_y = beam_y + base_stem_ys[i];
323 Real length_pen = STEM_LENGTH_DEMERIT_FACTOR;
325 Stem_info info = stem_infos[i];
326 Direction d = info.dir_;
328 score[d] += limit_penalty * (0 >? (d * (info.shortest_y_ - current_y)));
330 Real ideal_diff = d * (current_y - info.ideal_y_);
331 Real ideal_score = shrink_extra_weight (ideal_diff, 1.5);
333 /* We introduce a power, to make the scoring strictly
334 convex. Otherwise a symmetric knee beam (up/down/up/down)
335 does not have an optimum in the middle. */
337 ideal_score = pow (ideal_score, 1.1);
339 score[d] += length_pen * ideal_score;
347 score[d] /= (count[d] >? 1);
349 while (flip (&d) != DOWN);
351 return score[LEFT]+score[RIGHT];
355 Beam::score_slopes_dy (Real yl, Real yr,
356 Real dy_mus, Real dy_damp,
362 if (sign (dy_damp) != sign (dy))
364 dem += DAMPING_DIRECTIION_PENALTY;
367 dem += MUSICAL_DIRECTION_FACTOR * (0 >? (fabs (dy) - fabs (dy_mus)));
370 Real slope_penalty = IDEAL_SLOPE_FACTOR;
372 /* Xstaff beams tend to use extreme slopes to get short stems. We
373 put in a penalty here. */
377 /* Huh, why would a too steep beam be better than a too flat one ? */
378 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy), 1.5)
386 return x - floor (x);
391 TODO: The fixed value SECONDARY_BEAM_DEMERIT is probably flawed:
392 because for 32nd and 64th beams the forbidden quants are relatively
393 more important than stem lengths.
396 Beam::score_forbidden_quants (Real yl, Real yr,
399 Real thickness, Real beam_translation,
401 Direction ldir, Direction rdir)
405 Real extra_demerit = SECONDARY_BEAM_DEMERIT / beam_count;
408 for (int i = 0; i < 2; i++)
411 if (fabs (y) <= (radius + 0.5) && fabs ( my_modf (y) - 0.5) < 1e-3)
412 dem += INTER_QUANT_PENALTY;
415 // todo: use beam_count of outer stems.
419 Real sit = (thickness - slt) / 2;
421 Real hang = 1.0 - (thickness - slt) / 2;
423 if (fabs (yl - ldir * beam_translation) < radius
424 && fabs (my_modf (yl) - inter) < 1e-3)
425 dem += extra_demerit;
426 if (fabs (yr - rdir * beam_translation) < radius
427 && fabs (my_modf (yr) - inter) < 1e-3)
428 dem += extra_demerit;
433 Can't we simply compute the distance between the nearest
434 staffline and the secondary beam? That would get rid of the
435 silly case analysis here (which is probably not valid when we
436 have different beam-thicknesses.)
442 // hmm, without Interval/Drul_array, you get ~ 4x same code...
443 if (fabs (yl - ldir * beam_translation) < radius + inter)
445 if (ldir == UP && dy <= eps
446 && fabs (my_modf (yl) - sit) < eps)
447 dem += extra_demerit;
449 if (ldir == DOWN && dy >= eps
450 && fabs (my_modf (yl) - hang) < eps)
451 dem += extra_demerit;
454 if (fabs (yr - rdir * beam_translation) < radius + inter)
456 if (rdir == UP && dy >= eps
457 && fabs (my_modf (yr) - sit) < eps)
458 dem += extra_demerit;
460 if (rdir == DOWN && dy <= eps
461 && fabs (my_modf (yr) - hang) < eps)
462 dem += extra_demerit;
467 if (fabs (yl - 2 * ldir * beam_translation) < radius + inter)
469 if (ldir == UP && dy <= eps
470 && fabs (my_modf (yl) - straddle) < eps)
471 dem += extra_demerit;
473 if (ldir == DOWN && dy >= eps
474 && fabs (my_modf (yl) - straddle) < eps)
475 dem += extra_demerit;
478 if (fabs (yr - 2 * rdir * beam_translation) < radius + inter)
480 if (rdir == UP && dy >= eps
481 && fabs (my_modf (yr) - straddle) < eps)
482 dem += extra_demerit;
484 if (rdir == DOWN && dy <= eps
485 && fabs (my_modf (yr) - straddle) < eps)
486 dem += extra_demerit;