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