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