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