]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam-quanting.cc
2002-07-17 Han-Wen <hanwen@cs.uu.nl>
[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     if (qscores[i].demerits < 100)
193       {
194         qscores[i].demerits
195           += score_slopes_dy (me, qscores[i].yl, qscores[i].yr,
196                               dy_mus, yr- yl, xstaff); 
197       }
198
199   Real rad = Staff_symbol_referencer::staff_radius (me);
200   int beam_count = get_beam_count (me);
201   Real beam_space = beam_count < 4
202     ? (2*ss + slt - thickness) / 2.0
203      : (3*ss + slt - thickness) / 3.0;
204
205   for (int i = qscores.size (); i--;)
206     if (qscores[i].demerits < 100)
207       {
208         qscores[i].demerits
209           += score_forbidden_quants (me, qscores[i].yl, qscores[i].yr,
210                                      rad, slt, thickness, beam_space,
211                                      beam_count, ldir, rdir); 
212       }
213
214
215   for (int i = qscores.size (); i--;)
216     if (qscores[i].demerits < 100)
217       {
218         qscores[i].demerits
219           += score_stem_lengths (stems, stem_infos,
220                                  base_lengths, stem_xposns,
221                                  xl, xr,
222                                  knee_b,
223                                  me, qscores[i].yl, qscores[i].yr);
224       }
225
226
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                           Grob*me,
252                           Real yl, Real yr)
253 {
254   Real demerit_score = 0.0 ;
255   Real pen = STEM_LENGTH_LIMIT_PENALTY;
256   
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       demerit_score += pen
272         * (0 >? (info.dir_ * (info.shortest_y_ - current_y)));
273       
274       demerit_score += STEM_LENGTH_DEMERIT_FACTOR
275         * shrink_extra_weight (d * current_y  - info.dir_ * info.ideal_y_);
276     }
277
278   demerit_score *= 2.0 / stems.size (); 
279
280   return demerit_score;
281 }
282
283 Real
284 Beam::score_slopes_dy (Grob *me,
285                        Real yl, Real yr,
286                        Real dy_mus, Real dy_damp,
287                        bool xstaff)
288 {
289   Real dy = yr - yl;
290
291   Real dem = 0.0;
292   if (sign (dy_damp) != sign (dy))
293     {
294       dem += DAMPING_DIRECTIION_PENALTY;
295     }
296
297    dem += MUSICAL_DIRECTION_FACTOR * (0 >? (fabs (dy) - fabs (dy_mus)));
298
299
300    Real slope_penalty = IDEAL_SLOPE_FACTOR;
301
302    /*
303      Xstaff beams tend to use extreme slopes to get short stems. We
304      put in a penalty here.
305    */
306    if (xstaff)
307      slope_penalty *= 10;
308
309    dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy))* slope_penalty;
310    return dem;
311 }
312
313 static Real
314 my_modf (Real x)
315 {
316   return x - floor (x);
317 }
318
319 Real
320 Beam::score_forbidden_quants (Grob*me,
321                               Real yl, Real yr,
322                               Real rad,
323                               Real slt,
324                               Real thickness, Real beam_space,
325                               int beam_count,
326                               Direction ldir, Direction rdir)
327 {
328   Real dy = yr - yl;
329
330   Real dem = 0.0;
331   if (fabs (yl) < rad && fabs ( my_modf (yl) - 0.5) < 1e-3)
332     dem += INTER_QUANT_PENALTY;
333   if (fabs (yr) < rad && fabs ( my_modf (yr) - 0.5) < 1e-3)
334     dem += INTER_QUANT_PENALTY;
335
336   // todo: use beam_count of outer stems.
337   if (beam_count >= 2)
338     {
339      
340       Real straddle = 0.0;
341       Real sit = (thickness - slt) / 2;
342       Real inter = 0.5;
343       Real hang = 1.0 - (thickness - slt) / 2;
344       
345
346       if (fabs (yl - ldir * beam_space) < rad
347           && fabs (my_modf (yl) - inter) < 1e-3)
348         dem += SECONDARY_BEAM_DEMERIT;
349       if (fabs (yr - rdir * beam_space) < rad
350           && fabs (my_modf (yr) - inter) < 1e-3)
351         dem += SECONDARY_BEAM_DEMERIT;
352
353       Real eps = 1e-3;
354
355       /*
356         Can't we simply compute the distance between the nearest
357         staffline and the secondary beam? That would get rid of the
358         silly case analysis here (which is probably not when we have
359         different beam-thicknesses.)
360
361         --hwn
362        */
363
364
365       // hmm, without Interval/Drul_array, you get ~ 4x same code...
366       if (fabs (yl - ldir * beam_space) < rad + inter)
367         {
368           if (ldir == UP && dy <= eps
369               && fabs (my_modf (yl) - sit) < eps)
370             dem += SECONDARY_BEAM_DEMERIT;
371           
372           if (ldir == DOWN && dy >= eps
373               && fabs (my_modf (yl) - hang) < eps)
374             dem += SECONDARY_BEAM_DEMERIT;
375         }
376
377       if (fabs (yr - rdir * beam_space) < rad + inter)
378         {
379           if (rdir == UP && dy >= eps
380               && fabs (my_modf (yr) - sit) < eps)
381             dem += SECONDARY_BEAM_DEMERIT;
382           
383           if (rdir == DOWN && dy <= eps
384               && fabs (my_modf (yr) - hang) < eps)
385             dem += SECONDARY_BEAM_DEMERIT;
386         }
387       
388       if (beam_count >= 3)
389         {
390           if (fabs (yl - 2 * ldir * beam_space) < rad + inter)
391             {
392               if (ldir == UP && dy <= eps
393                   && fabs (my_modf (yl) - straddle) < eps)
394                 dem += SECONDARY_BEAM_DEMERIT;
395               
396               if (ldir == DOWN && dy >= eps
397                   && fabs (my_modf (yl) - straddle) < eps)
398                 dem += SECONDARY_BEAM_DEMERIT;
399         }
400           
401           if (fabs (yr - 2 * rdir * beam_space) < rad + inter)
402             {
403               if (rdir == UP && dy >= eps
404                   && fabs (my_modf (yr) - straddle) < eps)
405                 dem += SECONDARY_BEAM_DEMERIT;
406               
407               if (rdir == DOWN && dy <= eps
408                   && fabs (my_modf (yr) - straddle) < eps)
409                 dem += SECONDARY_BEAM_DEMERIT;
410             }
411         }
412     }
413   
414   return dem;
415 }
416
417