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