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