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>
16 #include "staff-symbol-referencer.hh"
19 #include "paper-def.hh"
20 #include "group-interface.hh"
21 #include "align-interface.hh"
23 const int INTER_QUANT_PENALTY = 1000;
24 const int SECONDARY_BEAM_DEMERIT = 5;
25 const int STEM_LENGTH_DEMERIT_FACTOR = 5;
27 // possibly ridiculous, but too short stems just won't do
28 const int STEM_LENGTH_LIMIT_PENALTY = 5000;
29 const int DAMPING_DIRECTIION_PENALTY = 800;
30 const int MUSICAL_DIRECTION_FACTOR = 400;
31 const int IDEAL_SLOPE_FACTOR = 10;
35 shrink_extra_weight (Real x, Real fac)
37 return fabs (x) * ((x < 0) ? fac : 1.0);
52 - Make all demerits customisable
54 - One sensible check per demerit (what's this --hwn)
56 - Add demerits for quants per se, as to forbid a specific quant
61 int best_quant_score_idx (Array<Quant_score> const & qscores)
65 for (int i = qscores.size (); i--;)
67 if (qscores[i].demerits < best)
69 best = qscores [i].demerits ;
77 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
79 Beam::quanting (SCM smob)
81 Grob *me = unsmob_grob (smob);
83 SCM s = me->get_grob_property ("positions");
84 Real yl = gh_scm2double (gh_car (s));
85 Real yr = gh_scm2double (gh_cdr (s));
87 Real ss = Staff_symbol_referencer::staff_space (me);
88 Real thickness = gh_scm2double (me->get_grob_property ("thickness")) / ss;
89 Real slt = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness")) / ss;
92 SCM sdy = me->get_grob_property ("least-squares-dy");
93 Real dy_mus = gh_number_p (sdy) ? gh_scm2double (sdy) : 0.0;
96 Real sit = (thickness - slt) / 2;
98 Real hang = 1.0 - (thickness - slt) / 2;
99 Real quants [] = {straddle, sit, inter, hang };
101 int num_quants = int (sizeof (quants)/sizeof (Real));
106 going to REGION_SIZE == 2, yields another 0.6 second with
110 (result indexes between 70 and 575) ? --hwn.
117 Do stem computations. These depend on YL and YR linearly, so we can
118 precompute for every stem 2 factors.
120 Link_array<Grob> stems=
121 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
122 Array<Stem_info> stem_infos;
123 Array<Real> base_lengths;
124 Array<Real> stem_xposns;
126 Drul_array<bool> dirs_found(0,0);
128 for (int a = 2; a--;)
129 common[a] = common_refpoint_of_array (stems, me, Axis(a));
131 Grob * fvs = first_visible_stem (me);
132 Grob *lvs = last_visible_stem (me);
133 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
134 Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
137 We store some info to quickly interpolate.
139 Sometimes my head is screwed on backwards. The stemlength are
140 AFFINE linear in YL and YR. If YL == YR == 0, then we might have
141 stem_y != 0.0, when we're cross staff.
144 for (int i= 0; i < stems.size(); i++)
147 stem_infos.push (Stem::get_stem_info (s));
148 dirs_found[stem_infos.top ().dir_] = true;
150 bool f = to_boolean (s->get_grob_property ("french-beaming"))
151 && s != lvs && s != fvs;
153 base_lengths.push (calc_stem_y (me, s, common, xl, xr,
155 stem_xposns.push (s->relative_coordinate (common[X_AXIS], X_AXIS));
161 Grob *commony = fvs->common_refpoint (lvs, Y_AXIS);
162 xstaff = Align_interface::has_interface (commony);
165 Direction ldir = Direction (stem_infos[0].dir_);
166 Direction rdir = Direction (stem_infos.top ().dir_);
167 bool knee_b = dirs_found[LEFT] && dirs_found[RIGHT];
170 int region_size = REGION_SIZE;
172 Knees are harder, lets try some more possibilities for knees.
178 Asymetry ? should run to <= region_size ?
180 for (int i = -region_size ; i < region_size; i++)
181 for (int j = 0; j < num_quants; j++)
183 quantsl.push (i + quants[j] + int (yl));
184 quantsr.push (i + quants[j] + int (yr));
187 Array<Quant_score> qscores;
189 for (int l =0; l < quantsl.size (); l++)
190 for (int r =0; r < quantsr.size (); r++)
200 /* This is a longish function, but we don't separate this out into
201 neat modular separate subfunctions, as the subfunctions would be
202 called for many values of YL, YR. By precomputing various
203 parameters outside of the loop, we can save a lot of time. */
204 for (int i = qscores.size (); i--;)
207 += score_slopes_dy (qscores[i].yl, qscores[i].yr,
208 dy_mus, yr- yl, xstaff);
211 Real rad = Staff_symbol_referencer::staff_radius (me);
212 int beam_count = get_beam_count (me);
213 Real beam_translation = get_beam_translation (me);
215 Real reasonable_score = (knee_b) ? 200000 : 100;
216 for (int i = qscores.size (); i--;)
217 if (qscores[i].demerits < reasonable_score)
220 += score_forbidden_quants (qscores[i].yl, qscores[i].yr,
221 rad, slt, thickness, beam_translation,
222 beam_count, ldir, rdir);
225 for (int i = qscores.size (); i--;)
226 if (qscores[i].demerits < reasonable_score)
229 += score_stem_lengths (stems, stem_infos,
230 base_lengths, stem_xposns,
233 qscores[i].yl, qscores[i].yr);
236 int best_idx = best_quant_score_idx (qscores);
237 me->set_grob_property ("positions",
238 gh_cons (gh_double2scm (qscores[best_idx].yl),
239 gh_double2scm (qscores[best_idx].yr))
245 me->set_grob_property ("quant-score",
246 gh_double2scm (qscores[best_idx].demerits));
247 me->set_grob_property ("best-idx", scm_int2num (best_idx));
250 return SCM_UNSPECIFIED;
254 Beam::score_stem_lengths (Link_array<Grob> const &stems,
255 Array<Stem_info> const &stem_infos,
256 Array<Real> const &base_stem_ys,
257 Array<Real> const &stem_xs,
262 Real limit_penalty = STEM_LENGTH_LIMIT_PENALTY;
263 Drul_array<Real> score (0, 0);
264 Drul_array<int> count (0, 0);
266 for (int i=0; i < stems.size (); i++)
269 if (Stem::invisible_b (s))
274 Real beam_y = dx ? yr *(x - xl)/dx + yl * ( xr - x)/dx : (yr + yl)/2;
275 Real current_y = beam_y + base_stem_ys[i];
276 Real length_pen = STEM_LENGTH_DEMERIT_FACTOR;
278 Stem_info info = stem_infos[i];
279 Direction d = info.dir_;
281 score[d] += limit_penalty * (0 >? (d * (info.shortest_y_ - current_y)));
283 Real ideal_diff = d * (current_y - info.ideal_y_);
284 Real ideal_score = shrink_extra_weight (ideal_diff, 1.5);
286 /* We introduce a power, to make the scoring strictly
287 convex. Otherwise a symmetric knee beam (up/down/up/down)
288 does not have an optimum in the middle. */
290 ideal_score = pow (ideal_score, 1.1);
292 score[d] += length_pen * ideal_score;
300 score[d] /= (count[d] >? 1);
302 while (flip (&d) != DOWN);
304 return score[LEFT]+score[RIGHT];
308 Beam::score_slopes_dy (Real yl, Real yr,
309 Real dy_mus, Real dy_damp,
315 if (sign (dy_damp) != sign (dy))
317 dem += DAMPING_DIRECTIION_PENALTY;
320 dem += MUSICAL_DIRECTION_FACTOR * (0 >? (fabs (dy) - fabs (dy_mus)));
323 Real slope_penalty = IDEAL_SLOPE_FACTOR;
325 /* Xstaff beams tend to use extreme slopes to get short stems. We
326 put in a penalty here. */
330 /* Huh, why would a too steep beam be better than a too flat one ? */
331 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy), 1.5)
339 return x - floor (x);
344 TODO: The fixed value SECONDARY_BEAM_DEMERIT is probably flawed:
345 because for 32nd and 64th beams the forbidden quants are relatively
346 more important than stem lengths.
349 Beam::score_forbidden_quants (Real yl, Real yr,
352 Real thickness, Real beam_translation,
354 Direction ldir, Direction rdir)
359 for (int i = 0; i < 2; i++)
362 if (fabs (y) <= (radius + 0.5) && fabs ( my_modf (y) - 0.5) < 1e-3)
363 dem += INTER_QUANT_PENALTY;
366 // todo: use beam_count of outer stems.
370 Real sit = (thickness - slt) / 2;
372 Real hang = 1.0 - (thickness - slt) / 2;
375 if (fabs (yl - ldir * beam_translation) < radius
376 && fabs (my_modf (yl) - inter) < 1e-3)
377 dem += SECONDARY_BEAM_DEMERIT;
378 if (fabs (yr - rdir * beam_translation) < radius
379 && fabs (my_modf (yr) - inter) < 1e-3)
380 dem += SECONDARY_BEAM_DEMERIT;
385 Can't we simply compute the distance between the nearest
386 staffline and the secondary beam? That would get rid of the
387 silly case analysis here (which is probably not valid when we
388 have different beam-thicknesses.)
394 // hmm, without Interval/Drul_array, you get ~ 4x same code...
395 if (fabs (yl - ldir * beam_translation) < radius + inter)
397 if (ldir == UP && dy <= eps
398 && fabs (my_modf (yl) - sit) < eps)
399 dem += SECONDARY_BEAM_DEMERIT;
401 if (ldir == DOWN && dy >= eps
402 && fabs (my_modf (yl) - hang) < eps)
403 dem += SECONDARY_BEAM_DEMERIT;
406 if (fabs (yr - rdir * beam_translation) < radius + inter)
408 if (rdir == UP && dy >= eps
409 && fabs (my_modf (yr) - sit) < eps)
410 dem += SECONDARY_BEAM_DEMERIT;
412 if (rdir == DOWN && dy <= eps
413 && fabs (my_modf (yr) - hang) < eps)
414 dem += SECONDARY_BEAM_DEMERIT;
419 if (fabs (yl - 2 * ldir * beam_translation) < radius + inter)
421 if (ldir == UP && dy <= eps
422 && fabs (my_modf (yl) - straddle) < eps)
423 dem += SECONDARY_BEAM_DEMERIT;
425 if (ldir == DOWN && dy >= eps
426 && fabs (my_modf (yl) - straddle) < eps)
427 dem += SECONDARY_BEAM_DEMERIT;
430 if (fabs (yr - 2 * rdir * beam_translation) < radius + inter)
432 if (rdir == UP && dy >= eps
433 && fabs (my_modf (yr) - straddle) < eps)
434 dem += SECONDARY_BEAM_DEMERIT;
436 if (rdir == DOWN && dy <= eps
437 && fabs (my_modf (yr) - straddle) < eps)
438 dem += SECONDARY_BEAM_DEMERIT;