]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
517896e80c7965a271c06a8fb21b88b17679d272
[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--2003 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7   Jan Nieuwenhuizen <janneke@gnu.org>
8 */
9
10 /*
11 TODO:
12
13   * Use Number_pair i.s.o Interval to represent (yl, yr).
14   
15   - Determine auto knees based on positions if it's set by the user.
16
17   - rounded corners.
18
19 Notes:
20
21
22  - Stems run to the Y-center of the beam.
23   
24  - beam_translation is the offset between Y centers of the beam.
25
26 */
27
28
29 #include <math.h> // tanh.
30
31 #include "molecule.hh" 
32 #include "directional-element-interface.hh"
33 #include "beaming.hh"
34 #include "beam.hh"
35 #include "misc.hh"
36 #include "least-squares.hh"
37 #include "stem.hh"
38 #include "paper-def.hh"
39 #include "lookup.hh"
40 #include "group-interface.hh"
41 #include "staff-symbol-referencer.hh"
42 #include "item.hh"
43 #include "spanner.hh"
44 #include "warn.hh"
45
46
47 #define DEBUG_QUANTING 1
48
49 bool debug_beam_quanting_flag;
50
51
52 #if DEBUG_QUANTING
53 #include "text-item.hh"  // debug output.
54 #include "font-interface.hh"  // debug output.
55 #endif
56
57
58 void
59 Beam::add_stem (Grob *me, Grob *s)
60 {
61   Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
62   
63   s->add_dependency (me);
64
65   assert (!Stem::get_beam (s));
66   s->set_grob_property ("beam", me->self_scm ());
67
68   add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
69 }
70
71
72 Real
73 Beam::get_thickness (Grob * me)
74 {
75   SCM th = me->get_grob_property ("thickness");
76   if (gh_number_p (th))
77     return gh_scm2double (th)* Staff_symbol_referencer::staff_space (me);
78   else
79     return 0.0;
80 }
81
82 /* Return the translation between 2 adjoining beams. */
83 Real
84 Beam::get_beam_translation (Grob *me)
85 {
86   SCM func = me->get_grob_property ("space-function");
87   SCM s = gh_call2 (func, me->self_scm (), scm_int2num (get_beam_count (me)));
88   return gh_scm2double (s);
89 }
90
91 /* Maximum beam_count. */
92 int
93 Beam::get_beam_count (Grob *me) 
94 {
95   int m = 0;
96   for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
97     {
98       Grob *stem = unsmob_grob (ly_car (s));
99       m = m >? (Stem::beam_multiplicity (stem).length () + 1);
100     }
101   return m;
102 }
103
104
105 /*
106   Space return space between beams.
107  */
108 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
109 SCM
110 Beam::space_function (SCM smob, SCM beam_count)
111 {
112   Grob *me = unsmob_grob (smob);
113   
114   Real staff_space = Staff_symbol_referencer::staff_space (me);
115   Real line = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
116   Real thickness = get_thickness (me);
117   
118   Real beam_translation = gh_scm2int (beam_count) < 4
119     ? (2*staff_space + line - thickness) / 2.0
120     : (3*staff_space + line - thickness) / 3.0;
121   
122   return gh_double2scm (beam_translation);
123 }
124
125
126 /* After pre-processing all directions should be set.
127    Several post-processing routines (stem, slur, script) need stem/beam
128    direction.
129    Currenly, this means that beam has set all stem's directions.
130    [Alternatively, stems could set its own directions, according to
131    their beam, during 'final-pre-processing'.] */
132 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
133 SCM
134 Beam::before_line_breaking (SCM smob)
135 {
136   Grob *me =  unsmob_grob (smob);
137
138   /* Beams with less than 2 two stems don't make much sense, but could happen
139      when you do
140      
141      [r8 c8 r8].
142      
143     For a beam that  only has one stem, we try to do some disappearance magic:
144     we revert the flag, and move on to The Eternal Engraving Fields. */
145
146   int count = visible_stem_count (me);
147   if (count < 2)
148     {
149       me->warning (_ ("beam has less than two visible stems"));
150
151       SCM stems = me->get_grob_property ("stems");
152       if (scm_ilength (stems) == 1)
153         {
154           me->warning (_ ("Beam has less than two stems. Removing beam."));
155
156           unsmob_grob (gh_car (stems))->set_grob_property ("beam", SCM_EOL);
157           me->suicide ();
158
159           return SCM_UNSPECIFIED;
160         }
161       else if (scm_ilength (stems) == 0)
162         {
163           me->suicide ();
164           return SCM_UNSPECIFIED;         
165         }
166     }
167   if (count >= 1)
168     {
169       Direction d = get_default_dir (me);
170
171       consider_auto_knees (me);
172       set_stem_directions (me, d);
173
174       connect_beams (me);
175
176       set_stem_shorten (me);
177     }
178
179   return SCM_EOL;
180 }
181
182
183 /*
184   We want a maximal number of shared beams, but if there is choice, we
185   take the one that is closest to the end of the stem. This is for situations like
186
187        x
188       |
189       |
190   |===|
191   |=
192   |
193   x
194
195   
196  */
197 int
198 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
199                                     Direction left_dir,
200                                     Direction right_dir)
201 {
202   Slice lslice = int_list_to_slice (gh_cdr (left_beaming));
203
204   int best_count = 0;
205   int best_start = 0;
206   for (int i = lslice[-left_dir];
207        (i - lslice[left_dir])* left_dir <= 0 ; i+= left_dir) 
208     {
209       int count =0;
210       for ( SCM s = gh_car (right_beaming); gh_pair_p (s); s = gh_cdr (s))
211         {
212           int k = - right_dir * gh_scm2int (gh_car (s)) + i;
213           if (scm_memq (scm_int2num (k), left_beaming) != SCM_BOOL_F)
214             count ++;
215         }
216
217       if (count >= best_count)
218         {
219           best_count = count; 
220           best_start = i;
221         }
222     }
223
224   return best_start;
225 }
226
227 void
228 Beam::connect_beams (Grob *me)
229 {
230   Link_array<Grob> stems=
231     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
232
233   Slice last_int;
234   last_int.set_empty();
235   SCM last_beaming = SCM_EOL;
236   Direction last_dir = CENTER;
237   for (int i = 0; i< stems.size(); i++)
238     {
239       Grob *this_stem = stems[i];
240       SCM this_beaming = this_stem->get_grob_property ("beaming");
241
242       Direction this_dir = get_grob_direction (this_stem);
243       if (gh_pair_p (last_beaming) && gh_pair_p (this_beaming))
244         {
245           int start_point = position_with_maximal_common_beams
246             (last_beaming, this_beaming,
247              last_dir, this_dir);
248           
249           Direction d = LEFT;
250           Slice new_slice ; 
251           do
252             {
253               if (d == RIGHT && i == stems.size()-1)
254                 continue;
255               
256               new_slice.set_empty();
257               SCM s = index_get_cell (this_beaming, d);
258               for (; gh_pair_p (s); s = gh_cdr (s))
259                 {
260                   int new_beam_pos =
261                     start_point - this_dir * gh_scm2int (gh_car (s));
262
263                   new_slice.add_point (new_beam_pos);
264                   gh_set_car_x (s, scm_int2num (new_beam_pos));
265                 }
266
267
268             }
269           while (flip (&d) != LEFT);
270
271           if (!new_slice.is_empty ())
272             last_int =  new_slice;
273         }
274       else
275         {
276           gh_set_car_x ( this_beaming, SCM_EOL);
277           SCM s = gh_cdr (this_beaming);
278           for (; gh_pair_p (s); s = gh_cdr (s))
279             {
280               int np = - this_dir * gh_scm2int (gh_car(s));
281               gh_set_car_x (s, scm_int2num (np));
282               last_int.add_point (np);
283             }
284         }
285
286       if (i == stems.size () -1)
287         {
288           gh_set_cdr_x (this_beaming, SCM_EOL);
289         }
290
291       if (scm_ilength (gh_cdr (this_beaming)) > 0)
292         {
293           last_beaming = this_beaming;
294           last_dir = this_dir;
295         }
296     }
297  }
298
299 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
300 SCM
301 Beam::brew_molecule (SCM grob)
302 {
303   Grob *me = unsmob_grob (grob);
304   Link_array<Grob> stems=
305     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
306   Grob* xcommon = common_refpoint_of_array (stems, me, X_AXIS);
307
308   Real x0, dx;
309   if (visible_stem_count (me))
310     {
311       // ugh -> use commonx
312       x0 = first_visible_stem (me)->relative_coordinate (xcommon, X_AXIS);
313       dx = last_visible_stem (me)->relative_coordinate (xcommon, X_AXIS) - x0;
314     }
315   else
316     {
317       x0 = stems[0]->relative_coordinate (xcommon, X_AXIS);
318       dx = stems.top ()->relative_coordinate (xcommon, X_AXIS) - x0;
319     }
320
321   SCM posns = me->get_grob_property ("positions");
322   Interval pos;
323   if (!is_number_pair (posns))
324     {
325       programming_error ("No beam posns");
326       pos = Interval (0,0);
327     }
328   else
329     pos= ly_scm2interval (posns);
330
331   Real dy = pos.delta ();
332   Real dydx = (dy && dx) ? dy/dx : 0;
333   
334   Real thick = get_thickness (me);
335   Real bdy = get_beam_translation (me);
336
337   SCM last_beaming = SCM_EOL;;
338   Real last_xposn = -1;
339   Real last_width = -1 ;
340
341   Real gap_length =0.0;
342   SCM scm_gap = me->get_grob_property ("gap");
343   if (gh_number_p (scm_gap))
344     gap_length = gh_scm2double (scm_gap);
345   
346   Molecule the_beam;
347   Real lt = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
348   
349   for (int i = 0; i<= stems.size(); i++)
350     {
351       Grob * st = (i < stems.size()) ? stems[i] : 0;
352       
353       SCM this_beaming = st ? st->get_grob_property ("beaming") : SCM_EOL;
354       Real xposn = st ? st->relative_coordinate (xcommon, X_AXIS) : 0.0;
355       Real stem_width = st ? gh_scm2double (st->get_grob_property ("thickness")) *lt : 0 ;
356       Direction stem_dir = st ? to_dir (st->get_grob_property ("direction")) : CENTER;
357       /*
358         We do the space left of ST, with lfliebertjes pointing to the
359         right from the left stem, and rfliebertjes pointing left from
360         right stem.
361        */
362       SCM left = (i>0) ? gh_cdr (last_beaming) : SCM_EOL;
363       SCM right = st ? gh_car (this_beaming) : SCM_EOL;
364
365       Array<int> full_beams;
366       Array<int> lfliebertjes;
367       Array<int> rfliebertjes;    
368
369       for (SCM s = left;
370            gh_pair_p (s); s =gh_cdr (s))
371         {
372           int b = gh_scm2int (gh_car (s));
373           if (scm_memq (gh_car(s), right) != SCM_BOOL_F)
374             {
375               full_beams.push (b);
376             }
377           else
378             {
379               lfliebertjes.push (b); 
380             }
381         }
382       for (SCM s = right;
383            gh_pair_p (s); s =gh_cdr (s))
384         {
385           int b = gh_scm2int (gh_car (s));
386           if (scm_memq (gh_car(s), left) == SCM_BOOL_F)
387             {
388               rfliebertjes.push (b);
389             }
390         }
391
392       /*
393         how much to stick out for beams across linebreaks
394        */
395       Real break_overshoot = 3.0;
396       Real w = (i>0 && st)? xposn - last_xposn : break_overshoot;
397       Real stem_offset = 0.0;
398       Real width_corr = 0.0;
399       if (i == 1)
400         {
401           stem_offset -= last_width/2;
402           width_corr += last_width/2;
403         }
404           
405       if (i == stems.size() -1)
406         {
407           width_corr += stem_width/2;
408         }
409
410           
411       Molecule whole = Lookup::beam (dydx, w + width_corr, thick);
412       Molecule gapped;
413
414       int gap_count = 0;
415       if (gh_number_p (me->get_grob_property ("gap-count")))
416         {
417           gap_count = gh_scm2int (me->get_grob_property ("gap-count"));
418           gapped = Lookup::beam (dydx, w + width_corr - 2 * gap_length, thick);
419
420           full_beams.sort (default_compare);
421           if (stem_dir == UP)
422             full_beams.reverse ();
423         }
424
425       int k = 0;
426       for (int j = full_beams.size (); j--;)
427         {
428           Molecule b (whole);
429           
430           if (k++ < gap_count)
431             {
432               b = gapped;
433               b.translate_axis (gap_length, X_AXIS);
434             }
435           b.translate_axis (last_xposn -  x0 + stem_offset, X_AXIS);
436           b.translate_axis (dydx * (last_xposn - x0) + bdy * full_beams[j], Y_AXIS);
437
438           the_beam.add_molecule (b);          
439         }
440
441       
442           
443       if (lfliebertjes.size() || rfliebertjes.size())
444         {
445           Real nw_f;
446
447           if (st)
448             {
449               int t = Stem::duration_log (st); 
450
451               SCM proc = me->get_grob_property ("flag-width-function");
452               SCM result = gh_call1 (proc, scm_int2num (t));
453               nw_f = gh_scm2double (result);
454             }
455           else
456             nw_f = break_overshoot;
457               
458           /* Half beam should be one note-width,
459              but let's make sure two half-beams never touch */
460           Real w = (i>0 && st) ? (xposn - last_xposn) : break_overshoot;
461           w = w/2 <? nw_f;
462
463           Molecule half = Lookup::beam (dydx, w, thick);
464           for (int j = lfliebertjes.size(); j--;)
465             {
466               Molecule b (half);
467               b.translate_axis (last_xposn -  x0, X_AXIS);
468               b.translate_axis (dydx * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
469               the_beam.add_molecule (b);              
470             }
471           for (int j = rfliebertjes.size(); j--;)
472             {
473               Molecule b (half);
474               b.translate_axis (xposn -  x0 - w , X_AXIS);
475               b.translate_axis (dydx * (xposn-x0 -w) + bdy * rfliebertjes[j], Y_AXIS);
476               the_beam.add_molecule (b);              
477             }
478         }
479
480
481       last_xposn = xposn;
482       last_width = stem_width;
483       last_beaming = this_beaming;
484     }
485
486   the_beam.translate_axis (x0 - me->relative_coordinate (xcommon, X_AXIS), X_AXIS);
487   the_beam.translate_axis (pos[LEFT], Y_AXIS);
488
489 #if (DEBUG_QUANTING)
490   if (debug_beam_quanting_flag)
491     {
492       /*
493         This code prints the demerits for each beam. Perhaps this
494         should be switchable for those who want to twiddle with the
495         parameters.
496       */
497       String str;
498       SCM properties = Font_interface::font_alist_chain (me);
499
500       Molecule tm = *unsmob_molecule (Text_item::interpret_markup
501         (me->get_paper ()->self_scm (), properties, me->get_grob_property ("quant-score")));
502       the_beam.add_at_edge (Y_AXIS, UP, tm, 5.0, 0);
503     }
504 #endif
505     
506   
507   
508   return the_beam.smobbed_copy();
509 }
510   
511
512
513
514 Direction
515 Beam::get_default_dir (Grob *me) 
516 {
517   Drul_array<int> total;
518   total[UP]  = total[DOWN] = 0;
519   Drul_array<int> count; 
520   count[UP]  = count[DOWN] = 0;
521   Direction d = DOWN;
522
523   Link_array<Grob> stems=
524         Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
525
526   for (int i=0; i <stems.size (); i++)
527     do {
528       Grob *s = stems[i];
529       Direction sd = get_grob_direction (s);
530
531       int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
532       int current = sd  ? (1 + d * sd)/2 : center_distance;
533
534       if (current)
535         {
536           total[d] += current;
537           count[d] ++;
538         }
539     } while (flip (&d) != DOWN);
540   
541   SCM func = me->get_grob_property ("dir-function");
542   SCM s = gh_call2 (func,
543                     gh_cons (scm_int2num (count[UP]),
544                              scm_int2num (count[DOWN])),
545                     gh_cons (scm_int2num (total[UP]),
546                              scm_int2num (total[DOWN])));
547
548   if (gh_number_p (s) && gh_scm2int (s))
549     return to_dir (s);
550   
551   /* If dir is not determined: get default */
552   return to_dir (me->get_grob_property ("neutral-direction"));
553 }
554
555
556 /* Set all stems with non-forced direction to beam direction.
557    Urg: non-forced should become `without/with unforced' direction,
558    once stem gets cleaned-up. */
559 void
560 Beam::set_stem_directions (Grob *me, Direction d)
561 {
562   Link_array<Grob> stems
563     =Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
564   
565   for (int i=0; i <stems.size (); i++)
566     {
567       Grob *s = stems[i];
568   
569       SCM forcedir = s->get_grob_property ("direction");
570       if (!to_dir (forcedir))
571         set_grob_direction (s,  d);
572     }
573 }
574
575 /*
576   A union of intervals in the real line.
577
578   Abysmal performance (quadratic) for large N, hopefully we don't have
579   that large N. In any case, this should probably be rewritten to use
580   a balanced tree.
581  */
582 struct Int_set
583 {
584   Array<Interval> allowed_regions_;
585
586   Int_set()
587   {
588     set_full();
589   }
590
591   void set_full()
592   {
593     allowed_regions_.clear();
594     Interval s;
595     s.set_full ();
596     allowed_regions_.push (s);
597   }
598
599   void remove_interval (Interval rm)
600   {
601     for (int i = 0; i < allowed_regions_.size(); )
602       {
603         Interval s = rm;
604
605         s.intersect (allowed_regions_[i]);
606
607         if (!s.is_empty ())
608           {
609             Interval before = allowed_regions_[i];
610             Interval after = allowed_regions_[i];
611
612             before[RIGHT] = s[LEFT];
613             after[LEFT] = s[RIGHT];
614
615             if (!before.is_empty () && before.length () > 0.0)
616               {
617                 allowed_regions_.insert (before, i);
618                 i++;
619               }
620             allowed_regions_.del (i);
621             if (!after.is_empty () && after.length () > 0.0)
622               {
623                 allowed_regions_.insert (after, i);
624                 i++;
625               }
626           }
627         else
628           i++;
629       }
630   }
631 };
632
633
634 /*
635   Only try horizontal beams for knees.  No reliable detection of
636   anything else is possible here, since we don't know funky-beaming
637   settings, or X-distances (slopes!)  People that want sloped
638   knee-beams, should set the directions manually.
639  */
640 void
641 Beam::consider_auto_knees (Grob* me)
642 {
643   SCM scm = me->get_grob_property ("auto-knee-gap");
644   if (!gh_number_p (scm))
645     return ;
646
647   Real threshold = gh_scm2double (scm);
648   
649   Int_set gaps;
650
651   gaps.set_full ();
652
653   Link_array<Grob> stems=
654     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
655       
656   Grob *common = common_refpoint_of_array (stems, me,  Y_AXIS);
657   Real staff_space = Staff_symbol_referencer::staff_space (me);
658   
659   Array<Interval> hps_array;  
660   for (int i=0; i < stems.size (); i++)
661     {
662       Grob* stem = stems[i];
663       if (Stem::invisible_b (stem))
664         continue;
665
666       Interval hps = Stem::head_positions (stem);
667       if(!hps.is_empty ())
668         {
669           hps[LEFT] += -1;
670           hps[RIGHT] += 1; 
671           hps *= staff_space * 0.5 ;
672
673           /*
674             We could subtract beam Y position, but this routine only
675             sets stem directions, a constant shift does not have an
676             influence.
677             
678            */
679           hps += stem->relative_coordinate (common, Y_AXIS);
680
681           if (to_dir (stem->get_grob_property ("direction")))
682             {
683               Direction stemdir = to_dir (stem->get_grob_property ("direction"));
684               hps[-stemdir] = - stemdir * infinity_f;
685             }
686         }
687       hps_array.push (hps);
688
689       gaps.remove_interval (hps);
690     }
691
692   Interval max_gap;
693   Real max_gap_len =0.0;
694
695   for (int i  = gaps.allowed_regions_.size() -1;  i >=  0 ; i--)
696     {
697       Interval gap = gaps.allowed_regions_[i];
698
699       /*
700         the outer gaps are not knees.
701        */
702       if (isinf (gap[LEFT]) || isinf(gap[RIGHT]))
703         continue;
704       
705       if (gap.length () >= max_gap_len)
706         {
707           max_gap_len = gap.length();
708           max_gap = gap;
709         }
710     }
711
712   if (max_gap_len > threshold)
713     {
714       int j = 0;
715       for (int i = 0; i < stems.size(); i++)
716         {
717           Grob* stem = stems[i];
718           if (Stem::invisible_b (stem))
719             continue;
720
721           Interval hps = hps_array[j++];
722
723
724           Direction d =  (hps.center () < max_gap.center()) ?
725             UP : DOWN ;
726           
727           stem->set_grob_property ("direction", scm_int2num (d));
728           
729           hps.intersect (max_gap);
730           assert (hps.is_empty () || hps.length () < 1e-6 );
731         }
732     }
733 }
734
735
736
737 /* Set stem's shorten property if unset.
738
739  TODO:
740    take some y-position (chord/beam/nearest?) into account
741    scmify forced-fraction
742  
743   This is done in beam because the shorten has to be uniform over the
744   entire beam.
745
746 */
747 void
748 Beam::set_stem_shorten (Grob *me)
749 {
750   /*
751     shortening looks silly for x staff beams
752    */
753   if (knee_b(me))
754     return ;
755   
756   Real forced_fraction = 1.0 * forced_stem_count (me)
757     / visible_stem_count (me);
758
759   int beam_count = get_beam_count (me);
760
761   SCM shorten_list = me->get_grob_property ("beamed-stem-shorten");
762   if (shorten_list == SCM_EOL)
763     return;
764
765   Real staff_space = Staff_symbol_referencer::staff_space (me);
766   
767   SCM shorten_elt =
768     robust_list_ref (beam_count -1, shorten_list);
769   Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
770
771   /* your similar cute comment here */
772   shorten_f *= forced_fraction;
773
774   if (shorten_f)
775     me->set_grob_property ("shorten", gh_double2scm (shorten_f));
776 }
777
778 /*  Call list of y-dy-callbacks, that handle setting of
779     grob-properties
780
781 */
782 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
783 SCM
784 Beam::after_line_breaking (SCM smob)
785 {
786   Grob *me = unsmob_grob (smob);
787   
788   /* Copy to mutable list. */
789   SCM s = ly_deep_copy (me->get_grob_property ("positions"));
790   me->set_grob_property ("positions", s);
791
792   if (ly_car (s) == SCM_BOOL_F)
793     {
794
795       // one wonders if such genericity is necessary  --hwn.
796       SCM callbacks = me->get_grob_property ("position-callbacks");
797       for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
798         gh_call1 (ly_car (i), smob);
799     }
800
801   set_stem_lengths (me);  
802   return SCM_UNSPECIFIED;
803 }
804
805
806 /*
807   Compute  a first approximation to the beam slope.
808  */
809 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
810 SCM
811 Beam::least_squares (SCM smob)
812 {
813   Grob *me = unsmob_grob (smob);
814
815   int count = visible_stem_count (me);
816   Interval pos (0, 0);
817   
818   if (count < 1)
819     {
820       me->set_grob_property ("positions", ly_interval2scm (pos));
821       return SCM_UNSPECIFIED;
822     }
823
824
825   Array<Real> x_posns ;
826   Link_array<Grob> stems=
827     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
828   Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
829   Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);  
830
831   Real my_y = me->relative_coordinate (commony, Y_AXIS);
832   
833   Grob *fvs  = first_visible_stem (me);
834   Grob *lvs  = last_visible_stem (me);
835   
836   Interval ideal (Stem::get_stem_info (fvs).ideal_y_
837                   + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
838                   Stem::get_stem_info (lvs).ideal_y_
839                   + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
840   
841   Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
842   for (int i=0; i < stems.size (); i++)
843     {
844       Grob* s = stems[i];
845
846       Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
847       x_posns.push (x);
848     }
849   Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
850
851   
852   Real y =0;  
853   Real dydx = 0;
854   Real dy = 0;
855   
856   if (!ideal.delta ())
857     {
858       Interval chord (Stem::chord_start_y (first_visible_stem (me)),
859                       Stem::chord_start_y (last_visible_stem (me)));
860
861       /* Simple beams (2 stems) on middle line should be allowed to be
862          slightly sloped.
863          
864          However, if both stems reach middle line,
865          ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
866
867          For that case, we apply artificial slope */
868       if (!ideal[LEFT] && chord.delta () && count == 2)
869         {
870           /* FIXME. -> UP */
871           Direction d = (Direction) (sign (chord.delta ()) * UP);
872           pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
873           pos[-d] = - pos[d];
874         }
875       else
876         {
877           pos = ideal;
878         }
879
880       /*
881         For broken beams this doesn't work well. In this case, the
882          slope esp. of the first part of a broken beam should predict
883          where the second part goes.
884        */
885
886       y = pos[LEFT];
887       dy = pos[RIGHT]- y;
888       dydx = dy/dx;
889
890
891
892     }
893   else
894     {
895       Array<Offset> ideals;
896       for (int i=0; i < stems.size (); i++)
897         {
898           Grob* s = stems[i];
899           if (Stem::invisible_b (s))
900             continue;
901           ideals.push (Offset (x_posns[i],
902                                Stem::get_stem_info (s).ideal_y_
903                                + s->relative_coordinate (commony, Y_AXIS)
904                                - my_y));
905         }
906       
907       minimise_least_squares (&dydx, &y, ideals);
908
909       dy = dydx * dx;
910       me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
911       pos = Interval (y, (y+dy));
912     }
913
914   me->set_grob_property ("positions", ly_interval2scm (pos));
915  
916   return SCM_UNSPECIFIED;
917 }
918
919
920 /*
921   We can't combine with previous function, since check concave and
922   slope damping comes first.
923
924 TODO: we should use the concaveness to control the amount of damping
925 applied.
926   
927  */
928 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
929 SCM
930 Beam::shift_region_to_valid (SCM grob)
931 {
932   Grob *me = unsmob_grob (grob);
933   /*
934     Code dup.
935    */
936   Array<Real> x_posns ;
937   Link_array<Grob> stems=
938     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
939   Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
940   Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);  
941
942   Grob *fvs = first_visible_stem (me);
943
944   if (!fvs)
945     return SCM_UNSPECIFIED;
946     
947   Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
948   for (int i=0; i < stems.size (); i++)
949     {
950       Grob* s = stems[i];
951
952       Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
953       x_posns.push (x);
954     }
955
956   Grob *lvs = last_visible_stem (me);
957   if (!lvs)
958     return SCM_UNSPECIFIED;
959   
960   Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
961
962   Interval pos = ly_scm2interval ( me->get_grob_property ("positions"));
963   Real dy = pos.delta();
964   Real y = pos[LEFT];
965   Real dydx =dy/dx;
966
967   
968   /*
969     Shift the positions so that we have a chance of finding good
970     quants (i.e. no short stem failures.)
971    */
972   Interval feasible_left_point;
973   feasible_left_point.set_full ();
974   for (int i=0; i < stems.size (); i++)
975     {
976       Grob* s = stems[i];
977       if (Stem::invisible_b (s))
978         continue;
979
980       Direction d = Stem::get_direction (s);
981
982       Real left_y =
983         Stem::get_stem_info (s).shortest_y_
984         - dydx * x_posns [i];
985
986       /*
987         left_y is now relative to the stem S. We want relative to
988         ourselves, so translate:
989        */
990       left_y += 
991         + s->relative_coordinate (commony, Y_AXIS)
992         - me->relative_coordinate (commony, Y_AXIS);
993
994       Interval flp ;
995       flp.set_full ();
996       flp[-d] = left_y;
997
998       feasible_left_point.intersect (flp);
999     }
1000       
1001   if (feasible_left_point.is_empty ())
1002     {
1003       warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
1004     }
1005   else if (!feasible_left_point.elem_b(y))
1006     {
1007       if (isinf (feasible_left_point[DOWN]))
1008         y = feasible_left_point[UP] - REGION_SIZE;
1009       else if (isinf (feasible_left_point[UP]))
1010         y = feasible_left_point[DOWN]+ REGION_SIZE;
1011       else
1012         y = feasible_left_point.center ();
1013     }
1014   pos = Interval (y, (y+dy));
1015   me->set_grob_property ("positions", ly_interval2scm (pos));
1016   return SCM_UNSPECIFIED;
1017 }
1018
1019
1020 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
1021 SCM
1022 Beam::check_concave (SCM smob)
1023 {
1024   Grob *me = unsmob_grob (smob);
1025
1026   Link_array<Grob> stems = 
1027     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1028
1029   for (int i = 0; i < stems.size ();)
1030     {
1031       if (Stem::invisible_b (stems[i]))
1032         stems.del (i);
1033       else
1034         i++;
1035     }
1036   
1037   if (stems.size () < 3)
1038     return SCM_UNSPECIFIED;
1039
1040
1041   /* Concaveness #1: If distance of an inner notehead to line between
1042      two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
1043      beam is concave (Heinz Stolba).
1044
1045      In the case of knees, the line connecting outer heads is often
1046      not related to the beam slope (it may even go in the other
1047      direction). Skip the check when the outer stems point in
1048      different directions. --hwn
1049      
1050   */
1051   bool concaveness1 = false;
1052   SCM gap = me->get_grob_property ("concaveness-gap");
1053   if (gh_number_p (gap)
1054       && Stem::get_direction(stems.top ())
1055          == Stem::get_direction(stems[0]))
1056     {
1057       Real r1 = gh_scm2double (gap);
1058       Real dy = Stem::chord_start_y (stems.top ())
1059         - Stem::chord_start_y (stems[0]);
1060
1061       
1062       Real slope = dy / (stems.size () - 1);
1063       
1064       Real y0 = Stem::chord_start_y (stems[0]);
1065       for (int i = 1; i < stems.size () - 1; i++)
1066         {
1067           Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
1068           if (c > r1)
1069             {
1070               concaveness1 = true;
1071               break;
1072             }
1073         }
1074     }
1075
1076     
1077   /* Concaveness #2: Sum distances of inner noteheads that fall
1078      outside the interval of the two outer noteheads.
1079
1080      We only do this for beams where first and last stem have the same
1081      direction. --hwn.
1082
1083
1084      Note that "convex" stems compensate for "concave" stems.
1085      (is that intentional?) --hwn.
1086   */
1087   
1088   Real concaveness2 = 0;
1089   SCM thresh = me->get_grob_property ("concaveness-threshold");
1090   Real r2 = infinity_f;
1091   if (!concaveness1 && gh_number_p (thresh)
1092       && Stem::get_direction(stems.top ())
1093          == Stem::get_direction(stems[0]))
1094     {
1095       r2 = gh_scm2double (thresh);
1096
1097       Direction dir = Stem::get_direction(stems.top ());
1098       Real concave = 0;
1099       Interval iv (Stem::chord_start_y (stems[0]),
1100                    Stem::chord_start_y (stems.top ()));
1101       
1102       if (iv[MAX] < iv[MIN])
1103         iv.swap ();
1104       
1105       for (int i = 1; i < stems.size () - 1; i++)
1106         {
1107           Real f = Stem::chord_start_y (stems[i]);
1108           concave += ((f - iv[MAX] ) >? 0) +
1109             ((f - iv[MIN] ) <? 0);
1110         }
1111       concave *= dir;
1112       concaveness2 = concave / (stems.size () - 2);
1113       
1114       /*
1115
1116       ugh: this is the a kludge to get
1117       input/regression/beam-concave.ly to behave as
1118       baerenreiter.
1119
1120       */
1121
1122       /*
1123         huh? we're dividing twice (which is not scalable) meaning that
1124         the longer the beam, the more unlikely it will be
1125         concave. Maybe you would even expect the other way around??
1126
1127         --hwn.
1128         
1129        */
1130       concaveness2 /= (stems.size () - 2);
1131     }
1132   
1133   /* TODO: some sort of damping iso -> plain horizontal */
1134   if (concaveness1 || concaveness2 > r2)
1135     {
1136       Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1137       Real r = pos.linear_combination (0);
1138       me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
1139       me->set_grob_property ("least-squares-dy", gh_double2scm (0));
1140     }
1141
1142   return SCM_UNSPECIFIED;
1143 }
1144
1145 /* This neat trick is by Werner Lemberg,
1146    damped = tanh (slope)
1147    corresponds with some tables in [Wanske] CHECKME */
1148 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1149 SCM
1150 Beam::slope_damping (SCM smob)
1151 {
1152   Grob *me = unsmob_grob (smob);
1153
1154   if (visible_stem_count (me) <= 1)
1155     return SCM_UNSPECIFIED;
1156
1157   SCM s = me->get_grob_property ("damping"); 
1158   int damping = gh_scm2int (s);
1159
1160   if (damping)
1161     {
1162       Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1163       Real dy = pos.delta ();
1164
1165       Grob *fvs  = first_visible_stem (me);
1166       Grob *lvs  = last_visible_stem (me);
1167
1168       Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1169
1170
1171       Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1172         - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1173       Real dydx = dy && dx ? dy/dx : 0;
1174       dydx = 0.6 * tanh (dydx) / damping;
1175
1176       Real damped_dy = dydx * dx;
1177       pos[LEFT] += (dy - damped_dy) / 2;
1178       pos[RIGHT] -= (dy - damped_dy) / 2;
1179       
1180       me->set_grob_property ("positions", ly_interval2scm (pos));
1181     }
1182   return SCM_UNSPECIFIED;
1183 }
1184
1185 /*
1186   Report slice containing the numbers that are both in (car BEAMING)
1187   and (cdr BEAMING)
1188  */
1189 Slice
1190 where_are_the_whole_beams(SCM beaming)
1191 {
1192   Slice l; 
1193   
1194   for( SCM s = gh_car (beaming); gh_pair_p (s) ; s = gh_cdr (s))
1195     {
1196       if (scm_memq (gh_car (s), gh_cdr (beaming)) != SCM_BOOL_F)
1197         
1198         l.add_point (gh_scm2int (gh_car (s)));
1199     }
1200
1201   return l;
1202 }
1203
1204 /* Return the Y position of the stem-end, given the Y-left, Y-right
1205    in POS for stem S.  This Y position is relative to S. */
1206 Real
1207 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1208                    Real xl, Real xr,
1209                    Interval pos, bool french) 
1210 {
1211   Real beam_translation = get_beam_translation (me);
1212
1213     
1214   Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1215   Real dy = pos.delta ();
1216   Real dx = xr - xl;
1217   Real stem_y_beam0 = (dy && dx
1218                        ? r / dx
1219                        * dy
1220                        : 0) + pos[LEFT];
1221   
1222   Direction my_dir = get_grob_direction (s);
1223   SCM beaming = s->get_grob_property ("beaming");
1224  
1225   Real stem_y = stem_y_beam0;
1226   if (french)
1227     {
1228       Slice bm = where_are_the_whole_beams (beaming);
1229       if (!bm.is_empty ())
1230         stem_y += beam_translation * bm[-my_dir];
1231     }
1232   else
1233     {
1234       Slice bm = Stem::beam_multiplicity(s);
1235       if (!bm.is_empty ())
1236         stem_y +=bm[my_dir] * beam_translation;
1237     }
1238   
1239   Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1240     - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1241   
1242   return stem_y + id;
1243 }
1244
1245 /*
1246   Hmm.  At this time, beam position and slope are determined.  Maybe,
1247   stem directions and length should set to relative to the chord's
1248   position of the beam.  */
1249 void
1250 Beam::set_stem_lengths (Grob *me)
1251 {
1252   Link_array<Grob> stems=
1253     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1254
1255   if (!stems.size ())
1256     return;
1257   
1258   Grob *common[2];
1259   for (int a = 2; a--;)
1260     common[a] = common_refpoint_of_array (stems, me, Axis(a));
1261   
1262   Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1263   Real staff_space = Staff_symbol_referencer::staff_space (me);
1264
1265   bool gap = false;
1266   Real thick =0.0;
1267   if (gh_number_p (me->get_grob_property ("gap-count"))
1268       &&gh_scm2int (me->get_grob_property ("gap-count")))
1269     {
1270       gap = true;
1271       thick = get_thickness(me);
1272     }
1273       
1274   // ugh -> use commonx
1275   Grob * fvs = first_visible_stem (me);
1276   Grob *lvs = last_visible_stem (me);
1277     
1278   Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1279   Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1280   
1281   for (int i=0; i < stems.size (); i++)
1282     {
1283       Grob* s = stems[i];
1284       if (Stem::invisible_b (s))
1285         continue;
1286
1287       bool french = to_boolean (s->get_grob_property ("french-beaming"));
1288       Real stem_y = calc_stem_y (me, s, common,
1289                                  xl, xr,
1290                                  pos, french && s != lvs && s!= fvs);
1291
1292       /*
1293         Make the stems go up to the end of the beam. This doesn't matter
1294         for normal beams, but for tremolo beams it looks silly otherwise.
1295        */
1296       if (gap)
1297         stem_y += thick * 0.5 * get_grob_direction (s);
1298
1299       Stem::set_stemend (s, 2* stem_y / staff_space);
1300     }
1301 }
1302
1303 void
1304 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1305 {
1306   Link_array<Grob> stems=
1307     Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1308   
1309   Direction d = LEFT;
1310   for (int i=0; i  < stems.size (); i++)
1311     {
1312       /*
1313         Don't overwrite user settings.
1314        */
1315       
1316       do
1317         {
1318           /* Don't set beaming for outside of outer stems */      
1319           if ((d == LEFT && i == 0)
1320               ||(d == RIGHT && i == stems.size () -1))
1321             continue;
1322
1323           Grob *st =  stems[i];
1324           SCM beaming_prop = st->get_grob_property ("beaming");
1325           if (beaming_prop == SCM_EOL ||
1326               index_get_cell (beaming_prop, d) == SCM_EOL)
1327             {
1328               int b = beaming->infos_.elem (i).beams_i_drul_[d];
1329               if (i>0
1330                   && i < stems.size() -1
1331                   && Stem::invisible_b (st))
1332                 b = b <? beaming->infos_.elem(i).beams_i_drul_[-d];
1333               
1334               Stem::set_beaming (st, b, d);
1335             }
1336         }
1337       while (flip (&d) != LEFT);
1338     }
1339 }
1340
1341 int
1342 Beam::forced_stem_count (Grob *me) 
1343 {
1344   Link_array<Grob>stems = 
1345     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1346   int f = 0;
1347   for (int i=0; i < stems.size (); i++)
1348     {
1349       Grob *s = stems[i];
1350
1351       if (Stem::invisible_b (s))
1352         continue;
1353
1354       /* I can imagine counting those boundaries as a half forced stem,
1355          but let's count them full for now. */
1356       if (abs (Stem::chord_start_y (s)) > 0.1
1357         && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1358         f++;
1359     }
1360   return f;
1361 }
1362
1363
1364
1365
1366 int
1367 Beam::visible_stem_count (Grob *me) 
1368 {
1369   Link_array<Grob>stems = 
1370     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1371   int c = 0;
1372   for (int i = stems.size (); i--;)
1373     {
1374       if (!Stem::invisible_b (stems[i]))
1375         c++;
1376     }
1377   return c;
1378 }
1379
1380 Grob*
1381 Beam::first_visible_stem (Grob *me) 
1382 {
1383   Link_array<Grob>stems = 
1384     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1385   
1386   for (int i = 0; i < stems.size (); i++)
1387     {
1388       if (!Stem::invisible_b (stems[i]))
1389         return stems[i];
1390     }
1391   return 0;
1392 }
1393
1394 Grob*
1395 Beam::last_visible_stem (Grob *me) 
1396 {
1397   Link_array<Grob>stems = 
1398     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1399   for (int i = stems.size (); i--;)
1400     {
1401       if (!Stem::invisible_b (stems[i]))
1402         return stems[i];
1403     }
1404   return 0;
1405 }
1406
1407
1408 /*
1409   [TODO]
1410   
1411   handle rest under beam (do_post: beams are calculated now)
1412   what about combination of collisions and rest under beam.
1413
1414   Should lookup
1415     
1416     rest -> stem -> beam -> interpolate_y_position ()
1417 */
1418 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1419 SCM
1420 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1421 {
1422   Grob *rest = unsmob_grob (element_smob);
1423   Axis a = (Axis) gh_scm2int (axis);
1424   
1425   assert (a == Y_AXIS);
1426
1427   Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1428   Grob *stem = st;
1429   if (!stem)
1430     return gh_double2scm (0.0);
1431   Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1432   if (!beam
1433       || !Beam::has_interface (beam)
1434       || !Beam::visible_stem_count (beam))
1435     return gh_double2scm (0.0);
1436
1437   Interval pos (0, 0);
1438   SCM s = beam->get_grob_property ("positions");
1439   if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1440     pos = ly_scm2interval (s);
1441
1442   Real dy = pos.delta ();
1443   // ugh -> use commonx
1444   Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1445   Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1446   Real dydx = dy && dx ? dy/dx : 0;
1447   
1448   Direction d = Stem::get_direction (stem);
1449   Real stem_y = (pos[LEFT]
1450                  + (stem->relative_coordinate (0, X_AXIS) - x0) * dydx)
1451     * d;
1452   
1453   Real beam_translation = get_beam_translation (beam);
1454   Real beam_thickness = gh_scm2double (beam->get_grob_property ("thickness"));
1455   int beam_count = get_direction_beam_count (beam, d);
1456   Real height_of_my_beams = beam_thickness
1457     + (beam_count - 1) * beam_translation;
1458   Real beam_y = stem_y - height_of_my_beams + beam_thickness / 2.0;
1459
1460   Real staff_space = Staff_symbol_referencer::staff_space (rest);
1461
1462   /* Better calculate relative-distance directly, rather than using
1463      rest_dim? */
1464   Grob *common_x = rest->common_refpoint (beam, Y_AXIS);
1465   Real rest_dim = rest->extent (common_x, Y_AXIS)[d] / staff_space * d;
1466
1467   Real minimum_distance = gh_scm2double
1468     (rest->get_grob_property ("minimum-beam-collision-distance"));
1469
1470   Real distance = beam_y - rest_dim;
1471   Real shift = 0;
1472   if (distance < 0)
1473     shift = minimum_distance - distance;
1474   else if (minimum_distance > distance)
1475     shift = minimum_distance - distance;
1476       
1477   int stafflines = Staff_symbol_referencer::line_count (rest);
1478
1479   /* Always move discretely by half spaces */
1480   Real discrete_shift = ceil (shift * 2.0) / 2.0;
1481
1482   /* Inside staff, move by whole spaces*/
1483   if ((rest->extent (common_x, Y_AXIS)[d] + discrete_shift) * d
1484       < stafflines / 2.0
1485       ||(rest->extent (common_x, Y_AXIS)[-d] + discrete_shift) * -d
1486       < stafflines / 2.0)
1487     discrete_shift = ceil (discrete_shift);
1488
1489   return gh_double2scm (-d * discrete_shift);
1490 }
1491
1492 bool
1493 Beam::knee_b (Grob* me)
1494 {
1495   SCM k = me->get_grob_property ("knee");
1496   if (gh_boolean_p (k))
1497     return gh_scm2bool (k);
1498
1499   bool knee = false;
1500   int d = 0;
1501   for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
1502     {
1503       Direction dir = get_grob_direction (unsmob_grob (ly_car (s)));
1504       if (d && d != dir)
1505         {
1506           knee = true;
1507           break;
1508         }
1509       d = dir;
1510     }
1511   
1512   me->set_grob_property ("knee", gh_bool2scm (knee));
1513
1514   return knee;
1515 }
1516
1517 int
1518 Beam::get_direction_beam_count (Grob *me, Direction d )
1519 {
1520   Link_array<Grob>stems = 
1521     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1522   int bc = 0;
1523   
1524   for (int i = stems.size (); i--;)
1525     {
1526       /*
1527         Should we take invisible stems into account?
1528        */
1529       if (Stem::get_direction (stems[i]) == d)
1530         bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1531     }
1532
1533   return bc;
1534 }
1535
1536
1537 ADD_INTERFACE (Beam, "beam-interface",
1538   "A beam. \n\n"
1539 " "
1540 "#'thickness= weight of beams, in staffspace "
1541 " "
1542 " "
1543 "We take the least squares line through the ideal-length stems, and "
1544 "then damp that using "
1545 " \n"
1546 "       damped = tanh (slope) \n"
1547 " \n"
1548 "this gives an unquantized left and right position for the beam end. "
1549 "Then we take all combinations of quantings near these left and right "
1550 "positions, and give them a score (according to how close they are to "
1551 "the ideal slope, how close the result is to the ideal stems, etc.). We "
1552 "take the best scoring combination. "
1553 ,
1554   "knee position-callbacks concaveness-gap concaveness-threshold dir-function quant-score auto-knee-gap gap gap-count chord-tremolo beamed-stem-shorten shorten least-squares-dy damping flag-width-function neutral-direction positions space-function thickness");
1555
1556