]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
patch::: 1.5.38.rz1: Re: beams
[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   if (forced_fraction < 0.5)
271     return;
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, gh_int2scm (multiplicity <? (sz - 1)));
283   Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
284
285   /* cute, but who invented me -- how to customise ? */
286   if (forced_fraction < 1)
287     shorten_f /= 2;
288
289   Link_array<Item> stems=
290     Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
291
292   for (int i=0; i < stems.size (); i++)
293     {
294       Item* s = stems[i];
295       if (Stem::invisible_b (s))
296         continue;
297       if (gh_number_p (s->get_grob_property ("shorten")))
298         s->set_grob_property ("shorten", gh_double2scm (shorten_f));
299     }
300 }
301
302 /*
303   Call list of y-dy-callbacks, that handle setting of
304   grob-properties y, dy.
305
306   User may set grob-properties: y-position-hs and height-hs
307  (to be fixed) that override the calculated y and dy.
308
309   Because y and dy cannot be calculated and quanted separately, we
310   always calculate both, then check for user override.
311  */
312 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
313 SCM
314 Beam::after_line_breaking (SCM smob)
315 {
316   Grob * me =  unsmob_grob (smob);
317   
318   me->set_grob_property ("y", gh_double2scm (0));
319   me->set_grob_property ("dy", gh_double2scm (0));
320
321   /* Hmm, callbacks should be called by, a eh, callback mechanism
322     somewhere (?), I guess, not by looping here. */
323   
324   SCM list = me->get_grob_property ("y-dy-callbacks");
325   for (SCM i = list; gh_pair_p (i); i = ly_cdr (i))
326     gh_call1 (ly_car (i), smob);
327
328   // UGH. Y is not in staff position unit?
329   // Ik dacht datwe daar juist van weg wilden?
330   
331   // Hmm, nu hebben we 3 dimensies, want inmiddels zijn we daar
332   // weer terug, maar dan / 2
333   // (staff-space iso staff-position)
334   
335   set_stem_lengths (me);
336
337   return SCM_UNSPECIFIED;
338 }
339
340
341 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
342 SCM
343 Beam::least_squares (SCM smob)
344 {
345  Grob *me = unsmob_grob (smob);
346
347  if (visible_stem_count (me) <= 1)
348    return SCM_UNSPECIFIED;
349
350   Real y = 0;
351   Real dy = 0;
352
353   /* Stem_info, and thus y,dy in this function are corrected for beam-dir */
354   Real first_ideal = Stem::calc_stem_info (first_visible_stem (me)).idealy_f_;
355   if (first_ideal == Stem::calc_stem_info (last_visible_stem (me)).idealy_f_)
356     {
357       y = first_ideal;
358       dy = 0;
359     }
360   else
361     {
362       Array<Offset> ideals;
363
364       // ugh -> use commonx
365       Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
366       Link_array<Item> stems=
367         Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
368
369       for (int i=0; i < stems.size (); i++)
370         {
371           Item* s = stems[i];
372           if (Stem::invisible_b (s))
373             continue;
374           ideals.push (Offset (s->relative_coordinate (0, X_AXIS) - x0, 
375                                Stem::calc_stem_info (s).idealy_f_));
376         }
377       Real dydx;
378       minimise_least_squares (&dydx, &y, ideals);
379
380       Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
381       dy = dydx * dx;
382     }
383
384   /* Store true, not dir-corrected values */
385   Direction dir = Directional_element_interface::get (me);
386   me->set_grob_property ("y", gh_double2scm (y * dir));
387   me->set_grob_property ("dy", gh_double2scm (dy * dir));
388   return SCM_UNSPECIFIED;
389 }
390
391 #include <stdio.h>
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   SCM s = me->get_grob_property ("concaveness-no-slope");
413
414   Real concave = 0;
415   if (!to_boolean (s))
416     {
417       /* Concaveness try #1: Sum distances of inner noteheads to line
418          between two outer noteheads.  */
419       
420       Real dy = Stem::chord_start_f (stems.top ())
421         - Stem::chord_start_f (stems[0]);
422       Real slope = dy / (stems.size () - 1);
423       
424       Real y0 = Stem::chord_start_f (stems[0]);
425       for (int i = 1; i < stems.size () - 1; i++)
426         {
427           Real c = (Stem::chord_start_f (stems[i]) - y0) - i * slope;
428           concave += c;
429         }
430
431     }
432   else
433     {
434       /* Concaveness try #2: Sum distances of inner noteheads that
435          fall outside the interval of the two outer noteheads */
436          
437       Interval iv = Interval (Stem::chord_start_f (stems[0]),
438                               Stem::chord_start_f (stems.top ()));
439
440       if (iv[MAX] < iv[MIN])
441         //      iv.swap ();
442         iv = Interval (iv[MAX], iv[MIN]);
443       
444       for (int i = 1; i < stems.size () - 1; i++)
445         {
446           Real c = 0;
447           Real f = Stem::chord_start_f (stems[i]);
448           if ((c = f - iv[MAX]) > 0)
449             concave += c;
450           else if ((c = f - iv[MIN]) < 0)
451             concave += c;
452         }
453     }
454   
455   concave *= Directional_element_interface::get (me);
456       
457   Real concaveness = concave / (stems.size () - 2);
458
459   /* ugh: this is the a kludge to get input/regression/beam-concave.ly
460      to behave as baerenreiter. */
461   s = me->get_grob_property ("concaveness-square");
462   if (to_boolean (s))
463     concaveness /= (stems.size () - 2);
464   
465   s = me->get_grob_property ("concaveness");
466   Real r = gh_scm2double (s);
467
468   if (concaveness > r)
469     {
470       Direction dir = Directional_element_interface::get (me);
471       Real y = gh_scm2double (me->get_grob_property ("y")) * dir;
472       Real dy = gh_scm2double (me->get_grob_property ("dy")) * dir;
473   
474       Real adjusted_y = y + dy / 2;
475       /* Store true, not dir-corrected values */
476       me->set_grob_property ("y", gh_double2scm (adjusted_y * dir));
477       me->set_grob_property ("dy", gh_double2scm (0)); 
478     }
479
480   s = me->get_grob_property ("debug-concave");
481   if (to_boolean (s))
482     {
483 #if 0
484       Item *text = new Item (me->get_property ("TextScript"));
485       text->set_grob_property ("text",
486                                ly_str02scm (to_str (concaveness).ch_C ())),
487       Side_position_interface::add_support (text, stem[0]);
488 #else
489       printf ("concaveness: %.2f\n", concaveness);
490 #endif
491     }
492   
493   return SCM_UNSPECIFIED;
494 }
495
496 /*
497   This neat trick is by Werner Lemberg,
498   damped = tanh (slope)
499   corresponds with some tables in [Wanske]
500 */
501 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
502 SCM
503 Beam::slope_damping (SCM smob)
504 {
505   Grob *me = unsmob_grob (smob);
506
507   if (visible_stem_count (me) <= 1)
508     return SCM_UNSPECIFIED;
509
510   SCM s = me->get_grob_property ("damping"); 
511   int damping = gh_scm2int (s);
512
513   if (damping)
514     {
515       /* y,dy in this function are corrected for beam-dir */
516       Direction dir = Directional_element_interface::get (me);
517       Real y = gh_scm2double (me->get_grob_property ("y")) * dir;
518       Real dy = gh_scm2double (me->get_grob_property ("dy")) * dir;
519       
520       // ugh -> use commonx
521       Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS)
522         - first_visible_stem (me)->relative_coordinate (0, X_AXIS);
523       Real dydx = dy && dx ? dy/dx : 0;
524       dydx = 0.6 * tanh (dydx) / damping;
525
526       Real damped_dy = dydx * dx;
527       Real adjusted_y = y + (dy - damped_dy) / 2;
528       /* Store true, not dir-corrected values */
529       me->set_grob_property ("y", gh_double2scm (adjusted_y * dir));
530       me->set_grob_property ("dy", gh_double2scm (damped_dy * dir));
531     }
532     return SCM_UNSPECIFIED;
533 }
534
535 /*
536   Quantise dy (height) of beam.
537   Generalisation of [Ross].
538   */
539 MAKE_SCHEME_CALLBACK (Beam, quantise_dy, 1);
540 SCM
541 Beam::quantise_dy (SCM smob)
542 {
543   Grob *me = unsmob_grob (smob);
544
545   if (visible_stem_count (me) <= 1)
546     return SCM_UNSPECIFIED;
547
548   Array<Real> a;
549   SCM proc = me->get_grob_property ("height-quants");
550   SCM quants = gh_call2 (proc, me->self_scm (),
551                          gh_double2scm (me->paper_l ()->get_var ("stafflinethickness")
552                                         / 1.0));
553   
554   for (SCM s = quants; gh_pair_p (s); s = ly_cdr (s))
555     a.push (gh_scm2double (ly_car (s)));
556   
557   if (a.size () > 1)
558     {
559       /* y,dy in this function are corrected for beam-dir */
560       Direction dir = Directional_element_interface::get (me);
561       Real y = gh_scm2double (me->get_grob_property ("y")) * dir;
562       Real dy = gh_scm2double (me->get_grob_property ("dy")) * dir;
563
564       Real staff_space = Staff_symbol_referencer::staff_space (me);
565       
566       Interval iv = quantise_iv (a, abs (dy)/staff_space) * staff_space;
567       Real q = (abs (dy) - iv[SMALLER] <= iv[BIGGER] - abs (dy))
568         ? iv[SMALLER]
569         : iv[BIGGER];
570       
571       Real quantised_dy = q * sign (dy);
572       Real adjusted_y = y + (dy - quantised_dy) / 2;
573       /* Store true, not dir-corrected values */
574       me->set_grob_property ("y", gh_double2scm (adjusted_y * dir));
575       me->set_grob_property ("dy", gh_double2scm (quantised_dy * dir));
576     }
577   return SCM_UNSPECIFIED;
578 }
579
580 /* It's tricky to have the user override y,dy directly, so we use this
581    translation func.  Also, if our staff_space != 1 (smaller staff, eg),
582    user will expect staff-position to be discrete values. */
583 MAKE_SCHEME_CALLBACK (Beam, user_override, 1);
584 SCM
585 Beam::user_override (SCM smob)
586 {
587   Grob *me = unsmob_grob (smob);
588   Real staff_space = Staff_symbol_referencer::staff_space (me);
589
590   SCM s = me->get_grob_property ("staff-position");
591   if (gh_number_p (s))
592     {
593       Real y = gh_scm2double (s) * staff_space * 0.5;
594       me->set_grob_property ("y", gh_double2scm (y));
595     }
596
597   /* Name suggestions? Tilt, slope, vertical-* ? */
598   s = me->get_grob_property ("height");
599   if (gh_number_p (s))
600     {
601       Real dy = gh_scm2double (s) * staff_space * 0.5;
602       me->set_grob_property ("dy", gh_double2scm (dy));
603     }
604   
605   return SCM_UNSPECIFIED;
606 }
607
608 /*
609   Ugh, this must be last, after user_override
610   Assumes directionised y/dy.
611  */
612 MAKE_SCHEME_CALLBACK (Beam, do_quantise_y, 1);
613 SCM
614 Beam::do_quantise_y (SCM smob)
615 {
616   Grob *me = unsmob_grob (smob);
617
618   /*
619     If the user set y-position, we shouldn't do quanting.
620    */
621   if (gh_number_p (me->get_grob_property ("y-position-hs")))
622     return SCM_UNSPECIFIED;
623
624   Real y = gh_scm2double (me->get_grob_property ("y"));
625   Real dy = gh_scm2double (me->get_grob_property ("dy"));
626       
627   /* we can modify y, so we should quantise y */
628   Real half_space = Staff_symbol_referencer::staff_space (me) / 2;
629   Real y_shift = check_stem_length_f (me, y, dy);
630   y += y_shift;
631   y = quantise_y_f (me, y, dy, 0);
632
633   /*
634     Hmm, this is a bit keyhole operation: we're passing `this' as a
635     parameter, and member vars as SCM properties.  We should decide on
636     SCM/C/C++ boundary */
637   me->set_grob_property ("y", gh_double2scm (y));
638   set_stem_lengths (me);
639   y = gh_scm2double (me->get_grob_property ("y"));
640   
641   y_shift = check_stem_length_f (me, y, dy);
642
643   if (y_shift > half_space / 4)
644     {
645       y += y_shift;
646
647       /*
648         for significantly lengthened or shortened stems,
649         request quanting the other way.
650       */
651       int quant_dir = 0;
652       if (abs (y_shift) > half_space / 2)
653         quant_dir = sign (y_shift) * Directional_element_interface::get (me);
654       y = quantise_y_f (me, y, dy, quant_dir);
655     }
656   
657   me->set_grob_property ("y", gh_double2scm (y));
658   // me->set_grob_property ("dy", gh_double2scm (dy));
659   return SCM_UNSPECIFIED;
660 }
661
662
663 Real
664 Beam::calc_stem_y_f (Grob*me,Item* s, Real y, Real dy) 
665 {
666   int beam_multiplicity = get_multiplicity (me);
667   int stem_multiplicity = (Stem::flag_i (s) - 2) >? 0;
668
669   SCM space_proc = me->get_grob_property ("space-function");
670   SCM space = gh_call1 (space_proc, gh_int2scm (beam_multiplicity));
671
672   Real thick = gh_scm2double (me->get_grob_property ("thickness")) ;
673   Real interbeam_f = gh_scm2double (space) ;
674
675   // ugh -> use commonx
676   Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
677   Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
678   Real stem_y = (dy && dx ? (s->relative_coordinate (0, X_AXIS) - x0) / dx * dy : 0) + y;
679
680   /* knee */
681    Direction dir  = Directional_element_interface::get (me);
682    Direction sdir = Directional_element_interface::get (s);
683    
684     /* knee */
685    if (dir!= sdir)
686       {
687        stem_y -= dir 
688         * (thick / 2 + (beam_multiplicity - 1) * interbeam_f);
689
690
691       
692       // huh, why not for first visible?
693        if (Staff_symbol_referencer::staff_symbol_l (s)
694            != Staff_symbol_referencer::staff_symbol_l (last_visible_stem (me)))
695          stem_y += Directional_element_interface::get (me)
696            * (beam_multiplicity - stem_multiplicity) * interbeam_f;
697       }
698
699   return stem_y;
700 }
701
702 Real
703 Beam::check_stem_length_f (Grob*me,Real y, Real dy) 
704 {
705   Real shorten = 0;
706   Real lengthen = 0;
707   Direction dir = Directional_element_interface::get (me);
708
709   Link_array<Item> stems=
710     Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
711
712   for (int i=0; i < stems.size (); i++)
713     {
714       Item* s = stems[i];
715       if (Stem::invisible_b (s))
716         continue;
717
718       Real stem_y = calc_stem_y_f (me, s, y, dy);
719         
720       stem_y *= dir;
721       Stem_info info = Stem::calc_stem_info (s);
722
723       // if (0 > info.maxy_f_ - stem_y)
724       shorten = shorten <? info.maxy_f_ - stem_y;
725       // if (0 < info.miny_f_ - stem_y)
726       lengthen = lengthen >? info.miny_f_ - stem_y; 
727     }
728
729   if (lengthen && shorten)
730     me->warning (_ ("weird beam vertical offset"));
731
732   /* when all stems are too short, normal stems win */
733   return dir * ((shorten) ?  shorten : lengthen);
734 }
735
736 /*
737   Hmm.  At this time, beam position and slope are determined.  Maybe,
738   stem directions and length should set to relative to the chord's
739   position of the beam.  */
740 void
741 Beam::set_stem_lengths (Grob *me)
742 {
743   if (visible_stem_count (me) <= 1)
744     return;
745   
746   Real y = gh_scm2double (me->get_grob_property ("y"));
747   Real dy = gh_scm2double (me->get_grob_property ("dy"));
748
749   Real half_space = Staff_symbol_referencer::staff_space (me)/2;
750   Link_array<Item> stems=
751     Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
752
753   Grob *common = me->common_refpoint (stems[0], Y_AXIS);
754   for (int i=1; i < stems.size (); i++)
755     if (!Stem::invisible_b (stems[i]))
756       common = common->common_refpoint (stems[i], Y_AXIS);
757
758   for (int i=0; i < stems.size (); i++)
759     {
760       Item* s = stems[i];
761       if (Stem::invisible_b (s))
762         continue;
763
764       Real stem_y = calc_stem_y_f (me, s, y, dy);
765
766       // doesn't play well with dvips
767       if (scm_definedp (ly_symbol2scm ("ps-testing"), SCM_UNDEFINED)
768           == SCM_BOOL_T)
769         if (Stem::get_direction (s) == Directional_element_interface::get (me))
770           stem_y += Stem::get_direction (s)
771             * gh_scm2double (me->get_grob_property ("thickness")) / 2;
772       
773       /* caution: stem measures in staff-positions */
774       Real id = me->relative_coordinate (common, Y_AXIS)
775         - stems[i]->relative_coordinate (common, Y_AXIS);
776       Stem::set_stemend (s, (stem_y + id) / half_space);
777     }
778 }
779
780 /*
781   Prevent interference from stafflines and beams.
782
783   We only need to quantise the (left) y of the beam,
784   since dy is quantised too.
785   if extend_b then stems must *not* get shorter
786  */
787 Real
788 Beam::quantise_y_f (Grob*me,Real y, Real dy, int quant_dir)
789 {
790   int multiplicity = get_multiplicity (me);
791
792   Real staff_space = Staff_symbol_referencer::staff_space (me);
793   Real thick = me->paper_l ()->get_var ("stafflinethickness");
794
795
796   SCM proc = me->get_grob_property ("vertical-position-quant-function");
797   SCM quants = scm_apply (proc,
798                           me->self_scm (),
799                           scm_list_n (gh_int2scm (multiplicity),
800                                    gh_double2scm (dy/staff_space),
801                                    gh_double2scm (thick/staff_space),
802                                    SCM_EOL, SCM_UNDEFINED));
803   
804   Array<Real> a;
805
806   for (; gh_pair_p (quants); quants = ly_cdr (quants))
807     a.push (gh_scm2double (ly_car (quants)));
808
809   if (a.size () <= 1)
810     return y;
811
812   Real up_y = Directional_element_interface::get (me) * y;
813   Interval iv = quantise_iv (a, up_y/staff_space) * staff_space;
814
815   Real q = up_y - iv[SMALLER] <= iv[BIGGER] - up_y 
816     ? iv[SMALLER] : iv[BIGGER];
817   if (quant_dir)
818     q = iv[ (Direction)quant_dir];
819
820   return q * Directional_element_interface::get (me);
821 }
822
823 void
824 Beam::set_beaming (Grob*me,Beaming_info_list *beaming)
825 {
826   Link_array<Grob> stems=
827     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
828   
829   Direction d = LEFT;
830   for (int i=0; i  < stems.size (); i++)
831     {
832       do
833         {
834           /* Don't overwrite user override (?) */
835           if (Stem::beam_count (stems[i], d) == -1
836               /* Don't set beaming for outside of outer stems */
837               && ! (d == LEFT && i == 0)
838               && ! (d == RIGHT && i == stems.size () -1))
839             {
840               int b = beaming->infos_.elem (i).beams_i_drul_[d];
841               Stem::set_beaming (stems[i], b, d);
842             }
843         }
844       while (flip (&d) != LEFT);
845     }
846 }
847
848
849
850 /*
851   beams to go with one stem.
852
853   FIXME: clean me up.
854   */
855 Molecule
856 Beam::stem_beams (Grob*me,Item *here, Item *next, Item *prev,
857                   Real /* dy */ , Real dydx
858                   ) 
859 {
860   // ugh -> use commonx
861   if ((next && ! (next->relative_coordinate (0, X_AXIS) > here->relative_coordinate (0, X_AXIS))) ||
862  (prev && ! (prev->relative_coordinate (0, X_AXIS) < here->relative_coordinate (0, X_AXIS))))
863       programming_error ("Beams are not left-to-right");
864
865   int multiplicity = get_multiplicity (me);
866
867   SCM space_proc = me->get_grob_property ("space-function");
868   SCM space = gh_call1 (space_proc, gh_int2scm (multiplicity));
869
870   Real thick = gh_scm2double (me->get_grob_property ("thickness")) ;
871   Real interbeam_f = gh_scm2double (space) ;
872     
873   Real bdy = interbeam_f;
874   
875 #if 0
876     // ugh -> use commonx
877   Real dx = visible_stem_count (me) ?
878     last_visible_stem (me)->relative_coordinate (0, X_AXIS) - first_visible_stem (me)->relative_coordinate (0, X_AXIS)
879     : 0.0;
880 #endif
881   
882   Molecule leftbeams;
883   Molecule rightbeams;
884
885   Real nw_f;
886   if (!Stem::first_head (here))
887     nw_f = 0;
888   else {
889     int t = Stem::type_i (here); 
890
891     SCM proc = me->get_grob_property ("flag-width-function");
892     SCM result = gh_call1 (proc, gh_int2scm (t));
893     nw_f = gh_scm2double (result);
894   }
895
896
897   Direction dir = Directional_element_interface::get (me);
898
899   /* [Tremolo] beams on whole notes may not have direction set? */
900  if (dir == CENTER)
901     dir = Directional_element_interface::get (here);
902
903
904   /* half beams extending to the left. */
905   if (prev)
906     {
907       int lhalfs= lhalfs = Stem::beam_count (here,LEFT) - Stem::beam_count (prev,RIGHT);
908       int lwholebeams= Stem::beam_count (here,LEFT) <? Stem::beam_count (prev,RIGHT) ;
909       /*
910        Half beam should be one note-width, 
911        but let's make sure two half-beams never touch
912        */
913
914       // FIXME: TODO (check) stem width / sloped beams
915       Real w = here->relative_coordinate (0, X_AXIS)
916         - prev->relative_coordinate (0, X_AXIS);
917       Real stem_w = gh_scm2double (prev->get_grob_property ("thickness"))
918         // URG
919         * me->paper_l ()->get_var ("stafflinethickness");
920
921       w = w/2 <? nw_f;
922       Molecule a;
923       if (lhalfs)               // generates warnings if not
924         a =  Lookup::beam (dydx, w + stem_w, thick);
925       a.translate (Offset (-w, -w * dydx));
926       a.translate_axis (-stem_w/2, X_AXIS);
927       for (int j = 0; j  < lhalfs; j++)
928         {
929           Molecule b (a);
930           b.translate_axis (-dir * bdy * (lwholebeams+j), Y_AXIS);
931           leftbeams.add_molecule (b);
932         }
933     }
934
935   if (next)
936     {
937       int rhalfs  = Stem::beam_count (here,RIGHT)
938         - Stem::beam_count (next,LEFT);
939       int rwholebeams= Stem::beam_count (here,RIGHT)
940         <? Stem::beam_count (next,LEFT) ;
941
942       Real w = next->relative_coordinate (0, X_AXIS)
943         - here->relative_coordinate (0, X_AXIS);
944
945       Real stem_w = gh_scm2double (next->get_grob_property ("thickness"))
946         // URG
947         * me->paper_l ()->get_var ("stafflinethickness");
948
949       Molecule a = Lookup::beam (dydx, w + stem_w, thick);
950       a.translate_axis (- stem_w/2, X_AXIS);
951       int j = 0;
952       Real gap_f = 0;
953       
954       SCM gap = me->get_grob_property ("gap");
955       if (gh_number_p (gap))
956         {
957           int gap_i = gh_scm2int ((gap));
958           int nogap = rwholebeams - gap_i;
959           
960           for (; j  < nogap; j++)
961             {
962               Molecule b (a);
963               b.translate_axis (-dir  * bdy * j, Y_AXIS);
964               rightbeams.add_molecule (b);
965             }
966           if (Stem::invisible_b (here))
967             gap_f = nw_f;
968           else
969             gap_f = nw_f / 2;
970           w -= 2 * gap_f;
971           a = Lookup::beam (dydx, w + stem_w, thick);
972         }
973
974       for (; j  < rwholebeams; j++)
975         {
976           Molecule b (a);
977           Real tx = 0;
978           if (Stem::invisible_b (here))
979             // ugh, see chord-tremolo.ly
980             tx = (-dir + 1) / 2 * nw_f * 1.5 + gap_f/4;
981           else
982             tx = gap_f;
983           b.translate (Offset (tx, -dir * bdy * j));
984           rightbeams.add_molecule (b);
985         }
986
987       w = w/2 <? nw_f;
988       if (rhalfs)
989         a = Lookup::beam (dydx, w, thick);
990
991       for (; j  < rwholebeams + rhalfs; j++)
992         {
993           Molecule b (a);
994           b.translate_axis (- dir * bdy * j, Y_AXIS);
995           rightbeams.add_molecule (b);
996         }
997
998     }
999   leftbeams.add_molecule (rightbeams);
1000
1001   /*
1002     Does beam quanting think  of the asymetry of beams? 
1003     Refpoint is on bottom of symbol. (FIXTHAT) --hwn.
1004    */
1005   return leftbeams;
1006 }
1007
1008 MAKE_SCHEME_CALLBACK (Beam,brew_molecule,1);
1009 SCM
1010 Beam::brew_molecule (SCM smob)
1011 {
1012   Grob * me =unsmob_grob (smob);
1013
1014   Molecule mol;
1015   if (!gh_pair_p (me->get_grob_property ("stems")))
1016     return SCM_EOL;
1017   Real x0,dx;
1018   Link_array<Item>stems = 
1019     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");  
1020   if (visible_stem_count (me))
1021     {
1022   // ugh -> use commonx
1023       x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
1024       dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
1025     }
1026   else
1027     {
1028       x0 = stems[0]->relative_coordinate (0, X_AXIS);
1029       dx = stems.top ()->relative_coordinate (0, X_AXIS) - x0;
1030     }
1031   
1032
1033
1034   /*
1035     TODO: the naming of the grob properties sucks.
1036    */
1037   SCM dy_s = me->get_grob_property ("dy");
1038   SCM y_s = me->get_grob_property ("y");
1039
1040   
1041   Real dy = gh_number_p (dy_s) ? gh_scm2double (dy_s) : 0.0;
1042   Real dydx = dy && dx ? dy/dx : 0;
1043   Real y = gh_number_p (y_s) ? gh_scm2double (y_s) : 0.0;
1044
1045
1046   for (int j=0; j <stems.size (); j++)
1047     {
1048       Item *i = stems[j];
1049       Item * prev = (j > 0)? stems[j-1] : 0;
1050       Item * next = (j < stems.size ()-1) ? stems[j+1] :0;
1051
1052       Molecule sb = stem_beams (me, i, next, prev, dy, dydx);
1053       Real x = i->relative_coordinate (0, X_AXIS)-x0;
1054       sb.translate (Offset (x, x * dydx + y));
1055       mol.add_molecule (sb);
1056     }
1057   mol.translate_axis (x0 
1058     - dynamic_cast<Spanner*> (me)->get_bound (LEFT)->relative_coordinate (0, X_AXIS), X_AXIS);
1059
1060   return mol.smobbed_copy ();
1061 }
1062
1063 int
1064 Beam::forced_stem_count (Grob*me) 
1065 {
1066   Link_array<Item>stems = 
1067     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1068   int f = 0;
1069   for (int i=0; i < stems.size (); i++)
1070     {
1071       Item *s = stems[i];
1072
1073       if (Stem::invisible_b (s))
1074         continue;
1075
1076       if (( (int)Stem::chord_start_f (s)) 
1077         && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1078         f++;
1079     }
1080   return f;
1081 }
1082
1083
1084
1085
1086 /* TODO:
1087    use filter and standard list functions.
1088  */
1089 int
1090 Beam::visible_stem_count (Grob*me) 
1091 {
1092   Link_array<Item>stems = 
1093     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1094   int c = 0;
1095   for (int i = stems.size (); i--;)
1096     {
1097       if (!Stem::invisible_b (stems[i]))
1098         c++;
1099     }
1100   return c;
1101 }
1102
1103 Item*
1104 Beam::first_visible_stem (Grob*me) 
1105 {
1106   Link_array<Item>stems = 
1107     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1108   
1109   for (int i = 0; i < stems.size (); i++)
1110     {
1111       if (!Stem::invisible_b (stems[i]))
1112         return stems[i];
1113     }
1114   return 0;
1115 }
1116
1117 Item*
1118 Beam::last_visible_stem (Grob*me) 
1119 {
1120   Link_array<Item>stems = 
1121     Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1122   for (int i = stems.size (); i--;)
1123     {
1124       if (!Stem::invisible_b (stems[i]))
1125         return stems[i];
1126     }
1127   return 0;
1128 }
1129
1130
1131 /*
1132   [TODO]
1133   handle rest under beam (do_post: beams are calculated now)
1134   what about combination of collisions and rest under beam.
1135
1136   Should lookup
1137     
1138     rest -> stem -> beam -> interpolate_y_position ()
1139 */
1140 MAKE_SCHEME_CALLBACK (Beam,rest_collision_callback,2);
1141 SCM
1142 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1143 {
1144   Grob *rest = unsmob_grob (element_smob);
1145   Axis a = (Axis) gh_scm2int (axis);
1146   
1147   assert (a == Y_AXIS);
1148
1149   Grob * st = unsmob_grob (rest->get_grob_property ("stem"));
1150   Grob * stem = st;
1151   if (!stem)
1152     return gh_double2scm (0.0);
1153   Grob * beam = unsmob_grob (stem->get_grob_property ("beam"));
1154   if (!beam || !Beam::has_interface (beam) || !Beam::visible_stem_count (beam))
1155     return gh_double2scm (0.0);
1156
1157   // make callback for rest from this.
1158   Real beam_dy = 0;
1159   Real beam_y = 0;
1160
1161
1162   // todo: make sure this calced already.
1163   SCM s = beam->get_grob_property ("dy");
1164   if (gh_number_p (s))
1165     beam_dy = gh_scm2double (s);
1166   
1167   s = beam->get_grob_property ("y");
1168   if (gh_number_p (s))
1169     beam_y = gh_scm2double (s);
1170   
1171   // ugh -> use commonx
1172   Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1173   Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1174   Real dydx = beam_dy && dx ? beam_dy/dx : 0;
1175
1176   Direction d = Stem::get_direction (stem);
1177   Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + beam_y;
1178
1179   Real staff_space =   Staff_symbol_referencer::staff_space (rest);
1180
1181   
1182   Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space ; // refp??
1183
1184   Real minimum_dist
1185     = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1186   Real dist =
1187     minimum_dist +  -d  * (beamy - rest_dim) >? 0;
1188
1189   int stafflines = Staff_symbol_referencer::line_count (rest);
1190
1191   // move discretely by half spaces.
1192   int discrete_dist = int (ceil (dist));
1193
1194   // move by whole spaces inside the staff.
1195   if (discrete_dist < stafflines+1)
1196     discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1197
1198   return gh_double2scm (-d *  discrete_dist);
1199 }
1200
1201
1202 bool
1203 Beam::has_interface (Grob*me)
1204 {
1205   return me->has_interface (ly_symbol2scm ("beam-interface"));
1206 }
1207