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