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