]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
* scm/grob-property-description.scm (beamed-minimum-free-lengths):
[lilypond.git] / lily / beam.cc
1 /*
2   beam.cc -- implement Beam
3   
4   source file of the GNU LilyPond music typesetter
5   
6   (c)  1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7   Jan Nieuwenhuizen <janneke@gnu.org>
8   
9 */
10
11 /*
12 TODO:
13
14
15   * Junk stem_info.
16
17   * Use Number_pair i.s.o Interval to represent (yl, yr).
18   
19   - Determine auto knees based on positions if it's set by the user.
20
21
22 Notes:
23
24
25  - Stems run to the Y-center of the beam.
26   
27  - beam_translation is the offset between Y centers of the beam.
28
29 */
30
31
32 #include <math.h> // tanh.
33
34 #include "molecule.hh" 
35 #include "directional-element-interface.hh"
36 #include "beaming.hh"
37 #include "beam.hh"
38 #include "misc.hh"
39 #include "least-squares.hh"
40 #include "stem.hh"
41 #include "paper-def.hh"
42 #include "lookup.hh"
43 #include "group-interface.hh"
44 #include "staff-symbol-referencer.hh"
45 #include "item.hh"
46 #include "spanner.hh"
47 #include "warn.hh"
48
49
50 #define DEBUG_QUANTING 0
51
52
53 #if DEBUG_QUANTING
54 #include "text-item.hh"  // debug output.
55 #include "font-interface.hh"  // debug output.
56 #endif
57
58
59 void
60 Beam::add_stem (Grob *me, Grob *s)
61 {
62   Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
63   
64   s->add_dependency (me);
65
66   assert (!Stem::get_beam (s));
67   s->set_grob_property ("beam", me->self_scm ());
68
69   add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
70 }
71
72
73 /*
74   this returns the translation between 2 adjoining beams.
75  */
76 Real
77 Beam::get_beam_translation (Grob *me)
78 {
79   SCM func = me->get_grob_property ("space-function");
80   SCM s = gh_call2 (func, me->self_scm (), scm_int2num (get_beam_count (me)));
81   return gh_scm2double (s);
82 }
83
84 /* Maximum beam_count. */
85 int
86 Beam::get_beam_count (Grob *me) 
87 {
88   int m = 0;
89   for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
90     {
91       Grob *stem = unsmob_grob (ly_car (s));
92       m = m >? (Stem::beam_multiplicity (stem).length () + 1);
93     }
94   return m;
95 }
96
97 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
98 SCM
99 Beam::space_function (SCM smob, SCM beam_count)
100 {
101   Grob *me = unsmob_grob (smob);
102   
103   Real staff_space = Staff_symbol_referencer::staff_space (me);
104   Real line = me->get_paper ()->get_var ("linethickness");
105   Real thickness = gh_scm2double (me->get_grob_property ("thickness"))
106     * staff_space;
107   
108   Real beam_translation = gh_scm2int (beam_count) < 4
109     ? (2*staff_space + line - thickness) / 2.0
110     : (3*staff_space + line - thickness) / 3.0;
111   
112   return gh_double2scm (beam_translation);
113 }
114
115
116 /* After pre-processing all directions should be set.
117    Several post-processing routines (stem, slur, script) need stem/beam
118    direction.
119    Currenly, this means that beam has set all stem's directions.
120    [Alternatively, stems could set its own directions, according to
121    their beam, during 'final-pre-processing'.] */
122 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
123 SCM
124 Beam::before_line_breaking (SCM smob)
125 {
126   Grob *me =  unsmob_grob (smob);
127
128   /* Beams with less than 2 two stems don't make much sense, but could happen
129      when you do
130      
131      [r8 c8 r8].
132      
133     For a beam that  only has one stem, we try to do some disappearance magic:
134     we revert the flag, and move on to The Eternal Engraving Fields. */
135
136   int count = visible_stem_count (me);
137   if (count < 2)
138     {
139       me->warning (_ ("beam has less than two visible stems"));
140
141       SCM stems = me->get_grob_property ("stems");
142       if (scm_ilength (stems) == 1)
143         {
144           me->warning (_ ("Beam has less than two stems. Removing beam."));
145
146           unsmob_grob (gh_car (stems))->set_grob_property ("beam", SCM_EOL);
147           me->suicide ();
148
149           return SCM_UNSPECIFIED;
150         }
151       else if (scm_ilength (stems) == 0)
152         {
153           me->suicide ();
154           return SCM_UNSPECIFIED;         
155         }
156     }
157   if (count >= 1)
158     {
159       Direction d = get_default_dir (me);
160
161       consider_auto_knees (me);
162       set_stem_directions (me, d);
163
164       connect_beams (me);
165
166       set_stem_shorten (me);
167     }
168
169   return SCM_EOL;
170 }
171
172
173 /*
174   We want a maximal number of shared beams, but if there is choice, we
175   take the one that is closest to the end of the stem. This is for situations like
176
177        x
178       |
179       |
180   |===|
181   |=
182   |
183   x
184
185   
186  */
187 int
188 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
189                                     Direction left_dir,
190                                     Direction right_dir)
191 {
192   Slice lslice = int_list_to_slice (gh_cdr (left_beaming));
193
194   int best_count = 0;
195   int best_start = 0;
196   for (int i = lslice[-left_dir];
197        (i - lslice[left_dir])* left_dir <= 0 ; i+= left_dir) 
198     {
199       int count =0;
200       for ( SCM s = gh_car (right_beaming); gh_pair_p (s); s = gh_cdr (s))
201         {
202           int k = - right_dir * gh_scm2int (gh_car (s)) + i;
203           if (scm_memq (scm_int2num (k), left_beaming) != SCM_BOOL_F)
204             count ++;
205         }
206
207       if (count >= best_count)
208         {
209           best_count = count; 
210           best_start = i;
211         }
212     }
213
214   return best_start;
215 }
216
217 void
218 Beam::connect_beams (Grob *me)
219 {
220   Link_array<Grob> stems=
221     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
222
223   Slice last_int;
224   last_int.set_empty();
225   SCM last_beaming = SCM_EOL;
226   Direction last_dir = CENTER;
227   for (int i = 0; i< stems.size(); i++)
228     {
229       Grob *this_stem = stems[i];
230       SCM this_beaming = this_stem->get_grob_property ("beaming");
231
232       Direction this_dir = Directional_element_interface::get(this_stem);
233       if (i > 0)
234         {
235           int start_point = position_with_maximal_common_beams
236             (last_beaming, this_beaming,
237              last_dir, this_dir);
238           
239           Direction d = LEFT;
240           Slice new_slice ; 
241           do
242             {
243               if (d == RIGHT && i == stems.size()-1)
244                 continue;
245               
246               new_slice.set_empty();
247               SCM s = index_get_cell (this_beaming, d);
248               for (; gh_pair_p (s); s = gh_cdr (s))
249                 {
250                   int new_beam_pos =
251                     start_point - this_dir * gh_scm2int (gh_car (s));
252
253                   new_slice.add_point (new_beam_pos);
254                   gh_set_car_x (s, scm_int2num (new_beam_pos));
255                 }
256
257
258             }
259           while (flip (&d) != LEFT);
260
261           if (!new_slice.empty_b())
262             last_int =  new_slice;
263         }
264       else
265         {
266           gh_set_car_x ( this_beaming, SCM_EOL);
267           SCM s = gh_cdr (this_beaming);
268           for (; gh_pair_p (s); s = gh_cdr (s))
269             {
270               int np = - this_dir * gh_scm2int (gh_car(s));
271               gh_set_car_x (s, scm_int2num (np));
272               last_int.add_point (np);
273             }
274         }
275
276       if (i == stems.size () -1)
277         {
278           gh_set_cdr_x (this_beaming, SCM_EOL);
279         }
280
281       if (scm_ilength (gh_cdr (this_beaming)) > 0)
282         {
283           last_beaming = this_beaming;
284           last_dir = this_dir;
285         }
286     }
287  }
288
289 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
290 SCM
291 Beam::brew_molecule (SCM grob)
292 {
293   Grob *me = unsmob_grob (grob);
294   Link_array<Grob> stems=
295     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
296   Grob* xcommon = common_refpoint_of_array (stems, me, X_AXIS);
297
298   Real x0, dx;
299   if (visible_stem_count (me))
300     {
301       // ugh -> use commonx
302       x0 = first_visible_stem (me)->relative_coordinate (xcommon, X_AXIS);
303       dx = last_visible_stem (me)->relative_coordinate (xcommon, X_AXIS) - x0;
304     }
305   else
306     {
307       x0 = stems[0]->relative_coordinate (xcommon, X_AXIS);
308       dx = stems.top ()->relative_coordinate (xcommon, X_AXIS) - x0;
309     }
310
311   SCM posns = me->get_grob_property ("positions");
312   Interval pos;
313   if (!ly_number_pair_p (posns))
314     {
315       programming_error ("No beam posns");
316       pos = Interval (0,0);
317     }
318   else
319     pos= ly_scm2interval (posns);
320
321   Real dy = pos.delta ();
322   Real dydx = dy && dx ? dy/dx : 0;
323   
324   Real thick = gh_scm2double (me->get_grob_property ("thickness"));
325   Real bdy = get_beam_translation (me);
326
327   SCM last_beaming = SCM_EOL;;
328   Real last_xposn = -1;
329   Real last_width = -1 ;
330
331
332   SCM gap = me->get_grob_property ("gap");
333   Molecule the_beam;
334   Real lt = me->get_paper ()->get_var ("linethickness");
335   for (int i = 0; i< stems.size(); i++)
336     {
337       Grob * st =stems[i];
338       
339       SCM this_beaming = st->get_grob_property ("beaming");
340       Real xposn = st->relative_coordinate (xcommon, X_AXIS);
341       Real stem_width = gh_scm2double (st->get_grob_property ("thickness")) *lt;
342
343       if (i > 0)
344         {
345           SCM left = gh_cdr (last_beaming);
346           SCM right = gh_car (this_beaming);
347
348           Array<int> fullbeams;
349           Array<int> lfliebertjes;
350           Array<int> rfliebertjes;        
351
352           for (SCM s = left;
353                gh_pair_p (s); s =gh_cdr (s))
354             {
355               int b = gh_scm2int (gh_car (s));
356               if (scm_memq (gh_car(s), right) != SCM_BOOL_F)
357                 {
358                   fullbeams.push (b);
359                 }
360               else
361                 {
362                   lfliebertjes.push (b); 
363                 }
364             }
365           for (SCM s = right;
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), left) == SCM_BOOL_F)
370                 {
371                   rfliebertjes.push (b);
372                 }
373             }
374
375           
376           Real w = xposn - last_xposn;
377           Real stem_offset = 0.0;
378           Real width_corr = 0.0;
379           if (i == 1)
380             {
381               stem_offset -= last_width/2;
382               width_corr += last_width/2;
383             }
384           
385           if (i == stems.size() -1)
386             {
387               width_corr += stem_width/2;
388             }
389
390           if (gh_number_p (gap))
391             {
392               Real g = gh_scm2double (gap);
393               stem_offset += g;
394               width_corr -= 2*g; 
395             }
396           
397           Molecule whole = Lookup::beam (dydx, w + width_corr, thick);
398           for (int j = fullbeams.size(); j--;)
399             {
400               Molecule b (whole);
401               b.translate_axis (last_xposn -  x0 + stem_offset, X_AXIS);
402               b.translate_axis (dydx * (last_xposn - x0) + bdy * fullbeams[j], Y_AXIS);
403               the_beam.add_molecule (b);              
404             }
405
406           if (lfliebertjes.size() || rfliebertjes.size())
407             {
408
409               Real nw_f;
410               if (!Stem::first_head (st))
411                 nw_f = 0;
412               else
413                 {
414                   int t = Stem::duration_log (st); 
415
416                   SCM proc = me->get_grob_property ("flag-width-function");
417                   SCM result = gh_call1 (proc, scm_int2num (t));
418                   nw_f = gh_scm2double (result);
419                 }
420               
421               /* Half beam should be one note-width,
422                  but let's make sure two half-beams never touch */
423               
424               Real w = xposn - last_xposn;
425               w = w/2 <? nw_f;
426
427               Molecule half = Lookup::beam (dydx, w, thick);
428               for (int j = lfliebertjes.size(); j--;)
429                 {
430                   Molecule b (half);
431                   b.translate_axis (last_xposn -  x0, X_AXIS);
432                   b.translate_axis (dydx * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
433                   the_beam.add_molecule (b);          
434                 }
435               for (int j = rfliebertjes.size(); j--;)
436                 {
437                   Molecule b (half);
438                   b.translate_axis (xposn -  x0 - w , X_AXIS);
439                   b.translate_axis (dydx * (xposn-x0 -w) + bdy * rfliebertjes[j], Y_AXIS);
440                   the_beam.add_molecule (b);          
441                 }
442             }
443         }
444
445       last_xposn = xposn;
446       last_width = stem_width;
447       last_beaming = this_beaming;
448     }
449
450   the_beam.translate_axis (x0 - me->relative_coordinate (xcommon, X_AXIS), X_AXIS);
451   the_beam.translate_axis (pos[LEFT], Y_AXIS);
452
453 #if (DEBUG_QUANTING)
454     {
455       /*
456         This code prints the demerits for each beam. Perhaps this
457         should be switchable for those who want to twiddle with the
458         parameters.
459       */
460       String str;
461       if (1)
462         {
463           str += to_string (gh_scm2int (me->get_grob_property ("best-idx")));
464           str += ":";
465         }
466       str += to_string (gh_scm2double (me->get_grob_property ("quant-score")),
467                      "%.2f");
468
469       SCM properties = Font_interface::font_alist_chain (me);
470
471       
472       Molecule tm = Text_item::text2molecule (me, scm_makfrom0str (str.to_str0 ()), properties);
473       the_beam.add_at_edge (Y_AXIS, UP, tm, 5.0);
474     }
475 #endif
476     
477   
478   
479   return the_beam.smobbed_copy();
480 }
481   
482
483
484
485 Direction
486 Beam::get_default_dir (Grob *me) 
487 {
488   Drul_array<int> total;
489   total[UP]  = total[DOWN] = 0;
490   Drul_array<int> count; 
491   count[UP]  = count[DOWN] = 0;
492   Direction d = DOWN;
493
494   Link_array<Grob> stems=
495         Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
496
497   for (int i=0; i <stems.size (); i++)
498     do {
499       Grob *s = stems[i];
500       Direction sd = Directional_element_interface::get (s);
501
502       int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
503       int current = sd  ? (1 + d * sd)/2 : center_distance;
504
505       if (current)
506         {
507           total[d] += current;
508           count[d] ++;
509         }
510     } while (flip (&d) != DOWN);
511   
512   SCM func = me->get_grob_property ("dir-function");
513   SCM s = gh_call2 (func,
514                     gh_cons (scm_int2num (count[UP]),
515                              scm_int2num (count[DOWN])),
516                     gh_cons (scm_int2num (total[UP]),
517                              scm_int2num (total[DOWN])));
518
519   if (gh_number_p (s) && gh_scm2int (s))
520     return to_dir (s);
521   
522   /* If dir is not determined: get default */
523   return to_dir (me->get_grob_property ("neutral-direction"));
524 }
525
526
527 /* Set all stems with non-forced direction to beam direction.
528    Urg: non-forced should become `without/with unforced' direction,
529    once stem gets cleaned-up. */
530 void
531 Beam::set_stem_directions (Grob *me, Direction d)
532 {
533   Link_array<Grob> stems
534     =Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
535   
536   for (int i=0; i <stems.size (); i++)
537     {
538       Grob *s = stems[i];
539   
540       SCM forcedir = s->get_grob_property ("direction");
541       if (!to_dir (forcedir))
542         Directional_element_interface::set (s, d);
543     }
544 }
545
546 /*
547   A union of intervals in the real line.
548
549   Abysmal performance (quadratic) for large N, hopefully we don't have
550   that large N. In any case, this should probably be rewritten to use
551   a balanced tree.
552  */
553 struct Int_set
554 {
555   Array<Interval> allowed_regions_;
556
557   Int_set()
558   {
559     set_full();
560   }
561
562   void set_full()
563   {
564     allowed_regions_.clear();
565     Interval s;
566     s.set_full ();
567     allowed_regions_.push (s);
568   }
569
570   void remove_interval (Interval rm)
571   {
572     for (int i = 0; i < allowed_regions_.size(); )
573       {
574         Interval s = rm;
575
576         s.intersect (allowed_regions_[i]);
577
578         if (!s.empty_b ())
579           {
580             Interval before = allowed_regions_[i];
581             Interval after = allowed_regions_[i];
582
583             before[RIGHT] = s[LEFT];
584             after[LEFT] = s[RIGHT];
585
586             if (!before.empty_b() && before.length () > 0.0)
587               {
588                 allowed_regions_.insert (before, i);
589                 i++;
590               }
591             allowed_regions_.del (i);
592             if (!after.empty_b () && after.length () > 0.0)
593               {
594                 allowed_regions_.insert (after, i);
595                 i++;
596               }
597           }
598         else
599           i++;
600       }
601   }
602 };
603
604
605 /*
606   Only try horizontal beams for knees.  No reliable detection of
607   anything else is possible here, since we don't know funky-beaming
608   settings, or X-distances (slopes!)  People that want sloped
609   knee-beams, should set the directions manually.
610  */
611 void
612 Beam::consider_auto_knees (Grob* me)
613 {
614   SCM scm = me->get_grob_property ("auto-knee-gap");
615   if (!gh_number_p (scm))
616     return ;
617
618   Real threshold = gh_scm2double (scm);
619   
620   Int_set gaps;
621
622   gaps.set_full ();
623
624   Link_array<Grob> stems=
625     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
626       
627   Grob *common = common_refpoint_of_array (stems, me,  Y_AXIS);
628   Real staff_space = Staff_symbol_referencer::staff_space (me);
629   
630   Array<Interval> hps_array;  
631   for (int i=0; i < stems.size (); i++)
632     {
633       Grob* stem = stems[i];
634       if (Stem::invisible_b (stem))
635         continue;
636
637       Interval hps = Stem::head_positions (stem);
638       if(!hps.empty_b())
639         {
640           hps[LEFT] += -1;
641           hps[RIGHT] += 1; 
642           hps *= staff_space * 0.5 ;
643
644           /*
645             We could subtract beam Y position, but this routine only
646             sets stem directions, a constant shift does not have an
647             influence.
648             
649            */
650           hps += stem->relative_coordinate (common, Y_AXIS);
651
652           if (to_dir (stem->get_grob_property ("direction")))
653             {
654               Direction stemdir = to_dir (stem->get_grob_property ("direction"));
655               hps[-stemdir] = - stemdir * infinity_f;
656             }
657         }
658       hps_array.push (hps);
659
660       gaps.remove_interval (hps);
661     }
662
663   Interval max_gap;
664   Real max_gap_len =0.0;
665
666   for (int i  = gaps.allowed_regions_.size() -1;  i >=  0 ; i--)
667     {
668       Interval gap = gaps.allowed_regions_[i];
669
670       /*
671         the outer gaps are not knees.
672        */
673       if (isinf (gap[LEFT]) || isinf(gap[RIGHT]))
674         continue;
675       
676       if (gap.length () >= max_gap_len)
677         {
678           max_gap_len = gap.length();
679           max_gap = gap;
680         }
681     }
682
683   if (max_gap_len > threshold)
684     {
685       int j = 0;
686       for (int i = 0; i < stems.size(); i++)
687         {
688           Grob* stem = stems[i];
689           if (Stem::invisible_b (stem))
690             continue;
691
692           Interval hps = hps_array[j++];
693
694
695           Direction d =  (hps.center () < max_gap.center()) ?
696             UP : DOWN ;
697           
698           stem->set_grob_property ("direction", scm_int2num (d));
699           
700           hps.intersect (max_gap);
701           assert (hps.empty_b () || hps.length () < 1e-6 );
702         }
703     }
704 }
705
706
707
708 /* Set stem's shorten property if unset.
709
710  TODO:
711    take some y-position (chord/beam/nearest?) into account
712    scmify forced-fraction
713  
714   This is done in beam because the shorten has to be uniform over the
715   entire beam.
716
717 */
718 void
719 Beam::set_stem_shorten (Grob *me)
720 {
721   /*
722     shortening looks silly for x staff beams
723    */
724   if (knee_b(me))
725     return ;
726   
727   Real forced_fraction = 1.0 * forced_stem_count (me)
728     / visible_stem_count (me);
729
730   int beam_count = get_beam_count (me);
731
732   SCM shorten_list = me->get_grob_property ("beamed-stem-shorten");
733   if (shorten_list == SCM_EOL)
734     return;
735
736   Real staff_space = Staff_symbol_referencer::staff_space (me);
737   
738   SCM shorten_elt =
739     robust_list_ref (beam_count -1, shorten_list);
740   Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
741
742   /* your similar cute comment here */
743   shorten_f *= forced_fraction;
744
745   if (shorten_f)
746     me->set_grob_property ("shorten", gh_double2scm (shorten_f));
747 }
748
749 /*  Call list of y-dy-callbacks, that handle setting of
750     grob-properties
751
752 */
753 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
754 SCM
755 Beam::after_line_breaking (SCM smob)
756 {
757   Grob *me = unsmob_grob (smob);
758   
759   /* Copy to mutable list. */
760   SCM s = ly_deep_copy (me->get_grob_property ("positions"));
761   me->set_grob_property ("positions", s);
762
763   if (ly_car (s) == SCM_BOOL_F)
764     {
765
766       // one wonders if such genericity is necessary  --hwn.
767       SCM callbacks = me->get_grob_property ("position-callbacks");
768       for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
769         gh_call1 (ly_car (i), smob);
770     }
771
772   set_stem_lengths (me);  
773   return SCM_UNSPECIFIED;
774 }
775
776 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
777 SCM
778 Beam::least_squares (SCM smob)
779 {
780   Grob *me = unsmob_grob (smob);
781
782   int count = visible_stem_count (me);
783   Interval pos (0, 0);
784   
785   if (count <= 1)
786     {
787       me->set_grob_property ("positions", ly_interval2scm (pos));
788       return SCM_UNSPECIFIED;
789     }
790
791
792   Array<Real> x_posns ;
793   Link_array<Grob> stems=
794     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
795   Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
796   Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);  
797
798   Real my_y = me->relative_coordinate (commony, Y_AXIS);
799   
800   Grob *fvs  = first_visible_stem (me);
801   Grob *lvs  = last_visible_stem (me);
802   
803   Interval ideal (Stem::get_stem_info (fvs).ideal_y_
804                   + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
805                   Stem::get_stem_info (lvs).ideal_y_
806                   + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
807   
808   Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
809   for (int i=0; i < stems.size (); i++)
810     {
811       Grob* s = stems[i];
812
813       Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
814       x_posns.push (x);
815     }
816   Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
817
818   Real y =0;  
819   Real dydx = 0;
820   Real dy = 0;
821   
822   if (!ideal.delta ())
823     {
824       Interval chord (Stem::chord_start_y (first_visible_stem (me)),
825                       Stem::chord_start_y (last_visible_stem (me)));
826
827
828       /*
829         TODO -- use scoring for this.
830
831         complicated, because we take stem-info.ideal for determining
832         beam slopes.
833        */
834       /* Make simple beam on middle line have small tilt */
835       if (!ideal[LEFT] && chord.delta () && count == 2)
836         {
837
838           /*
839             FIXME. -> UP
840           */
841           Direction d = (Direction) (sign (chord.delta ()) * UP);
842           pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
843           //                * dir;
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 /*
1158   Calculate the Y position of the stem-end, given the Y-left, Y-right
1159   in POS for stem S. This Y position is relative to S.
1160  */
1161 Real
1162 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1163                    Real xl, Real xr,
1164                    Interval pos, bool french) 
1165 {
1166   Real beam_translation = get_beam_translation (me);
1167
1168     
1169   Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1170   Real dy = pos.delta ();
1171   Real dx = xr - xl;
1172   Real stem_y_beam0 = (dy && dx
1173                        ? r / dx
1174                        * dy
1175                        : 0) + pos[LEFT];
1176   
1177   Direction my_dir = Directional_element_interface::get (s);
1178   SCM beaming = s->get_grob_property ("beaming");
1179  
1180   Real stem_y = stem_y_beam0;
1181   if (french)
1182     {
1183       Slice bm = where_are_the_whole_beams (beaming);
1184       if (!bm.empty_b())
1185         stem_y += beam_translation * bm[-my_dir];
1186     }
1187   else
1188     {
1189       Slice bm = Stem::beam_multiplicity(s);
1190       if (!bm.empty_b())
1191         stem_y +=bm[my_dir] * beam_translation;
1192     }
1193   
1194   Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1195     - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1196   
1197   return stem_y + id;
1198 }
1199
1200 /*
1201   Hmm.  At this time, beam position and slope are determined.  Maybe,
1202   stem directions and length should set to relative to the chord's
1203   position of the beam.  */
1204 void
1205 Beam::set_stem_lengths (Grob *me)
1206 {
1207   Link_array<Grob> stems=
1208     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1209
1210   if (stems.size () <= 1)
1211     return;
1212   
1213   Grob *common[2];
1214   for (int a = 2; a--;)
1215     common[a] = common_refpoint_of_array (stems, me, Axis(a));
1216   
1217   Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1218   Real staff_space = Staff_symbol_referencer::staff_space (me);
1219
1220   bool french = to_boolean (me->get_grob_property ("french-beaming"));
1221
1222   
1223   bool gap = false;
1224   Real thick =0.0;
1225   if (gh_number_p (me->get_grob_property ("gap"))
1226       &&gh_scm2double (me->get_grob_property ("gap")))
1227   {
1228     gap = true;
1229     thick = gh_scm2double (me->get_grob_property ("thickness"))
1230       * Staff_symbol_referencer::staff_space(me);
1231   }
1232       
1233   // ugh -> use commonx
1234   Grob * fvs = first_visible_stem (me);
1235   Grob *lvs = last_visible_stem (me);
1236     
1237   Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1238   Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1239   
1240   for (int i=0; i < stems.size (); i++)
1241     {
1242       Grob* s = stems[i];
1243       if (Stem::invisible_b (s))
1244         continue;
1245
1246       Real stem_y = calc_stem_y (me, s, common,
1247                                  xl, xr,
1248                                  pos, french && i > 0&& (i < stems.size  () -1));
1249
1250       /*
1251         Make the stems go up to the end of the beam. This doesn't matter
1252         for normal beams, but for tremolo beams it looks silly otherwise.
1253        */
1254       if (gap)
1255         stem_y += thick * 0.5 * Directional_element_interface::get(s);
1256       
1257       Stem::set_stemend (s, 2* stem_y / staff_space);
1258     }
1259 }
1260
1261 void
1262 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1263 {
1264   Link_array<Grob> stems=
1265     Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1266   
1267   Direction d = LEFT;
1268   for (int i=0; i  < stems.size (); i++)
1269     {
1270       /*
1271         Don't overwrite user settings.
1272        */
1273       
1274       do
1275         {
1276           /* Don't set beaming for outside of outer stems */      
1277           if ((d == LEFT && i == 0)
1278               ||(d == RIGHT && i == stems.size () -1))
1279             continue;
1280
1281
1282           SCM beaming_prop = stems[i]->get_grob_property ("beaming");
1283           if (beaming_prop == SCM_EOL ||
1284               index_get_cell (beaming_prop, d) == SCM_EOL)
1285             {
1286               int b = beaming->infos_.elem (i).beams_i_drul_[d];
1287               Stem::set_beaming (stems[i], b, d);
1288             }
1289         }
1290       while (flip (&d) != LEFT);
1291     }
1292 }
1293
1294 int
1295 Beam::forced_stem_count (Grob *me) 
1296 {
1297   Link_array<Grob>stems = 
1298     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1299   int f = 0;
1300   for (int i=0; i < stems.size (); i++)
1301     {
1302       Grob *s = stems[i];
1303
1304       if (Stem::invisible_b (s))
1305         continue;
1306
1307       /* I can imagine counting those boundaries as a half forced stem,
1308          but let's count them full for now. */
1309       if (abs (Stem::chord_start_y (s)) > 0.1
1310         && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1311         f++;
1312     }
1313   return f;
1314 }
1315
1316
1317
1318
1319 int
1320 Beam::visible_stem_count (Grob *me) 
1321 {
1322   Link_array<Grob>stems = 
1323     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1324   int c = 0;
1325   for (int i = stems.size (); i--;)
1326     {
1327       if (!Stem::invisible_b (stems[i]))
1328         c++;
1329     }
1330   return c;
1331 }
1332
1333 Grob*
1334 Beam::first_visible_stem (Grob *me) 
1335 {
1336   Link_array<Grob>stems = 
1337     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1338   
1339   for (int i = 0; i < stems.size (); i++)
1340     {
1341       if (!Stem::invisible_b (stems[i]))
1342         return stems[i];
1343     }
1344   return 0;
1345 }
1346
1347 Grob*
1348 Beam::last_visible_stem (Grob *me) 
1349 {
1350   Link_array<Grob>stems = 
1351     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1352   for (int i = stems.size (); i--;)
1353     {
1354       if (!Stem::invisible_b (stems[i]))
1355         return stems[i];
1356     }
1357   return 0;
1358 }
1359
1360
1361 /*
1362   [TODO]
1363   
1364   handle rest under beam (do_post: beams are calculated now)
1365   what about combination of collisions and rest under beam.
1366
1367   Should lookup
1368     
1369     rest -> stem -> beam -> interpolate_y_position ()
1370 */
1371 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1372 SCM
1373 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1374 {
1375   Grob *rest = unsmob_grob (element_smob);
1376   Axis a = (Axis) gh_scm2int (axis);
1377   
1378   assert (a == Y_AXIS);
1379
1380   Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1381   Grob *stem = st;
1382   if (!stem)
1383     return gh_double2scm (0.0);
1384   Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1385   if (!beam
1386       || !Beam::has_interface (beam)
1387       || !Beam::visible_stem_count (beam))
1388     return gh_double2scm (0.0);
1389
1390   // make callback for rest from this.
1391   // todo: make sure this calced already.
1392
1393   //  Interval pos = ly_scm2interval (beam->get_grob_property ("positions"));
1394   Interval pos (0, 0);
1395   SCM s = beam->get_grob_property ("positions");
1396   if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1397     pos = ly_scm2interval (s);
1398
1399   Real dy = pos.delta ();
1400   // ugh -> use commonx
1401   Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1402   Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1403   Real dydx = dy && dx ? dy/dx : 0;
1404   
1405   Direction d = Stem::get_direction (stem);
1406   Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + pos[LEFT];
1407
1408   Real staff_space = Staff_symbol_referencer::staff_space (rest);
1409
1410   
1411   Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space; // refp??
1412
1413   Real minimum_dist
1414     = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1415   Real dist =
1416     minimum_dist +  -d  * (beamy - rest_dim) >? 0;
1417
1418   int stafflines = Staff_symbol_referencer::line_count (rest);
1419
1420   // move discretely by half spaces.
1421   int discrete_dist = int (ceil (dist));
1422
1423   // move by whole spaces inside the staff.
1424   if (discrete_dist < stafflines+1)
1425     discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1426
1427   return gh_double2scm (-d *  discrete_dist);
1428 }
1429
1430 bool
1431 Beam::knee_b (Grob* me)
1432 {
1433   SCM k = me->get_grob_property ("knee");
1434   if (gh_boolean_p (k))
1435     return gh_scm2bool (k);
1436
1437   bool knee = false;
1438   int d = 0;
1439   for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
1440     {
1441       Direction dir = Directional_element_interface::get
1442         (unsmob_grob (ly_car (s)));
1443       if (d && d != dir)
1444         {
1445           knee = true;
1446           break;
1447         }
1448       d = dir;
1449     }
1450   
1451   me->set_grob_property ("knee", gh_bool2scm (knee));
1452
1453   return knee;
1454 }
1455
1456 int
1457 Beam::get_direction_beam_count (Grob *me, Direction d )
1458 {
1459   Link_array<Grob>stems = 
1460     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1461   int bc = 0;
1462   
1463   for (int i = stems.size (); i--;)
1464     {
1465       /*
1466         Should we take invisible stems into account?
1467        */
1468       if (Stem::get_direction (stems[i]) == d)
1469         bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1470     }
1471
1472   return bc;
1473 }
1474
1475
1476 ADD_INTERFACE (Beam, "beam-interface",
1477   "A beam.
1478
1479 #'thickness= weight of beams, in staffspace
1480
1481
1482 We take the least squares line through the ideal-length stems, and
1483 then damp that using
1484
1485         damped = tanh (slope)
1486
1487 this gives an unquantized left and right position for the beam end.
1488 Then we take all combinations of quantings near these left and right
1489 positions, and give them a score (according to how close they are to
1490 the ideal slope, how close the result is to the ideal stems, etc.). We
1491 take the best scoring combination.
1492
1493 ",
1494   "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");
1495
1496