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