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