]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
* input/regression/beam-concave.ly: Add to-be-considered-concave
[lilypond.git] / lily / beam.cc
1 /*
2   beam.cc -- implement Beam
3   
4   source file of the GNU LilyPond music typesetter
5   
6   (c)  1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7   Jan Nieuwenhuizen <janneke@gnu.org>
8   
9 */
10
11 /*
12   [TODO]
13
14   * Fix TODO
15   
16   * Junk stem_info.
17   
18   * Remove #'direction from beam.  A beam has no direction per se.
19     It may only set directions for stems.
20
21   * Rewrite stem_beams.
22
23   * Use Number_pair i.s.o Interval to represent (yl, yr).
24   
25   */
26
27
28 #include <math.h> // tanh.
29
30 #include "molecule.hh" 
31 #include "directional-element-interface.hh"
32 #include "beaming.hh"
33 #include "beam.hh"
34 #include "misc.hh"
35 #include "least-squares.hh"
36 #include "stem.hh"
37 #include "paper-def.hh"
38 #include "lookup.hh"
39 #include "group-interface.hh"
40 #include "staff-symbol-referencer.hh"
41 #include "item.hh"
42 #include "spanner.hh"
43 #include "warn.hh"
44 #include "text-item.hh"  // debug output.
45 #include "font-interface.hh"  // debug output.
46
47
48 #define DEBUG_QUANTING 0
49
50
51 static Real
52 shrink_extra_weight (Real x)
53 {
54   return fabs (x) * ((x < 0) ? 1.5 : 1.0);
55 }
56
57 void
58 Beam::add_stem (Grob *me, Grob *s)
59 {
60   Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
61   
62   s->add_dependency (me);
63
64   assert (!Stem::beam_l (s));
65   s->set_grob_property ("beam", me->self_scm ());
66
67   add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
68 }
69
70
71 /*
72   TODO: fix this for grace notes.
73  */
74 Real
75 Beam::get_interbeam (Grob *me)
76 {
77   Real slt = me->paper_l ()->get_var ("stafflinethickness");
78   Real ss = Staff_symbol_referencer::staff_space (me);
79   Real thickness = gh_scm2double (me->get_grob_property ("thickness"))
80     * ss;
81
82   int multiplicity = get_multiplicity (me);
83   Real interbeam = multiplicity < 4
84     ? (2*ss + slt - thickness) / 2.0
85     : (3*ss + slt - thickness) / 3.0;
86   
87   return interbeam;
88 }
89
90 int
91 Beam::get_multiplicity (Grob *me) 
92 {
93   int m = 0;
94   for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
95     {
96       Grob *sc = unsmob_grob (ly_car (s));
97
98       if (Stem::has_interface (sc))
99         m = m >? Stem::beam_count (sc, LEFT) >? Stem::beam_count (sc, RIGHT);
100     }
101   return m;
102 }
103
104 /* After pre-processing all directions should be set.
105    Several post-processing routines (stem, slur, script) need stem/beam
106    direction.
107    Currenly, this means that beam has set all stem's directions.
108    [Alternatively, stems could set its own directions, according to
109    their beam, during 'final-pre-processing'.] */
110 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
111 SCM
112 Beam::before_line_breaking (SCM smob)
113 {
114   Grob *me =  unsmob_grob (smob);
115
116   /* Beams with less than 2 two stems don't make much sense, but could happen
117      when you do
118      
119      [r8 c8 r8].
120      
121     For a beam that  only has one stem, we try to do some disappearance magic:
122     we revert the flag, and move on to The Eternal Engraving Fields. */
123
124   int count = visible_stem_count (me);
125   if (count < 2)
126     {
127       me->warning (_ ("beam has less than two visible stems"));
128
129       SCM stems = me->get_grob_property ("stems");
130       if (scm_ilength (stems) == 1)
131         {
132           me->warning (_ ("Beam has less than two stems. Removing beam."));
133
134           unsmob_grob (gh_car (stems))->remove_grob_property ("beam");
135           me->suicide ();
136
137           return SCM_UNSPECIFIED;
138         }
139       else if (scm_ilength (stems) == 0)
140         {
141           me->suicide ();
142           return SCM_UNSPECIFIED;         
143         }
144     }
145   if (count >= 1)
146     {
147       if (!Directional_element_interface::get (me))
148         Directional_element_interface::set (me, get_default_dir (me));
149       
150       consider_auto_knees (me);
151       set_stem_directions (me);
152       set_stem_shorten (me);
153     }
154   return SCM_EOL;
155 }
156
157 Direction
158 Beam::get_default_dir (Grob *me) 
159 {
160   Drul_array<int> total;
161   total[UP]  = total[DOWN] = 0;
162   Drul_array<int> count; 
163   count[UP]  = count[DOWN] = 0;
164   Direction d = DOWN;
165
166   Link_array<Item> stems=
167         Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
168
169   for (int i=0; i <stems.size (); i++)
170     do {
171       Grob *s = stems[i];
172       Direction sd = Directional_element_interface::get (s);
173       int current = sd  ? (1 + d * sd)/2
174         : Stem::get_center_distance (s, (Direction)-d);
175
176       if (current)
177         {
178           total[d] += current;
179           count[d] ++;
180         }
181     } while (flip (&d) != DOWN);
182   
183   SCM func = me->get_grob_property ("dir-function");
184   SCM s = gh_call2 (func,
185                     gh_cons (gh_int2scm (count[UP]),
186                              gh_int2scm (count[DOWN])),
187                     gh_cons (gh_int2scm (total[UP]),
188                              gh_int2scm (total[DOWN])));
189
190   if (gh_number_p (s) && gh_scm2int (s))
191     return to_dir (s);
192   
193   /* If dir is not determined: get default */
194   return to_dir (me->get_grob_property ("neutral-direction"));
195 }
196
197
198 /* Set all stems with non-forced direction to beam direction.
199    Urg: non-forced should become `without/with unforced' direction,
200    once stem gets cleaned-up. */
201 void
202 Beam::set_stem_directions (Grob *me)
203 {
204   Link_array<Item> stems
205     =Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
206   Direction d = Directional_element_interface::get (me);
207   
208   for (int i=0; i <stems.size (); i++)
209     {
210       Grob *s = stems[i];
211       SCM force = s->remove_grob_property ("dir-forced");
212       if (!gh_boolean_p (force) || !gh_scm2bool (force))
213         Directional_element_interface::set (s, d);
214     }
215
216
217 /* Simplistic auto-knees; only consider vertical gap between two
218    adjacent chords.
219
220   `Forced' stem directions are ignored.  If you don't want auto-knees,
221   don't set, or unset auto-knee-gap. */
222 void
223 Beam::consider_auto_knees (Grob *me)
224 {
225   SCM scm = me->get_grob_property ("auto-knee-gap");
226
227   if (gh_number_p (scm))
228     {
229       bool knee_b = false;
230       Real knee_y = 0;
231       Real staff_space = Staff_symbol_referencer::staff_space (me);
232       Real gap = gh_scm2double (scm) / staff_space;
233
234       Direction d = Directional_element_interface::get (me);
235       Link_array<Item> stems=
236         Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
237       
238       Grob *common = me->common_refpoint (stems[0], Y_AXIS);
239       for (int i=1; i < stems.size (); i++)
240         if (!Stem::invisible_b (stems[i]))
241           common = common->common_refpoint (stems[i], Y_AXIS);
242
243       int l = 0;
244       for (int i=1; i < stems.size (); i++)
245         {
246           if (!Stem::invisible_b (stems[i-1]))
247             l = i - 1;
248           if (Stem::invisible_b (stems[l]))
249             continue;
250           if (Stem::invisible_b (stems[i]))
251             continue;
252           
253           Real left = Stem::extremal_heads (stems[l])[d]
254             ->relative_coordinate (common, Y_AXIS);
255           Real right = Stem::extremal_heads (stems[i])[-d]
256             ->relative_coordinate (common, Y_AXIS);
257
258           Real dy = right - left;
259
260           if (abs (dy) >= gap)
261             {
262               knee_y = (right + left) / 2;
263               knee_b = true;
264               break;
265             }
266         }
267       
268       if (knee_b)
269         {
270           for (int i=0; i < stems.size (); i++)
271             {
272               if (Stem::invisible_b (stems[i]))
273                 continue;
274               Item *s = stems[i];         
275               Real y = Stem::extremal_heads (stems[i])[d]
276                 ->relative_coordinate (common, Y_AXIS);
277
278               Directional_element_interface::set (s, y < knee_y ? UP : DOWN);
279               s->set_grob_property ("dir-forced", SCM_BOOL_T);
280             }
281         }
282     }
283 }
284
285 /* Set stem's shorten property if unset.
286
287  TODO:
288    take some y-position (chord/beam/nearest?) into account
289    scmify forced-fraction
290
291    TODO:
292    
293    why is shorten stored in beam, and not directly in stem?
294
295 */
296 void
297 Beam::set_stem_shorten (Grob *m)
298 {
299   Spanner*me = dynamic_cast<Spanner*> (m);
300
301   Real forced_fraction = forced_stem_count (me) / visible_stem_count (me);
302
303   int multiplicity = get_multiplicity (me);
304
305   SCM shorten = me->get_grob_property ("beamed-stem-shorten");
306   if (shorten == SCM_EOL)
307     return;
308
309   int sz = scm_ilength (shorten);
310   
311   Real staff_space = Staff_symbol_referencer::staff_space (me);
312   SCM shorten_elt = scm_list_ref (shorten,
313                                   gh_int2scm (multiplicity <? (sz - 1)));
314   Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
315
316   /* your similar cute comment here */
317   shorten_f *= forced_fraction;
318   
319   me->set_grob_property ("shorten", gh_double2scm (shorten_f));
320 }
321
322 /*  Call list of y-dy-callbacks, that handle setting of
323     grob-properties y, dy.
324     
325     User may set grob-properties: y-position-hs and height-hs
326  (to be fixed) that override the calculated y and dy.
327     
328     Because y and dy cannot be calculated and quanted separately, we
329     always calculate both, then check for user override. */
330 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
331 SCM
332 Beam::after_line_breaking (SCM smob)
333 {
334   Grob *me = unsmob_grob (smob);
335   
336   /* Copy to mutable list. */
337   SCM s = ly_deep_copy (me->get_grob_property ("positions"));
338   me->set_grob_property ("positions", s);
339
340   if (ly_car (s) != SCM_BOOL_F)
341     return SCM_UNSPECIFIED;
342
343   SCM callbacks = me->get_grob_property ("position-callbacks");
344   for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
345     gh_call1 (ly_car (i), smob);
346
347   return SCM_UNSPECIFIED;
348 }
349
350 struct Quant_score
351 {
352   Real yl;
353   Real yr;
354   Real demerits;
355 };
356
357
358 /*
359   TODO:
360   
361    - Make all demerits customisable
362
363    - One sensible check per demerit (what's this --hwn)
364
365    - Add demerits for quants per se, as to forbid a specific quant
366      entirely
367
368 */
369 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
370 SCM
371 Beam::quanting (SCM smob)
372 {
373   Grob *me = unsmob_grob (smob);
374
375   SCM s = me->get_grob_property ("positions");
376   Real yl = gh_scm2double (gh_car (s));
377   Real yr = gh_scm2double (gh_cdr (s));
378
379   Real ss = Staff_symbol_referencer::staff_space (me);
380   Real thickness = gh_scm2double (me->get_grob_property ("thickness")) / ss;
381   Real slt = me->paper_l ()->get_var ("stafflinethickness") / ss;
382
383
384   SCM sdy = me->get_grob_property ("least-squares-dy");
385   Real dy_mus = gh_number_p (sdy) ? gh_scm2double (sdy) : 0.0;
386   
387   Real straddle = 0.0;
388   Real sit = (thickness - slt) / 2;
389   Real inter = 0.5;
390   Real hang = 1.0 - (thickness - slt) / 2;
391   Real quants [] = {straddle, sit, inter, hang };
392   
393   int num_quants = int (sizeof (quants)/sizeof (Real));
394   Array<Real> quantsl;
395   Array<Real> quantsr;
396
397   /*
398     going to REGION_SIZE == 2, yields another 0.6 second with
399     wtk1-fugue2.
400
401
402     (result indexes between 70 and 575)  ? --hwn. 
403
404   */
405
406   const int REGION_SIZE = 3;
407   for (int i  = -REGION_SIZE ; i < REGION_SIZE; i++)
408     for (int j = 0; j < num_quants; j++)
409       {
410         quantsl.push (i + quants[j] + int (yl));
411         quantsr.push (i + quants[j] + int (yr));
412       }
413
414   Array<Quant_score> qscores;
415   
416   for (int l =0; l < quantsl.size (); l++)  
417     for (int r =0; r < quantsr.size (); r++)
418       {
419         Quant_score qs;
420         qs.yl = quantsl[l];
421         qs.yr = quantsr[r];
422         qs.demerits = 0.0;
423         
424         qscores.push (qs);
425       }
426
427
428   /*
429     This is a longish function, but we don't separate this out into
430     neat modular separate subfunctions, as the subfunctions would be
431     called for many values of YL, YR. By precomputing various
432     parameters outside of the loop, we can save a lot of time.
433
434   */
435   for (int i = qscores.size (); i--;)
436     if (qscores[i].demerits < 100)
437       {
438         qscores[i].demerits
439           += score_slopes_dy (me, qscores[i].yl, qscores[i].yr,
440                               dy_mus, yr- yl); 
441       }
442
443   Real rad = Staff_symbol_referencer::staff_radius (me);
444   int multiplicity = get_multiplicity (me);
445   Real interbeam = multiplicity < 4
446     ? (2*ss + slt - thickness) / 2.0
447      : (3*ss + slt - thickness) / 3.0;
448
449   for (int i = qscores.size (); i--;)
450     if (qscores[i].demerits < 100)
451       {
452         qscores[i].demerits
453           += score_forbidden_quants (me, qscores[i].yl, qscores[i].yr,
454                                      rad, slt, thickness, interbeam,
455                                      multiplicity); 
456       }
457
458
459   /*
460     Do stem lengths.  These depend on YL and YR linearly, so we can
461     precompute for every stem 2 factors.
462    */
463   Link_array<Grob> stems=
464     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
465   Array<Stem_info> stem_infos;
466   Array<Real> lbase_lengths;
467   Array<Real> rbase_lengths;  
468
469   Array<int> directions;
470   for (int i= 0; i < stems.size(); i++)
471     {
472       Grob*s = stems[i];
473       stem_infos.push( Stem::calc_stem_info (s));
474
475       Real b = calc_stem_y (me, s, Interval (1,0));
476       lbase_lengths.push (b);
477
478       b = calc_stem_y (me, s, Interval (0,1));
479       rbase_lengths.push (b);
480       directions.push( Directional_element_interface::get( s));
481     }
482
483   for (int i = qscores.size (); i--;)
484     if (qscores[i].demerits < 100)
485       {
486         qscores[i].demerits
487           += score_stem_lengths (stems, stem_infos,
488                                  lbase_lengths, rbase_lengths,
489                                  directions,
490                                  me, qscores[i].yl, qscores[i].yr);
491       }
492
493
494   Real best = 1e6;
495   int best_idx = -1;
496   for (int i = qscores.size (); i--;)
497     {
498       if (qscores[i].demerits < best)
499         {
500           best = qscores [i].demerits ;
501           best_idx = i;
502         }
503     }
504
505   
506   me->set_grob_property ("positions",
507                          gh_cons (gh_double2scm (qscores[best_idx].yl),
508                                   gh_double2scm (qscores[best_idx].yr))
509                          );
510
511   if (DEBUG_QUANTING)
512   {
513           // debug quanting
514           me->set_grob_property ("quant-score",
515                                  gh_double2scm (qscores[best_idx].demerits));
516           me->set_grob_property ("best-idx", gh_int2scm (best_idx));
517   }
518
519   return SCM_UNSPECIFIED;
520 }
521
522 Real
523 Beam::score_stem_lengths (Link_array<Grob>stems,
524                           Array<Stem_info> stem_infos,
525                           Array<Real> left_factor,
526                           Array<Real> right_factor,
527                           Array<int> directions,
528                           Grob*me, Real yl, Real yr)
529 {
530   Real demerit_score = 0.0 ;
531   
532   for (int i=0; i < stems.size (); i++)
533     {
534       Grob* s = stems[i];
535       if (Stem::invisible_b (s))
536         continue;
537
538       Real current_y =
539         yl * left_factor[i] + right_factor[i]* yr;
540
541       Stem_info info = stem_infos[i];
542       Direction d = Direction (directions[i]);
543       
544       demerit_score += 500 * ( 0 >? (info.min_y - d * current_y));
545       demerit_score += 500 * ( 0 >? (d * current_y  - info.max_y));
546
547       demerit_score += 5 * shrink_extra_weight (d * current_y  - info.ideal_y);
548     }
549
550   demerit_score *= 2.0  /stems.size (); 
551
552   return demerit_score;
553 }
554
555 Real
556 Beam::score_slopes_dy (Grob *me, Real yl, Real yr,
557                        Real dy_mus, Real dy_damp)
558 {
559   Real dy = yr - yl;
560
561   Real dem = 0.0;
562   if (sign (dy_damp) != sign (dy))
563     {
564       dem += 800;
565     }
566   
567    dem += 400* (0 >? (fabs (dy) - fabs (dy_mus)));
568   
569
570    dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy))* 10;
571    return dem;
572 }
573
574 static Real
575 my_modf (Real x)
576 {
577   return x - floor (x);
578 }
579
580 Real
581 Beam::score_forbidden_quants (Grob*me,
582                               Real yl, Real yr,
583                               Real rad,
584                               Real slt,
585                               Real thickness, Real interbeam,
586                               int multiplicity)
587 {
588   Real dy = yr - yl;
589
590   Real dem = 0.0;
591   if (fabs (yl) < rad && fabs ( my_modf (yl) - 0.5) < 1e-3)
592     dem += 1000;
593   if (fabs (yr) < rad && fabs ( my_modf (yr) - 0.5) < 1e-3)
594     dem += 1000;
595
596   // todo: use multiplicity of outer stems.
597   if (multiplicity >= 2)
598     {
599      
600       Real straddle = 0.0;
601       Real sit = (thickness - slt) / 2;
602       Real inter = 0.5;
603       Real hang = 1.0 - (thickness - slt) / 2;
604       
605       Direction dir = Directional_element_interface::get (me);
606       if (fabs (yl - dir * interbeam) < rad
607           && fabs (my_modf (yl) - inter) < 1e-3)
608         dem += 15;
609       if (fabs (yr - dir * interbeam) < rad
610           && fabs (my_modf (yr) - inter) < 1e-3)
611         dem += 15;
612
613       Real eps = 1e-3;
614
615       /*
616         Can't we simply compute the distance between the nearest
617         staffline and the secondary beam? That would get rid of the
618         silly case analysis here (which is probably not when we have
619         different beam-thicknesses.)
620
621         --hwn
622        */
623       
624       // hmm, without Interval/Drul_array, you get ~ 4x same code...
625       if (fabs (yl - dir * interbeam) < rad + inter)
626         {
627           if (dir == UP && dy <= eps
628               && fabs (my_modf (yl) - sit) < eps)
629             dem += 15;
630           
631           if (dir == DOWN && dy >= eps
632               && fabs (my_modf (yl) - hang) < eps)
633             dem += 15;
634         }
635
636       if (fabs (yr - dir * interbeam) < rad + inter)
637         {
638           if (dir == UP && dy >= eps
639               && fabs (my_modf (yr) - sit) < eps)
640             dem += 15;
641           
642           if (dir == DOWN && dy <= eps
643               && fabs (my_modf (yr) - hang) < eps)
644             dem += 15;
645         }
646       
647       if (multiplicity >= 3)
648         {
649           if (fabs (yl - 2 * dir * interbeam) < rad + inter)
650             {
651               if (dir == UP && dy <= eps
652                   && fabs (my_modf (yl) - straddle) < eps)
653                 dem += 15;
654               
655               if (dir == DOWN && dy >= eps
656                   && fabs (my_modf (yl) - straddle) < eps)
657                 dem += 15;
658         }
659           
660           if (fabs (yr - 2 * dir * interbeam) < rad + inter)
661             {
662               if (dir == UP && dy >= eps
663                   && fabs (my_modf (yr) - straddle) < eps)
664                 dem += 15;
665               
666               if (dir == DOWN && dy <= eps
667                   && fabs (my_modf (yr) - straddle) < eps)
668                 dem += 15;
669             }
670         }
671     }
672   
673   return dem;
674 }
675
676   
677
678 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
679 SCM
680 Beam::least_squares (SCM smob)
681 {
682   Grob *me = unsmob_grob (smob);
683
684   int count = visible_stem_count (me);
685   Interval pos (0, 0);
686   
687   if (count <= 1)
688     {
689       me->set_grob_property ("positions", ly_interval2scm (pos));
690       return SCM_UNSPECIFIED;
691     }
692   
693   Direction dir = Directional_element_interface::get (me);
694
695   Interval ideal (Stem::calc_stem_info (first_visible_stem (me)).ideal_y,
696                   Stem::calc_stem_info (last_visible_stem (me)).ideal_y);
697   
698   if (!ideal.delta ())
699     {
700       Interval chord (Stem::chord_start_f (first_visible_stem (me)),
701                       Stem::chord_start_f (last_visible_stem (me)));
702
703
704       /*
705         TODO  : use scoring for this.
706
707         complicated, because we take stem-info.ideal for determining
708         beam slopes.
709         
710        */
711       /* Make simple beam on middle line have small tilt */
712       if (!ideal[LEFT] && chord.delta () && count == 2)
713         {
714           Direction d = (Direction) (sign (chord.delta ()) * dir);
715           pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2
716             * dir;
717           pos[-d] = - pos[d];
718         }
719       else
720         {
721           pos = ideal;
722           pos[LEFT] *= dir ;
723           pos[RIGHT] *= dir ;
724         }
725     }
726   else
727     {
728       Array<Offset> ideals;
729
730       // ugh -> use commonx
731       Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
732       Link_array<Item> stems=
733         Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
734
735       for (int i=0; i < stems.size (); i++)
736         {
737           Item* s = stems[i];
738           if (Stem::invisible_b (s))
739             continue;
740           ideals.push (Offset (s->relative_coordinate (0, X_AXIS) - x0,
741                                Stem::calc_stem_info (s).ideal_y));
742         }
743       Real y; 
744       Real dydx;
745       minimise_least_squares (&dydx, &y, ideals);
746
747       Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
748       Real dy = dydx * dx;
749       me->set_grob_property ("least-squares-dy", gh_double2scm (dy * dir));
750
751       pos = Interval (y*dir, (y+dy) * dir);
752     }
753
754   me->set_grob_property ("positions", ly_interval2scm (pos));
755   return SCM_UNSPECIFIED;
756 }
757
758 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
759 SCM
760 Beam::check_concave (SCM smob)
761 {
762   Grob *me = unsmob_grob (smob);
763
764   Link_array<Item> stems = 
765     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
766
767   for (int i = 0; i < stems.size ();)
768     {
769       if (Stem::invisible_b (stems[i]))
770         stems.del (i);
771       else
772         i++;
773     }
774   
775   if (stems.size () < 3)
776     return SCM_UNSPECIFIED;
777
778   Direction dir = Directional_element_interface::get (me);
779   /* Concaveness #1: If distance of an inner notehead to line between
780      two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
781      beam is concave (Heinz Stolba). */
782   bool concaveness1 = false;
783   Real r1 = gh_scm2double (me->get_grob_property ("concaveness-gap"));
784   if (r1 > 0)
785     {
786       Real dy = Stem::chord_start_f (stems.top ())
787         - Stem::chord_start_f (stems[0]);
788       Real slope = dy / (stems.size () - 1);
789       
790       Real y0 = Stem::chord_start_f (stems[0]);
791       for (int i = 1; i < stems.size () - 1; i++)
792         {
793           Real c = (Stem::chord_start_f (stems[i]) - y0) - i * slope;
794           if (c > r1)
795             {
796               concaveness1 = true;
797               break;
798             }
799         }
800     }
801
802     
803   /* Concaveness #2: Sum distances of inner noteheads that fall
804      outside the interval of the two outer noteheads */
805   Real concaveness2 = 0;
806   Real r2 = gh_scm2double (me->get_grob_property ("concaveness-threshold"));
807   if (!concaveness1 && r2 > 0)
808     {
809       Real concave = 0;
810       Interval iv (Stem::chord_start_f (stems[0]),
811                    Stem::chord_start_f (stems.top ()));
812       
813       if (iv[MAX] < iv[MIN])
814         iv.swap ();
815       
816       for (int i = 1; i < stems.size () - 1; i++)
817         {
818           Real c = 0;
819           Real f = Stem::chord_start_f (stems[i]);
820           if ((c = f - iv[MAX]) > 0)
821             concave += c;
822           else if ((c = f - iv[MIN]) < 0)
823             concave += c;
824         }
825       
826       concave *= dir;
827
828       concaveness2 = concave / (stems.size () - 2);
829       /* ugh: this is the a kludge to get input/regression/beam-concave.ly
830          to behave as baerenreiter. */
831       concaveness2 /= (stems.size () - 2);
832     }
833   
834   /* TODO: some sort of damping iso -> plain horizontal */
835   if (concaveness1 || concaveness2 > r2)
836     {
837       Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
838       Real r = pos.linear_combination (0);
839       me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
840       me->set_grob_property ("least-squares-dy", gh_double2scm (0));
841     }
842
843   return SCM_UNSPECIFIED;
844 }
845
846 /* This neat trick is by Werner Lemberg,
847    damped = tanh (slope)
848    corresponds with some tables in [Wanske] CHECKME */
849 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
850 SCM
851 Beam::slope_damping (SCM smob)
852 {
853   Grob *me = unsmob_grob (smob);
854
855   if (visible_stem_count (me) <= 1)
856     return SCM_UNSPECIFIED;
857
858   SCM s = me->get_grob_property ("damping"); 
859   int damping = gh_scm2int (s);
860
861   if (damping)
862     {
863       Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
864       Real dy = pos.delta ();
865       
866       // ugh -> use commonx
867       Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS)
868         - first_visible_stem (me)->relative_coordinate (0, X_AXIS);
869       Real dydx = dy && dx ? dy/dx : 0;
870       dydx = 0.6 * tanh (dydx) / damping;
871
872       Real damped_dy = dydx * dx;
873       pos[LEFT] += (dy - damped_dy) / 2;
874       pos[RIGHT] -= (dy - damped_dy) / 2;
875       
876       me->set_grob_property ("positions", ly_interval2scm (pos));
877     }
878     return SCM_UNSPECIFIED;
879 }
880
881 MAKE_SCHEME_CALLBACK (Beam, end_after_line_breaking, 1);
882 SCM
883 Beam::end_after_line_breaking (SCM smob)
884 {
885   Grob *me = unsmob_grob (smob);
886   set_stem_lengths (me);
887   
888   return SCM_UNSPECIFIED;
889 }
890
891 /*
892   Calculate the Y position of the stem-end, given the Y-left, Y-right
893   in POS, and for stem S.
894  */
895 Real
896 Beam::calc_stem_y (Grob *me, Grob* s, Interval pos)
897 {
898   int beam_multiplicity = get_multiplicity (me);
899   int stem_multiplicity = (Stem::flag_i (s) - 2) >? 0;
900
901   Real thick = gh_scm2double (me->get_grob_property ("thickness"));
902   Real interbeam = get_interbeam (me);
903
904   // ugh -> use commonx
905   Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
906   Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
907   Real dy = pos.delta ();
908   Real stem_y = (dy && dx
909                  ? (s->relative_coordinate (0, X_AXIS) - x0) / dx
910                  * dy
911                  : 0) + pos[LEFT];
912
913   /* knee */
914   Direction dir  = Directional_element_interface::get (me);
915   Direction sdir = Directional_element_interface::get (s);
916   
917   /* knee */
918   if (dir!= sdir)
919     {
920       stem_y -= dir * (thick / 2 + (beam_multiplicity - 1) * interbeam);
921
922       // huh, why not for first visible?
923
924       Grob *last_visible = last_visible_stem (me);
925       if (last_visible)
926         {
927           if ( Staff_symbol_referencer::staff_symbol_l (s)
928                != Staff_symbol_referencer::staff_symbol_l (last_visible))
929             stem_y += Directional_element_interface::get (me)
930               * (beam_multiplicity - stem_multiplicity) * interbeam;
931         }
932       else
933         programming_error ("No last visible stem");
934     }
935
936   return stem_y;
937 }
938
939 /*
940   Hmm.  At this time, beam position and slope are determined.  Maybe,
941   stem directions and length should set to relative to the chord's
942   position of the beam.  */
943 void
944 Beam::set_stem_lengths (Grob *me)
945 {
946   Link_array<Item> stems=
947     Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
948
949   if (stems.size () <= 1)
950     return;
951   
952   Grob *common = me->common_refpoint (stems[0], Y_AXIS);
953   for (int i=1; i < stems.size (); i++)
954     if (!Stem::invisible_b (stems[i]))
955       common = common->common_refpoint (stems[i], Y_AXIS);
956
957   Direction dir = Directional_element_interface::get (me);
958   Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
959   Real staff_space = Staff_symbol_referencer::staff_space (me);
960   Real thick = gh_scm2double (me->get_grob_property ("thickness"));
961   bool ps_testing = to_boolean (ly_symbol2scm ("ps-testing"));
962   for (int i=0; i < stems.size (); i++)
963     {
964       Item* s = stems[i];
965       if (Stem::invisible_b (s))
966         continue;
967
968       Real stem_y = calc_stem_y (me, s, pos);
969
970       // doesn't play well with dvips
971       if (ps_testing)
972         if (Stem::get_direction (s) == dir)
973           stem_y += Stem::get_direction (s) * thick / 2;
974       
975       /* caution: stem measures in staff-positions */
976       Real id = me->relative_coordinate (common, Y_AXIS)
977         - stems[i]->relative_coordinate (common, Y_AXIS);
978       Stem::set_stemend (s, (stem_y + id) / staff_space * 2);
979     }
980 }
981
982 void
983 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
984 {
985   Link_array<Grob> stems=
986     Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
987   
988   Direction d = LEFT;
989   for (int i=0; i  < stems.size (); i++)
990     {
991       do
992         {
993           /* Don't overwrite user override (?) */
994           if (Stem::beam_count (stems[i], d) == -1
995               /* Don't set beaming for outside of outer stems */
996               && ! (d == LEFT && i == 0)
997               && ! (d == RIGHT && i == stems.size () -1))
998             {
999               int b = beaming->infos_.elem (i).beams_i_drul_[d];
1000               Stem::set_beaming (stems[i], b, d);
1001             }
1002         }
1003       while (flip (&d) != LEFT);
1004     }
1005 }
1006
1007
1008
1009 /*
1010   beams to go with one stem.
1011
1012   FIXME: clean me up.
1013   */
1014 Molecule
1015 Beam::stem_beams (Grob *me, Item *here, Item *next, Item *prev, Real dydx)
1016 {
1017   // ugh -> use commonx
1018   if ((next
1019        && ! (next->relative_coordinate (0, X_AXIS)
1020             > here->relative_coordinate (0, X_AXIS)))
1021       || (prev
1022           && ! (prev->relative_coordinate (0, X_AXIS)
1023                < here->relative_coordinate (0, X_AXIS))))
1024     programming_error ("Beams are not left-to-right");
1025
1026   Real thick = gh_scm2double (me->get_grob_property ("thickness"));
1027   Real bdy = get_interbeam (me);
1028   
1029   Molecule leftbeams;
1030   Molecule rightbeams;
1031
1032   Real nw_f;
1033   if (!Stem::first_head (here))
1034     nw_f = 0;
1035   else {
1036     int t = Stem::type_i (here); 
1037
1038     SCM proc = me->get_grob_property ("flag-width-function");
1039     SCM result = gh_call1 (proc, gh_int2scm (t));
1040     nw_f = gh_scm2double (result);
1041   }
1042
1043
1044   Direction dir = Directional_element_interface::get (me);
1045
1046   /* [Tremolo] beams on whole notes may not have direction set? */
1047  if (dir == CENTER)
1048     dir = Directional_element_interface::get (here);
1049
1050
1051   /* half beams extending to the left. */
1052   if (prev)
1053     {
1054       int lhalfs= lhalfs = Stem::beam_count (here, LEFT)
1055         - Stem::beam_count (prev, RIGHT);
1056       int lwholebeams= Stem::beam_count (here, LEFT)
1057         <? Stem::beam_count (prev, RIGHT);
1058       
1059       /* Half beam should be one note-width,
1060          but let's make sure two half-beams never touch */
1061
1062       // FIXME: TODO (check) stem width / sloped beams
1063       Real w = here->relative_coordinate (0, X_AXIS)
1064         - prev->relative_coordinate (0, X_AXIS);
1065       Real stem_w = gh_scm2double (prev->get_grob_property ("thickness"))
1066         // URG
1067         * me->paper_l ()->get_var ("stafflinethickness");
1068
1069       w = w/2 <? nw_f;
1070       Molecule a;
1071       if (lhalfs)               // generates warnings if not
1072         a =  Lookup::beam (dydx, w + stem_w, thick);
1073       a.translate (Offset (-w, -w * dydx));
1074       a.translate_axis (-stem_w/2, X_AXIS);
1075       for (int j = 0; j  < lhalfs; j++)
1076         {
1077           Molecule b (a);
1078           b.translate_axis (-dir * bdy * (lwholebeams+j), Y_AXIS);
1079           leftbeams.add_molecule (b);
1080         }
1081     }
1082
1083   if (next)
1084     {
1085       int rhalfs  = Stem::beam_count (here, RIGHT)
1086         - Stem::beam_count (next, LEFT);
1087       int rwholebeams= Stem::beam_count (here, RIGHT)
1088         <? Stem::beam_count (next, LEFT);
1089
1090       Real w = next->relative_coordinate (0, X_AXIS)
1091         - here->relative_coordinate (0, X_AXIS);
1092
1093       Real stem_w = gh_scm2double (next->get_grob_property ("thickness"))
1094         // URG
1095         * me->paper_l ()->get_var ("stafflinethickness");
1096
1097       Molecule a = Lookup::beam (dydx, w + stem_w, thick);
1098       a.translate_axis (- stem_w/2, X_AXIS);
1099       int j = 0;
1100       Real gap_f = 0;
1101       
1102       SCM gap = me->get_grob_property ("gap");
1103       if (gh_number_p (gap))
1104         {
1105           int gap_i = gh_scm2int ((gap));
1106           int nogap = rwholebeams - gap_i;
1107           
1108           for (; j  < nogap; j++)
1109             {
1110               Molecule b (a);
1111               b.translate_axis (-dir  * bdy * j, Y_AXIS);
1112               rightbeams.add_molecule (b);
1113             }
1114           if (Stem::invisible_b (here))
1115             gap_f = nw_f;
1116           else
1117             gap_f = nw_f / 2;
1118           w -= 2 * gap_f;
1119           a = Lookup::beam (dydx, w + stem_w, thick);
1120         }
1121
1122       for (; j  < rwholebeams; j++)
1123         {
1124           Molecule b (a);
1125           Real tx = 0;
1126           if (Stem::invisible_b (here))
1127             // ugh, see chord-tremolo.ly
1128             tx = (-dir + 1) / 2 * nw_f * 1.5 + gap_f/4;
1129           else
1130             tx = gap_f;
1131           b.translate (Offset (tx, -dir * bdy * j));
1132           rightbeams.add_molecule (b);
1133         }
1134
1135       w = w/2 <? nw_f;
1136       if (rhalfs)
1137         a = Lookup::beam (dydx, w, thick);
1138
1139       for (; j  < rwholebeams + rhalfs; j++)
1140         {
1141           Molecule b (a);
1142           b.translate_axis (- dir * bdy * j, Y_AXIS);
1143           rightbeams.add_molecule (b);
1144         }
1145
1146     }
1147   leftbeams.add_molecule (rightbeams);
1148
1149   /* Does beam quanting think  of the asymetry of beams? 
1150      Refpoint is on bottom of symbol. (FIXTHAT) --hwn. */
1151   return leftbeams;
1152 }
1153
1154
1155 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
1156 SCM
1157 Beam::brew_molecule (SCM smob)
1158 {
1159   Grob *me =unsmob_grob (smob);
1160
1161   Molecule mol;
1162   if (!gh_pair_p (me->get_grob_property ("stems")))
1163     return SCM_EOL;
1164   Real x0, dx;
1165   Link_array<Item>stems = 
1166     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");  
1167   if (visible_stem_count (me))
1168     {
1169       // ugh -> use commonx
1170       x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
1171       dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
1172     }
1173   else
1174     {
1175       x0 = stems[0]->relative_coordinate (0, X_AXIS);
1176       dx = stems.top ()->relative_coordinate (0, X_AXIS) - x0;
1177     }
1178
1179   Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1180   Real dy = pos.delta ();
1181   Real dydx = dy && dx ? dy/dx : 0;
1182
1183   for (int i=0; i < stems.size (); i++)
1184     {
1185       Item *item = stems[i];
1186       Item *prev = (i > 0)? stems[i-1] : 0;
1187       Item *next = (i < stems.size ()-1) ? stems[i+1] :0;
1188
1189       Molecule sb = stem_beams (me, item, next, prev, dydx);
1190       Real x = item->relative_coordinate (0, X_AXIS) - x0;
1191       sb.translate (Offset (x, x * dydx + pos[LEFT]));
1192       mol.add_molecule (sb);
1193     }
1194   
1195   mol.translate_axis (x0 
1196                       - dynamic_cast<Spanner*> (me)
1197                       ->get_bound (LEFT)->relative_coordinate (0, X_AXIS),
1198                       X_AXIS);
1199
1200   if (DEBUG_QUANTING)
1201     {
1202       /*
1203         This code prints the demerits for each beam. Perhaps this
1204         should be switchable for those who want to twiddle with the
1205         parameters.
1206       */
1207       String str;
1208       if (1)
1209         {
1210           str += to_str (gh_scm2int (me->get_grob_property ("best-idx")));
1211           str += ":";
1212         }
1213       str += to_str (gh_scm2double (me->get_grob_property ("quant-score")),
1214                      "%.2f");
1215
1216       SCM properties = Font_interface::font_alist_chain (me);
1217   
1218       Molecule tm = Text_item::text2molecule (me, gh_str02scm (str.ch_C ()), properties);
1219       mol.add_at_edge (Y_AXIS, UP, tm, 5.0);
1220     }
1221   
1222   return mol.smobbed_copy ();
1223 }
1224
1225 int
1226 Beam::forced_stem_count (Grob *me) 
1227 {
1228   Link_array<Item>stems = 
1229     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1230   int f = 0;
1231   for (int i=0; i < stems.size (); i++)
1232     {
1233       Item *s = stems[i];
1234
1235       if (Stem::invisible_b (s))
1236         continue;
1237
1238       if (((int)Stem::chord_start_f (s)) 
1239         && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1240         f++;
1241     }
1242   return f;
1243 }
1244
1245
1246
1247
1248 int
1249 Beam::visible_stem_count (Grob *me) 
1250 {
1251   Link_array<Item>stems = 
1252     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1253   int c = 0;
1254   for (int i = stems.size (); i--;)
1255     {
1256       if (!Stem::invisible_b (stems[i]))
1257         c++;
1258     }
1259   return c;
1260 }
1261
1262 Item*
1263 Beam::first_visible_stem (Grob *me) 
1264 {
1265   Link_array<Item>stems = 
1266     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1267   
1268   for (int i = 0; i < stems.size (); i++)
1269     {
1270       if (!Stem::invisible_b (stems[i]))
1271         return stems[i];
1272     }
1273   return 0;
1274 }
1275
1276 Item*
1277 Beam::last_visible_stem (Grob *me) 
1278 {
1279   Link_array<Item>stems = 
1280     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1281   for (int i = stems.size (); i--;)
1282     {
1283       if (!Stem::invisible_b (stems[i]))
1284         return stems[i];
1285     }
1286   return 0;
1287 }
1288
1289
1290 /*
1291   [TODO]
1292   
1293   handle rest under beam (do_post: beams are calculated now)
1294   what about combination of collisions and rest under beam.
1295
1296   Should lookup
1297     
1298     rest -> stem -> beam -> interpolate_y_position ()
1299 */
1300 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1301 SCM
1302 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1303 {
1304   Grob *rest = unsmob_grob (element_smob);
1305   Axis a = (Axis) gh_scm2int (axis);
1306   
1307   assert (a == Y_AXIS);
1308
1309   Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1310   Grob *stem = st;
1311   if (!stem)
1312     return gh_double2scm (0.0);
1313   Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1314   if (!beam
1315       || !Beam::has_interface (beam)
1316       || !Beam::visible_stem_count (beam))
1317     return gh_double2scm (0.0);
1318
1319   // make callback for rest from this.
1320   // todo: make sure this calced already.
1321
1322   //  Interval pos = ly_scm2interval (beam->get_grob_property ("positions"));
1323   Interval pos (0, 0);
1324   SCM s = beam->get_grob_property ("positions");
1325   if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1326     pos = ly_scm2interval (s);
1327
1328   Real dy = pos.delta ();
1329   // ugh -> use commonx
1330   Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1331   Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1332   Real dydx = dy && dx ? dy/dx : 0;
1333   
1334   Direction d = Stem::get_direction (stem);
1335   Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + pos[LEFT];
1336
1337   Real staff_space = Staff_symbol_referencer::staff_space (rest);
1338
1339   
1340   Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space; // refp??
1341
1342   Real minimum_dist
1343     = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1344   Real dist =
1345     minimum_dist +  -d  * (beamy - rest_dim) >? 0;
1346
1347   int stafflines = Staff_symbol_referencer::line_count (rest);
1348
1349   // move discretely by half spaces.
1350   int discrete_dist = int (ceil (dist));
1351
1352   // move by whole spaces inside the staff.
1353   if (discrete_dist < stafflines+1)
1354     discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1355
1356   return gh_double2scm (-d *  discrete_dist);
1357 }
1358
1359
1360 bool
1361 Beam::has_interface (Grob *me)
1362 {
1363   return me->has_interface (ly_symbol2scm ("beam-interface"));
1364 }
1365
1366
1367 ADD_INTERFACE (Beam, "beam-interface",
1368   "A beam.
1369
1370 #'thickness= weight of beams, in staffspace
1371
1372
1373 We take the least squares line through the ideal-length stems, and
1374 then damp that using
1375
1376         damped = tanh (slope)
1377
1378 this gives an unquantized left and right position for the beam end.
1379 Then we take all combinations of quantings near these left and right
1380 positions, and give them a score (according to how close they are to
1381 the ideal slope, how close the result is to the ideal stems, etc.). We
1382 take the best scoring combination.
1383
1384 ",
1385   "concaveness-threshold dir-function quant-score auto-knee-gap gap chord-tremolo beamed-stem-shorten shorten least-squares-dy direction damping flag-width-function neutral-direction positions thickness");
1386