]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
trem fix
[lilypond.git] / lily / beam.cc
1 /*
2   beam.cc -- implement Beam
3   
4   source file of the GNU LilyPond music typesetter
5   
6   (c)  1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7   Jan Nieuwenhuizen <janneke@gnu.org>
8   
9 */
10
11 /*
12   [TODO]
13
14   * Fix TODO
15   
16   * Junk stem_info.
17   
18   * Remove #'direction from beam.  A beam has no direction per se.
19     It may only set directions for stems.
20
21   * Rewrite stem_beams.
22
23   * Use Number_pair i.s.o Interval to represent (yl, yr).
24
25   
26   */
27
28
29 #include <math.h> // tanh.
30
31 #include "molecule.hh" 
32 #include "directional-element-interface.hh"
33 #include "beaming.hh"
34 #include "beam.hh"
35 #include "misc.hh"
36 #include "least-squares.hh"
37 #include "stem.hh"
38 #include "paper-def.hh"
39 #include "lookup.hh"
40 #include "group-interface.hh"
41 #include "staff-symbol-referencer.hh"
42 #include "item.hh"
43 #include "spanner.hh"
44 #include "warn.hh"
45
46
47 #define DEBUG_QUANTING 0
48
49
50 #if DEBUG_QUANTING
51 #include "text-item.hh"  // debug output.
52 #include "font-interface.hh"  // debug output.
53 #endif
54
55
56 const int INTER_QUANT_PENALTY = 1000; 
57 const int SECONDARY_BEAM_DEMERIT  = 15;
58 const int STEM_LENGTH_DEMERIT_FACTOR = 5;
59 // possibly ridiculous, but too short stems just won't do
60 const int STEM_LENGTH_LIMIT_PENALTY = 5000;
61 const int DAMPING_DIRECTIION_PENALTY = 800;
62 const int MUSICAL_DIRECTION_FACTOR = 400;
63 const int IDEAL_SLOPE_FACTOR = 10;
64 const int REGION_SIZE = 2;
65
66
67 static Real
68 shrink_extra_weight (Real x)
69 {
70   return fabs (x) * ((x < 0) ? 1.5 : 1.0);
71 }
72
73 void
74 Beam::add_stem (Grob *me, Grob *s)
75 {
76   Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
77   
78   s->add_dependency (me);
79
80   assert (!Stem::beam_l (s));
81   s->set_grob_property ("beam", me->self_scm ());
82
83   add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
84 }
85
86 Real
87 Beam::get_interbeam (Grob *me)
88 {
89   SCM func = me->get_grob_property ("space-function");
90   SCM s = gh_call2 (func, me->self_scm (), gh_int2scm (get_multiplicity (me)));
91   return gh_scm2double (s);
92 }
93
94 /*
95   Maximum multiplicity.
96  */
97 int
98 Beam::get_multiplicity (Grob *me) 
99 {
100   int m = 0;
101   for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
102     {
103       Grob *sc = unsmob_grob (ly_car (s));
104
105       if (Stem::has_interface (sc))
106         m = m >? Stem::beam_count (sc, LEFT) >? Stem::beam_count (sc, RIGHT);
107     }
108   return m;
109 }
110
111 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
112 SCM
113 Beam::space_function (SCM smob, SCM multiplicity)
114 {
115   Grob *me = unsmob_grob (smob);
116   
117   Real staff_space = Staff_symbol_referencer::staff_space (me);
118   Real line = me->paper_l ()->get_var ("linethickness");
119   Real thickness = gh_scm2double (me->get_grob_property ("thickness"))
120     * staff_space;
121   
122   Real interbeam = gh_scm2int (multiplicity) < 4
123     ? (2*staff_space + line - thickness) / 2.0
124     : (3*staff_space + line - thickness) / 3.0;
125   
126   return gh_double2scm (interbeam);
127 }
128
129
130 /* After pre-processing all directions should be set.
131    Several post-processing routines (stem, slur, script) need stem/beam
132    direction.
133    Currenly, this means that beam has set all stem's directions.
134    [Alternatively, stems could set its own directions, according to
135    their beam, during 'final-pre-processing'.] */
136 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
137 SCM
138 Beam::before_line_breaking (SCM smob)
139 {
140   Grob *me =  unsmob_grob (smob);
141
142   /* Beams with less than 2 two stems don't make much sense, but could happen
143      when you do
144      
145      [r8 c8 r8].
146      
147     For a beam that  only has one stem, we try to do some disappearance magic:
148     we revert the flag, and move on to The Eternal Engraving Fields. */
149
150   int count = visible_stem_count (me);
151   if (count < 2)
152     {
153       me->warning (_ ("beam has less than two visible stems"));
154
155       SCM stems = me->get_grob_property ("stems");
156       if (scm_ilength (stems) == 1)
157         {
158           me->warning (_ ("Beam has less than two stems. Removing beam."));
159
160           unsmob_grob (gh_car (stems))->remove_grob_property ("beam");
161           me->suicide ();
162
163           return SCM_UNSPECIFIED;
164         }
165       else if (scm_ilength (stems) == 0)
166         {
167           me->suicide ();
168           return SCM_UNSPECIFIED;         
169         }
170     }
171   if (count >= 1)
172     {
173       Direction d = get_default_dir (me);
174
175       consider_auto_knees (me, d);
176       set_stem_directions (me, d);
177       set_stem_shorten (me);
178     }
179
180   return SCM_EOL;
181 }
182
183 Direction
184 Beam::get_default_dir (Grob *me) 
185 {
186   Drul_array<int> total;
187   total[UP]  = total[DOWN] = 0;
188   Drul_array<int> count; 
189   count[UP]  = count[DOWN] = 0;
190   Direction d = DOWN;
191
192   Link_array<Item> stems=
193         Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
194
195   for (int i=0; i <stems.size (); i++)
196     do {
197       Grob *s = stems[i];
198       Direction sd = Directional_element_interface::get (s);
199
200       int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
201       int current = sd  ? (1 + d * sd)/2 : center_distance;
202
203       if (current)
204         {
205           total[d] += current;
206           count[d] ++;
207         }
208     } while (flip (&d) != DOWN);
209   
210   SCM func = me->get_grob_property ("dir-function");
211   SCM s = gh_call2 (func,
212                     gh_cons (gh_int2scm (count[UP]),
213                              gh_int2scm (count[DOWN])),
214                     gh_cons (gh_int2scm (total[UP]),
215                              gh_int2scm (total[DOWN])));
216
217   if (gh_number_p (s) && gh_scm2int (s))
218     return to_dir (s);
219   
220   /* If dir is not determined: get default */
221   return to_dir (me->get_grob_property ("neutral-direction"));
222 }
223
224
225 /* Set all stems with non-forced direction to beam direction.
226    Urg: non-forced should become `without/with unforced' direction,
227    once stem gets cleaned-up. */
228 void
229 Beam::set_stem_directions (Grob *me, Direction d)
230 {
231   Link_array<Item> stems
232     =Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
233   
234   for (int i=0; i <stems.size (); i++)
235     {
236       Grob *s = stems[i];
237       SCM force = s->remove_grob_property ("dir-forced");
238       if (!gh_boolean_p (force) || !gh_scm2bool (force))
239         Directional_element_interface::set (s, d);
240     }
241
242
243 /* Simplistic auto-knees; only consider vertical gap between two
244    adjacent chords.
245
246   `Forced' stem directions are ignored.  If you don't want auto-knees,
247   don't set, or unset auto-knee-gap. */
248 void
249 Beam::consider_auto_knees (Grob *me, Direction d)
250 {
251   SCM scm = me->get_grob_property ("auto-knee-gap");
252
253   if (gh_number_p (scm))
254     {
255       bool knee_b = false;
256       Real knee_y = 0;
257       Real staff_space = Staff_symbol_referencer::staff_space (me);
258       Real gap = gh_scm2double (scm) / staff_space;
259
260
261       Link_array<Item> stems=
262         Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
263       
264       Grob *common = me->common_refpoint (stems[0], Y_AXIS);
265       for (int i=1; i < stems.size (); i++)
266         if (!Stem::invisible_b (stems[i]))
267           common = common->common_refpoint (stems[i], Y_AXIS);
268
269       int l = 0;
270       for (int i=1; i < stems.size (); i++)
271         {
272           if (!Stem::invisible_b (stems[i-1]))
273             l = i - 1;
274           if (Stem::invisible_b (stems[l]))
275             continue;
276           if (Stem::invisible_b (stems[i]))
277             continue;
278           
279           Real left = Stem::extremal_heads (stems[l])[d]
280             ->relative_coordinate (common, Y_AXIS);
281           Real right = Stem::extremal_heads (stems[i])[-d]
282             ->relative_coordinate (common, Y_AXIS);
283
284           Real dy = right - left;
285
286           if (abs (dy) >= gap)
287             {
288               knee_y = (right + left) / 2;
289               knee_b = true;
290               break;
291             }
292         }
293       
294       if (knee_b)
295         {
296           for (int i=0; i < stems.size (); i++)
297             {
298               if (Stem::invisible_b (stems[i]))
299                 continue;
300               Item *s = stems[i];         
301               Real y = Stem::extremal_heads (stems[i])[d]
302                 ->relative_coordinate (common, Y_AXIS);
303
304               Directional_element_interface::set (s, y < knee_y ? UP : DOWN);
305               s->set_grob_property ("dir-forced", SCM_BOOL_T);
306             }
307         }
308     }
309 }
310
311 /* Set stem's shorten property if unset.
312
313  TODO:
314    take some y-position (chord/beam/nearest?) into account
315    scmify forced-fraction
316
317    TODO:
318    
319    why is shorten stored in beam, and not directly in stem?
320
321 */
322 void
323 Beam::set_stem_shorten (Grob *m)
324 {
325   Spanner*me = dynamic_cast<Spanner*> (m);
326
327   Real forced_fraction = forced_stem_count (me) / visible_stem_count (me);
328
329   int multiplicity = get_multiplicity (me);
330
331   SCM shorten = me->get_grob_property ("beamed-stem-shorten");
332   if (shorten == SCM_EOL)
333     return;
334
335   int sz = scm_ilength (shorten);
336   
337   Real staff_space = Staff_symbol_referencer::staff_space (me);
338   SCM shorten_elt = scm_list_ref (shorten,
339                                   gh_int2scm (multiplicity <? (sz - 1)));
340   Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
341
342   /* your similar cute comment here */
343   shorten_f *= forced_fraction;
344
345   if (shorten_f)
346     me->set_grob_property ("shorten", gh_double2scm (shorten_f));
347 }
348
349 /*  Call list of y-dy-callbacks, that handle setting of
350     grob-properties
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   Grob *fvs = first_visible_stem (me);
839
840   if (!fvs)
841     return SCM_UNSPECIFIED;
842     
843   Real x0 =fvs->relative_coordinate (common, X_AXIS);
844   for (int i=0; i < stems.size (); i++)
845     {
846       Item* s = stems[i];
847
848       Real x = s->relative_coordinate (common, X_AXIS) - x0;
849       x_posns.push (x);
850     }
851
852   Grob *lvs = last_visible_stem (me);
853   if (!lvs)
854     return SCM_UNSPECIFIED;
855   
856   Real dx = lvs->relative_coordinate (common, X_AXIS) - x0;
857
858   Interval pos = ly_scm2interval ( me->get_grob_property ("positions"));
859   Real dy = pos.delta();
860   Real y = pos[LEFT];
861   Real dydx =dy/dx;
862
863   
864   /*
865     Shift the positions so that we have a chance of finding good
866     quants (i.e. no short stem failures.)
867    */
868   Interval feasible_left_point;
869   feasible_left_point.set_full ();
870   for (int i=0; i < stems.size (); i++)
871     {
872       Item* s = stems[i];
873       if (Stem::invisible_b (s))
874         continue;
875
876
877       Direction d = Stem::get_direction (s);
878
879
880       Real left_y = Stem::calc_stem_info (s).shortest_y_
881         - dydx * x_posns [i];
882
883       Interval flp ;
884       flp.set_full ();
885       flp[-d] = left_y;
886
887       feasible_left_point.intersect (flp);
888     }
889       
890   if (feasible_left_point.empty_b())
891     {
892       warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
893     }
894   else if (!feasible_left_point.elem_b(y))
895     {
896       if (isinf (feasible_left_point[DOWN]))
897         y = feasible_left_point[UP] - REGION_SIZE;
898       else if (isinf (feasible_left_point[UP]))
899         y = feasible_left_point[DOWN]+ REGION_SIZE;
900       else
901         y = feasible_left_point.center ();
902     }
903   pos = Interval (y, (y+dy));
904   me->set_grob_property ("positions", ly_interval2scm (pos));
905   return SCM_UNSPECIFIED;
906 }
907
908
909 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
910 SCM
911 Beam::check_concave (SCM smob)
912 {
913   Grob *me = unsmob_grob (smob);
914
915   Link_array<Item> stems = 
916     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
917
918   for (int i = 0; i < stems.size ();)
919     {
920       if (Stem::invisible_b (stems[i]))
921         stems.del (i);
922       else
923         i++;
924     }
925   
926   if (stems.size () < 3)
927     return SCM_UNSPECIFIED;
928
929
930   /* Concaveness #1: If distance of an inner notehead to line between
931      two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
932      beam is concave (Heinz Stolba).
933
934      In the case of knees, the line connecting outer heads is often
935      not related to the beam slope (it may even go in the other
936      direction). Skip the check when the outer stems point in
937      different directions. --hwn
938      
939   */
940   bool concaveness1 = false;
941   SCM gap = me->get_grob_property ("concaveness-gap");
942   if (gh_number_p (gap)
943       && Stem::get_direction(stems.top ())
944          == Stem::get_direction(stems[0]))
945     {
946       Real r1 = gh_scm2double (gap);
947       Real dy = Stem::chord_start_y (stems.top ())
948         - Stem::chord_start_y (stems[0]);
949
950       
951       Real slope = dy / (stems.size () - 1);
952       
953       Real y0 = Stem::chord_start_y (stems[0]);
954       for (int i = 1; i < stems.size () - 1; i++)
955         {
956           Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
957           if (c > r1)
958             {
959               concaveness1 = true;
960               break;
961             }
962         }
963     }
964
965     
966   /* Concaveness #2: Sum distances of inner noteheads that fall
967      outside the interval of the two outer noteheads.
968
969      We only do this for beams where first and last stem have the same
970      direction. --hwn.
971
972
973      Note that "convex" stems compensate for "concave" stems.
974      (is that intentional?) --hwn.
975   */
976   
977   Real concaveness2 = 0;
978   SCM thresh = me->get_grob_property ("concaveness-threshold");
979   Real r2 = infinity_f;
980   if (!concaveness1 && gh_number_p (thresh)
981       && Stem::get_direction(stems.top ())
982          == Stem::get_direction(stems[0]))
983     {
984       r2 = gh_scm2double (thresh);
985
986       Direction dir = Stem::get_direction(stems.top ());
987       Real concave = 0;
988       Interval iv (Stem::chord_start_y (stems[0]),
989                    Stem::chord_start_y (stems.top ()));
990       
991       if (iv[MAX] < iv[MIN])
992         iv.swap ();
993       
994       for (int i = 1; i < stems.size () - 1; i++)
995         {
996           Real f = Stem::chord_start_y (stems[i]);
997           concave += ((f - iv[MAX] ) >? 0) +
998             ((f - iv[MIN] ) <? 0);
999         }
1000       concave *= dir;
1001       concaveness2 = concave / (stems.size () - 2);
1002       
1003       /* ugh: this is the a kludge to get
1004          input/regression/beam-concave.ly to behave as
1005          baerenreiter. */
1006
1007       /*
1008         huh? we're dividing twice (which is not scalable) meaning that
1009         the longer the beam, the more unlikely it will be
1010         concave. Maybe you would even expect the other way around??
1011
1012         --hwn.
1013         
1014        */
1015       concaveness2 /= (stems.size () - 2);
1016     }
1017   
1018   /* TODO: some sort of damping iso -> plain horizontal */
1019   if (concaveness1 || concaveness2 > r2)
1020     {
1021       Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1022       Real r = pos.linear_combination (0);
1023       me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
1024       me->set_grob_property ("least-squares-dy", gh_double2scm (0));
1025     }
1026
1027   return SCM_UNSPECIFIED;
1028 }
1029
1030 /* This neat trick is by Werner Lemberg,
1031    damped = tanh (slope)
1032    corresponds with some tables in [Wanske] CHECKME */
1033 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1034 SCM
1035 Beam::slope_damping (SCM smob)
1036 {
1037   Grob *me = unsmob_grob (smob);
1038
1039   if (visible_stem_count (me) <= 1)
1040     return SCM_UNSPECIFIED;
1041
1042   SCM s = me->get_grob_property ("damping"); 
1043   int damping = gh_scm2int (s);
1044
1045   if (damping)
1046     {
1047       Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1048       Real dy = pos.delta ();
1049       
1050       // ugh -> use commonx
1051       Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS)
1052         - first_visible_stem (me)->relative_coordinate (0, X_AXIS);
1053       Real dydx = dy && dx ? dy/dx : 0;
1054       dydx = 0.6 * tanh (dydx) / damping;
1055
1056       Real damped_dy = dydx * dx;
1057       pos[LEFT] += (dy - damped_dy) / 2;
1058       pos[RIGHT] -= (dy - damped_dy) / 2;
1059       
1060       me->set_grob_property ("positions", ly_interval2scm (pos));
1061     }
1062   return SCM_UNSPECIFIED;
1063 }
1064
1065 /*
1066   Calculate the Y position of the stem-end, given the Y-left, Y-right
1067   in POS, and for stem S.
1068
1069   If CORRECT, correct for multiplicity of beam in case of knees.
1070
1071
1072   TODO: junk CORRECT from this.
1073  */
1074 Real
1075 Beam::calc_stem_y (Grob *me, Grob* s, Interval pos, bool correct)
1076 {
1077   int beam_multiplicity = get_multiplicity (me);
1078   int stem_multiplicity = (Stem::duration_log (s) - 2) >? 0;
1079   
1080
1081   Real thick = gh_scm2double (me->get_grob_property ("thickness"));
1082   Real interbeam = get_interbeam (me);
1083
1084   // ugh -> use commonx
1085   Grob * fvs = first_visible_stem (me);
1086   Grob *lvs = last_visible_stem (me);
1087     
1088   Real x0 = fvs ? fvs->relative_coordinate (0, X_AXIS) : 0.0;
1089   Real dx = fvs ? lvs->relative_coordinate (0, X_AXIS) - x0 : 0.0;
1090   Real r = s->relative_coordinate (0, X_AXIS) - x0;
1091   Real dy = pos.delta ();
1092   Real stem_y = (dy && dx
1093                  ? r / dx
1094                  * dy
1095                  : 0) + pos[LEFT];
1096
1097   Direction my_dir = Directional_element_interface::get (s);
1098   Direction first_dir = fvs? Directional_element_interface::get (fvs) : my_dir;
1099
1100   if (correct && my_dir != first_dir)
1101     {
1102       /*
1103         WTF is happening here ?
1104
1105          It looks as if this is some kind of fixup for multiple kneed
1106          beams to get a piece of stem at the #.
1107          
1108
1109                 x
1110                 |
1111          =======|
1112          |======#
1113          |
1114          |
1115         x 
1116
1117         Rules for this kind of stuff are hairy. In any event, the
1118         current stem should look at the multiplicity of its
1119         predecessor.
1120
1121         --hwn.
1122         
1123        */
1124       
1125       // FIXME, hairy stuff
1126       stem_y += my_dir * (thick / 2 + (beam_multiplicity - 1) * interbeam);
1127
1128       // huh, why not for first visible?
1129
1130       /*
1131         What the heck is happening here?? 
1132        */
1133       Grob *last_visible = last_visible_stem (me);
1134       if (last_visible)
1135         {
1136           if ( Staff_symbol_referencer::staff_symbol_l (s)
1137                != Staff_symbol_referencer::staff_symbol_l (last_visible))
1138             stem_y += Directional_element_interface::get (me)
1139               * (beam_multiplicity - stem_multiplicity) * interbeam;
1140         }
1141       else
1142         programming_error ("No last visible stem");
1143     }
1144   return stem_y;
1145 }
1146
1147 /*
1148   Hmm.  At this time, beam position and slope are determined.  Maybe,
1149   stem directions and length should set to relative to the chord's
1150   position of the beam.  */
1151 void
1152 Beam::set_stem_lengths (Grob *me)
1153 {
1154   Link_array<Item> stems=
1155     Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
1156
1157   if (stems.size () <= 1)
1158     return;
1159   
1160   Grob *common = me->common_refpoint (stems[0], Y_AXIS);
1161   for (int i=1; i < stems.size (); i++)
1162     if (!Stem::invisible_b (stems[i]))
1163       common = common->common_refpoint (stems[i], Y_AXIS);
1164
1165   Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1166   Real staff_space = Staff_symbol_referencer::staff_space (me);
1167
1168   /*
1169     DOCUMENT THIS.
1170    */
1171 #if 0
1172   Real thick = gh_scm2double (me->get_grob_property ("thickness"));
1173   Direction dir = Directional_element_interface::get (me);
1174   bool ps_testing = to_boolean (ly_symbol2scm ("ps-testing"));
1175 #endif
1176   
1177   for (int i=0; i < stems.size (); i++)
1178     {
1179       Item* s = stems[i];
1180       if (Stem::invisible_b (s))
1181         continue;
1182
1183       Real stem_y = calc_stem_y (me, s, pos, true);
1184
1185 #if 0
1186       // doesn't play well with dvips
1187       if (ps_testing)
1188         if (Stem::get_direction (s) == dir)
1189           stem_y += Stem::get_direction (s) * thick / 2;
1190 #endif
1191       
1192       /* caution: stem measures in staff-positions */
1193       Real id = me->relative_coordinate (common, Y_AXIS)
1194         - stems[i]->relative_coordinate (common, Y_AXIS);
1195       Stem::set_stemend (s, (stem_y + id) / staff_space * 2);
1196     }
1197 }
1198
1199 void
1200 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1201 {
1202   Link_array<Grob> stems=
1203     Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1204   
1205   Direction d = LEFT;
1206   for (int i=0; i  < stems.size (); i++)
1207     {
1208       do
1209         {
1210           /* Don't overwrite user override (?) */
1211           if (Stem::beam_count (stems[i], d) == -1
1212               /* Don't set beaming for outside of outer stems */
1213               && ! (d == LEFT && i == 0)
1214               && ! (d == RIGHT && i == stems.size () -1))
1215             {
1216               int b = beaming->infos_.elem (i).beams_i_drul_[d];
1217               Stem::set_beaming (stems[i], b, d);
1218             }
1219         }
1220       while (flip (&d) != LEFT);
1221     }
1222 }
1223
1224
1225
1226 /*
1227   beams to go with one stem.
1228
1229   FIXME: clean me up:
1230
1231   The beam should be constructed by one function that knows where the
1232   X and Y points are, and only inspects the stems to obtain
1233   multiplicity and stem directions.
1234   
1235   */
1236 Molecule
1237 Beam::stem_beams (Grob *me, Item *here, Item *next, Item *prev, Real dydx)
1238 {
1239   // ugh -> use commonx
1240   if ((next
1241        && ! (next->relative_coordinate (0, X_AXIS)
1242             > here->relative_coordinate (0, X_AXIS)))
1243       || (prev
1244           && ! (prev->relative_coordinate (0, X_AXIS)
1245                < here->relative_coordinate (0, X_AXIS))))
1246     programming_error ("Beams are not left-to-right");
1247
1248   Real thick = gh_scm2double (me->get_grob_property ("thickness"));
1249   Real bdy = get_interbeam (me);
1250   
1251   Molecule leftbeams;
1252   Molecule rightbeams;
1253
1254   Real nw_f;
1255   if (!Stem::first_head (here))
1256     nw_f = 0;
1257   else
1258     {
1259       int t = Stem::duration_log (here); 
1260
1261       SCM proc = me->get_grob_property ("flag-width-function");
1262       SCM result = gh_call1 (proc, gh_int2scm (t));
1263       nw_f = gh_scm2double (result);
1264     }
1265
1266
1267   /* [Tremolo] beams on whole notes may not have direction set? */
1268   Direction dir = Directional_element_interface::get (here);
1269
1270   /* half beams extending to the left. */
1271   if (prev)
1272     {
1273       int lhalfs= lhalfs = Stem::beam_count (here, LEFT)
1274         - Stem::beam_count (prev, RIGHT);
1275       int lwholebeams= Stem::beam_count (here, LEFT)
1276         <? Stem::beam_count (prev, RIGHT);
1277       
1278       /* Half beam should be one note-width,
1279          but let's make sure two half-beams never touch */
1280
1281       // FIXME: TODO (check) stem width / sloped beams
1282       Real w = here->relative_coordinate (0, X_AXIS)
1283         - prev->relative_coordinate (0, X_AXIS);
1284       Real stem_w = gh_scm2double (prev->get_grob_property ("thickness"))
1285         // URG
1286         * me->paper_l ()->get_var ("linethickness");
1287
1288       w = w/2 <? nw_f;
1289       Molecule a;
1290       if (lhalfs)               // generates warnings if not
1291         a =  Lookup::beam (dydx, w + stem_w, thick);
1292       a.translate (Offset (-w, -w * dydx));
1293       a.translate_axis (-stem_w/2, X_AXIS);
1294       for (int j = 0; j  < lhalfs; j++)
1295         {
1296           Molecule b (a);
1297           b.translate_axis (-dir * bdy * (lwholebeams+j), Y_AXIS);
1298           leftbeams.add_molecule (b);
1299         }
1300     }
1301
1302   if (next)
1303     {
1304       int rhalfs  = Stem::beam_count (here, RIGHT)
1305         - Stem::beam_count (next, LEFT);
1306       int rwholebeams= Stem::beam_count (here, RIGHT)
1307         <? Stem::beam_count (next, LEFT);
1308
1309       Real w = next->relative_coordinate (0, X_AXIS)
1310         - here->relative_coordinate (0, X_AXIS);
1311
1312       Real stem_w = gh_scm2double (next->get_grob_property ("thickness"))
1313         // URG
1314         * me->paper_l ()->get_var ("linethickness");
1315
1316       Molecule a = Lookup::beam (dydx, w + stem_w, thick);
1317       a.translate_axis (- stem_w/2, X_AXIS);
1318       int j = 0;
1319       Real gap_f = 0;
1320       
1321       SCM gap = me->get_grob_property ("gap");
1322       if (gh_number_p (gap))
1323         {
1324           int gap_i = gh_scm2int ((gap));
1325           int nogap = rwholebeams - gap_i;
1326           
1327           for (; j  < nogap; j++)
1328             {
1329               Molecule b (a);
1330               b.translate_axis (-dir  * bdy * j, Y_AXIS);
1331               rightbeams.add_molecule (b);
1332             }
1333           if (Stem::invisible_b (here))
1334             gap_f = nw_f;
1335           else
1336             gap_f = nw_f / 2;
1337           w -= 2 * gap_f;
1338           a = Lookup::beam (dydx, w + stem_w, thick);
1339         }
1340
1341       for (; j  < rwholebeams; j++)
1342         {
1343           Molecule b (a);
1344           Real tx = 0;
1345           if (Stem::invisible_b (here))
1346             // ugh, see chord-tremolo.ly
1347             tx = (-dir + 1) / 2 * nw_f * 1.5 + gap_f/4;
1348           else
1349             tx = gap_f;
1350           b.translate (Offset (tx, -dir * bdy * j));
1351           rightbeams.add_molecule (b);
1352         }
1353
1354       w = w/2 <? nw_f;
1355       if (rhalfs)
1356         a = Lookup::beam (dydx, w, thick);
1357
1358       for (; j  < rwholebeams + rhalfs; j++)
1359         {
1360           Molecule b (a);
1361           b.translate_axis (- dir * bdy * j, Y_AXIS);
1362           rightbeams.add_molecule (b);
1363         }
1364
1365     }
1366   leftbeams.add_molecule (rightbeams);
1367
1368   return leftbeams;
1369 }
1370
1371
1372 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
1373 SCM
1374 Beam::brew_molecule (SCM smob)
1375 {
1376   Grob *me =unsmob_grob (smob);
1377
1378   Molecule mol;
1379   if (!gh_pair_p (me->get_grob_property ("stems")))
1380     return SCM_EOL;
1381   Real x0, dx;
1382   Link_array<Item>stems = 
1383     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");  
1384   if (visible_stem_count (me))
1385     {
1386       // ugh -> use commonx
1387       x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
1388       dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
1389     }
1390   else
1391     {
1392       x0 = stems[0]->relative_coordinate (0, X_AXIS);
1393       dx = stems.top ()->relative_coordinate (0, X_AXIS) - x0;
1394     }
1395
1396   SCM posns = me->get_grob_property ("positions");
1397   Interval pos;
1398   if (!ly_number_pair_p (posns))
1399     {
1400       programming_error ("No beam posns");
1401       pos = Interval (0,0);
1402     }
1403   else
1404     pos= ly_scm2interval (posns);
1405   Real dy = pos.delta ();
1406   Real dydx = dy && dx ? dy/dx : 0;
1407
1408
1409   for (int i=0; i < stems.size (); i++)
1410     {
1411       Item *item = stems[i];
1412       Item *prev = (i > 0)? stems[i-1] : 0;
1413       Item *next = (i < stems.size ()-1) ? stems[i+1] :0;
1414
1415
1416       
1417       Molecule sb = stem_beams (me, item, next, prev, dydx);
1418       Real x = item->relative_coordinate (0, X_AXIS) - x0;
1419       sb.translate (Offset (x, x * dydx + pos[LEFT]));
1420
1421
1422       mol.add_molecule (sb);
1423     }
1424   
1425   mol.translate_axis (x0 
1426                       - dynamic_cast<Spanner*> (me)
1427                       ->get_bound (LEFT)->relative_coordinate (0, X_AXIS),
1428                       X_AXIS);
1429
1430 #if (DEBUG_QUANTING)
1431     {
1432       /*
1433         This code prints the demerits for each beam. Perhaps this
1434         should be switchable for those who want to twiddle with the
1435         parameters.
1436       */
1437       String str;
1438       if (1)
1439         {
1440           str += to_str (gh_scm2int (me->get_grob_property ("best-idx")));
1441           str += ":";
1442         }
1443       str += to_str (gh_scm2double (me->get_grob_property ("quant-score")),
1444                      "%.2f");
1445
1446       SCM properties = Font_interface::font_alist_chain (me);
1447
1448       
1449       Molecule tm = Text_item::text2molecule (me, ly_str02scm (str.ch_C ()), properties);
1450       mol.add_at_edge (Y_AXIS, UP, tm, 5.0);
1451     }
1452 #endif
1453     
1454   return mol.smobbed_copy ();
1455 }
1456
1457 int
1458 Beam::forced_stem_count (Grob *me) 
1459 {
1460   Link_array<Item>stems = 
1461     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1462   int f = 0;
1463   for (int i=0; i < stems.size (); i++)
1464     {
1465       Item *s = stems[i];
1466
1467       if (Stem::invisible_b (s))
1468         continue;
1469
1470       if (((int)Stem::chord_start_y (s)) 
1471         && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1472         f++;
1473     }
1474   return f;
1475 }
1476
1477
1478
1479
1480 int
1481 Beam::visible_stem_count (Grob *me) 
1482 {
1483   Link_array<Item>stems = 
1484     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1485   int c = 0;
1486   for (int i = stems.size (); i--;)
1487     {
1488       if (!Stem::invisible_b (stems[i]))
1489         c++;
1490     }
1491   return c;
1492 }
1493
1494 Item*
1495 Beam::first_visible_stem (Grob *me) 
1496 {
1497   Link_array<Item>stems = 
1498     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1499   
1500   for (int i = 0; i < stems.size (); i++)
1501     {
1502       if (!Stem::invisible_b (stems[i]))
1503         return stems[i];
1504     }
1505   return 0;
1506 }
1507
1508 Item*
1509 Beam::last_visible_stem (Grob *me) 
1510 {
1511   Link_array<Item>stems = 
1512     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1513   for (int i = stems.size (); i--;)
1514     {
1515       if (!Stem::invisible_b (stems[i]))
1516         return stems[i];
1517     }
1518   return 0;
1519 }
1520
1521
1522 /*
1523   [TODO]
1524   
1525   handle rest under beam (do_post: beams are calculated now)
1526   what about combination of collisions and rest under beam.
1527
1528   Should lookup
1529     
1530     rest -> stem -> beam -> interpolate_y_position ()
1531 */
1532 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1533 SCM
1534 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1535 {
1536   Grob *rest = unsmob_grob (element_smob);
1537   Axis a = (Axis) gh_scm2int (axis);
1538   
1539   assert (a == Y_AXIS);
1540
1541   Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1542   Grob *stem = st;
1543   if (!stem)
1544     return gh_double2scm (0.0);
1545   Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1546   if (!beam
1547       || !Beam::has_interface (beam)
1548       || !Beam::visible_stem_count (beam))
1549     return gh_double2scm (0.0);
1550
1551   // make callback for rest from this.
1552   // todo: make sure this calced already.
1553
1554   //  Interval pos = ly_scm2interval (beam->get_grob_property ("positions"));
1555   Interval pos (0, 0);
1556   SCM s = beam->get_grob_property ("positions");
1557   if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1558     pos = ly_scm2interval (s);
1559
1560   Real dy = pos.delta ();
1561   // ugh -> use commonx
1562   Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1563   Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1564   Real dydx = dy && dx ? dy/dx : 0;
1565   
1566   Direction d = Stem::get_direction (stem);
1567   Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + pos[LEFT];
1568
1569   Real staff_space = Staff_symbol_referencer::staff_space (rest);
1570
1571   
1572   Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space; // refp??
1573
1574   Real minimum_dist
1575     = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1576   Real dist =
1577     minimum_dist +  -d  * (beamy - rest_dim) >? 0;
1578
1579   int stafflines = Staff_symbol_referencer::line_count (rest);
1580
1581   // move discretely by half spaces.
1582   int discrete_dist = int (ceil (dist));
1583
1584   // move by whole spaces inside the staff.
1585   if (discrete_dist < stafflines+1)
1586     discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1587
1588   return gh_double2scm (-d *  discrete_dist);
1589 }
1590
1591
1592
1593
1594 ADD_INTERFACE (Beam, "beam-interface",
1595   "A beam.
1596
1597 #'thickness= weight of beams, in staffspace
1598
1599
1600 We take the least squares line through the ideal-length stems, and
1601 then damp that using
1602
1603         damped = tanh (slope)
1604
1605 this gives an unquantized left and right position for the beam end.
1606 Then we take all combinations of quantings near these left and right
1607 positions, and give them a score (according to how close they are to
1608 the ideal slope, how close the result is to the ideal stems, etc.). We
1609 take the best scoring combination.
1610
1611 ",
1612   "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");
1613
1614