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