]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam-quanting.cc
6f5733f0d535fd99e58ea41f20629a192cba59ee
[lilypond.git] / lily / beam-quanting.cc
1 #include <math.h>
2
3 #include "grob.hh"
4 #include "staff-symbol-referencer.hh"
5 #include "beam.hh"
6 #include "stem.hh"
7 #include "paper-def.hh"
8 #include "group-interface.hh"
9 #include "align-interface.hh"
10
11 const int INTER_QUANT_PENALTY = 1000; 
12 const int SECONDARY_BEAM_DEMERIT  = 15;
13 const int STEM_LENGTH_DEMERIT_FACTOR = 5;
14
15 // possibly ridiculous, but too short stems just won't do
16 const int STEM_LENGTH_LIMIT_PENALTY = 5000;
17 const int DAMPING_DIRECTIION_PENALTY = 800;
18 const int MUSICAL_DIRECTION_FACTOR = 400;
19 const int IDEAL_SLOPE_FACTOR = 10;
20
21
22 static Real
23 shrink_extra_weight (Real x)
24 {
25   return fabs (x) * ((x < 0) ? 1.5 : 1.0);
26 }
27
28
29 struct Quant_score
30 {
31   Real yl;
32   Real yr;
33   Real demerits;
34 };
35
36
37 /*
38   TODO:
39   
40    - Make all demerits customisable
41
42    - One sensible check per demerit (what's this --hwn)
43
44    - Add demerits for quants per se, as to forbid a specific quant
45      entirely
46
47 */
48
49 int best_quant_score_idx (Array<Quant_score>  const & qscores)
50 {
51   Real best = 1e6;
52   int best_idx = -1;
53   for (int i = qscores.size (); i--;)
54     {
55       if (qscores[i].demerits < best)
56         {
57           best = qscores [i].demerits ;
58           best_idx = i;
59         }
60     }
61
62   return best_idx;
63 }
64   
65 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
66 SCM
67 Beam::quanting (SCM smob)
68 {
69   Grob *me = unsmob_grob (smob);
70
71   SCM s = me->get_grob_property ("positions");
72   Real yl = gh_scm2double (gh_car (s));
73   Real yr = gh_scm2double (gh_cdr (s));
74
75   Real ss = Staff_symbol_referencer::staff_space (me);
76   Real thickness = gh_scm2double (me->get_grob_property ("thickness")) / ss;
77   Real slt = me->paper_l ()->get_var ("linethickness") / ss;
78
79
80   SCM sdy = me->get_grob_property ("least-squares-dy");
81   Real dy_mus = gh_number_p (sdy) ? gh_scm2double (sdy) : 0.0;
82   
83   Real straddle = 0.0;
84   Real sit = (thickness - slt) / 2;
85   Real inter = 0.5;
86   Real hang = 1.0 - (thickness - slt) / 2;
87   Real quants [] = {straddle, sit, inter, hang };
88   
89   int num_quants = int (sizeof (quants)/sizeof (Real));
90   Array<Real> quantsl;
91   Array<Real> quantsr;
92
93   /*
94     going to REGION_SIZE == 2, yields another 0.6 second with
95     wtk1-fugue2.
96
97
98     (result indexes between 70 and 575)  ? --hwn. 
99
100   */
101
102
103   
104   /*
105     Do stem computations.  These depend on YL and YR linearly, so we can
106     precompute for every stem 2 factors.
107    */
108   Link_array<Grob> stems=
109     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
110   Array<Stem_info> stem_infos;
111   Array<Real> base_lengths;
112   Array<Real> stem_xposns;  
113
114   Drul_array<bool> dirs_found(0,0);
115   Grob *common[2];
116   for (int a = 2; a--;)
117     common[a] = common_refpoint_of_array (stems, me, Axis(a));
118
119   Grob * fvs = first_visible_stem (me);
120   Grob *lvs = last_visible_stem (me);
121   Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
122   Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
123
124   /*
125     We store some info to quickly interpolate.
126
127     Sometimes my head is screwed on backwards.  The stemlength are
128     AFFINE linear in YL and YR. If YL == YR == 0, then we might have
129     stem_y != 0.0, when we're cross staff.
130     
131    */
132   bool french = to_boolean (me->get_grob_property ("french-beaming"));
133   for (int i= 0; i < stems.size(); i++)
134     {
135       Grob*s = stems[i];
136       stem_infos.push (Stem::calc_stem_info (s));
137       dirs_found[stem_infos.top ().dir_] = true;
138
139       bool f = french && i > 0&& (i < stems.size  () -1);
140       base_lengths.push (calc_stem_y (me, s, common, xl, xr,
141                                       Interval (0,0), f));
142       stem_xposns.push (s->relative_coordinate (common[X_AXIS], X_AXIS));
143     }
144
145   bool xstaff= false;
146   if (lvs && fvs)
147     {
148       Grob *commony = fvs->common_refpoint (lvs, Y_AXIS);
149       xstaff = Align_interface::has_interface (commony);
150     }
151   
152   Direction ldir = Direction (stem_infos[0].dir_);
153   Direction rdir = Direction (stem_infos.top ().dir_);
154   bool knee_b = dirs_found[LEFT] && dirs_found[RIGHT];
155
156
157   int region_size = REGION_SIZE;
158   /*
159     Knees are harder, lets try some more possibilities for knees. 
160    */
161   if (knee_b)
162     region_size += 2;
163   
164   for (int i = -region_size ; i < region_size; i++)
165     for (int j = 0; j < num_quants; j++)
166       {
167         quantsl.push (i + quants[j] + int (yl));
168         quantsr.push (i + quants[j] + int (yr));
169       }
170
171   Array<Quant_score> qscores;
172   
173   for (int l =0; l < quantsl.size (); l++)  
174     for (int r =0; r < quantsr.size (); r++)
175       {
176         Quant_score qs;
177         qs.yl = quantsl[l];
178         qs.yr = quantsr[r];
179         qs.demerits = 0.0;
180         
181         qscores.push (qs);
182       }
183
184   /*
185     This is a longish function, but we don't separate this out into
186     neat modular separate subfunctions, as the subfunctions would be
187     called for many values of YL, YR. By precomputing various
188     parameters outside of the loop, we can save a lot of time.
189   */
190
191   for (int i = qscores.size (); i--;)
192     {
193       qscores[i].demerits
194         += score_slopes_dy (qscores[i].yl, qscores[i].yr,
195                             dy_mus, yr- yl, xstaff); 
196     }
197
198   Real rad = Staff_symbol_referencer::staff_radius (me);
199   int beam_count = get_beam_count (me);
200   Real beam_translation = beam_count < 4
201     ? (2*ss + slt - thickness) / 2.0
202      : (3*ss + slt - thickness) / 3.0;
203
204   Real reasonable_score = (knee_b) ? 200000 : 100;
205   for (int i = qscores.size (); i--;)
206     if (qscores[i].demerits < reasonable_score)
207       {
208         qscores[i].demerits
209           += score_forbidden_quants (qscores[i].yl, qscores[i].yr,
210                                      rad, slt, thickness, beam_translation,
211                                      beam_count, ldir, rdir); 
212       }
213
214   ; /* silly gdb thinks best_idx is inside for loop. */
215   for (int i = qscores.size (); i--;)
216     if (qscores[i].demerits < reasonable_score)
217       {
218         qscores[i].demerits
219           += score_stem_lengths (stems, stem_infos,
220                                  base_lengths, stem_xposns,
221                                  xl, xr,
222                                  knee_b,
223                                  qscores[i].yl, qscores[i].yr);
224       }
225
226   ; /* silly gdb thinks best_idx is inside for loop. */
227   int best_idx = best_quant_score_idx (qscores);
228   me->set_grob_property ("positions",
229                          gh_cons (gh_double2scm (qscores[best_idx].yl),
230                                   gh_double2scm (qscores[best_idx].yr))
231                          );
232
233 #if DEBUG_QUANTING
234
235   // debug quanting
236   me->set_grob_property ("quant-score",
237                          gh_double2scm (qscores[best_idx].demerits));
238   me->set_grob_property ("best-idx", gh_int2scm (best_idx));
239 #endif
240
241   return SCM_UNSPECIFIED;
242 }
243
244 Real
245 Beam::score_stem_lengths (Link_array<Grob>stems,
246                           Array<Stem_info> stem_infos,
247                           Array<Real> base_stem_ys,
248                           Array<Real> stem_xs,
249                           Real xl, Real xr, 
250                           bool knee, 
251                           Real yl, Real yr)
252 {
253   Real pen = STEM_LENGTH_LIMIT_PENALTY;
254
255   Drul_array<Real> score (0, 0);
256   Drul_array<int> count (0, 0);
257   for (int i=0; i < stems.size (); i++)
258     {
259       Grob* s = stems[i];
260       if (Stem::invisible_b (s))
261         continue;
262
263       Real x = stem_xs[i];
264       Real dx = xr-xl;
265       Real beam_y = yr *(x - xl)/dx + yl * ( xr - x)/dx;
266       Real current_y = beam_y + base_stem_ys[i];
267       
268       Stem_info info = stem_infos[i];
269       Direction d = info.dir_;
270
271       score[d] += pen
272         * (0 >? (d * (info.shortest_y_ - current_y)));
273
274       Real ideal_score = shrink_extra_weight (d * current_y  - d * info.ideal_y_);
275       
276       /*
277
278       we introduce a power, to make the scoring strictly
279       convex. Otherwise a symmetric knee beam (up/down/up/down) does
280       not have an optimum in the middle.
281         
282        */
283       if (knee)
284         ideal_score = pow (ideal_score, 1.1);
285       score[d] += STEM_LENGTH_DEMERIT_FACTOR * ideal_score;
286
287       count[d] ++;
288     }
289   
290   if(count[LEFT])
291     score[LEFT] /= count[LEFT];
292   if(count[RIGHT])
293     score[RIGHT] /= count[RIGHT];
294
295   return score[LEFT]+score[RIGHT];
296 }
297
298 Real
299 Beam::score_slopes_dy (Real yl, Real yr,
300                        Real dy_mus, Real dy_damp,
301                        bool xstaff)
302 {
303   Real dy = yr - yl;
304
305   Real dem = 0.0;
306   if (sign (dy_damp) != sign (dy))
307     {
308       dem += DAMPING_DIRECTIION_PENALTY;
309     }
310
311    dem += MUSICAL_DIRECTION_FACTOR * (0 >? (fabs (dy) - fabs (dy_mus)));
312
313
314    Real slope_penalty = IDEAL_SLOPE_FACTOR;
315
316    /*
317      Xstaff beams tend to use extreme slopes to get short stems. We
318      put in a penalty here.
319    */
320    if (xstaff)
321      slope_penalty *= 10;
322
323    dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy))* slope_penalty;
324    return dem;
325 }
326
327 static Real
328 my_modf (Real x)
329 {
330   return x - floor (x);
331 }
332
333 Real
334 Beam::score_forbidden_quants (Real yl, Real yr,
335                               Real rad,
336                               Real slt,
337                               Real thickness, Real beam_translation,
338                               int beam_count,
339                               Direction ldir, Direction rdir)
340 {
341   Real dy = yr - yl;
342
343   Real dem = 0.0;
344   if (fabs (yl) < rad && fabs ( my_modf (yl) - 0.5) < 1e-3)
345     dem += INTER_QUANT_PENALTY;
346   if (fabs (yr) < rad && fabs ( my_modf (yr) - 0.5) < 1e-3)
347     dem += INTER_QUANT_PENALTY;
348
349   // todo: use beam_count of outer stems.
350   if (beam_count >= 2)
351     {
352      
353       Real straddle = 0.0;
354       Real sit = (thickness - slt) / 2;
355       Real inter = 0.5;
356       Real hang = 1.0 - (thickness - slt) / 2;
357       
358
359       if (fabs (yl - ldir * beam_translation) < rad
360           && fabs (my_modf (yl) - inter) < 1e-3)
361         dem += SECONDARY_BEAM_DEMERIT;
362       if (fabs (yr - rdir * beam_translation) < rad
363           && fabs (my_modf (yr) - inter) < 1e-3)
364         dem += SECONDARY_BEAM_DEMERIT;
365
366       Real eps = 1e-3;
367
368       /*
369         Can't we simply compute the distance between the nearest
370         staffline and the secondary beam? That would get rid of the
371         silly case analysis here (which is probably not when we have
372         different beam-thicknesses.)
373
374         --hwn
375        */
376
377
378       // hmm, without Interval/Drul_array, you get ~ 4x same code...
379       if (fabs (yl - ldir * beam_translation) < rad + inter)
380         {
381           if (ldir == UP && dy <= eps
382               && fabs (my_modf (yl) - sit) < eps)
383             dem += SECONDARY_BEAM_DEMERIT;
384           
385           if (ldir == DOWN && dy >= eps
386               && fabs (my_modf (yl) - hang) < eps)
387             dem += SECONDARY_BEAM_DEMERIT;
388         }
389
390       if (fabs (yr - rdir * beam_translation) < rad + inter)
391         {
392           if (rdir == UP && dy >= eps
393               && fabs (my_modf (yr) - sit) < eps)
394             dem += SECONDARY_BEAM_DEMERIT;
395           
396           if (rdir == DOWN && dy <= eps
397               && fabs (my_modf (yr) - hang) < eps)
398             dem += SECONDARY_BEAM_DEMERIT;
399         }
400       
401       if (beam_count >= 3)
402         {
403           if (fabs (yl - 2 * ldir * beam_translation) < rad + inter)
404             {
405               if (ldir == UP && dy <= eps
406                   && fabs (my_modf (yl) - straddle) < eps)
407                 dem += SECONDARY_BEAM_DEMERIT;
408               
409               if (ldir == DOWN && dy >= eps
410                   && fabs (my_modf (yl) - straddle) < eps)
411                 dem += SECONDARY_BEAM_DEMERIT;
412         }
413           
414           if (fabs (yr - 2 * rdir * beam_translation) < rad + inter)
415             {
416               if (rdir == UP && dy >= eps
417                   && fabs (my_modf (yr) - straddle) < eps)
418                 dem += SECONDARY_BEAM_DEMERIT;
419               
420               if (rdir == DOWN && dy <= eps
421                   && fabs (my_modf (yr) - straddle) < eps)
422                 dem += SECONDARY_BEAM_DEMERIT;
423             }
424         }
425     }
426   
427   return dem;
428 }
429
430