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