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