]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
8c06658d4a5ef99390e6a43e9e43b5f25cb282e2
[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 /*
12 TODO:
13
14
15   * Junk stem_info.
16
17   * Use Number_pair i.s.o Interval to represent (yl, yr).
18   
19   - Determine auto knees based on positions if it's set by the user.
20
21
22 Notes:
23
24
25  - Stems run to the Y-center of the beam.
26   
27  - beam_translation is the offset between Y centers of the beam.
28
29 */
30
31
32 #include <math.h> // tanh.
33
34 #include "molecule.hh" 
35 #include "directional-element-interface.hh"
36 #include "beaming.hh"
37 #include "beam.hh"
38 #include "misc.hh"
39 #include "least-squares.hh"
40 #include "stem.hh"
41 #include "paper-def.hh"
42 #include "lookup.hh"
43 #include "group-interface.hh"
44 #include "staff-symbol-referencer.hh"
45 #include "item.hh"
46 #include "spanner.hh"
47 #include "warn.hh"
48
49
50 #define DEBUG_QUANTING 0
51
52
53 #if DEBUG_QUANTING
54 #include "text-item.hh"  // debug output.
55 #include "font-interface.hh"  // debug output.
56 #endif
57
58
59 void
60 Beam::add_stem (Grob *me, Grob *s)
61 {
62   Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
63   
64   s->add_dependency (me);
65
66   assert (!Stem::get_beam (s));
67   s->set_grob_property ("beam", me->self_scm ());
68
69   add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
70 }
71
72
73 /*
74   this returns the translation between 2 adjoining beams.
75  */
76 Real
77 Beam::get_beam_translation (Grob *me)
78 {
79   SCM func = me->get_grob_property ("space-function");
80   SCM s = gh_call2 (func, me->self_scm (), scm_int2num (get_beam_count (me)));
81   return gh_scm2double (s);
82 }
83
84 /*
85   Maximum beam_count.
86  */
87 int
88 Beam::get_beam_count (Grob *me) 
89 {
90   int m = 0;
91   for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
92     {
93       Grob *sc = unsmob_grob (ly_car (s));
94       
95       m = m >? (Stem::beam_multiplicity (sc).length () + 1);
96     }
97   return m;
98 }
99
100 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
101 SCM
102 Beam::space_function (SCM smob, SCM beam_count)
103 {
104   Grob *me = unsmob_grob (smob);
105   
106   Real staff_space = Staff_symbol_referencer::staff_space (me);
107   Real line = me->get_paper ()->get_var ("linethickness");
108   Real thickness = gh_scm2double (me->get_grob_property ("thickness"))
109     * staff_space;
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))->remove_grob_property ("beam");
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 (i > 0)
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 = gh_scm2double (me->get_grob_property ("thickness"));
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       /* For knees, non-forced stems should probably have their
543          natural direction. In any case, when knee, beam direction is
544          foe.
545          
546          TODO: for x staff knees, set direction pointing to 'the
547          other' staff, rather than natural.
548       */
549       if (knee_b(me))
550         {
551           Stem::get_direction (s); // this actually sets it, if necessary
552         }
553       else
554         {
555           SCM force = s->remove_grob_property ("dir-forced");
556           if (!gh_boolean_p (force) || !gh_scm2bool (force))
557             Directional_element_interface::set (s, d);
558         }
559     }
560
561
562 /*
563   A union of intervals in the real line.
564
565   Abysmal performance (quadratic) for large N, hopefully we don't have
566   that large N. In any case, this should probably be rewritten to use
567   a balanced tree.
568  */
569 struct Int_set
570 {
571   Array<Interval> allowed_regions_;
572
573   Int_set()
574   {
575     set_full();
576   }
577
578   void set_full()
579   {
580     allowed_regions_.clear();
581     Interval s;
582     s.set_full ();
583     allowed_regions_.push (s);
584   }
585
586   void remove_interval (Interval rm)
587   {
588     for (int i = 0; i < allowed_regions_.size(); )
589       {
590         Interval s = rm;
591
592         s.intersect (allowed_regions_[i]);
593
594         if (!s.empty_b ())
595           {
596             Interval before = allowed_regions_[i];
597             Interval after = allowed_regions_[i];
598
599             before[RIGHT] = s[LEFT];
600             after[LEFT] = s[RIGHT];
601
602             if (!before.empty_b() && before.length () > 0.0)
603               {
604                 allowed_regions_.insert (before, i);
605                 i++;
606               }
607             allowed_regions_.del (i);
608             if (!after.empty_b () && after.length () > 0.0)
609               {
610                 allowed_regions_.insert (after, i);
611                 i++;
612               }
613           }
614         else
615           i++;
616       }
617   }
618 };
619
620
621 /*
622   Only try horizontal beams for knees.  No reliable detection of
623   anything else is possible here, since we don't know funky-beaming
624   settings, or X-distances (slopes!)  People that want sloped
625   knee-beams, should set the directions manually.
626  */
627 void
628 Beam::consider_auto_knees (Grob* me)
629 {
630   SCM scm = me->get_grob_property ("auto-knee-gap");
631   if (!gh_number_p (scm))
632     return ;
633
634   Real threshold = gh_scm2double (scm);
635   
636   Int_set gaps;
637
638   gaps.set_full ();
639
640   Link_array<Grob> stems=
641     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
642       
643   Grob *common = common_refpoint_of_array (stems, me,  Y_AXIS);
644   Real staff_space = Staff_symbol_referencer::staff_space (me);
645   
646   Array<Interval> hps_array;  
647   for (int i=0; i < stems.size (); i++)
648     {
649       Grob* stem = stems[i];
650       if (Stem::invisible_b (stem))
651         continue;
652       
653
654       Interval hps = Stem::head_positions (stem);
655
656       if(!hps.empty_b())
657         {
658           hps[LEFT] += -1;
659           hps[RIGHT] += 1; 
660           hps *= staff_space * 0.5 ;
661           hps += stem->relative_coordinate (common, Y_AXIS);
662       
663           if (to_boolean (stem->get_grob_property ("dir-forced")))
664             {
665               Direction stemdir =Directional_element_interface::get (stem);
666               hps[-stemdir] = - stemdir * infinity_f;
667             }
668         }
669       hps_array.push (hps);
670
671       gaps.remove_interval (hps);
672     }
673
674   Interval max_gap;
675   Real max_gap_len =0.0;
676
677   for (int i  = gaps.allowed_regions_.size() -1;  i >=  0 ; i--)
678     {
679       Interval gap = gaps.allowed_regions_[i];
680
681       /*
682         the outer gaps are not knees.
683        */
684       if (isinf (gap[LEFT]) || isinf(gap[RIGHT]))
685         continue;
686       
687       if (gap.length () >= max_gap_len)
688         {
689           max_gap_len = gap.length();
690           max_gap = gap;
691         }
692     }
693
694   if (max_gap_len > threshold)
695     {
696       int j = 0;
697       for (int i = 0; i < stems.size(); i++)
698         {
699           Grob* stem = stems[i];
700           if (Stem::invisible_b (stem))
701             continue;
702
703           Interval hps = hps_array[j++];
704
705
706           Direction d =  (hps.center () < max_gap.center()) ?
707             UP : DOWN ;
708           
709           stem->set_grob_property ("direction", scm_int2num (d));
710
711           /*
712             UGH. Check why we still need dir-forced; I think we can
713             junk it.
714            */
715           stem->set_grob_property ("dir-forced", SCM_BOOL_T);
716           
717           hps.intersect (max_gap);
718           assert (hps.empty_b () || hps.length () < 1e-6 );
719         }
720     }
721 }
722
723
724
725 /* Set stem's shorten property if unset.
726
727  TODO:
728    take some y-position (chord/beam/nearest?) into account
729    scmify forced-fraction
730  
731   This is done in beam because the shorten has to be uniform over the
732   entire beam.
733
734 */
735 void
736 Beam::set_stem_shorten (Grob *me)
737 {
738   /*
739     shortening looks silly for x staff beams
740    */
741   if (knee_b(me))
742     return ;
743   
744   Real forced_fraction = forced_stem_count (me) / visible_stem_count (me);
745
746   int beam_count = get_beam_count (me);
747
748   SCM shorten = me->get_grob_property ("beamed-stem-shorten");
749   if (shorten == SCM_EOL)
750     return;
751
752   int sz = scm_ilength (shorten);
753   
754   Real staff_space = Staff_symbol_referencer::staff_space (me);
755   SCM shorten_elt = scm_list_ref (shorten,
756                                   scm_int2num (beam_count <? (sz - 1)));
757   Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
758
759   /* your similar cute comment here */
760   shorten_f *= forced_fraction;
761
762   if (shorten_f)
763     me->set_grob_property ("shorten", gh_double2scm (shorten_f));
764 }
765
766 /*  Call list of y-dy-callbacks, that handle setting of
767     grob-properties
768
769 */
770 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
771 SCM
772 Beam::after_line_breaking (SCM smob)
773 {
774   Grob *me = unsmob_grob (smob);
775   
776   /* Copy to mutable list. */
777   SCM s = ly_deep_copy (me->get_grob_property ("positions"));
778   me->set_grob_property ("positions", s);
779
780   if (ly_car (s) == SCM_BOOL_F)
781     {
782
783       // one wonders if such genericity is necessary  --hwn.
784       SCM callbacks = me->get_grob_property ("position-callbacks");
785       for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
786         gh_call1 (ly_car (i), smob);
787     }
788
789   set_stem_lengths (me);  
790   return SCM_UNSPECIFIED;
791 }
792
793 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
794 SCM
795 Beam::least_squares (SCM smob)
796 {
797   Grob *me = unsmob_grob (smob);
798
799   int count = visible_stem_count (me);
800   Interval pos (0, 0);
801   
802   if (count <= 1)
803     {
804       me->set_grob_property ("positions", ly_interval2scm (pos));
805       return SCM_UNSPECIFIED;
806     }
807
808
809   Array<Real> x_posns ;
810   Link_array<Grob> stems=
811     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
812   Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
813   Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);  
814
815   Real my_y = me->relative_coordinate (commony, Y_AXIS);
816   
817   Grob *fvs  = first_visible_stem (me);
818   Grob *lvs  = last_visible_stem (me);
819   
820   Interval ideal (Stem::calc_stem_info (fvs).ideal_y_
821                   + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
822                   Stem::calc_stem_info (lvs).ideal_y_
823                   + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
824   
825   Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
826   for (int i=0; i < stems.size (); i++)
827     {
828       Grob* s = stems[i];
829
830       Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
831       x_posns.push (x);
832     }
833   Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
834
835   Real y =0;  
836   Real dydx = 0;
837   Real dy = 0;
838   
839   if (!ideal.delta ())
840     {
841       Interval chord (Stem::chord_start_y (first_visible_stem (me)),
842                       Stem::chord_start_y (last_visible_stem (me)));
843
844
845       /*
846         TODO -- use scoring for this.
847
848         complicated, because we take stem-info.ideal for determining
849         beam slopes.
850        */
851       /* Make simple beam on middle line have small tilt */
852       if (!ideal[LEFT] && chord.delta () && count == 2)
853         {
854
855           /*
856             FIXME. -> UP
857           */
858           Direction d = (Direction) (sign (chord.delta ()) * UP);
859           pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
860           //                * dir;
861           pos[-d] = - pos[d];
862         }
863       else
864         {
865           pos = ideal;
866         }
867
868       y = pos[LEFT];
869       dy = pos[RIGHT]- y;
870       dydx = dy/dx;
871     }
872   else
873     {
874       Array<Offset> ideals;
875       for (int i=0; i < stems.size (); i++)
876         {
877           Grob* s = stems[i];
878           if (Stem::invisible_b (s))
879             continue;
880           ideals.push (Offset (x_posns[i],
881                                Stem::calc_stem_info (s).ideal_y_
882                                + s->relative_coordinate (commony, Y_AXIS)
883                                - my_y));
884         }
885       minimise_least_squares (&dydx, &y, ideals);
886
887       dy = dydx * dx;
888       me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
889       pos = Interval (y, (y+dy));
890     }
891
892   me->set_grob_property ("positions", ly_interval2scm (pos));
893  
894   return SCM_UNSPECIFIED;
895 }
896
897
898 /*
899   We can't combine with previous function, since check concave and
900   slope damping comes first.
901  */
902 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
903 SCM
904 Beam::shift_region_to_valid (SCM grob)
905 {
906   Grob *me = unsmob_grob (grob);
907   /*
908     Code dup.
909    */
910   Array<Real> x_posns ;
911   Link_array<Grob> stems=
912     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
913   Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
914   Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);  
915
916   Grob *fvs = first_visible_stem (me);
917
918   if (!fvs)
919     return SCM_UNSPECIFIED;
920     
921   Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
922   for (int i=0; i < stems.size (); i++)
923     {
924       Grob* s = stems[i];
925
926       Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
927       x_posns.push (x);
928     }
929
930   Grob *lvs = last_visible_stem (me);
931   if (!lvs)
932     return SCM_UNSPECIFIED;
933   
934   Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
935
936   Interval pos = ly_scm2interval ( me->get_grob_property ("positions"));
937   Real dy = pos.delta();
938   Real y = pos[LEFT];
939   Real dydx =dy/dx;
940
941   
942   /*
943     Shift the positions so that we have a chance of finding good
944     quants (i.e. no short stem failures.)
945    */
946   Interval feasible_left_point;
947   feasible_left_point.set_full ();
948   for (int i=0; i < stems.size (); i++)
949     {
950       Grob* s = stems[i];
951       if (Stem::invisible_b (s))
952         continue;
953
954       Direction d = Stem::get_direction (s);
955
956       Real left_y =
957         Stem::calc_stem_info (s).shortest_y_
958         - dydx * x_posns [i];
959
960       /*
961         left_y is now relative to the stem S. We want relative to
962         ourselves, so translate:
963        */
964       left_y += 
965         + s->relative_coordinate (commony, Y_AXIS)
966         - me->relative_coordinate (commony, Y_AXIS);
967
968       Interval flp ;
969       flp.set_full ();
970       flp[-d] = left_y;
971
972       feasible_left_point.intersect (flp);
973     }
974       
975   if (feasible_left_point.empty_b())
976     {
977       warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
978     }
979   else if (!feasible_left_point.elem_b(y))
980     {
981       if (isinf (feasible_left_point[DOWN]))
982         y = feasible_left_point[UP] - REGION_SIZE;
983       else if (isinf (feasible_left_point[UP]))
984         y = feasible_left_point[DOWN]+ REGION_SIZE;
985       else
986         y = feasible_left_point.center ();
987     }
988   pos = Interval (y, (y+dy));
989   me->set_grob_property ("positions", ly_interval2scm (pos));
990   return SCM_UNSPECIFIED;
991 }
992
993
994 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
995 SCM
996 Beam::check_concave (SCM smob)
997 {
998   Grob *me = unsmob_grob (smob);
999
1000   Link_array<Grob> stems = 
1001     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1002
1003   for (int i = 0; i < stems.size ();)
1004     {
1005       if (Stem::invisible_b (stems[i]))
1006         stems.del (i);
1007       else
1008         i++;
1009     }
1010   
1011   if (stems.size () < 3)
1012     return SCM_UNSPECIFIED;
1013
1014
1015   /* Concaveness #1: If distance of an inner notehead to line between
1016      two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
1017      beam is concave (Heinz Stolba).
1018
1019      In the case of knees, the line connecting outer heads is often
1020      not related to the beam slope (it may even go in the other
1021      direction). Skip the check when the outer stems point in
1022      different directions. --hwn
1023      
1024   */
1025   bool concaveness1 = false;
1026   SCM gap = me->get_grob_property ("concaveness-gap");
1027   if (gh_number_p (gap)
1028       && Stem::get_direction(stems.top ())
1029          == Stem::get_direction(stems[0]))
1030     {
1031       Real r1 = gh_scm2double (gap);
1032       Real dy = Stem::chord_start_y (stems.top ())
1033         - Stem::chord_start_y (stems[0]);
1034
1035       
1036       Real slope = dy / (stems.size () - 1);
1037       
1038       Real y0 = Stem::chord_start_y (stems[0]);
1039       for (int i = 1; i < stems.size () - 1; i++)
1040         {
1041           Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
1042           if (c > r1)
1043             {
1044               concaveness1 = true;
1045               break;
1046             }
1047         }
1048     }
1049
1050     
1051   /* Concaveness #2: Sum distances of inner noteheads that fall
1052      outside the interval of the two outer noteheads.
1053
1054      We only do this for beams where first and last stem have the same
1055      direction. --hwn.
1056
1057
1058      Note that "convex" stems compensate for "concave" stems.
1059      (is that intentional?) --hwn.
1060   */
1061   
1062   Real concaveness2 = 0;
1063   SCM thresh = me->get_grob_property ("concaveness-threshold");
1064   Real r2 = infinity_f;
1065   if (!concaveness1 && gh_number_p (thresh)
1066       && Stem::get_direction(stems.top ())
1067          == Stem::get_direction(stems[0]))
1068     {
1069       r2 = gh_scm2double (thresh);
1070
1071       Direction dir = Stem::get_direction(stems.top ());
1072       Real concave = 0;
1073       Interval iv (Stem::chord_start_y (stems[0]),
1074                    Stem::chord_start_y (stems.top ()));
1075       
1076       if (iv[MAX] < iv[MIN])
1077         iv.swap ();
1078       
1079       for (int i = 1; i < stems.size () - 1; i++)
1080         {
1081           Real f = Stem::chord_start_y (stems[i]);
1082           concave += ((f - iv[MAX] ) >? 0) +
1083             ((f - iv[MIN] ) <? 0);
1084         }
1085       concave *= dir;
1086       concaveness2 = concave / (stems.size () - 2);
1087       
1088       /* ugh: this is the a kludge to get
1089          input/regression/beam-concave.ly to behave as
1090          baerenreiter. */
1091
1092       /*
1093         huh? we're dividing twice (which is not scalable) meaning that
1094         the longer the beam, the more unlikely it will be
1095         concave. Maybe you would even expect the other way around??
1096
1097         --hwn.
1098         
1099        */
1100       concaveness2 /= (stems.size () - 2);
1101     }
1102   
1103   /* TODO: some sort of damping iso -> plain horizontal */
1104   if (concaveness1 || concaveness2 > r2)
1105     {
1106       Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1107       Real r = pos.linear_combination (0);
1108       me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
1109       me->set_grob_property ("least-squares-dy", gh_double2scm (0));
1110     }
1111
1112   return SCM_UNSPECIFIED;
1113 }
1114
1115 /* This neat trick is by Werner Lemberg,
1116    damped = tanh (slope)
1117    corresponds with some tables in [Wanske] CHECKME */
1118 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1119 SCM
1120 Beam::slope_damping (SCM smob)
1121 {
1122   Grob *me = unsmob_grob (smob);
1123
1124   if (visible_stem_count (me) <= 1)
1125     return SCM_UNSPECIFIED;
1126
1127   SCM s = me->get_grob_property ("damping"); 
1128   int damping = gh_scm2int (s);
1129
1130   if (damping)
1131     {
1132       Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1133       Real dy = pos.delta ();
1134
1135       Grob *fvs  = first_visible_stem (me);
1136       Grob *lvs  = last_visible_stem (me);
1137
1138       Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1139
1140
1141       Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1142         - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1143       Real dydx = dy && dx ? dy/dx : 0;
1144       dydx = 0.6 * tanh (dydx) / damping;
1145
1146       Real damped_dy = dydx * dx;
1147       pos[LEFT] += (dy - damped_dy) / 2;
1148       pos[RIGHT] -= (dy - damped_dy) / 2;
1149       
1150       me->set_grob_property ("positions", ly_interval2scm (pos));
1151     }
1152   return SCM_UNSPECIFIED;
1153 }
1154
1155 /*
1156   Report slice containing the numbers that are both in (car BEAMING)
1157   and (cdr BEAMING)
1158  */
1159 Slice
1160 where_are_the_whole_beams(SCM beaming)
1161 {
1162   Slice l; 
1163   
1164   for( SCM s = gh_car (beaming); gh_pair_p (s) ; s = gh_cdr (s))
1165     {
1166       if (scm_memq (gh_car (s), gh_cdr (beaming)) != SCM_BOOL_F)
1167         
1168         l.add_point (gh_scm2int (gh_car (s)));
1169     }
1170
1171   return l;
1172 }
1173
1174 /*
1175   Calculate the Y position of the stem-end, given the Y-left, Y-right
1176   in POS for stem S. This Y position is relative to S.
1177  */
1178 Real
1179 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1180                    Real xl, Real xr,
1181                    Interval pos, bool french) 
1182 {
1183   Real beam_translation = get_beam_translation (me);
1184
1185     
1186   Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1187   Real dy = pos.delta ();
1188   Real dx = xr - xl;
1189   Real stem_y_beam0 = (dy && dx
1190                        ? r / dx
1191                        * dy
1192                        : 0) + pos[LEFT];
1193   
1194   Direction my_dir = Directional_element_interface::get (s);
1195   SCM beaming = s->get_grob_property ("beaming");
1196  
1197   Real stem_y = stem_y_beam0;
1198   if (french)
1199     {
1200       Slice bm = where_are_the_whole_beams (beaming);
1201       if (!bm.empty_b())
1202         stem_y += beam_translation * bm[-my_dir];
1203     }
1204   else
1205     {
1206       Slice bm = Stem::beam_multiplicity(s);
1207       if (!bm.empty_b())
1208         stem_y +=bm[my_dir] * beam_translation;
1209     }
1210   
1211   Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1212     - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1213   
1214   return stem_y + id;
1215 }
1216
1217 /*
1218   Hmm.  At this time, beam position and slope are determined.  Maybe,
1219   stem directions and length should set to relative to the chord's
1220   position of the beam.  */
1221 void
1222 Beam::set_stem_lengths (Grob *me)
1223 {
1224   Link_array<Grob> stems=
1225     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1226
1227   if (stems.size () <= 1)
1228     return;
1229   
1230   Grob *common[2];
1231   for (int a = 2; a--;)
1232     common[a] = common_refpoint_of_array (stems, me, Axis(a));
1233   
1234   Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1235   Real staff_space = Staff_symbol_referencer::staff_space (me);
1236
1237   bool french = to_boolean (me->get_grob_property ("french-beaming"));
1238
1239   
1240   bool gap = false;
1241   Real thick =0.0;
1242   if (gh_number_p (me->get_grob_property ("gap"))
1243       &&gh_scm2double (me->get_grob_property ("gap")))
1244   {
1245     gap = true;
1246     thick = gh_scm2double (me->get_grob_property ("thickness"))
1247       * Staff_symbol_referencer::staff_space(me);
1248   }
1249       
1250   // ugh -> use commonx
1251   Grob * fvs = first_visible_stem (me);
1252   Grob *lvs = last_visible_stem (me);
1253     
1254   Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1255   Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1256   
1257   for (int i=0; i < stems.size (); i++)
1258     {
1259       Grob* s = stems[i];
1260       if (Stem::invisible_b (s))
1261         continue;
1262
1263       Real stem_y = calc_stem_y (me, s, common,
1264                                  xl, xr,
1265                                  pos, french && i > 0&& (i < stems.size  () -1));
1266
1267       /*
1268         Make the stems go up to the end of the beam. This doesn't matter
1269         for normal beams, but for tremolo beams it looks silly otherwise.
1270        */
1271       if (gap)
1272         stem_y += thick * 0.5 * Directional_element_interface::get(s);
1273       
1274       Stem::set_stemend (s, 2* stem_y / staff_space);
1275     }
1276 }
1277
1278 void
1279 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1280 {
1281   Link_array<Grob> stems=
1282     Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1283   
1284   Direction d = LEFT;
1285   for (int i=0; i  < stems.size (); i++)
1286     {
1287       /*
1288         Don't overwrite user settings.
1289        */
1290       
1291       do
1292         {
1293           /* Don't set beaming for outside of outer stems */      
1294           if ((d == LEFT && i == 0)
1295               ||(d == RIGHT && i == stems.size () -1))
1296             continue;
1297
1298
1299           SCM beaming_prop = stems[i]->get_grob_property ("beaming");
1300           if (beaming_prop == SCM_EOL ||
1301               index_get_cell (beaming_prop, d) == SCM_EOL)
1302             {
1303               int b = beaming->infos_.elem (i).beams_i_drul_[d];
1304               Stem::set_beaming (stems[i], b, d);
1305             }
1306         }
1307       while (flip (&d) != LEFT);
1308     }
1309 }
1310
1311 int
1312 Beam::forced_stem_count (Grob *me) 
1313 {
1314   Link_array<Grob>stems = 
1315     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1316   int f = 0;
1317   for (int i=0; i < stems.size (); i++)
1318     {
1319       Grob *s = stems[i];
1320
1321       if (Stem::invisible_b (s))
1322         continue;
1323
1324       if (((int)Stem::chord_start_y (s)) 
1325         && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1326         f++;
1327     }
1328   return f;
1329 }
1330
1331
1332
1333
1334 int
1335 Beam::visible_stem_count (Grob *me) 
1336 {
1337   Link_array<Grob>stems = 
1338     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1339   int c = 0;
1340   for (int i = stems.size (); i--;)
1341     {
1342       if (!Stem::invisible_b (stems[i]))
1343         c++;
1344     }
1345   return c;
1346 }
1347
1348 Grob*
1349 Beam::first_visible_stem (Grob *me) 
1350 {
1351   Link_array<Grob>stems = 
1352     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1353   
1354   for (int i = 0; i < stems.size (); i++)
1355     {
1356       if (!Stem::invisible_b (stems[i]))
1357         return stems[i];
1358     }
1359   return 0;
1360 }
1361
1362 Grob*
1363 Beam::last_visible_stem (Grob *me) 
1364 {
1365   Link_array<Grob>stems = 
1366     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1367   for (int i = stems.size (); i--;)
1368     {
1369       if (!Stem::invisible_b (stems[i]))
1370         return stems[i];
1371     }
1372   return 0;
1373 }
1374
1375
1376 /*
1377   [TODO]
1378   
1379   handle rest under beam (do_post: beams are calculated now)
1380   what about combination of collisions and rest under beam.
1381
1382   Should lookup
1383     
1384     rest -> stem -> beam -> interpolate_y_position ()
1385 */
1386 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1387 SCM
1388 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1389 {
1390   Grob *rest = unsmob_grob (element_smob);
1391   Axis a = (Axis) gh_scm2int (axis);
1392   
1393   assert (a == Y_AXIS);
1394
1395   Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1396   Grob *stem = st;
1397   if (!stem)
1398     return gh_double2scm (0.0);
1399   Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1400   if (!beam
1401       || !Beam::has_interface (beam)
1402       || !Beam::visible_stem_count (beam))
1403     return gh_double2scm (0.0);
1404
1405   // make callback for rest from this.
1406   // todo: make sure this calced already.
1407
1408   //  Interval pos = ly_scm2interval (beam->get_grob_property ("positions"));
1409   Interval pos (0, 0);
1410   SCM s = beam->get_grob_property ("positions");
1411   if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1412     pos = ly_scm2interval (s);
1413
1414   Real dy = pos.delta ();
1415   // ugh -> use commonx
1416   Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1417   Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1418   Real dydx = dy && dx ? dy/dx : 0;
1419   
1420   Direction d = Stem::get_direction (stem);
1421   Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + pos[LEFT];
1422
1423   Real staff_space = Staff_symbol_referencer::staff_space (rest);
1424
1425   
1426   Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space; // refp??
1427
1428   Real minimum_dist
1429     = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1430   Real dist =
1431     minimum_dist +  -d  * (beamy - rest_dim) >? 0;
1432
1433   int stafflines = Staff_symbol_referencer::line_count (rest);
1434
1435   // move discretely by half spaces.
1436   int discrete_dist = int (ceil (dist));
1437
1438   // move by whole spaces inside the staff.
1439   if (discrete_dist < stafflines+1)
1440     discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1441
1442   return gh_double2scm (-d *  discrete_dist);
1443 }
1444
1445 bool
1446 Beam::knee_b (Grob* me)
1447 {
1448   SCM k = me->get_grob_property ("knee");
1449   if (gh_boolean_p (k))
1450     return gh_scm2bool (k);
1451
1452   bool knee = false;
1453   int d = 0;
1454   for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
1455     {
1456       Direction dir = Directional_element_interface::get
1457         (unsmob_grob (ly_car (s)));
1458       if (d && d != dir)
1459         {
1460           knee = true;
1461           break;
1462         }
1463       d = dir;
1464     }
1465   
1466   me->set_grob_property ("knee", gh_bool2scm (knee));
1467
1468   return knee;
1469 }
1470
1471 int
1472 Beam::get_direction_beam_count (Grob *me, Direction d )
1473 {
1474   Link_array<Grob>stems = 
1475     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1476   int bc = 0;
1477   
1478   for (int i = stems.size (); i--;)
1479     {
1480       /*
1481         Should we take invisible stems into account?
1482        */
1483       if (Stem::get_direction (stems[i]) == d)
1484         bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1485     }
1486
1487   return bc;
1488 }
1489
1490
1491 ADD_INTERFACE (Beam, "beam-interface",
1492   "A beam.
1493
1494 #'thickness= weight of beams, in staffspace
1495
1496
1497 We take the least squares line through the ideal-length stems, and
1498 then damp that using
1499
1500         damped = tanh (slope)
1501
1502 this gives an unquantized left and right position for the beam end.
1503 Then we take all combinations of quantings near these left and right
1504 positions, and give them a score (according to how close they are to
1505 the ideal slope, how close the result is to the ideal stems, etc.). We
1506 take the best scoring combination.
1507
1508 ",
1509   "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");
1510
1511