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