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