staff-symbol-referencer.cc: Junk redundant functions.
[lilypond.git] / lily / beam-quanting.cc
blob17ad169a1fc34d18a2372d57ce2346bae0c2789c
1 /*
2 beam-quanting.cc -- implement Beam quanting functions
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
8 */
10 #include "beam.hh"
12 #include <algorithm>
13 using namespace std;
15 #include "grob.hh"
16 #include "align-interface.hh"
17 #include "international.hh"
18 #include "output-def.hh"
19 #include "pointer-group-interface.hh"
20 #include "staff-symbol-referencer.hh"
21 #include "stem.hh"
22 #include "warn.hh"
23 #include "main.hh"
25 Real
26 get_detail (SCM alist, SCM sym, Real def)
28 SCM entry = scm_assq (sym, alist);
30 if (scm_is_pair (entry))
31 return robust_scm2double (scm_cdr (entry), def);
32 return def;
35 void
36 Beam_quant_parameters::fill (Grob *him)
38 SCM details = him->get_property ("details");
40 /*
41 TODO: The default values should be copied to define-grobs.scm.
43 INTER_QUANT_PENALTY = get_detail (details, ly_symbol2scm ("inter-quant-penalty"), 1000.0);
44 SECONDARY_BEAM_DEMERIT = get_detail (details, ly_symbol2scm ("secondary-beam-demerit"), 10.0);
45 STEM_LENGTH_DEMERIT_FACTOR = get_detail (details, ly_symbol2scm ("stem-length-demerit-factor"), 5);
46 REGION_SIZE = get_detail (details, ly_symbol2scm ("region-size"), 2);
47 BEAM_EPS = get_detail (details, ly_symbol2scm ("beam-eps"), 1e-3);
48 STEM_LENGTH_LIMIT_PENALTY = get_detail (details, ly_symbol2scm ("stem-length-limit-penalty"), 5000);
49 DAMPING_DIRECTION_PENALTY = get_detail (details, ly_symbol2scm ("damping-direction-penalty"), 800);
50 HINT_DIRECTION_PENALTY = get_detail (details, ly_symbol2scm ("hint-direction-penalty"), 20);
51 MUSICAL_DIRECTION_FACTOR = get_detail (details, ly_symbol2scm ("musical-direction-factor"), 400);
52 IDEAL_SLOPE_FACTOR = get_detail (details, ly_symbol2scm ("ideal-slope-factor"), 10);
53 ROUND_TO_ZERO_SLOPE = get_detail (details, ly_symbol2scm ("round-to-zero-slope"), 0.02);
56 static Real
57 shrink_extra_weight (Real x, Real fac)
59 return fabs (x) * ((x < 0) ? fac : 1.0);
62 struct Quant_score
64 Real yl;
65 Real yr;
66 Real demerits;
68 #if DEBUG_BEAM_SCORING
69 string score_card_;
70 #endif
74 TODO:
76 - Make all demerits customisable
78 - One sensible check per demerit (what's this --hwn)
80 - Add demerits for quants per se, as to forbid a specific quant
81 entirely
84 int
85 best_quant_score_idx (vector<Quant_score> const &qscores)
87 Real best = 1e6;
88 int best_idx = -1;
89 for (vsize i = qscores.size (); i--;)
91 if (qscores[i].demerits < best)
93 best = qscores [i].demerits;
94 best_idx = i;
98 return best_idx;
101 MAKE_SCHEME_CALLBACK (Beam, quanting, 2);
103 Beam::quanting (SCM smob, SCM posns)
105 Grob *me = unsmob_grob (smob);
107 Beam_quant_parameters parameters;
108 parameters.fill (me);
110 Real yl = scm_to_double (scm_car (posns));
111 Real yr = scm_to_double (scm_cdr (posns));
114 Calculations are relative to a unit-scaled staff, i.e. the quants are
115 divided by the current staff_space.
117 Real ss = Staff_symbol_referencer::staff_space (me);
118 Real thickness = Beam::get_thickness (me) / ss;
119 Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
121 Real dy_mus = robust_scm2double (me->get_property ("least-squares-dy"), 0);
122 Real straddle = 0.0;
123 Real sit = (thickness - slt) / 2;
124 Real inter = 0.5;
125 Real hang = 1.0 - (thickness - slt) / 2;
126 Real quants [] = {straddle, sit, inter, hang };
128 int num_quants = int (sizeof (quants) / sizeof (Real));
129 vector<Real> quantsl;
130 vector<Real> quantsr;
133 going to REGION_SIZE == 2, yields another 0.6 second with
134 wtk1-fugue2.
136 (result indexes between 70 and 575) ? --hwn.
141 Do stem computations. These depend on YL and YR linearly, so we can
142 precompute for every stem 2 factors.
144 vector<Grob*> stems
145 = extract_grob_array (me, "stems");
146 vector<Stem_info> stem_infos;
147 vector<Real> base_lengths;
148 vector<Real> stem_xposns;
150 Drul_array<bool> dirs_found (0, 0);
151 Grob *common[2];
152 for (int a = 2; a--;)
153 common[a] = common_refpoint_of_array (stems, me, Axis (a));
155 Grob *fvs = first_normal_stem (me);
156 Grob *lvs = last_normal_stem (me);
157 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
158 Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
161 We store some info to quickly interpolate. The stemlength are
162 affine linear in YL and YR. If YL == YR == 0, then we might have
163 stem_y != 0.0, when we're cross staff.
166 for (vsize i = 0; i < stems.size (); i++)
168 Grob *s = stems[i];
170 Stem_info si (Stem::get_stem_info (s));
171 si.scale (1 / ss);
172 stem_infos.push_back (si);
173 dirs_found[stem_infos.back ().dir_] = true;
175 bool f = to_boolean (s->get_property ("french-beaming"))
176 && s != lvs && s != fvs;
178 if (Stem::is_normal_stem (s))
180 base_lengths.push_back (calc_stem_y (me, s, common, xl, xr, CENTER,
181 Interval (0, 0), f) / ss);
183 else
185 base_lengths.push_back (0);
188 stem_xposns.push_back (s->relative_coordinate (common[X_AXIS], X_AXIS));
190 bool xstaff = Align_interface::has_interface (common[Y_AXIS]);
192 Direction ldir = Direction (stem_infos[0].dir_);
193 Direction rdir = Direction (stem_infos.back ().dir_);
194 bool is_knee = dirs_found[LEFT] && dirs_found[RIGHT];
196 int region_size = (int) parameters.REGION_SIZE;
199 Knees are harder, lets try some more possibilities for knees.
201 if (is_knee)
202 region_size += 2;
205 Asymetry ? should run to <= region_size ?
207 for (int i = -region_size; i < region_size; i++)
208 for (int j = 0; j < num_quants; j++)
210 quantsl.push_back (i + quants[j] + int (yl));
211 quantsr.push_back (i + quants[j] + int (yr));
214 vector<Quant_score> qscores;
216 for (vsize l = 0; l < quantsl.size (); l++)
217 for (vsize r = 0; r < quantsr.size (); r++)
219 Quant_score qs;
220 qs.yl = quantsl[l];
221 qs.yr = quantsr[r];
222 qs.demerits = 0.0;
224 qscores.push_back (qs);
227 /* This is a longish function, but we don't separate this out into
228 neat modular separate subfunctions, as the subfunctions would be
229 called for many values of YL, YR. By precomputing various
230 parameters outside of the loop, we can save a lot of time. */
231 for (vsize i = qscores.size (); i--;)
233 Real d = score_slopes_dy (qscores[i].yl, qscores[i].yr,
234 dy_mus, yr- yl,
235 xr - xl,
236 xstaff, &parameters);
237 qscores[i].demerits += d;
239 #if DEBUG_BEAM_SCORING
240 qscores[i].score_card_ += to_string ("S%.2f", d);
241 #endif
244 Real rad = Staff_symbol_referencer::staff_radius (me);
245 Drul_array<int> edge_beam_counts
246 (Stem::beam_multiplicity (stems[0]).length () + 1,
247 Stem::beam_multiplicity (stems.back ()).length () + 1);
249 Real beam_translation = get_beam_translation (me) / ss;
251 Real reasonable_score = (is_knee) ? 200000 : 100;
252 for (vsize i = qscores.size (); i--;)
253 if (qscores[i].demerits < reasonable_score)
255 Real d = score_forbidden_quants (qscores[i].yl, qscores[i].yr,
256 rad, slt, thickness, beam_translation,
257 edge_beam_counts, ldir, rdir, &parameters);
258 qscores[i].demerits += d;
260 #if DEBUG_BEAM_SCORING
261 qscores[i].score_card_ += to_string (" F %.2f", d);
262 #endif
265 for (vsize i = qscores.size (); i--;)
266 if (qscores[i].demerits < reasonable_score)
268 Real d = score_stem_lengths (stems, stem_infos,
269 base_lengths, stem_xposns,
270 xl, xr,
271 is_knee,
272 qscores[i].yl, qscores[i].yr, &parameters);
273 qscores[i].demerits += d;
275 #if DEBUG_BEAM_SCORING
276 qscores[i].score_card_ += to_string (" L %.2f", d);
277 #endif
280 int best_idx = best_quant_score_idx (qscores);
282 #if DEBUG_BEAM_SCORING
283 SCM inspect_quants = me->get_property ("inspect-quants");
284 if (to_boolean (me->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-scoring")))
285 && scm_is_pair (inspect_quants))
287 Drul_array<Real> ins = ly_scm2interval (inspect_quants);
289 Real mindist = 1e6;
290 for (vsize i = 0; i < qscores.size (); i++)
292 Real d = fabs (qscores[i].yl- ins[LEFT]) + fabs (qscores[i].yr - ins[RIGHT]);
293 if (d < mindist)
295 best_idx = i;
296 mindist = d;
299 if (mindist > 1e5)
300 programming_error ("cannot find quant");
302 #endif
304 Interval final_positions;
305 if (best_idx < 0)
307 warning (_ ("no feasible beam position"));
308 final_positions = Interval (0, 0);
310 else
312 final_positions = Drul_array<Real> (qscores[best_idx].yl,
313 qscores[best_idx].yr);
316 #if DEBUG_BEAM_SCORING
317 if (best_idx >= 0
318 && to_boolean (me->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-scoring"))))
320 qscores[best_idx].score_card_ += to_string ("i%d", best_idx);
322 // debug quanting
323 me->set_property ("quant-score",
324 ly_string2scm (qscores[best_idx].score_card_));
326 #endif
328 return ly_interval2scm (final_positions);
331 Real
332 Beam::score_stem_lengths (vector<Grob*> const &stems,
333 vector<Stem_info> const &stem_infos,
334 vector<Real> const &base_stem_ys,
335 vector<Real> const &stem_xs,
336 Real xl, Real xr,
337 bool knee,
338 Real yl, Real yr,
340 Beam_quant_parameters const *parameters)
342 Real limit_penalty = parameters->STEM_LENGTH_LIMIT_PENALTY;
343 Drul_array<Real> score (0, 0);
344 Drul_array<int> count (0, 0);
346 for (vsize i = 0; i < stems.size (); i++)
348 Grob *s = stems[i];
349 if (!Stem::is_normal_stem (s))
350 continue;
352 Real x = stem_xs[i];
353 Real dx = xr - xl;
354 Real beam_y = dx ? yr * (x - xl) / dx + yl * (xr - x) / dx : (yr + yl) / 2;
355 Real current_y = beam_y + base_stem_ys[i];
356 Real length_pen = parameters->STEM_LENGTH_DEMERIT_FACTOR;
358 Stem_info info = stem_infos[i];
359 Direction d = info.dir_;
361 score[d] += limit_penalty * max (0.0, (d * (info.shortest_y_ - current_y)));
363 Real ideal_diff = d * (current_y - info.ideal_y_);
364 Real ideal_score = shrink_extra_weight (ideal_diff, 1.5);
366 /* We introduce a power, to make the scoring strictly
367 convex. Otherwise a symmetric knee beam (up/down/up/down)
368 does not have an optimum in the middle. */
369 if (knee)
370 ideal_score = pow (ideal_score, 1.1);
372 score[d] += length_pen * ideal_score;
374 count[d]++;
377 Direction d = DOWN;
379 score[d] /= max (count[d], 1);
380 while (flip (&d) != DOWN);
382 return score[LEFT] + score[RIGHT];
385 Real
386 Beam::score_slopes_dy (Real yl, Real yr,
387 Real dy_mus, Real dy_damp,
388 Real dx,
389 bool xstaff,
391 Beam_quant_parameters const *parameters)
393 Real dy = yr - yl;
394 Real dem = 0.0;
397 DAMPING_DIRECTION_PENALTY is a very harsh measure, while for
398 complex beaming patterns, horizontal is often a good choice.
400 TODO: find a way to incorporate the complexity of the beam in this
401 penalty.
403 if (sign (dy_damp) != sign (dy))
405 if (!dy)
407 if (fabs (dy_damp / dx) > parameters->ROUND_TO_ZERO_SLOPE)
408 dem += parameters->DAMPING_DIRECTION_PENALTY;
409 else
410 dem += parameters->HINT_DIRECTION_PENALTY;
412 else
413 dem += parameters->DAMPING_DIRECTION_PENALTY;
416 dem += parameters->MUSICAL_DIRECTION_FACTOR
417 * max (0.0, (fabs (dy) - fabs (dy_mus)));
419 Real slope_penalty = parameters->IDEAL_SLOPE_FACTOR;
421 /* Xstaff beams tend to use extreme slopes to get short stems. We
422 put in a penalty here. */
423 if (xstaff)
424 slope_penalty *= 10;
426 /* Huh, why would a too steep beam be better than a too flat one ? */
427 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy), 1.5)
428 * slope_penalty;
430 return dem;
433 static Real
434 my_modf (Real x)
436 return x - floor (x);
440 TODO: The fixed value SECONDARY_BEAM_DEMERIT is probably flawed:
441 because for 32nd and 64th beams the forbidden quants are relatively
442 more important than stem lengths.
444 Real
445 Beam::score_forbidden_quants (Real yl, Real yr,
446 Real radius,
447 Real slt,
448 Real thickness, Real beam_translation,
449 Drul_array<int> beam_counts,
450 Direction ldir, Direction rdir,
452 Beam_quant_parameters const *parameters)
454 Real dy = yr - yl;
455 Drul_array<Real> y (yl, yr);
456 Drul_array<Direction> dirs (ldir, rdir);
458 Real extra_demerit = parameters->SECONDARY_BEAM_DEMERIT / (max (beam_counts[LEFT], beam_counts[RIGHT]));
460 Direction d = LEFT;
461 Real dem = 0.0;
462 Real eps = parameters->BEAM_EPS;
466 for (int j = 1; j <= beam_counts[d]; j++)
468 Direction stem_dir = dirs[d];
471 The 2.2 factor is to provide a little leniency for
472 borderline cases. If we do 2.0, then the upper outer line
473 will be in the gap of the (2, sit) quant, leading to a
474 false demerit.
476 Real gap1 = y[d] - stem_dir * ((j - 1) * beam_translation + thickness / 2 - slt / 2.2);
477 Real gap2 = y[d] - stem_dir * (j * beam_translation - thickness / 2 + slt / 2.2);
479 Interval gap;
480 gap.add_point (gap1);
481 gap.add_point (gap2);
483 for (Real k = -radius;
484 k <= radius + eps; k += 1.0)
485 if (gap.contains (k))
487 Real dist = min (fabs (gap[UP] - k), fabs (gap[DOWN] - k));
490 this parameter is tuned to grace-stem-length.ly
492 Real fixed_demerit = 0.4;
494 dem += extra_demerit
495 * (fixed_demerit
496 + (1 - fixed_demerit) * (dist / gap.length ()) * 2);
500 while ((flip (&d)) != LEFT);
502 if (max (beam_counts[LEFT], beam_counts[RIGHT]) >= 2)
504 Real straddle = 0.0;
505 Real sit = (thickness - slt) / 2;
506 Real inter = 0.5;
507 Real hang = 1.0 - (thickness - slt) / 2;
509 Direction d = LEFT;
512 if (beam_counts[d] >= 2
513 && fabs (y[d] - dirs[d] * beam_translation) < radius + inter)
515 if (dirs[d] == UP && dy <= eps
516 && fabs (my_modf (y[d]) - sit) < eps)
517 dem += extra_demerit;
519 if (dirs[d] == DOWN && dy >= eps
520 && fabs (my_modf (y[d]) - hang) < eps)
521 dem += extra_demerit;
524 if (beam_counts[d] >= 3
525 && fabs (y[d] - 2 * dirs[d] * beam_translation) < radius + inter)
527 if (dirs[d] == UP && dy <= eps
528 && fabs (my_modf (y[d]) - straddle) < eps)
529 dem += extra_demerit;
531 if (dirs[d] == DOWN && dy >= eps
532 && fabs (my_modf (y[d]) - straddle) < eps)
533 dem += extra_demerit;
536 while (flip (&d) != LEFT);
539 return dem;