2 beam-quanting.cc -- implement Beam quanting functions
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2002 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.
176 for (int i = -region_size ; i < region_size; i++)
177 for (int j = 0; j < num_quants; j++)
179 quantsl.push (i + quants[j] + int (yl));
180 quantsr.push (i + quants[j] + int (yr));
183 Array<Quant_score> qscores;
185 for (int l =0; l < quantsl.size (); l++)
186 for (int r =0; r < quantsr.size (); r++)
196 /* This is a longish function, but we don't separate this out into
197 neat modular separate subfunctions, as the subfunctions would be
198 called for many values of YL, YR. By precomputing various
199 parameters outside of the loop, we can save a lot of time. */
200 for (int i = qscores.size (); i--;)
203 += score_slopes_dy (qscores[i].yl, qscores[i].yr,
204 dy_mus, yr- yl, xstaff);
207 Real rad = Staff_symbol_referencer::staff_radius (me);
208 int beam_count = get_beam_count (me);
209 Real beam_translation = get_beam_translation (me);
211 Real reasonable_score = (knee_b) ? 200000 : 100;
212 for (int i = qscores.size (); i--;)
213 if (qscores[i].demerits < reasonable_score)
216 += score_forbidden_quants (qscores[i].yl, qscores[i].yr,
217 rad, slt, thickness, beam_translation,
218 beam_count, ldir, rdir);
221 ; /* silly gdb thinks best_idx is inside for loop. */
222 for (int i = qscores.size (); i--;)
223 if (qscores[i].demerits < reasonable_score)
226 += score_stem_lengths (stems, stem_infos,
227 base_lengths, stem_xposns,
230 qscores[i].yl, qscores[i].yr);
233 ; /* silly gdb thinks best_idx is inside for loop. */
234 int best_idx = best_quant_score_idx (qscores);
235 me->set_grob_property ("positions",
236 gh_cons (gh_double2scm (qscores[best_idx].yl),
237 gh_double2scm (qscores[best_idx].yr))
243 me->set_grob_property ("quant-score",
244 gh_double2scm (qscores[best_idx].demerits));
245 me->set_grob_property ("best-idx", scm_int2num (best_idx));
248 return SCM_UNSPECIFIED;
252 Beam::score_stem_lengths (Link_array<Grob>stems,
253 Array<Stem_info> stem_infos,
254 Array<Real> base_stem_ys,
260 Real limit_penalty = STEM_LENGTH_LIMIT_PENALTY;
261 Drul_array<Real> score (0, 0);
262 Drul_array<int> count (0, 0);
264 for (int i=0; i < stems.size (); i++)
267 if (Stem::invisible_b (s))
272 Real beam_y = yr *(x - xl)/dx + yl * ( xr - x)/dx;
273 Real current_y = beam_y + base_stem_ys[i];
274 Real length_pen = STEM_LENGTH_DEMERIT_FACTOR;
276 Stem_info info = stem_infos[i];
277 Direction d = info.dir_;
279 score[d] += limit_penalty * (0 >? (d * (info.shortest_y_ - current_y)));
281 Real ideal_diff = d * (current_y - info.ideal_y_);
282 Real ideal_score = shrink_extra_weight (ideal_diff, 1.5);
284 /* We introduce a power, to make the scoring strictly
285 convex. Otherwise a symmetric knee beam (up/down/up/down)
286 does not have an optimum in the middle. */
288 ideal_score = pow (ideal_score, 1.1);
290 score[d] += length_pen * ideal_score;
296 score[LEFT] /= count[LEFT];
298 score[RIGHT] /= count[RIGHT];
300 return score[LEFT]+score[RIGHT];
304 Beam::score_slopes_dy (Real yl, Real yr,
305 Real dy_mus, Real dy_damp,
311 if (sign (dy_damp) != sign (dy))
313 dem += DAMPING_DIRECTIION_PENALTY;
316 dem += MUSICAL_DIRECTION_FACTOR * (0 >? (fabs (dy) - fabs (dy_mus)));
319 Real slope_penalty = IDEAL_SLOPE_FACTOR;
321 /* Xstaff beams tend to use extreme slopes to get short stems. We
322 put in a penalty here. */
326 /* Huh, why would a too steep beam be better than a too flat one ? */
327 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy), 1.5)
335 return x - floor (x);
339 Beam::score_forbidden_quants (Real yl, Real yr,
342 Real thickness, Real beam_translation,
344 Direction ldir, Direction rdir)
349 if (fabs (yl) < rad && fabs ( my_modf (yl) - 0.5) < 1e-3)
350 dem += INTER_QUANT_PENALTY;
351 if (fabs (yr) < rad && fabs ( my_modf (yr) - 0.5) < 1e-3)
352 dem += INTER_QUANT_PENALTY;
354 // todo: use beam_count of outer stems.
359 Real sit = (thickness - slt) / 2;
361 Real hang = 1.0 - (thickness - slt) / 2;
364 if (fabs (yl - ldir * beam_translation) < rad
365 && fabs (my_modf (yl) - inter) < 1e-3)
366 dem += SECONDARY_BEAM_DEMERIT;
367 if (fabs (yr - rdir * beam_translation) < rad
368 && fabs (my_modf (yr) - inter) < 1e-3)
369 dem += SECONDARY_BEAM_DEMERIT;
374 Can't we simply compute the distance between the nearest
375 staffline and the secondary beam? That would get rid of the
376 silly case analysis here (which is probably not when we have
377 different beam-thicknesses.)
383 // hmm, without Interval/Drul_array, you get ~ 4x same code...
384 if (fabs (yl - ldir * beam_translation) < rad + inter)
386 if (ldir == UP && dy <= eps
387 && fabs (my_modf (yl) - sit) < eps)
388 dem += SECONDARY_BEAM_DEMERIT;
390 if (ldir == DOWN && dy >= eps
391 && fabs (my_modf (yl) - hang) < eps)
392 dem += SECONDARY_BEAM_DEMERIT;
395 if (fabs (yr - rdir * beam_translation) < rad + inter)
397 if (rdir == UP && dy >= eps
398 && fabs (my_modf (yr) - sit) < eps)
399 dem += SECONDARY_BEAM_DEMERIT;
401 if (rdir == DOWN && dy <= eps
402 && fabs (my_modf (yr) - hang) < eps)
403 dem += SECONDARY_BEAM_DEMERIT;
408 if (fabs (yl - 2 * ldir * beam_translation) < rad + inter)
410 if (ldir == UP && dy <= eps
411 && fabs (my_modf (yl) - straddle) < eps)
412 dem += SECONDARY_BEAM_DEMERIT;
414 if (ldir == DOWN && dy >= eps
415 && fabs (my_modf (yl) - straddle) < eps)
416 dem += SECONDARY_BEAM_DEMERIT;
419 if (fabs (yr - 2 * rdir * beam_translation) < rad + inter)
421 if (rdir == UP && dy >= eps
422 && fabs (my_modf (yr) - straddle) < eps)
423 dem += SECONDARY_BEAM_DEMERIT;
425 if (rdir == DOWN && dy <= eps
426 && fabs (my_modf (yr) - straddle) < eps)
427 dem += SECONDARY_BEAM_DEMERIT;