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