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