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