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_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 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> const &stems,
256 Array<Stem_info> const &stem_infos,
257 Array<Real> const &base_stem_ys,
258 Array<Real> const &stem_xs,
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;
302 score[d] /= count[d];
304 while (flip (&d) != DOWN);
306 return score[LEFT]+score[RIGHT];
310 Beam::score_slopes_dy (Real yl, Real yr,
311 Real dy_mus, Real dy_damp,
317 if (sign (dy_damp) != sign (dy))
319 dem += DAMPING_DIRECTIION_PENALTY;
322 dem += MUSICAL_DIRECTION_FACTOR * (0 >? (fabs (dy) - fabs (dy_mus)));
325 Real slope_penalty = IDEAL_SLOPE_FACTOR;
327 /* Xstaff beams tend to use extreme slopes to get short stems. We
328 put in a penalty here. */
332 /* Huh, why would a too steep beam be better than a too flat one ? */
333 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy), 1.5)
341 return x - floor (x);
345 Beam::score_forbidden_quants (Real yl, Real yr,
348 Real thickness, Real beam_translation,
350 Direction ldir, Direction rdir)
355 for (int i = 0; i < 2; i++)
358 if (fabs (y) <= (radius + 0.5) && fabs ( my_modf (y) - 0.5) < 1e-3)
359 dem += INTER_QUANT_PENALTY;
362 // todo: use beam_count of outer stems.
366 Real sit = (thickness - slt) / 2;
368 Real hang = 1.0 - (thickness - slt) / 2;
371 if (fabs (yl - ldir * beam_translation) < radius
372 && fabs (my_modf (yl) - inter) < 1e-3)
373 dem += SECONDARY_BEAM_DEMERIT;
374 if (fabs (yr - rdir * beam_translation) < radius
375 && fabs (my_modf (yr) - inter) < 1e-3)
376 dem += SECONDARY_BEAM_DEMERIT;
381 Can't we simply compute the distance between the nearest
382 staffline and the secondary beam? That would get rid of the
383 silly case analysis here (which is probably not valid when we
384 have different beam-thicknesses.)
390 // hmm, without Interval/Drul_array, you get ~ 4x same code...
391 if (fabs (yl - ldir * beam_translation) < radius + inter)
393 if (ldir == UP && dy <= eps
394 && fabs (my_modf (yl) - sit) < eps)
395 dem += SECONDARY_BEAM_DEMERIT;
397 if (ldir == DOWN && dy >= eps
398 && fabs (my_modf (yl) - hang) < eps)
399 dem += SECONDARY_BEAM_DEMERIT;
402 if (fabs (yr - rdir * beam_translation) < radius + inter)
404 if (rdir == UP && dy >= eps
405 && fabs (my_modf (yr) - sit) < eps)
406 dem += SECONDARY_BEAM_DEMERIT;
408 if (rdir == DOWN && dy <= eps
409 && fabs (my_modf (yr) - hang) < eps)
410 dem += SECONDARY_BEAM_DEMERIT;
415 if (fabs (yl - 2 * ldir * beam_translation) < radius + inter)
417 if (ldir == UP && dy <= eps
418 && fabs (my_modf (yl) - straddle) < eps)
419 dem += SECONDARY_BEAM_DEMERIT;
421 if (ldir == DOWN && dy >= eps
422 && fabs (my_modf (yl) - straddle) < eps)
423 dem += SECONDARY_BEAM_DEMERIT;
426 if (fabs (yr - 2 * rdir * beam_translation) < radius + inter)
428 if (rdir == UP && dy >= eps
429 && fabs (my_modf (yr) - straddle) < eps)
430 dem += SECONDARY_BEAM_DEMERIT;
432 if (rdir == DOWN && dy <= eps
433 && fabs (my_modf (yr) - straddle) < eps)
434 dem += SECONDARY_BEAM_DEMERIT;