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 = 15;
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_var ("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 bool french = to_boolean (me->get_grob_property ("french-beaming"));
145 for (int i= 0; i < stems.size(); i++)
148 stem_infos.push (Stem::get_stem_info (s));
149 dirs_found[stem_infos.top ().dir_] = true;
151 bool f = french && i > 0&& (i < stems.size () -1);
152 base_lengths.push (calc_stem_y (me, s, common, xl, xr,
154 stem_xposns.push (s->relative_coordinate (common[X_AXIS], X_AXIS));
160 Grob *commony = fvs->common_refpoint (lvs, Y_AXIS);
161 xstaff = Align_interface::has_interface (commony);
164 Direction ldir = Direction (stem_infos[0].dir_);
165 Direction rdir = Direction (stem_infos.top ().dir_);
166 bool knee_b = dirs_found[LEFT] && dirs_found[RIGHT];
169 int region_size = REGION_SIZE;
171 Knees are harder, lets try some more possibilities for knees.
177 Asymetry ? should run to <= region_size ?
179 for (int i = -region_size ; i < region_size; i++)
180 for (int j = 0; j < num_quants; j++)
182 quantsl.push (i + quants[j] + int (yl));
183 quantsr.push (i + quants[j] + int (yr));
186 Array<Quant_score> qscores;
188 for (int l =0; l < quantsl.size (); l++)
189 for (int r =0; r < quantsr.size (); r++)
199 /* This is a longish function, but we don't separate this out into
200 neat modular separate subfunctions, as the subfunctions would be
201 called for many values of YL, YR. By precomputing various
202 parameters outside of the loop, we can save a lot of time. */
203 for (int i = qscores.size (); i--;)
206 += score_slopes_dy (qscores[i].yl, qscores[i].yr,
207 dy_mus, yr- yl, xstaff);
210 Real rad = Staff_symbol_referencer::staff_radius (me);
211 int beam_count = get_beam_count (me);
212 Real beam_translation = get_beam_translation (me);
214 Real reasonable_score = (knee_b) ? 200000 : 100;
215 for (int i = qscores.size (); i--;)
216 if (qscores[i].demerits < reasonable_score)
219 += score_forbidden_quants (qscores[i].yl, qscores[i].yr,
220 rad, slt, thickness, beam_translation,
221 beam_count, ldir, rdir);
224 ; /* silly gdb thinks best_idx is inside for loop. */
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 ; /* silly gdb thinks best_idx is inside for loop. */
237 int best_idx = best_quant_score_idx (qscores);
238 me->set_grob_property ("positions",
239 gh_cons (gh_double2scm (qscores[best_idx].yl),
240 gh_double2scm (qscores[best_idx].yr))
246 me->set_grob_property ("quant-score",
247 gh_double2scm (qscores[best_idx].demerits));
248 me->set_grob_property ("best-idx", scm_int2num (best_idx));
251 return SCM_UNSPECIFIED;
255 Beam::score_stem_lengths (Link_array<Grob>stems,
256 Array<Stem_info> stem_infos,
257 Array<Real> base_stem_ys,
263 Real limit_penalty = STEM_LENGTH_LIMIT_PENALTY;
264 Drul_array<Real> score (0, 0);
265 Drul_array<int> count (0, 0);
267 for (int i=0; i < stems.size (); i++)
270 if (Stem::invisible_b (s))
275 Real beam_y = yr *(x - xl)/dx + yl * ( xr - x)/dx;
276 Real current_y = beam_y + base_stem_ys[i];
277 Real length_pen = STEM_LENGTH_DEMERIT_FACTOR;
279 Stem_info info = stem_infos[i];
280 Direction d = info.dir_;
282 score[d] += limit_penalty * (0 >? (d * (info.shortest_y_ - current_y)));
284 Real ideal_diff = d * (current_y - info.ideal_y_);
285 Real ideal_score = shrink_extra_weight (ideal_diff, 1.5);
287 /* We introduce a power, to make the scoring strictly
288 convex. Otherwise a symmetric knee beam (up/down/up/down)
289 does not have an optimum in the middle. */
291 ideal_score = pow (ideal_score, 1.1);
293 score[d] += length_pen * ideal_score;
299 score[LEFT] /= count[LEFT];
301 score[RIGHT] /= count[RIGHT];
303 return score[LEFT]+score[RIGHT];
307 Beam::score_slopes_dy (Real yl, Real yr,
308 Real dy_mus, Real dy_damp,
314 if (sign (dy_damp) != sign (dy))
316 dem += DAMPING_DIRECTIION_PENALTY;
319 dem += MUSICAL_DIRECTION_FACTOR * (0 >? (fabs (dy) - fabs (dy_mus)));
322 Real slope_penalty = IDEAL_SLOPE_FACTOR;
324 /* Xstaff beams tend to use extreme slopes to get short stems. We
325 put in a penalty here. */
329 /* Huh, why would a too steep beam be better than a too flat one ? */
330 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy), 1.5)
338 return x - floor (x);
342 Beam::score_forbidden_quants (Real yl, Real yr,
345 Real thickness, Real beam_translation,
347 Direction ldir, Direction rdir)
352 if (fabs (yl) < rad && fabs ( my_modf (yl) - 0.5) < 1e-3)
353 dem += INTER_QUANT_PENALTY;
354 if (fabs (yr) < rad && fabs ( my_modf (yr) - 0.5) < 1e-3)
355 dem += INTER_QUANT_PENALTY;
357 // todo: use beam_count of outer stems.
362 Real sit = (thickness - slt) / 2;
364 Real hang = 1.0 - (thickness - slt) / 2;
367 if (fabs (yl - ldir * beam_translation) < rad
368 && fabs (my_modf (yl) - inter) < 1e-3)
369 dem += SECONDARY_BEAM_DEMERIT;
370 if (fabs (yr - rdir * beam_translation) < rad
371 && fabs (my_modf (yr) - inter) < 1e-3)
372 dem += SECONDARY_BEAM_DEMERIT;
377 Can't we simply compute the distance between the nearest
378 staffline and the secondary beam? That would get rid of the
379 silly case analysis here (which is probably not valid when we
380 have different beam-thicknesses.)
386 // hmm, without Interval/Drul_array, you get ~ 4x same code...
387 if (fabs (yl - ldir * beam_translation) < rad + inter)
389 if (ldir == UP && dy <= eps
390 && fabs (my_modf (yl) - sit) < eps)
391 dem += SECONDARY_BEAM_DEMERIT;
393 if (ldir == DOWN && dy >= eps
394 && fabs (my_modf (yl) - hang) < eps)
395 dem += SECONDARY_BEAM_DEMERIT;
398 if (fabs (yr - rdir * beam_translation) < rad + inter)
400 if (rdir == UP && dy >= eps
401 && fabs (my_modf (yr) - sit) < eps)
402 dem += SECONDARY_BEAM_DEMERIT;
404 if (rdir == DOWN && dy <= eps
405 && fabs (my_modf (yr) - hang) < eps)
406 dem += SECONDARY_BEAM_DEMERIT;
411 if (fabs (yl - 2 * ldir * beam_translation) < rad + inter)
413 if (ldir == UP && dy <= eps
414 && fabs (my_modf (yl) - straddle) < eps)
415 dem += SECONDARY_BEAM_DEMERIT;
417 if (ldir == DOWN && dy >= eps
418 && fabs (my_modf (yl) - straddle) < eps)
419 dem += SECONDARY_BEAM_DEMERIT;
422 if (fabs (yr - 2 * rdir * beam_translation) < rad + inter)
424 if (rdir == UP && dy >= eps
425 && fabs (my_modf (yr) - straddle) < eps)
426 dem += SECONDARY_BEAM_DEMERIT;
428 if (rdir == DOWN && dy <= eps
429 && fabs (my_modf (yr) - straddle) < eps)
430 dem += SECONDARY_BEAM_DEMERIT;