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