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