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