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