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