]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
* lily/stem.cc (get_default_stem_end_position): use beam_count - 1
[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_list = me->get_grob_property ("beamed-stem-shorten");
749   if (shorten_list == SCM_EOL)
750     return;
751
752   Real staff_space = Staff_symbol_referencer::staff_space (me);
753   
754   SCM shorten_elt =
755     robust_list_ref (beam_count -1, shorten_list);
756   Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
757
758   /* your similar cute comment here */
759   shorten_f *= forced_fraction;
760
761   if (shorten_f)
762     me->set_grob_property ("shorten", gh_double2scm (shorten_f));
763 }
764
765 /*  Call list of y-dy-callbacks, that handle setting of
766     grob-properties
767
768 */
769 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
770 SCM
771 Beam::after_line_breaking (SCM smob)
772 {
773   Grob *me = unsmob_grob (smob);
774   
775   /* Copy to mutable list. */
776   SCM s = ly_deep_copy (me->get_grob_property ("positions"));
777   me->set_grob_property ("positions", s);
778
779   if (ly_car (s) == SCM_BOOL_F)
780     {
781
782       // one wonders if such genericity is necessary  --hwn.
783       SCM callbacks = me->get_grob_property ("position-callbacks");
784       for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
785         gh_call1 (ly_car (i), smob);
786     }
787
788   set_stem_lengths (me);  
789   return SCM_UNSPECIFIED;
790 }
791
792 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
793 SCM
794 Beam::least_squares (SCM smob)
795 {
796   Grob *me = unsmob_grob (smob);
797
798   int count = visible_stem_count (me);
799   Interval pos (0, 0);
800   
801   if (count <= 1)
802     {
803       me->set_grob_property ("positions", ly_interval2scm (pos));
804       return SCM_UNSPECIFIED;
805     }
806
807
808   Array<Real> x_posns ;
809   Link_array<Grob> stems=
810     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
811   Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
812   Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);  
813
814   Real my_y = me->relative_coordinate (commony, Y_AXIS);
815   
816   Grob *fvs  = first_visible_stem (me);
817   Grob *lvs  = last_visible_stem (me);
818   
819   Interval ideal (Stem::calc_stem_info (fvs).ideal_y_
820                   + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
821                   Stem::calc_stem_info (lvs).ideal_y_
822                   + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
823   
824   Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
825   for (int i=0; i < stems.size (); i++)
826     {
827       Grob* s = stems[i];
828
829       Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
830       x_posns.push (x);
831     }
832   Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
833
834   Real y =0;  
835   Real dydx = 0;
836   Real dy = 0;
837   
838   if (!ideal.delta ())
839     {
840       Interval chord (Stem::chord_start_y (first_visible_stem (me)),
841                       Stem::chord_start_y (last_visible_stem (me)));
842
843
844       /*
845         TODO -- use scoring for this.
846
847         complicated, because we take stem-info.ideal for determining
848         beam slopes.
849        */
850       /* Make simple beam on middle line have small tilt */
851       if (!ideal[LEFT] && chord.delta () && count == 2)
852         {
853
854           /*
855             FIXME. -> UP
856           */
857           Direction d = (Direction) (sign (chord.delta ()) * UP);
858           pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
859           //                * dir;
860           pos[-d] = - pos[d];
861         }
862       else
863         {
864           pos = ideal;
865         }
866
867       y = pos[LEFT];
868       dy = pos[RIGHT]- y;
869       dydx = dy/dx;
870     }
871   else
872     {
873       Array<Offset> ideals;
874       for (int i=0; i < stems.size (); i++)
875         {
876           Grob* s = stems[i];
877           if (Stem::invisible_b (s))
878             continue;
879           ideals.push (Offset (x_posns[i],
880                                Stem::calc_stem_info (s).ideal_y_
881                                + s->relative_coordinate (commony, Y_AXIS)
882                                - my_y));
883         }
884       minimise_least_squares (&dydx, &y, ideals);
885
886       dy = dydx * dx;
887       me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
888       pos = Interval (y, (y+dy));
889     }
890
891   me->set_grob_property ("positions", ly_interval2scm (pos));
892  
893   return SCM_UNSPECIFIED;
894 }
895
896
897 /*
898   We can't combine with previous function, since check concave and
899   slope damping comes first.
900  */
901 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
902 SCM
903 Beam::shift_region_to_valid (SCM grob)
904 {
905   Grob *me = unsmob_grob (grob);
906   /*
907     Code dup.
908    */
909   Array<Real> x_posns ;
910   Link_array<Grob> stems=
911     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
912   Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
913   Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);  
914
915   Grob *fvs = first_visible_stem (me);
916
917   if (!fvs)
918     return SCM_UNSPECIFIED;
919     
920   Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
921   for (int i=0; i < stems.size (); i++)
922     {
923       Grob* s = stems[i];
924
925       Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
926       x_posns.push (x);
927     }
928
929   Grob *lvs = last_visible_stem (me);
930   if (!lvs)
931     return SCM_UNSPECIFIED;
932   
933   Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
934
935   Interval pos = ly_scm2interval ( me->get_grob_property ("positions"));
936   Real dy = pos.delta();
937   Real y = pos[LEFT];
938   Real dydx =dy/dx;
939
940   
941   /*
942     Shift the positions so that we have a chance of finding good
943     quants (i.e. no short stem failures.)
944    */
945   Interval feasible_left_point;
946   feasible_left_point.set_full ();
947   for (int i=0; i < stems.size (); i++)
948     {
949       Grob* s = stems[i];
950       if (Stem::invisible_b (s))
951         continue;
952
953       Direction d = Stem::get_direction (s);
954
955       Real left_y =
956         Stem::calc_stem_info (s).shortest_y_
957         - dydx * x_posns [i];
958
959       /*
960         left_y is now relative to the stem S. We want relative to
961         ourselves, so translate:
962        */
963       left_y += 
964         + s->relative_coordinate (commony, Y_AXIS)
965         - me->relative_coordinate (commony, Y_AXIS);
966
967       Interval flp ;
968       flp.set_full ();
969       flp[-d] = left_y;
970
971       feasible_left_point.intersect (flp);
972     }
973       
974   if (feasible_left_point.empty_b())
975     {
976       warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
977     }
978   else if (!feasible_left_point.elem_b(y))
979     {
980       if (isinf (feasible_left_point[DOWN]))
981         y = feasible_left_point[UP] - REGION_SIZE;
982       else if (isinf (feasible_left_point[UP]))
983         y = feasible_left_point[DOWN]+ REGION_SIZE;
984       else
985         y = feasible_left_point.center ();
986     }
987   pos = Interval (y, (y+dy));
988   me->set_grob_property ("positions", ly_interval2scm (pos));
989   return SCM_UNSPECIFIED;
990 }
991
992
993 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
994 SCM
995 Beam::check_concave (SCM smob)
996 {
997   Grob *me = unsmob_grob (smob);
998
999   Link_array<Grob> stems = 
1000     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1001
1002   for (int i = 0; i < stems.size ();)
1003     {
1004       if (Stem::invisible_b (stems[i]))
1005         stems.del (i);
1006       else
1007         i++;
1008     }
1009   
1010   if (stems.size () < 3)
1011     return SCM_UNSPECIFIED;
1012
1013
1014   /* Concaveness #1: If distance of an inner notehead to line between
1015      two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
1016      beam is concave (Heinz Stolba).
1017
1018      In the case of knees, the line connecting outer heads is often
1019      not related to the beam slope (it may even go in the other
1020      direction). Skip the check when the outer stems point in
1021      different directions. --hwn
1022      
1023   */
1024   bool concaveness1 = false;
1025   SCM gap = me->get_grob_property ("concaveness-gap");
1026   if (gh_number_p (gap)
1027       && Stem::get_direction(stems.top ())
1028          == Stem::get_direction(stems[0]))
1029     {
1030       Real r1 = gh_scm2double (gap);
1031       Real dy = Stem::chord_start_y (stems.top ())
1032         - Stem::chord_start_y (stems[0]);
1033
1034       
1035       Real slope = dy / (stems.size () - 1);
1036       
1037       Real y0 = Stem::chord_start_y (stems[0]);
1038       for (int i = 1; i < stems.size () - 1; i++)
1039         {
1040           Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
1041           if (c > r1)
1042             {
1043               concaveness1 = true;
1044               break;
1045             }
1046         }
1047     }
1048
1049     
1050   /* Concaveness #2: Sum distances of inner noteheads that fall
1051      outside the interval of the two outer noteheads.
1052
1053      We only do this for beams where first and last stem have the same
1054      direction. --hwn.
1055
1056
1057      Note that "convex" stems compensate for "concave" stems.
1058      (is that intentional?) --hwn.
1059   */
1060   
1061   Real concaveness2 = 0;
1062   SCM thresh = me->get_grob_property ("concaveness-threshold");
1063   Real r2 = infinity_f;
1064   if (!concaveness1 && gh_number_p (thresh)
1065       && Stem::get_direction(stems.top ())
1066          == Stem::get_direction(stems[0]))
1067     {
1068       r2 = gh_scm2double (thresh);
1069
1070       Direction dir = Stem::get_direction(stems.top ());
1071       Real concave = 0;
1072       Interval iv (Stem::chord_start_y (stems[0]),
1073                    Stem::chord_start_y (stems.top ()));
1074       
1075       if (iv[MAX] < iv[MIN])
1076         iv.swap ();
1077       
1078       for (int i = 1; i < stems.size () - 1; i++)
1079         {
1080           Real f = Stem::chord_start_y (stems[i]);
1081           concave += ((f - iv[MAX] ) >? 0) +
1082             ((f - iv[MIN] ) <? 0);
1083         }
1084       concave *= dir;
1085       concaveness2 = concave / (stems.size () - 2);
1086       
1087       /* ugh: this is the a kludge to get
1088          input/regression/beam-concave.ly to behave as
1089          baerenreiter. */
1090
1091       /*
1092         huh? we're dividing twice (which is not scalable) meaning that
1093         the longer the beam, the more unlikely it will be
1094         concave. Maybe you would even expect the other way around??
1095
1096         --hwn.
1097         
1098        */
1099       concaveness2 /= (stems.size () - 2);
1100     }
1101   
1102   /* TODO: some sort of damping iso -> plain horizontal */
1103   if (concaveness1 || concaveness2 > r2)
1104     {
1105       Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1106       Real r = pos.linear_combination (0);
1107       me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
1108       me->set_grob_property ("least-squares-dy", gh_double2scm (0));
1109     }
1110
1111   return SCM_UNSPECIFIED;
1112 }
1113
1114 /* This neat trick is by Werner Lemberg,
1115    damped = tanh (slope)
1116    corresponds with some tables in [Wanske] CHECKME */
1117 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1118 SCM
1119 Beam::slope_damping (SCM smob)
1120 {
1121   Grob *me = unsmob_grob (smob);
1122
1123   if (visible_stem_count (me) <= 1)
1124     return SCM_UNSPECIFIED;
1125
1126   SCM s = me->get_grob_property ("damping"); 
1127   int damping = gh_scm2int (s);
1128
1129   if (damping)
1130     {
1131       Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1132       Real dy = pos.delta ();
1133
1134       Grob *fvs  = first_visible_stem (me);
1135       Grob *lvs  = last_visible_stem (me);
1136
1137       Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1138
1139
1140       Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1141         - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1142       Real dydx = dy && dx ? dy/dx : 0;
1143       dydx = 0.6 * tanh (dydx) / damping;
1144
1145       Real damped_dy = dydx * dx;
1146       pos[LEFT] += (dy - damped_dy) / 2;
1147       pos[RIGHT] -= (dy - damped_dy) / 2;
1148       
1149       me->set_grob_property ("positions", ly_interval2scm (pos));
1150     }
1151   return SCM_UNSPECIFIED;
1152 }
1153
1154 /*
1155   Report slice containing the numbers that are both in (car BEAMING)
1156   and (cdr BEAMING)
1157  */
1158 Slice
1159 where_are_the_whole_beams(SCM beaming)
1160 {
1161   Slice l; 
1162   
1163   for( SCM s = gh_car (beaming); gh_pair_p (s) ; s = gh_cdr (s))
1164     {
1165       if (scm_memq (gh_car (s), gh_cdr (beaming)) != SCM_BOOL_F)
1166         
1167         l.add_point (gh_scm2int (gh_car (s)));
1168     }
1169
1170   return l;
1171 }
1172
1173 /*
1174   Calculate the Y position of the stem-end, given the Y-left, Y-right
1175   in POS for stem S. This Y position is relative to S.
1176  */
1177 Real
1178 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1179                    Real xl, Real xr,
1180                    Interval pos, bool french) 
1181 {
1182   Real beam_translation = get_beam_translation (me);
1183
1184     
1185   Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1186   Real dy = pos.delta ();
1187   Real dx = xr - xl;
1188   Real stem_y_beam0 = (dy && dx
1189                        ? r / dx
1190                        * dy
1191                        : 0) + pos[LEFT];
1192   
1193   Direction my_dir = Directional_element_interface::get (s);
1194   SCM beaming = s->get_grob_property ("beaming");
1195  
1196   Real stem_y = stem_y_beam0;
1197   if (french)
1198     {
1199       Slice bm = where_are_the_whole_beams (beaming);
1200       if (!bm.empty_b())
1201         stem_y += beam_translation * bm[-my_dir];
1202     }
1203   else
1204     {
1205       Slice bm = Stem::beam_multiplicity(s);
1206       if (!bm.empty_b())
1207         stem_y +=bm[my_dir] * beam_translation;
1208     }
1209   
1210   Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1211     - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1212   
1213   return stem_y + id;
1214 }
1215
1216 /*
1217   Hmm.  At this time, beam position and slope are determined.  Maybe,
1218   stem directions and length should set to relative to the chord's
1219   position of the beam.  */
1220 void
1221 Beam::set_stem_lengths (Grob *me)
1222 {
1223   Link_array<Grob> stems=
1224     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1225
1226   if (stems.size () <= 1)
1227     return;
1228   
1229   Grob *common[2];
1230   for (int a = 2; a--;)
1231     common[a] = common_refpoint_of_array (stems, me, Axis(a));
1232   
1233   Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1234   Real staff_space = Staff_symbol_referencer::staff_space (me);
1235
1236   bool french = to_boolean (me->get_grob_property ("french-beaming"));
1237
1238   
1239   bool gap = false;
1240   Real thick =0.0;
1241   if (gh_number_p (me->get_grob_property ("gap"))
1242       &&gh_scm2double (me->get_grob_property ("gap")))
1243   {
1244     gap = true;
1245     thick = gh_scm2double (me->get_grob_property ("thickness"))
1246       * Staff_symbol_referencer::staff_space(me);
1247   }
1248       
1249   // ugh -> use commonx
1250   Grob * fvs = first_visible_stem (me);
1251   Grob *lvs = last_visible_stem (me);
1252     
1253   Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1254   Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1255   
1256   for (int i=0; i < stems.size (); i++)
1257     {
1258       Grob* s = stems[i];
1259       if (Stem::invisible_b (s))
1260         continue;
1261
1262       Real stem_y = calc_stem_y (me, s, common,
1263                                  xl, xr,
1264                                  pos, french && i > 0&& (i < stems.size  () -1));
1265
1266       /*
1267         Make the stems go up to the end of the beam. This doesn't matter
1268         for normal beams, but for tremolo beams it looks silly otherwise.
1269        */
1270       if (gap)
1271         stem_y += thick * 0.5 * Directional_element_interface::get(s);
1272       
1273       Stem::set_stemend (s, 2* stem_y / staff_space);
1274     }
1275 }
1276
1277 void
1278 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1279 {
1280   Link_array<Grob> stems=
1281     Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1282   
1283   Direction d = LEFT;
1284   for (int i=0; i  < stems.size (); i++)
1285     {
1286       /*
1287         Don't overwrite user settings.
1288        */
1289       
1290       do
1291         {
1292           /* Don't set beaming for outside of outer stems */      
1293           if ((d == LEFT && i == 0)
1294               ||(d == RIGHT && i == stems.size () -1))
1295             continue;
1296
1297
1298           SCM beaming_prop = stems[i]->get_grob_property ("beaming");
1299           if (beaming_prop == SCM_EOL ||
1300               index_get_cell (beaming_prop, d) == SCM_EOL)
1301             {
1302               int b = beaming->infos_.elem (i).beams_i_drul_[d];
1303               Stem::set_beaming (stems[i], b, d);
1304             }
1305         }
1306       while (flip (&d) != LEFT);
1307     }
1308 }
1309
1310 int
1311 Beam::forced_stem_count (Grob *me) 
1312 {
1313   Link_array<Grob>stems = 
1314     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1315   int f = 0;
1316   for (int i=0; i < stems.size (); i++)
1317     {
1318       Grob *s = stems[i];
1319
1320       if (Stem::invisible_b (s))
1321         continue;
1322
1323       if (((int)Stem::chord_start_y (s)) 
1324         && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1325         f++;
1326     }
1327   return f;
1328 }
1329
1330
1331
1332
1333 int
1334 Beam::visible_stem_count (Grob *me) 
1335 {
1336   Link_array<Grob>stems = 
1337     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1338   int c = 0;
1339   for (int i = stems.size (); i--;)
1340     {
1341       if (!Stem::invisible_b (stems[i]))
1342         c++;
1343     }
1344   return c;
1345 }
1346
1347 Grob*
1348 Beam::first_visible_stem (Grob *me) 
1349 {
1350   Link_array<Grob>stems = 
1351     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1352   
1353   for (int i = 0; i < stems.size (); i++)
1354     {
1355       if (!Stem::invisible_b (stems[i]))
1356         return stems[i];
1357     }
1358   return 0;
1359 }
1360
1361 Grob*
1362 Beam::last_visible_stem (Grob *me) 
1363 {
1364   Link_array<Grob>stems = 
1365     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1366   for (int i = stems.size (); i--;)
1367     {
1368       if (!Stem::invisible_b (stems[i]))
1369         return stems[i];
1370     }
1371   return 0;
1372 }
1373
1374
1375 /*
1376   [TODO]
1377   
1378   handle rest under beam (do_post: beams are calculated now)
1379   what about combination of collisions and rest under beam.
1380
1381   Should lookup
1382     
1383     rest -> stem -> beam -> interpolate_y_position ()
1384 */
1385 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1386 SCM
1387 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1388 {
1389   Grob *rest = unsmob_grob (element_smob);
1390   Axis a = (Axis) gh_scm2int (axis);
1391   
1392   assert (a == Y_AXIS);
1393
1394   Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1395   Grob *stem = st;
1396   if (!stem)
1397     return gh_double2scm (0.0);
1398   Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1399   if (!beam
1400       || !Beam::has_interface (beam)
1401       || !Beam::visible_stem_count (beam))
1402     return gh_double2scm (0.0);
1403
1404   // make callback for rest from this.
1405   // todo: make sure this calced already.
1406
1407   //  Interval pos = ly_scm2interval (beam->get_grob_property ("positions"));
1408   Interval pos (0, 0);
1409   SCM s = beam->get_grob_property ("positions");
1410   if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1411     pos = ly_scm2interval (s);
1412
1413   Real dy = pos.delta ();
1414   // ugh -> use commonx
1415   Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1416   Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1417   Real dydx = dy && dx ? dy/dx : 0;
1418   
1419   Direction d = Stem::get_direction (stem);
1420   Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + pos[LEFT];
1421
1422   Real staff_space = Staff_symbol_referencer::staff_space (rest);
1423
1424   
1425   Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space; // refp??
1426
1427   Real minimum_dist
1428     = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1429   Real dist =
1430     minimum_dist +  -d  * (beamy - rest_dim) >? 0;
1431
1432   int stafflines = Staff_symbol_referencer::line_count (rest);
1433
1434   // move discretely by half spaces.
1435   int discrete_dist = int (ceil (dist));
1436
1437   // move by whole spaces inside the staff.
1438   if (discrete_dist < stafflines+1)
1439     discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1440
1441   return gh_double2scm (-d *  discrete_dist);
1442 }
1443
1444 bool
1445 Beam::knee_b (Grob* me)
1446 {
1447   SCM k = me->get_grob_property ("knee");
1448   if (gh_boolean_p (k))
1449     return gh_scm2bool (k);
1450
1451   bool knee = false;
1452   int d = 0;
1453   for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
1454     {
1455       Direction dir = Directional_element_interface::get
1456         (unsmob_grob (ly_car (s)));
1457       if (d && d != dir)
1458         {
1459           knee = true;
1460           break;
1461         }
1462       d = dir;
1463     }
1464   
1465   me->set_grob_property ("knee", gh_bool2scm (knee));
1466
1467   return knee;
1468 }
1469
1470 int
1471 Beam::get_direction_beam_count (Grob *me, Direction d )
1472 {
1473   Link_array<Grob>stems = 
1474     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1475   int bc = 0;
1476   
1477   for (int i = stems.size (); i--;)
1478     {
1479       /*
1480         Should we take invisible stems into account?
1481        */
1482       if (Stem::get_direction (stems[i]) == d)
1483         bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1484     }
1485
1486   return bc;
1487 }
1488
1489
1490 ADD_INTERFACE (Beam, "beam-interface",
1491   "A beam.
1492
1493 #'thickness= weight of beams, in staffspace
1494
1495
1496 We take the least squares line through the ideal-length stems, and
1497 then damp that using
1498
1499         damped = tanh (slope)
1500
1501 this gives an unquantized left and right position for the beam end.
1502 Then we take all combinations of quantings near these left and right
1503 positions, and give them a score (according to how close they are to
1504 the ideal slope, how close the result is to the ideal stems, etc.). We
1505 take the best scoring combination.
1506
1507 ",
1508   "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");
1509
1510