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