]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam-quanting.cc
d5de148b3e0c79581c4cc4437d41488e127acdc2
[lilypond.git] / lily / beam-quanting.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
5   Jan Nieuwenhuizen <janneke@gnu.org>
6
7   LilyPond is free software: you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation, either version 3 of the License, or
10   (at your option) any later version.
11
12   LilyPond is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "beam-scoring-problem.hh"
22
23 #include <algorithm>
24 using namespace std;
25
26 #include "align-interface.hh"
27 #include "beam.hh"
28 #include "directional-element-interface.hh"
29 #include "grob.hh"
30 #include "international.hh"
31 #include "main.hh"
32 #include "output-def.hh"
33 #include "pointer-group-interface.hh"
34 #include "staff-symbol-referencer.hh"
35 #include "stem.hh"
36 #include "warn.hh"
37
38 Real
39 get_detail (SCM alist, SCM sym, Real def)
40 {
41   SCM entry = scm_assq (sym, alist);
42
43   if (scm_is_pair (entry))
44     return robust_scm2double (scm_cdr (entry), def);
45   return def;
46 }
47
48 void
49 Beam_quant_parameters::fill (Grob *him)
50 {
51   SCM details = him->get_property ("details");
52
53   SECONDARY_BEAM_DEMERIT = get_detail (details, ly_symbol2scm ("secondary-beam-demerit"), 10.0);
54   STEM_LENGTH_DEMERIT_FACTOR = get_detail (details, ly_symbol2scm ("stem-length-demerit-factor"), 5);
55   REGION_SIZE = get_detail (details, ly_symbol2scm ("region-size"), 2);
56   BEAM_EPS = get_detail (details, ly_symbol2scm ("beam-eps"), 1e-3);
57   STEM_LENGTH_LIMIT_PENALTY = get_detail (details, ly_symbol2scm ("stem-length-limit-penalty"), 5000);
58   DAMPING_DIRECTION_PENALTY = get_detail (details, ly_symbol2scm ("damping-direction-penalty"), 800);
59   HINT_DIRECTION_PENALTY = get_detail (details, ly_symbol2scm ("hint-direction-penalty"), 20);
60   MUSICAL_DIRECTION_FACTOR = get_detail (details, ly_symbol2scm ("musical-direction-factor"), 400);
61   IDEAL_SLOPE_FACTOR = get_detail (details, ly_symbol2scm ("ideal-slope-factor"), 10);
62   ROUND_TO_ZERO_SLOPE = get_detail (details, ly_symbol2scm ("round-to-zero-slope"), 0.02);
63 }
64
65 static Real
66 shrink_extra_weight (Real x, Real fac)
67 {
68   return fabs (x) * ((x < 0) ? fac : 1.0);
69 }
70
71 /****************************************************************/
72
73 Beam_configuration::Beam_configuration ()
74 {
75   y = Interval (0.0, 0.0);
76   demerits = 0.0;
77   next_scorer_todo = ORIGINAL_DISTANCE;
78 }
79
80 bool Beam_configuration::done () const
81 {
82   return next_scorer_todo >= NUM_SCORERS;
83 }
84
85 void Beam_configuration::add (Real demerit, const string &reason)
86 {
87   demerits += demerit;
88
89 #if DEBUG_BEAM_SCORING
90   if (demerit) 
91     score_card_ += to_string (" %s %.2f", reason.c_str (), demerit);
92 #endif
93 }
94   
95 Beam_configuration* Beam_configuration::new_config (Interval start,
96                                                     Interval offset)
97 {
98   Beam_configuration* qs = new Beam_configuration;
99   qs->y = Interval (int (start[LEFT]) + offset[LEFT],
100                     int (start[RIGHT]) + offset[RIGHT]);
101
102   // This orders the sequence so we try combinations closest to the
103   // the ideal offset first.
104   Real start_score = abs (offset[RIGHT]) + abs (offset[LEFT]);
105   qs->demerits = start_score / 1000.0;
106   qs->next_scorer_todo = ORIGINAL_DISTANCE + 1;
107   
108   return qs;
109 }
110
111 /****************************************************************/
112
113 /*
114   TODO:
115
116   - Make all demerits customisable
117
118   - One sensible check per demerit (what's this --hwn)
119
120   - Add demerits for quants per se, as to forbid a specific quant
121   entirely
122 */
123 int
124 best_quant_score_idx (vector<Beam_configuration*> const &configs)
125 {
126   Real best = 1e6;
127   int best_idx = -1;
128   for (vsize i = configs.size (); i--;)
129     {
130       if (configs[i]->demerits < best)
131         {
132           best = configs [i]->demerits;
133           best_idx = i;
134         }
135     }
136
137   return best_idx;
138 }
139
140 // This is a temporary hack to see how much we can gain by using a
141 // priority queue on the beams to score.
142 static int score_count = 0;
143 LY_DEFINE (ly_beam_score_count, "ly:beam-score-count", 0, 0, 0,
144            (),
145            "count number of beam scores.") {
146   return scm_from_int (score_count);
147 }
148
149 void Beam_scoring_problem::init_stems ()
150 {
151   extract_grob_set (beam, "stems", stems);
152   for (int a = 2; a--;)
153     common[a] = common_refpoint_of_array (stems, beam, Axis (a));
154
155   Grob *fvs = Beam::first_normal_stem (beam);
156   Grob *lvs = Beam::last_normal_stem (beam);
157     
158   x_span = Interval (fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0,
159                      lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0);
160
161   Drul_array<bool> dirs_found (0, 0);
162
163   for (vsize i = 0; i < stems.size (); i++)
164     {
165       Grob *s = stems[i];
166       if (!Stem::is_normal_stem (s))
167         continue;
168       
169       Stem_info si (Stem::get_stem_info (s));
170       si.scale (1 / staff_space);
171       stem_infos.push_back (si);
172       dirs_found[si.dir_] = true;
173
174       bool f = to_boolean (s->get_property ("french-beaming"))
175         && s != lvs && s != fvs;
176
177       Real y = Beam::calc_stem_y (beam, s, common, x_span[LEFT], x_span[RIGHT], CENTER, 
178                                   Interval (0, 0), f);
179       base_lengths.push_back (y / staff_space);
180       stem_xpositions.push_back (s->relative_coordinate (common[X_AXIS], X_AXIS));
181     }
182   
183   edge_dirs = Drul_array<Direction> (CENTER, CENTER);
184   if (stem_infos.size ())
185     {
186       edge_dirs = Drul_array<Direction> (stem_infos[0].dir_,
187                                          stem_infos.back().dir_);
188     }
189   is_xstaff = Align_interface::has_interface (common[Y_AXIS]);
190   is_knee = dirs_found[LEFT] && dirs_found[RIGHT];
191   
192   staff_radius = Staff_symbol_referencer::staff_radius (beam);
193   edge_beam_counts =  Drul_array<int>
194     (Stem::beam_multiplicity (stems[0]).length () + 1,
195      Stem::beam_multiplicity (stems.back ()).length () + 1);
196     
197   beam_translation = Beam::get_beam_translation (beam) / staff_space;
198 }
199
200 Beam_scoring_problem::Beam_scoring_problem (Grob *me, Drul_array<Real> ys)
201 {
202   beam = me;
203   unquanted_y = ys;
204     
205   /*
206     Calculations are relative to a unit-scaled staff, i.e. the quants are
207     divided by the current staff_space.
208   */
209   staff_space = Staff_symbol_referencer::staff_space (me);
210   beam_thickness = Beam::get_beam_thickness (me) / staff_space;
211   line_thickness = Staff_symbol_referencer::line_thickness (me) / staff_space;
212
213   // This is the least-squares DY, corrected for concave beams.
214   musical_dy = robust_scm2double (me->get_property ("least-squares-dy"), 0);
215
216   parameters.fill (me);
217   init_stems ();
218 }
219
220 void
221 Beam_scoring_problem::generate_quants (vector<Beam_configuration*> *scores) const
222 {
223   int region_size = (int) parameters.REGION_SIZE;
224
225   /*
226     Knees are harder, lets try some more possibilities for knees.
227   */  
228   if (is_knee)
229     region_size += 2;
230   
231   Real straddle = 0.0;
232   Real sit = (beam_thickness - line_thickness) / 2;
233   Real inter = 0.5;
234   Real hang = 1.0 - (beam_thickness - line_thickness) / 2;
235   Real base_quants [] = {straddle, sit, inter, hang};
236   int num_base_quants = int (sizeof (base_quants) / sizeof (Real));
237
238   /*
239     Asymetry ? should run to <= region_size ?
240   */
241   vector<Real> unshifted_quants;
242   for (int i = -region_size; i < region_size; i++)
243     for (int j = 0; j < num_base_quants; j++)
244       {
245         unshifted_quants.push_back (i + base_quants[j]);
246       }
247
248
249   scores->clear ();
250   for (vsize i = 0; i < unshifted_quants.size (); i++)
251     for (vsize j = 0; j < unshifted_quants.size (); j++)
252       scores->push_back (Beam_configuration::new_config (unquanted_y,
253                                                          Interval (unshifted_quants[i],
254                                                                    unshifted_quants[j])));
255 }
256
257 Drul_array<Real>
258 Beam_scoring_problem::solve () const {
259   vector<Beam_configuration*> configs;
260
261   generate_quants (&configs);
262   for (vsize i = configs.size (); i--;)
263     {
264       score_count ++;
265       score_slopes_dy (configs[i]);
266     }
267
268   Real reasonable_score = (is_knee) ? 200000 : 100;
269   for (vsize i = configs.size (); i--;)
270     if (configs[i]->demerits < reasonable_score)
271       {
272         score_count ++;
273         score_forbidden_quants (configs[i]);
274       }
275
276   for (vsize i = configs.size (); i--;)
277     if (configs[i]->demerits < reasonable_score)
278       {
279         score_count ++;
280         score_stem_lengths (configs[i]);
281       }
282
283   int best_idx = best_quant_score_idx (configs);
284
285 #if DEBUG_BEAM_SCORING
286   SCM inspect_quants = beam->get_property ("inspect-quants");
287   if (to_boolean (beam->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-scoring")))
288       && scm_is_pair (inspect_quants))
289     {
290       Drul_array<Real> ins = ly_scm2interval (inspect_quants);
291
292       Real mindist = 1e6;
293       for (vsize i = 0; i < configs.size (); i++)
294         {
295           Real d = fabs (configs[i]->y[LEFT]- ins[LEFT]) + fabs (configs[i]->y[RIGHT] - ins[RIGHT]);
296           if (d < mindist)
297             {
298               best_idx = i;
299               mindist = d;
300             }
301         }
302       if (mindist > 1e5)
303         programming_error ("cannot find quant");
304     }
305 #endif
306
307   Interval final_positions;
308   if (best_idx < 0)
309     {
310       warning (_ ("no feasible beam position"));
311       final_positions = Interval (0, 0);
312     }
313   else
314     {
315       final_positions = configs[best_idx]->y;
316     }
317   
318 #if DEBUG_BEAM_SCORING
319   if (best_idx >= 0
320       && to_boolean (beam->layout ()->lookup_variable (ly_symbol2scm ("debug-beam-scoring"))))
321     {
322       configs[best_idx]->score_card_ += to_string ("i%d", best_idx);
323
324       // debug quanting
325       beam->set_property ("quant-score",
326                           ly_string2scm (configs[best_idx]->score_card_));
327     }
328 #endif
329
330   junk_pointers (configs);
331   return final_positions;
332 }
333
334 void
335 Beam_scoring_problem::score_stem_lengths (Beam_configuration* config) const
336 {
337   Real limit_penalty = parameters.STEM_LENGTH_LIMIT_PENALTY;
338   Drul_array<Real> score (0, 0);
339   Drul_array<int> count (0, 0);
340
341   for (vsize i = 0; i < stem_xpositions.size (); i++)
342     {
343       Real x = stem_xpositions[i];
344       Real dx = x_span.delta ();
345       Real beam_y = dx
346         ? config->y[RIGHT] * (x - x_span[LEFT]) / dx + config->y[LEFT] * (x_span[RIGHT] - x) / dx
347         : (config->y[RIGHT] + config->y[LEFT]) / 2;
348       Real current_y = beam_y + base_lengths[i];
349       Real length_pen = parameters.STEM_LENGTH_DEMERIT_FACTOR;
350
351       Stem_info info = stem_infos[i];
352       Direction d = info.dir_;
353
354       score[d] += limit_penalty * max (0.0, (d * (info.shortest_y_ - current_y)));
355
356       Real ideal_diff = d * (current_y - info.ideal_y_);
357       Real ideal_score = shrink_extra_weight (ideal_diff, 1.5);
358
359       /* We introduce a power, to make the scoring strictly
360          convex. Otherwise a symmetric knee beam (up/down/up/down)
361          does not have an optimum in the middle. */
362       if (is_knee)
363         ideal_score = pow (ideal_score, 1.1);
364
365       score[d] += length_pen * ideal_score;
366
367       count[d]++;
368     }
369
370   /* Divide by number of stems, to make the measure scale-free. */
371   Direction d = DOWN;
372   do
373     score[d] /= max (count[d], 1);
374   while (flip (&d) != DOWN);
375
376   config->add (score[LEFT] + score[RIGHT], "L");
377 }
378
379 void
380 Beam_scoring_problem::score_slopes_dy (Beam_configuration *config) const
381 {
382   Real dy = config->y.delta ();
383   Real damped_dy = unquanted_y.delta();
384   Real dem = 0.0;
385   
386   /*
387     DAMPING_DIRECTION_PENALTY is a very harsh measure, while for
388     complex beaming patterns, horizontal is often a good choice.
389
390     TODO: find a way to incorporate the complexity of the beam in this
391     penalty.
392   */
393   if (sign (damped_dy) != sign (dy))
394     {
395       if (!dy)
396         {
397           if (fabs (damped_dy / x_span.delta ()) > parameters.ROUND_TO_ZERO_SLOPE)
398             dem += parameters.DAMPING_DIRECTION_PENALTY;
399           else
400             dem += parameters.HINT_DIRECTION_PENALTY;
401         }
402       else
403         dem += parameters.DAMPING_DIRECTION_PENALTY;
404     }
405   
406   dem += parameters.MUSICAL_DIRECTION_FACTOR
407     * max (0.0, (fabs (dy) - fabs (musical_dy)));
408
409   Real slope_penalty = parameters.IDEAL_SLOPE_FACTOR;
410
411   /* Xstaff beams tend to use extreme slopes to get short stems. We
412      put in a penalty here. */
413   if (is_xstaff)
414     slope_penalty *= 10;
415
416   /* Huh, why would a too steep beam be better than a too flat one ? */
417   dem += shrink_extra_weight (fabs (damped_dy) - fabs (dy), 1.5)
418     * slope_penalty;
419
420   config->add (dem, "S");
421 }
422
423 static Real
424 my_modf (Real x)
425 {
426   return x - floor (x);
427 }
428
429 /*
430   TODO: The fixed value SECONDARY_BEAM_DEMERIT is probably flawed:
431   because for 32nd and 64th beams the forbidden quants are relatively
432   more important than stem lengths.
433 */
434 void
435 Beam_scoring_problem::score_forbidden_quants (Beam_configuration *config) const
436 {
437   Real dy = config->y.delta ();
438
439   Real extra_demerit = parameters.SECONDARY_BEAM_DEMERIT /
440     max (edge_beam_counts[LEFT], edge_beam_counts[RIGHT]);
441
442   Direction d = LEFT;
443   Real dem = 0.0;
444   Real eps = parameters.BEAM_EPS;
445
446   do
447     {
448       for (int j = 1; j <= edge_beam_counts[d]; j++)
449         {
450           Direction stem_dir = edge_dirs[d];
451
452           /*
453             The 2.2 factor is to provide a little leniency for
454             borderline cases. If we do 2.0, then the upper outer line
455             will be in the gap of the (2, sit) quant, leading to a
456             false demerit.
457           */
458           Real gap1 = config->y[d] - stem_dir * ((j - 1) * beam_translation + beam_thickness / 2 - line_thickness / 2.2);
459           Real gap2 = config->y[d] - stem_dir * (j * beam_translation - beam_thickness / 2 + line_thickness / 2.2);
460
461           Interval gap;
462           gap.add_point (gap1);
463           gap.add_point (gap2);
464
465           for (Real k = -staff_radius;
466                k <= staff_radius + eps; k += 1.0)
467             if (gap.contains (k))
468               {
469                 Real dist = min (fabs (gap[UP] - k), fabs (gap[DOWN] - k));
470
471                 /*
472                   this parameter is tuned to grace-stem-length.ly
473                 */
474                 Real fixed_demerit = 0.4;
475
476                 dem += extra_demerit
477                   * (fixed_demerit
478                      + (1 - fixed_demerit) * (dist / gap.length ()) * 2);
479               }
480         }
481     }
482   while ((flip (&d)) != LEFT);
483
484   if (max (edge_beam_counts[LEFT], edge_beam_counts[RIGHT]) >= 2)
485     {
486       Real straddle = 0.0;
487       Real sit = (beam_thickness - line_thickness) / 2;
488       Real inter = 0.5;
489       Real hang = 1.0 - (beam_thickness - line_thickness) / 2;
490
491       Direction d = LEFT;
492       do
493         {
494           if (edge_beam_counts[d] >= 2
495               && fabs (config->y[d] - edge_dirs[d] * beam_translation) < staff_radius + inter)
496             {
497               // TODO up/down symmetry.
498               if (edge_dirs[d] == UP && dy <= eps
499                   && fabs (my_modf (config->y[d]) - sit) < eps)
500                 dem += extra_demerit;
501
502               if (edge_dirs[d] == DOWN && dy >= eps
503                   && fabs (my_modf (config->y[d]) - hang) < eps)
504                 dem += extra_demerit;
505             }
506
507           if (edge_beam_counts[d] >= 3
508               && fabs (config->y[d] - 2 * edge_dirs[d] * beam_translation) < staff_radius + inter)
509             {
510               // TODO up/down symmetry.
511               if (edge_dirs[d] == UP && dy <= eps
512                   && fabs (my_modf (config->y[d]) - straddle) < eps)
513                 dem += extra_demerit;
514
515               if (edge_dirs[d] == DOWN && dy >= eps
516                   && fabs (my_modf (config->y[d]) - straddle) < eps)
517                 dem += extra_demerit;
518             }
519         }
520       while (flip (&d) != LEFT);
521     }
522
523   config->add (dem, "F");
524 }
525