]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
* lily/beam.cc (shift_region_to_valid): fix stupido bug.
[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       Real left_y = Stem::calc_stem_info (s).shortest_y_
871         - dydx * x_posns [i];
872
873       Interval flp ;
874       flp.set_full ();
875       flp[-d] = left_y;
876
877       feasible_left_point.intersect (flp);
878     }
879       
880   if (feasible_left_point.empty_b())
881     {
882       warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
883     }
884   else if (!feasible_left_point.elem_b(y))
885     {
886       if (isinf (feasible_left_point[DOWN]))
887         y = feasible_left_point[UP] - REGION_SIZE;
888       else if (isinf (feasible_left_point[UP]))
889         y = feasible_left_point[DOWN]+ REGION_SIZE;
890       else
891         y = feasible_left_point.center ();
892     }
893   pos = Interval (y, (y+dy));
894   me->set_grob_property ("positions", ly_interval2scm (pos));
895   return SCM_UNSPECIFIED;
896 }
897
898
899 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
900 SCM
901 Beam::check_concave (SCM smob)
902 {
903   Grob *me = unsmob_grob (smob);
904
905   Link_array<Item> stems = 
906     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
907
908   for (int i = 0; i < stems.size ();)
909     {
910       if (Stem::invisible_b (stems[i]))
911         stems.del (i);
912       else
913         i++;
914     }
915   
916   if (stems.size () < 3)
917     return SCM_UNSPECIFIED;
918
919
920   /* Concaveness #1: If distance of an inner notehead to line between
921      two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
922      beam is concave (Heinz Stolba).
923
924      In the case of knees, the line connecting outer heads is often
925      not related to the beam slope (it may even go in the other
926      direction). Skip the check when the outer stems point in
927      different directions. --hwn
928      
929   */
930   bool concaveness1 = false;
931   SCM gap = me->get_grob_property ("concaveness-gap");
932   if (gh_number_p (gap)
933       && Stem::get_direction(stems.top ())
934          == Stem::get_direction(stems[0]))
935     {
936       Real r1 = gh_scm2double (gap);
937       Real dy = Stem::chord_start_y (stems.top ())
938         - Stem::chord_start_y (stems[0]);
939
940       
941       Real slope = dy / (stems.size () - 1);
942       
943       Real y0 = Stem::chord_start_y (stems[0]);
944       for (int i = 1; i < stems.size () - 1; i++)
945         {
946           Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
947           if (c > r1)
948             {
949               concaveness1 = true;
950               break;
951             }
952         }
953     }
954
955     
956   /* Concaveness #2: Sum distances of inner noteheads that fall
957      outside the interval of the two outer noteheads.
958
959      We only do this for beams where first and last stem have the same
960      direction. --hwn.
961
962
963      Note that "convex" stems compensate for "concave" stems.
964      (is that intentional?) --hwn.
965   */
966   
967   Real concaveness2 = 0;
968   SCM thresh = me->get_grob_property ("concaveness-threshold");
969   Real r2 = infinity_f;
970   if (!concaveness1 && gh_number_p (thresh)
971       && Stem::get_direction(stems.top ())
972          == Stem::get_direction(stems[0]))
973     {
974       r2 = gh_scm2double (thresh);
975
976       Direction dir = Stem::get_direction(stems.top ());
977       Real concave = 0;
978       Interval iv (Stem::chord_start_y (stems[0]),
979                    Stem::chord_start_y (stems.top ()));
980       
981       if (iv[MAX] < iv[MIN])
982         iv.swap ();
983       
984       for (int i = 1; i < stems.size () - 1; i++)
985         {
986           Real f = Stem::chord_start_y (stems[i]);
987           concave += ((f - iv[MAX] ) >? 0) +
988             ((f - iv[MIN] ) <? 0);
989         }
990       concave *= dir;
991       concaveness2 = concave / (stems.size () - 2);
992       
993       /* ugh: this is the a kludge to get
994          input/regression/beam-concave.ly to behave as
995          baerenreiter. */
996
997       /*
998         huh? we're dividing twice (which is not scalable) meaning that
999         the longer the beam, the more unlikely it will be
1000         concave. Maybe you would even expect the other way around??
1001
1002         --hwn.
1003         
1004        */
1005       concaveness2 /= (stems.size () - 2);
1006     }
1007   
1008   /* TODO: some sort of damping iso -> plain horizontal */
1009   if (concaveness1 || concaveness2 > r2)
1010     {
1011       Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1012       Real r = pos.linear_combination (0);
1013       me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
1014       me->set_grob_property ("least-squares-dy", gh_double2scm (0));
1015     }
1016
1017   return SCM_UNSPECIFIED;
1018 }
1019
1020 /* This neat trick is by Werner Lemberg,
1021    damped = tanh (slope)
1022    corresponds with some tables in [Wanske] CHECKME */
1023 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1024 SCM
1025 Beam::slope_damping (SCM smob)
1026 {
1027   Grob *me = unsmob_grob (smob);
1028
1029   if (visible_stem_count (me) <= 1)
1030     return SCM_UNSPECIFIED;
1031
1032   SCM s = me->get_grob_property ("damping"); 
1033   int damping = gh_scm2int (s);
1034
1035   if (damping)
1036     {
1037       Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1038       Real dy = pos.delta ();
1039       
1040       // ugh -> use commonx
1041       Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS)
1042         - first_visible_stem (me)->relative_coordinate (0, X_AXIS);
1043       Real dydx = dy && dx ? dy/dx : 0;
1044       dydx = 0.6 * tanh (dydx) / damping;
1045
1046       Real damped_dy = dydx * dx;
1047       pos[LEFT] += (dy - damped_dy) / 2;
1048       pos[RIGHT] -= (dy - damped_dy) / 2;
1049       
1050       me->set_grob_property ("positions", ly_interval2scm (pos));
1051     }
1052   return SCM_UNSPECIFIED;
1053 }
1054
1055 /*
1056   Calculate the Y position of the stem-end, given the Y-left, Y-right
1057   in POS, and for stem S.
1058
1059   If CORRECT, correct for multiplicity of beam in case of knees.
1060
1061
1062   TODO: junk CORRECT from this.
1063  */
1064 Real
1065 Beam::calc_stem_y (Grob *me, Grob* s, Interval pos, bool correct)
1066 {
1067   int beam_multiplicity = get_multiplicity (me);
1068   int stem_multiplicity = (Stem::duration_log (s) - 2) >? 0;
1069   
1070
1071   Real thick = gh_scm2double (me->get_grob_property ("thickness"));
1072   Real interbeam = get_interbeam (me);
1073
1074   // ugh -> use commonx
1075   Grob * fvs = first_visible_stem (me);
1076   Grob *lvs = last_visible_stem (me);
1077     
1078   Real x0 = fvs ? fvs->relative_coordinate (0, X_AXIS) : 0.0;
1079   Real dx = fvs ? lvs->relative_coordinate (0, X_AXIS) - x0 : 0.0;
1080   Real r = s->relative_coordinate (0, X_AXIS) - x0;
1081   Real dy = pos.delta ();
1082   Real stem_y = (dy && dx
1083                  ? r / dx
1084                  * dy
1085                  : 0) + pos[LEFT];
1086
1087   Direction my_dir = Directional_element_interface::get (s);
1088   Direction first_dir = fvs? Directional_element_interface::get (fvs) : my_dir;
1089
1090   if (correct && my_dir != first_dir)
1091     {
1092       /*
1093         WTF is happening here ?
1094
1095          It looks as if this is some kind of fixup for multiple kneed
1096          beams to get a piece of stem at the #.
1097          
1098
1099                 x
1100                 |
1101          =======|
1102          |======#
1103          |
1104          |
1105         x 
1106
1107         Rules for this kind of stuff are hairy. In any event, the
1108         current stem should look at the multiplicity of its
1109         predecessor.
1110
1111         --hwn.
1112         
1113        */
1114       
1115       // FIXME, hairy stuff
1116       stem_y += my_dir * (thick / 2 + (beam_multiplicity - 1) * interbeam);
1117
1118       // huh, why not for first visible?
1119
1120       /*
1121         What the heck is happening here?? 
1122        */
1123       Grob *last_visible = last_visible_stem (me);
1124       if (last_visible)
1125         {
1126           if ( Staff_symbol_referencer::staff_symbol_l (s)
1127                != Staff_symbol_referencer::staff_symbol_l (last_visible))
1128             stem_y += Directional_element_interface::get (me)
1129               * (beam_multiplicity - stem_multiplicity) * interbeam;
1130         }
1131       else
1132         programming_error ("No last visible stem");
1133     }
1134   return stem_y;
1135 }
1136
1137 /*
1138   Hmm.  At this time, beam position and slope are determined.  Maybe,
1139   stem directions and length should set to relative to the chord's
1140   position of the beam.  */
1141 void
1142 Beam::set_stem_lengths (Grob *me)
1143 {
1144   Link_array<Item> stems=
1145     Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
1146
1147   if (stems.size () <= 1)
1148     return;
1149   
1150   Grob *common = me->common_refpoint (stems[0], Y_AXIS);
1151   for (int i=1; i < stems.size (); i++)
1152     if (!Stem::invisible_b (stems[i]))
1153       common = common->common_refpoint (stems[i], Y_AXIS);
1154
1155   Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1156   Real staff_space = Staff_symbol_referencer::staff_space (me);
1157
1158   /*
1159     DOCUMENT THIS.
1160    */
1161 #if 0
1162   Real thick = gh_scm2double (me->get_grob_property ("thickness"));
1163   Direction dir = Directional_element_interface::get (me);
1164   bool ps_testing = to_boolean (ly_symbol2scm ("ps-testing"));
1165 #endif
1166   
1167   for (int i=0; i < stems.size (); i++)
1168     {
1169       Item* s = stems[i];
1170       if (Stem::invisible_b (s))
1171         continue;
1172
1173       Real stem_y = calc_stem_y (me, s, pos, true);
1174
1175 #if 0
1176       // doesn't play well with dvips
1177       if (ps_testing)
1178         if (Stem::get_direction (s) == dir)
1179           stem_y += Stem::get_direction (s) * thick / 2;
1180 #endif
1181       
1182       /* caution: stem measures in staff-positions */
1183       Real id = me->relative_coordinate (common, Y_AXIS)
1184         - stems[i]->relative_coordinate (common, Y_AXIS);
1185       Stem::set_stemend (s, (stem_y + id) / staff_space * 2);
1186     }
1187 }
1188
1189 void
1190 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1191 {
1192   Link_array<Grob> stems=
1193     Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1194   
1195   Direction d = LEFT;
1196   for (int i=0; i  < stems.size (); i++)
1197     {
1198       do
1199         {
1200           /* Don't overwrite user override (?) */
1201           if (Stem::beam_count (stems[i], d) == -1
1202               /* Don't set beaming for outside of outer stems */
1203               && ! (d == LEFT && i == 0)
1204               && ! (d == RIGHT && i == stems.size () -1))
1205             {
1206               int b = beaming->infos_.elem (i).beams_i_drul_[d];
1207               Stem::set_beaming (stems[i], b, d);
1208             }
1209         }
1210       while (flip (&d) != LEFT);
1211     }
1212 }
1213
1214
1215
1216 /*
1217   beams to go with one stem.
1218
1219   FIXME: clean me up:
1220
1221   The beam should be constructed by one function that knows where the
1222   X and Y points are, and only inspects the stems to obtain
1223   multiplicity and stem directions.
1224   
1225   */
1226 Molecule
1227 Beam::stem_beams (Grob *me, Item *here, Item *next, Item *prev, Real dydx)
1228 {
1229   // ugh -> use commonx
1230   if ((next
1231        && ! (next->relative_coordinate (0, X_AXIS)
1232             > here->relative_coordinate (0, X_AXIS)))
1233       || (prev
1234           && ! (prev->relative_coordinate (0, X_AXIS)
1235                < here->relative_coordinate (0, X_AXIS))))
1236     programming_error ("Beams are not left-to-right");
1237
1238   Real thick = gh_scm2double (me->get_grob_property ("thickness"));
1239   Real bdy = get_interbeam (me);
1240   
1241   Molecule leftbeams;
1242   Molecule rightbeams;
1243
1244   Real nw_f;
1245   if (!Stem::first_head (here))
1246     nw_f = 0;
1247   else
1248     {
1249       int t = Stem::duration_log (here); 
1250
1251       SCM proc = me->get_grob_property ("flag-width-function");
1252       SCM result = gh_call1 (proc, gh_int2scm (t));
1253       nw_f = gh_scm2double (result);
1254     }
1255
1256
1257   /* [Tremolo] beams on whole notes may not have direction set? */
1258   Direction dir = Directional_element_interface::get (here);
1259
1260   /* half beams extending to the left. */
1261   if (prev)
1262     {
1263       int lhalfs= lhalfs = Stem::beam_count (here, LEFT)
1264         - Stem::beam_count (prev, RIGHT);
1265       int lwholebeams= Stem::beam_count (here, LEFT)
1266         <? Stem::beam_count (prev, RIGHT);
1267       
1268       /* Half beam should be one note-width,
1269          but let's make sure two half-beams never touch */
1270
1271       // FIXME: TODO (check) stem width / sloped beams
1272       Real w = here->relative_coordinate (0, X_AXIS)
1273         - prev->relative_coordinate (0, X_AXIS);
1274       Real stem_w = gh_scm2double (prev->get_grob_property ("thickness"))
1275         // URG
1276         * me->paper_l ()->get_var ("linethickness");
1277
1278       w = w/2 <? nw_f;
1279       Molecule a;
1280       if (lhalfs)               // generates warnings if not
1281         a =  Lookup::beam (dydx, w + stem_w, thick);
1282       a.translate (Offset (-w, -w * dydx));
1283       a.translate_axis (-stem_w/2, X_AXIS);
1284       for (int j = 0; j  < lhalfs; j++)
1285         {
1286           Molecule b (a);
1287           b.translate_axis (-dir * bdy * (lwholebeams+j), Y_AXIS);
1288           leftbeams.add_molecule (b);
1289         }
1290     }
1291
1292   if (next)
1293     {
1294       int rhalfs  = Stem::beam_count (here, RIGHT)
1295         - Stem::beam_count (next, LEFT);
1296       int rwholebeams= Stem::beam_count (here, RIGHT)
1297         <? Stem::beam_count (next, LEFT);
1298
1299       Real w = next->relative_coordinate (0, X_AXIS)
1300         - here->relative_coordinate (0, X_AXIS);
1301
1302       Real stem_w = gh_scm2double (next->get_grob_property ("thickness"))
1303         // URG
1304         * me->paper_l ()->get_var ("linethickness");
1305
1306       Molecule a = Lookup::beam (dydx, w + stem_w, thick);
1307       a.translate_axis (- stem_w/2, X_AXIS);
1308       int j = 0;
1309       Real gap_f = 0;
1310       
1311       SCM gap = me->get_grob_property ("gap");
1312       if (gh_number_p (gap))
1313         {
1314           int gap_i = gh_scm2int ((gap));
1315           int nogap = rwholebeams - gap_i;
1316           
1317           for (; j  < nogap; j++)
1318             {
1319               Molecule b (a);
1320               b.translate_axis (-dir  * bdy * j, Y_AXIS);
1321               rightbeams.add_molecule (b);
1322             }
1323           if (Stem::invisible_b (here))
1324             gap_f = nw_f;
1325           else
1326             gap_f = nw_f / 2;
1327           w -= 2 * gap_f;
1328           a = Lookup::beam (dydx, w + stem_w, thick);
1329         }
1330
1331       for (; j  < rwholebeams; j++)
1332         {
1333           Molecule b (a);
1334           Real tx = 0;
1335           if (Stem::invisible_b (here))
1336             // ugh, see chord-tremolo.ly
1337             tx = (-dir + 1) / 2 * nw_f * 1.5 + gap_f/4;
1338           else
1339             tx = gap_f;
1340           b.translate (Offset (tx, -dir * bdy * j));
1341           rightbeams.add_molecule (b);
1342         }
1343
1344       w = w/2 <? nw_f;
1345       if (rhalfs)
1346         a = Lookup::beam (dydx, w, thick);
1347
1348       for (; j  < rwholebeams + rhalfs; j++)
1349         {
1350           Molecule b (a);
1351           b.translate_axis (- dir * bdy * j, Y_AXIS);
1352           rightbeams.add_molecule (b);
1353         }
1354
1355     }
1356   leftbeams.add_molecule (rightbeams);
1357
1358   return leftbeams;
1359 }
1360
1361
1362 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
1363 SCM
1364 Beam::brew_molecule (SCM smob)
1365 {
1366   Grob *me =unsmob_grob (smob);
1367
1368   Molecule mol;
1369   if (!gh_pair_p (me->get_grob_property ("stems")))
1370     return SCM_EOL;
1371   Real x0, dx;
1372   Link_array<Item>stems = 
1373     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");  
1374   if (visible_stem_count (me))
1375     {
1376       // ugh -> use commonx
1377       x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
1378       dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
1379     }
1380   else
1381     {
1382       x0 = stems[0]->relative_coordinate (0, X_AXIS);
1383       dx = stems.top ()->relative_coordinate (0, X_AXIS) - x0;
1384     }
1385
1386   SCM posns = me->get_grob_property ("positions");
1387   Interval pos;
1388   if (!ly_number_pair_p (posns))
1389     {
1390       programming_error ("No beam posns");
1391       pos = Interval (0,0);
1392     }
1393   else
1394     pos= ly_scm2interval (posns);
1395   Real dy = pos.delta ();
1396   Real dydx = dy && dx ? dy/dx : 0;
1397
1398
1399   for (int i=0; i < stems.size (); i++)
1400     {
1401       Item *item = stems[i];
1402       Item *prev = (i > 0)? stems[i-1] : 0;
1403       Item *next = (i < stems.size ()-1) ? stems[i+1] :0;
1404
1405
1406       
1407       Molecule sb = stem_beams (me, item, next, prev, dydx);
1408       Real x = item->relative_coordinate (0, X_AXIS) - x0;
1409       sb.translate (Offset (x, x * dydx + pos[LEFT]));
1410
1411
1412       mol.add_molecule (sb);
1413     }
1414   
1415   mol.translate_axis (x0 
1416                       - dynamic_cast<Spanner*> (me)
1417                       ->get_bound (LEFT)->relative_coordinate (0, X_AXIS),
1418                       X_AXIS);
1419
1420 #if (DEBUG_QUANTING)
1421     {
1422       /*
1423         This code prints the demerits for each beam. Perhaps this
1424         should be switchable for those who want to twiddle with the
1425         parameters.
1426       */
1427       String str;
1428       if (1)
1429         {
1430           str += to_str (gh_scm2int (me->get_grob_property ("best-idx")));
1431           str += ":";
1432         }
1433       str += to_str (gh_scm2double (me->get_grob_property ("quant-score")),
1434                      "%.2f");
1435
1436       SCM properties = Font_interface::font_alist_chain (me);
1437
1438       
1439       Molecule tm = Text_item::text2molecule (me, ly_str02scm (str.ch_C ()), properties);
1440       mol.add_at_edge (Y_AXIS, UP, tm, 5.0);
1441     }
1442 #endif
1443     
1444   return mol.smobbed_copy ();
1445 }
1446
1447 int
1448 Beam::forced_stem_count (Grob *me) 
1449 {
1450   Link_array<Item>stems = 
1451     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1452   int f = 0;
1453   for (int i=0; i < stems.size (); i++)
1454     {
1455       Item *s = stems[i];
1456
1457       if (Stem::invisible_b (s))
1458         continue;
1459
1460       if (((int)Stem::chord_start_y (s)) 
1461         && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1462         f++;
1463     }
1464   return f;
1465 }
1466
1467
1468
1469
1470 int
1471 Beam::visible_stem_count (Grob *me) 
1472 {
1473   Link_array<Item>stems = 
1474     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1475   int c = 0;
1476   for (int i = stems.size (); i--;)
1477     {
1478       if (!Stem::invisible_b (stems[i]))
1479         c++;
1480     }
1481   return c;
1482 }
1483
1484 Item*
1485 Beam::first_visible_stem (Grob *me) 
1486 {
1487   Link_array<Item>stems = 
1488     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1489   
1490   for (int i = 0; i < stems.size (); i++)
1491     {
1492       if (!Stem::invisible_b (stems[i]))
1493         return stems[i];
1494     }
1495   return 0;
1496 }
1497
1498 Item*
1499 Beam::last_visible_stem (Grob *me) 
1500 {
1501   Link_array<Item>stems = 
1502     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1503   for (int i = stems.size (); i--;)
1504     {
1505       if (!Stem::invisible_b (stems[i]))
1506         return stems[i];
1507     }
1508   return 0;
1509 }
1510
1511
1512 /*
1513   [TODO]
1514   
1515   handle rest under beam (do_post: beams are calculated now)
1516   what about combination of collisions and rest under beam.
1517
1518   Should lookup
1519     
1520     rest -> stem -> beam -> interpolate_y_position ()
1521 */
1522 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1523 SCM
1524 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1525 {
1526   Grob *rest = unsmob_grob (element_smob);
1527   Axis a = (Axis) gh_scm2int (axis);
1528   
1529   assert (a == Y_AXIS);
1530
1531   Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1532   Grob *stem = st;
1533   if (!stem)
1534     return gh_double2scm (0.0);
1535   Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1536   if (!beam
1537       || !Beam::has_interface (beam)
1538       || !Beam::visible_stem_count (beam))
1539     return gh_double2scm (0.0);
1540
1541   // make callback for rest from this.
1542   // todo: make sure this calced already.
1543
1544   //  Interval pos = ly_scm2interval (beam->get_grob_property ("positions"));
1545   Interval pos (0, 0);
1546   SCM s = beam->get_grob_property ("positions");
1547   if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1548     pos = ly_scm2interval (s);
1549
1550   Real dy = pos.delta ();
1551   // ugh -> use commonx
1552   Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1553   Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1554   Real dydx = dy && dx ? dy/dx : 0;
1555   
1556   Direction d = Stem::get_direction (stem);
1557   Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + pos[LEFT];
1558
1559   Real staff_space = Staff_symbol_referencer::staff_space (rest);
1560
1561   
1562   Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space; // refp??
1563
1564   Real minimum_dist
1565     = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1566   Real dist =
1567     minimum_dist +  -d  * (beamy - rest_dim) >? 0;
1568
1569   int stafflines = Staff_symbol_referencer::line_count (rest);
1570
1571   // move discretely by half spaces.
1572   int discrete_dist = int (ceil (dist));
1573
1574   // move by whole spaces inside the staff.
1575   if (discrete_dist < stafflines+1)
1576     discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1577
1578   return gh_double2scm (-d *  discrete_dist);
1579 }
1580
1581
1582
1583
1584 ADD_INTERFACE (Beam, "beam-interface",
1585   "A beam.
1586
1587 #'thickness= weight of beams, in staffspace
1588
1589
1590 We take the least squares line through the ideal-length stems, and
1591 then damp that using
1592
1593         damped = tanh (slope)
1594
1595 this gives an unquantized left and right position for the beam end.
1596 Then we take all combinations of quantings near these left and right
1597 positions, and give them a score (according to how close they are to
1598 the ideal slope, how close the result is to the ideal stems, etc.). We
1599 take the best scoring combination.
1600
1601 ",
1602   "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");
1603
1604