]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
release: 1.5.0
[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       stem_y += Stem::get_direction (s)
674         * gh_scm2double (me->get_grob_property ("thickness")) / 2;
675
676       /* caution: stem measures in staff-positions */
677       Real id = me->relative_coordinate (common, Y_AXIS)
678         - stems[i]->relative_coordinate (common, Y_AXIS);
679       Stem::set_stemend (s, (stem_y + id) / half_space);
680     }
681 }
682
683 /*
684   Prevent interference from stafflines and beams.
685
686   We only need to quantise the (left) y of the beam,
687   since dy is quantised too.
688   if extend_b then stems must *not* get shorter
689  */
690 Real
691 Beam::quantise_y_f (Grob*me,Real y, Real dy, int quant_dir)
692 {
693   int multiplicity = get_multiplicity (me);
694
695   Real staff_space = Staff_symbol_referencer::staff_space (me);
696   Real thick = me->paper_l ()->get_var ("stafflinethickness");
697
698
699   SCM proc = me->get_grob_property ("vertical-position-quant-function");
700   SCM quants = scm_apply (proc,
701                           me->self_scm (),
702                           gh_list (gh_int2scm (multiplicity),
703                                    gh_double2scm (dy/staff_space),
704                                    gh_double2scm (thick/staff_space),
705                                    SCM_EOL, SCM_UNDEFINED));
706   
707   Array<Real> a;
708
709   for (; gh_pair_p (quants); quants = gh_cdr (quants))
710     a.push (gh_scm2double (gh_car (quants)));
711
712   if (a.size () <= 1)
713     return y;
714
715   Real up_y = Directional_element_interface::get (me) * y;
716   Interval iv = quantise_iv (a, up_y/staff_space) * staff_space;
717
718   Real q = up_y - iv[SMALLER] <= iv[BIGGER] - up_y 
719     ? iv[SMALLER] : iv[BIGGER];
720   if (quant_dir)
721     q = iv[ (Direction)quant_dir];
722
723   return q * Directional_element_interface::get (me);
724 }
725
726 void
727 Beam::set_beaming (Grob*me,Beaming_info_list *beaming)
728 {
729   Link_array<Grob> stems=
730     Pointer_group_interface__extract_elements (me, (Grob*)0, "stems");
731   
732   Direction d = LEFT;
733   for (int i=0; i  < stems.size (); i++)
734     {
735       do
736         {
737           /* Don't overwrite user override (?) */
738           if (Stem::beam_count (stems[i], d) == 0
739               /* Don't set beaming for outside of outer stems */
740               && ! (d == LEFT && i == 0)
741               && ! (d == RIGHT && i == stems.size () -1))
742             {
743               int b = beaming->infos_.elem (i).beams_i_drul_[d];
744               Stem::set_beaming (stems[i], b, d);
745             }
746         }
747       while (flip (&d) != LEFT);
748     }
749 }
750
751
752
753 /*
754   beams to go with one stem.
755
756   FIXME: clean me up.
757   */
758 Molecule
759 Beam::stem_beams (Grob*me,Item *here, Item *next, Item *prev,
760                   Real dy, Real dydx
761                   ) 
762 {
763   // ugh -> use commonx
764   if ((next && ! (next->relative_coordinate (0, X_AXIS) > here->relative_coordinate (0, X_AXIS))) ||
765  (prev && ! (prev->relative_coordinate (0, X_AXIS) < here->relative_coordinate (0, X_AXIS))))
766       programming_error ("Beams are not left-to-right");
767
768   int multiplicity = get_multiplicity (me);
769
770   SCM space_proc = me->get_grob_property ("space-function");
771   SCM space = gh_call1 (space_proc, gh_int2scm (multiplicity));
772
773   Real thick = gh_scm2double (me->get_grob_property ("thickness")) ;
774   Real interbeam_f = gh_scm2double (space) ;
775     
776   Real bdy = interbeam_f;
777   
778 #if 0
779     // ugh -> use commonx
780   Real dx = visible_stem_count (me) ?
781     last_visible_stem (me)->relative_coordinate (0, X_AXIS) - first_visible_stem (me)->relative_coordinate (0, X_AXIS)
782     : 0.0;
783 #endif
784   
785   Molecule leftbeams;
786   Molecule rightbeams;
787
788   Real nw_f;
789   if (!Stem::first_head (here))
790     nw_f = 0;
791   else {
792     int t = Stem::type_i (here); 
793
794     SCM proc = me->get_grob_property ("flag-width-function");
795     SCM result = gh_call1 (proc, gh_int2scm (t));
796     nw_f = gh_scm2double (result);
797   }
798
799
800   Direction dir = Directional_element_interface::get (me);
801   
802   /* [Tremolo] beams on whole notes may not have direction set? */
803  if (dir == CENTER)
804     dir = Directional_element_interface::get (here);
805   
806   /* half beams extending to the left. */
807   if (prev)
808     {
809       int lhalfs= lhalfs = Stem::beam_count (here,LEFT) - Stem::beam_count (prev,RIGHT);
810       int lwholebeams= Stem::beam_count (here,LEFT) <? Stem::beam_count (prev,RIGHT) ;
811       /*
812        Half beam should be one note-width, 
813        but let's make sure two half-beams never touch
814        */
815
816       // FIXME: TODO (check) stem width / sloped beams
817       Real w = here->relative_coordinate (0, X_AXIS)
818         - prev->relative_coordinate (0, X_AXIS);
819       Real stem_w = gh_scm2double (prev->get_grob_property ("thickness"))
820         // URG
821         * me->paper_l ()->get_var ("stafflinethickness");
822
823       w = w/2 <? nw_f;
824       Molecule a;
825       if (lhalfs)               // generates warnings if not
826         a =  Lookup::beam (dydx, w + stem_w, thick);
827       a.translate (Offset (-w, -w * dydx));
828       //a.translate_axis (stem_w/2, X_AXIS);
829       for (int j = 0; j  < lhalfs; j++)
830         {
831           Molecule b (a);
832           b.translate_axis (-dir * bdy * (lwholebeams+j), Y_AXIS);
833           leftbeams.add_molecule (b);
834         }
835     }
836
837   if (next)
838     {
839       int rhalfs  = Stem::beam_count (here,RIGHT)
840         - Stem::beam_count (next,LEFT);
841       int rwholebeams= Stem::beam_count (here,RIGHT)
842         <? Stem::beam_count (next,LEFT) ;
843
844       Real w = next->relative_coordinate (0, X_AXIS)
845         - here->relative_coordinate (0, X_AXIS);
846
847       Real stem_w = gh_scm2double (next->get_grob_property ("thickness"))
848         // URG
849         * me->paper_l ()->get_var ("stafflinethickness");
850
851       Molecule a = Lookup::beam (dydx, w + stem_w, thick);
852       a.translate_axis (- stem_w/2, X_AXIS);
853       int j = 0;
854       Real gap_f = 0;
855       
856       SCM gap = me->get_grob_property ("gap");
857       if (gh_number_p (gap))
858         {
859           int gap_i = gh_scm2int ((gap));
860           int nogap = rwholebeams - gap_i;
861           
862           for (; j  < nogap; j++)
863             {
864               Molecule b (a);
865               b.translate_axis (-dir  * bdy * j, Y_AXIS);
866               rightbeams.add_molecule (b);
867             }
868           if (Stem::invisible_b (here))
869             gap_f = nw_f;
870           else
871             gap_f = nw_f / 2;
872           w -= 2 * gap_f;
873           a = Lookup::beam (dydx, w + stem_w, thick);
874         }
875
876       for (; j  < rwholebeams; j++)
877         {
878           Molecule b (a);
879           Real tx = 0;
880           if (Stem::invisible_b (here))
881             // ugh, see chord-tremolo.ly
882             tx = (-dir + 1) / 2 * nw_f * 1.5 + gap_f/4;
883           else
884             tx = gap_f;
885           b.translate (Offset (tx, -dir * bdy * j));
886           rightbeams.add_molecule (b);
887         }
888
889       w = w/2 <? nw_f;
890       if (rhalfs)
891         a = Lookup::beam (dydx, w, thick);
892
893       for (; j  < rwholebeams + rhalfs; j++)
894         {
895           Molecule b (a);
896           b.translate_axis (- dir * bdy * j, Y_AXIS);
897           rightbeams.add_molecule (b);
898         }
899
900     }
901   leftbeams.add_molecule (rightbeams);
902
903   /*
904     Does beam quanting think  of the asymetry of beams? 
905     Refpoint is on bottom of symbol. (FIXTHAT) --hwn.
906    */
907   return leftbeams;
908 }
909
910 MAKE_SCHEME_CALLBACK (Beam,brew_molecule,1);
911 SCM
912 Beam::brew_molecule (SCM smob)
913 {
914   Grob * me =unsmob_grob (smob);
915
916   Molecule mol;
917   if (!gh_pair_p (me->get_grob_property ("stems")))
918     return SCM_EOL;
919   Real x0,dx;
920   Link_array<Item>stems = 
921     Pointer_group_interface__extract_elements (me, (Item*) 0, "stems");  
922   if (visible_stem_count (me))
923     {
924   // ugh -> use commonx
925       x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
926       dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
927     }
928   else
929     {
930       x0 = stems[0]->relative_coordinate (0, X_AXIS);
931       dx = stems.top ()->relative_coordinate (0, X_AXIS) - x0;
932     }
933   
934
935
936   /*
937     TODO: the naming of the grob properties sucks.
938    */
939   SCM dy_s = me->get_grob_property ("dy");
940   SCM y_s = me->get_grob_property ("y");
941
942   
943   Real dy = gh_number_p (dy_s) ? gh_scm2double (dy_s) : 0.0;
944   Real dydx = dy && dx ? dy/dx : 0;
945   Real y = gh_number_p (y_s) ? gh_scm2double (y_s) : 0.0;
946
947
948   for (int j=0; j <stems.size (); j++)
949     {
950       Item *i = stems[j];
951       Item * prev = (j > 0)? stems[j-1] : 0;
952       Item * next = (j < stems.size ()-1) ? stems[j+1] :0;
953
954       Molecule sb = stem_beams (me, i, next, prev, dy, dydx);
955       Real x = i->relative_coordinate (0, X_AXIS)-x0;
956       sb.translate (Offset (x, x * dydx + y));
957       mol.add_molecule (sb);
958     }
959   mol.translate_axis (x0 
960     - dynamic_cast<Spanner*> (me)->get_bound (LEFT)->relative_coordinate (0, X_AXIS), X_AXIS);
961
962   return mol.smobbed_copy ();
963 }
964
965 int
966 Beam::forced_stem_count (Grob*me) 
967 {
968   Link_array<Item>stems = 
969     Pointer_group_interface__extract_elements (me, (Item*) 0, "stems");
970   int f = 0;
971   for (int i=0; i < stems.size (); i++)
972     {
973       Item *s = stems[i];
974
975       if (Stem::invisible_b (s))
976         continue;
977
978       if (( (int)Stem::chord_start_f (s)) 
979         && (Stem::get_direction (s) != Stem::get_default_dir (s)))
980         f++;
981     }
982   return f;
983 }
984
985
986
987
988 /* TODO:
989    use filter and standard list functions.
990  */
991 int
992 Beam::visible_stem_count (Grob*me) 
993 {
994   Link_array<Item>stems = 
995     Pointer_group_interface__extract_elements (me, (Item*) 0, "stems");
996   int c = 0;
997   for (int i = stems.size (); i--;)
998     {
999       if (!Stem::invisible_b (stems[i]))
1000         c++;
1001     }
1002   return c;
1003 }
1004
1005 Item*
1006 Beam::first_visible_stem (Grob*me) 
1007 {
1008   Link_array<Item>stems = 
1009     Pointer_group_interface__extract_elements (me, (Item*) 0, "stems");
1010   
1011   for (int i = 0; i < stems.size (); i++)
1012     {
1013       if (!Stem::invisible_b (stems[i]))
1014         return stems[i];
1015     }
1016   return 0;
1017 }
1018
1019 Item*
1020 Beam::last_visible_stem (Grob*me) 
1021 {
1022   Link_array<Item>stems = 
1023     Pointer_group_interface__extract_elements (me, (Item*) 0, "stems");
1024   for (int i = stems.size (); i--;)
1025     {
1026       if (!Stem::invisible_b (stems[i]))
1027         return stems[i];
1028     }
1029   return 0;
1030 }
1031
1032
1033 /*
1034   [TODO]
1035   handle rest under beam (do_post: beams are calculated now)
1036   what about combination of collisions and rest under beam.
1037
1038   Should lookup
1039     
1040     rest -> stem -> beam -> interpolate_y_position ()
1041 */
1042 MAKE_SCHEME_CALLBACK (Beam,rest_collision_callback,2);
1043 SCM
1044 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1045 {
1046   Grob *rest = unsmob_grob (element_smob);
1047   Axis a = (Axis) gh_scm2int (axis);
1048   
1049   assert (a == Y_AXIS);
1050
1051   Grob * st = unsmob_grob (rest->get_grob_property ("stem"));
1052   Grob * stem = st;
1053   if (!stem)
1054     return gh_double2scm (0.0);
1055   Grob * beam = unsmob_grob (stem->get_grob_property ("beam"));
1056   if (!beam || !Beam::has_interface (beam) || !Beam::visible_stem_count (beam))
1057     return gh_double2scm (0.0);
1058
1059   // make callback for rest from this.
1060   Real beam_dy = 0;
1061   Real beam_y = 0;
1062
1063
1064   // todo: make sure this calced already.
1065   SCM s = beam->get_grob_property ("dy");
1066   if (gh_number_p (s))
1067     beam_dy = gh_scm2double (s);
1068   
1069   s = beam->get_grob_property ("y");
1070   if (gh_number_p (s))
1071     beam_y = gh_scm2double (s);
1072   
1073   // ugh -> use commonx
1074   Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1075   Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1076   Real dydx = beam_dy && dx ? beam_dy/dx : 0;
1077
1078   Direction d = Stem::get_direction (stem);
1079   Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + beam_y;
1080
1081   Real staff_space =   Staff_symbol_referencer::staff_space (rest);
1082
1083   
1084   Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space ; // refp??
1085
1086   Real minimum_dist
1087     = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1088   Real dist =
1089     minimum_dist +  -d  * (beamy - rest_dim) >? 0;
1090
1091   int stafflines = Staff_symbol_referencer::line_count (rest);
1092
1093   // move discretely by half spaces.
1094   int discrete_dist = int (ceil (dist));
1095
1096   // move by whole spaces inside the staff.
1097   if (discrete_dist < stafflines+1)
1098     discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1099
1100   return gh_double2scm (-d *  discrete_dist);
1101 }
1102
1103
1104 bool
1105 Beam::has_interface (Grob*me)
1106 {
1107   return me->has_interface (ly_symbol2scm ("beam-interface"));
1108 }
1109