]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
9b71b10331057076c8945abe4af90c3979bfb1bc
[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--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7   Jan Nieuwenhuizen <janneke@gnu.org>
8 */
9
10 /*
11 TODO:
12
13   - Determine auto knees based on positions if it's set by the user.
14
15   - the code is littered with * and / staff_space calls for
16     #'positions. Consider moving to real-world coordinates?
17
18     Problematic issue is user tweaks (user tweaks are in staff-coordinates.) 
19   
20 Notes:
21
22  - Stems run to the Y-center of the beam.
23   
24  - beam_translation is the offset between Y centers of the beam.
25
26 */
27
28
29 #include <math.h> // tanh.
30
31 #include "stencil.hh" 
32 #include "directional-element-interface.hh"
33 #include "beaming.hh"
34 #include "beam.hh"
35 #include "misc.hh"
36 #include "least-squares.hh"
37 #include "stem.hh"
38 #include "output-def.hh"
39 #include "lookup.hh"
40 #include "group-interface.hh"
41 #include "staff-symbol-referencer.hh"
42 #include "item.hh"
43 #include "spanner.hh"
44 #include "warn.hh"
45
46
47 #if DEBUG_QUANTING
48 #include "text-item.hh"  // debug output.
49 #include "font-interface.hh"  // debug output.
50 #endif
51
52
53 void
54 Beam::add_stem (Grob *me, Grob *s)
55 {
56   Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
57   
58   s->add_dependency (me);
59
60   assert (!Stem::get_beam (s));
61   s->set_property ("beam", me->self_scm ());
62
63   add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
64 }
65
66
67 Real
68 Beam::get_thickness (Grob * me)
69 {
70   return robust_scm2double (me->get_property ("thickness"), 0)
71     * Staff_symbol_referencer::staff_space (me);
72 }
73
74 /* Return the translation between 2 adjoining beams. */
75 Real
76 Beam::get_beam_translation (Grob *me)
77 {
78   SCM func = me->get_property ("space-function");
79
80   if (ly_c_procedure_p (func))
81     {
82       SCM s = scm_call_2 (func, me->self_scm (), scm_int2num (get_beam_count (me)));
83       return scm_to_double (s);
84     }
85   else
86     {
87       return 0.81;
88     }
89 }
90
91 /* Maximum beam_count. */
92 int
93 Beam::get_beam_count (Grob *me) 
94 {
95   int m = 0;
96   for (SCM s = me->get_property ("stems"); ly_c_pair_p (s); s = ly_cdr (s))
97     {
98       Grob *stem = unsmob_grob (ly_car (s));
99       m = m >? (Stem::beam_multiplicity (stem).length () + 1);
100     }
101   return m;
102 }
103
104
105 /*
106   Space return space between beams.
107  */
108 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
109 SCM
110 Beam::space_function (SCM smob, SCM beam_count)
111 {
112   Grob *me = unsmob_grob (smob);
113   
114   Real staff_space = Staff_symbol_referencer::staff_space (me);
115   Real line = Staff_symbol_referencer::line_thickness (me);
116   Real thickness = get_thickness (me);
117   
118   Real beam_translation = scm_to_int (beam_count) < 4
119     ? (2*staff_space + line - thickness) / 2.0
120     : (3*staff_space + line - thickness) / 3.0;
121   
122   return scm_make_real (beam_translation);
123 }
124
125
126 /* After pre-processing all directions should be set.
127    Several post-processing routines (stem, slur, script) need stem/beam
128    direction.
129    Currenly, this means that beam has set all stem's directions.
130    [Alternatively, stems could set its own directions, according to
131    their beam, during 'final-pre-processing'.] */
132 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
133 SCM
134 Beam::before_line_breaking (SCM smob)
135 {
136   Grob *me =  unsmob_grob (smob);
137
138   /* Beams with less than 2 two stems don't make much sense, but could happen
139      when you do
140      
141      [r8 c8 r8].
142      
143     For a beam that  only has one stem, we try to do some disappearance magic:
144     we revert the flag, and move on to The Eternal Engraving Fields. */
145
146   int count = visible_stem_count (me);
147   if (count < 2)
148     {
149       me->warning (_ ("beam has less than two visible stems"));
150
151       SCM stems = me->get_property ("stems");
152       if (scm_ilength (stems) == 1)
153         {
154           me->warning (_ ("removing beam with less than two stems"));
155
156           unsmob_grob (ly_car (stems))->set_property ("beam", SCM_EOL);
157           me->suicide ();
158
159           return SCM_UNSPECIFIED;
160         }
161       else if (scm_ilength (stems) == 0)
162         {
163           me->suicide ();
164           return SCM_UNSPECIFIED;         
165         }
166     }
167   if (count >= 1)
168     {
169       Direction d = get_default_dir (me);
170
171       consider_auto_knees (me);
172       set_stem_directions (me, d);
173
174       connect_beams (me);
175
176       set_stem_shorten (me);
177     }
178
179   return SCM_EOL;
180 }
181
182
183 /*
184   We want a maximal number of shared beams, but if there is choice, we
185   take the one that is closest to the end of the stem. This is for situations like
186
187        x
188       |
189       |
190   |===|
191   |=
192   |
193   x
194
195   
196  */
197 int
198 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
199                                     Direction left_dir,
200                                     Direction right_dir)
201 {
202   Slice lslice = int_list_to_slice (ly_cdr (left_beaming));
203
204   int best_count = 0;
205   int best_start = 0;
206   for (int i = lslice[-left_dir];
207        (i - lslice[left_dir])* left_dir <= 0 ; i+= left_dir) 
208     {
209       int count =0;
210       for ( SCM s = ly_car (right_beaming); ly_c_pair_p (s); s = ly_cdr (s))
211         {
212           int k = - right_dir * scm_to_int (ly_car (s)) + i;
213           if (scm_c_memq (scm_int2num (k), left_beaming) != SCM_BOOL_F)
214             count ++;
215         }
216
217       if (count >= best_count)
218         {
219           best_count = count; 
220           best_start = i;
221         }
222     }
223
224   return best_start;
225 }
226
227 void
228 Beam::connect_beams (Grob *me)
229 {
230   Link_array<Grob> stems=
231     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
232
233   Slice last_int;
234   last_int.set_empty ();
235   SCM last_beaming = SCM_EOL;
236   Direction last_dir = CENTER;
237   for (int i = 0; i< stems.size (); i++)
238     {
239       Grob *this_stem = stems[i];
240       SCM this_beaming = this_stem->get_property ("beaming");
241
242       Direction this_dir = get_grob_direction (this_stem);
243       if (ly_c_pair_p (last_beaming) && ly_c_pair_p (this_beaming))
244         {
245           int start_point = position_with_maximal_common_beams
246             (last_beaming, this_beaming,
247              last_dir, this_dir);
248           
249           Direction d = LEFT;
250           Slice new_slice ; 
251           do
252             {
253               if (d == RIGHT && i == stems.size ()-1)
254                 continue;
255               
256               new_slice.set_empty ();
257               SCM s = index_get_cell (this_beaming, d);
258               for (; ly_c_pair_p (s); s = ly_cdr (s))
259                 {
260                   int new_beam_pos =
261                     start_point - this_dir * scm_to_int (ly_car (s));
262
263                   new_slice.add_point (new_beam_pos);
264                   scm_set_car_x (s, scm_int2num (new_beam_pos));
265                 }
266
267
268             }
269           while (flip (&d) != LEFT);
270
271           if (!new_slice.is_empty ())
272             last_int =  new_slice;
273         }
274       else
275         {
276           scm_set_car_x ( this_beaming, SCM_EOL);
277           SCM s = ly_cdr (this_beaming);
278           for (; ly_c_pair_p (s); s = ly_cdr (s))
279             {
280               int np = - this_dir * scm_to_int (ly_car (s));
281               scm_set_car_x (s, scm_int2num (np));
282               last_int.add_point (np);
283             }
284         }
285
286       if (i == stems.size () -1)
287         {
288           scm_set_cdr_x (this_beaming, SCM_EOL);
289         }
290
291       if (scm_ilength (ly_cdr (this_beaming)) > 0)
292         {
293           last_beaming = this_beaming;
294           last_dir = this_dir;
295         }
296     }
297  }
298
299
300 /*
301   TODO: should not make beams per stem, but per Y-level.
302  */
303 MAKE_SCHEME_CALLBACK (Beam, print, 1);
304 SCM
305 Beam::print (SCM grob)
306 {
307   Spanner *me = unsmob_spanner (grob);
308   position_beam (me);
309   
310   Link_array<Grob> stems=
311     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
312   Grob* xcommon = common_refpoint_of_array (stems, me, X_AXIS);
313
314   xcommon = me->get_bound (LEFT)->common_refpoint (xcommon, X_AXIS);
315   xcommon = me->get_bound (RIGHT)->common_refpoint (xcommon, X_AXIS);
316
317   Real x0, dx;
318   if (visible_stem_count (me))
319     {
320       // ugh -> use commonx
321       x0 = first_visible_stem (me)->relative_coordinate (xcommon, X_AXIS);
322       dx = last_visible_stem (me)->relative_coordinate (xcommon, X_AXIS) - x0;
323     }
324   else
325     {
326       x0 = stems[0]->relative_coordinate (xcommon, X_AXIS);
327       dx = stems.top ()->relative_coordinate (xcommon, X_AXIS) - x0;
328     }
329
330   SCM posns = me->get_property ("positions");
331   Drul_array<Real> pos;
332   if (!is_number_pair (posns))
333     {
334       programming_error ("No beam posns");
335       pos = Interval (0,0);
336     }
337   else
338     pos= ly_scm2realdrul (posns);
339
340   scale_drul (&pos,  Staff_symbol_referencer::staff_space (me));
341   
342   Real dy = pos[RIGHT] - pos[LEFT];
343   Real slope = (dy && dx) ? dy/dx : 0;
344   
345   Real thick = get_thickness (me);
346   Real bdy = get_beam_translation (me);
347
348   SCM last_beaming = SCM_EOL;
349   Real last_xposn = -1;
350   Real last_stem_width = -1 ;
351
352   Real gap_length = robust_scm2double (me->get_property ("gap"), 0.0);
353   
354   Stencil the_beam;
355   Real lt = me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
356   
357   for (int i = 0; i<= stems.size (); i++)
358     {
359       Grob * st = (i < stems.size ()) ? stems[i] : 0;
360       
361       SCM this_beaming = st ? st->get_property ("beaming") : SCM_EOL;
362       Real xposn = st ? st->relative_coordinate (xcommon, X_AXIS) : 0.0;
363       Real stem_width = st ? robust_scm2double (st->get_property ("thickness"), 1.0) *lt : 0 ;
364       Direction stem_dir = st ? to_dir (st->get_property ("direction")) : CENTER;
365       /*
366         We do the space left of ST, with lfliebertjes pointing to the
367         right from the left stem, and rfliebertjes pointing left from
368         right stem.
369        */
370       SCM left = (i > 0) ? ly_cdr (last_beaming) : SCM_EOL;
371       SCM right = st ? ly_car (this_beaming) : SCM_EOL;
372
373       Array<int> full_beams;
374       Array<int> lfliebertjes;
375       Array<int> rfliebertjes;    
376
377       for (SCM s = left;
378            ly_c_pair_p (s); s =ly_cdr (s))
379         {
380           int b = scm_to_int (ly_car (s));
381           if (scm_c_memq (ly_car (s), right) != SCM_BOOL_F)
382             {
383               full_beams.push (b);
384             }
385           else
386             {
387               lfliebertjes.push (b); 
388             }
389         }
390       for (SCM s = right;
391            ly_c_pair_p (s); s =ly_cdr (s))
392         {
393           int b = scm_to_int (ly_car (s));
394           if (scm_c_memq (ly_car (s), left) == SCM_BOOL_F)
395             {
396               rfliebertjes.push (b);
397             }
398         }
399
400       /*
401         how much to stick out for beams across linebreaks
402        */
403       Real break_overshoot = 3.0;
404       Real w = (i > 0 && st) ? (xposn - last_xposn) : break_overshoot;
405
406       Real stem_offset =0.0;
407       if (i > 0)
408         {
409           w += last_stem_width / 2;
410           stem_offset = -last_stem_width / 2;
411         }
412
413       if (st)
414         w += stem_width/ 2 ;
415       
416
417       Real blot = me->get_paper ()->get_dimension (ly_symbol2scm ("blotdiameter"));
418       Stencil whole = Lookup::beam (slope, w, thick, blot);
419       Stencil gapped;
420
421       int gap_count = 0;
422       if (scm_is_number (me->get_property ("gap-count")))
423         {
424           gap_count = scm_to_int (me->get_property ("gap-count"));
425           gapped = Lookup::beam (slope, w - 2 * gap_length, thick, blot);
426
427           full_beams.sort (default_compare);
428           if (stem_dir == UP)
429             full_beams.reverse ();
430         }
431
432       int k = 0;
433       for (int j = full_beams.size (); j--;)
434         {
435           Stencil b (whole);
436           
437           if (k++ < gap_count)
438             {
439               b = gapped;
440               b.translate_axis (gap_length, X_AXIS);
441             }
442           b.translate_axis (last_xposn -  x0 + stem_offset, X_AXIS);
443           b.translate_axis (slope * (last_xposn - x0) + bdy * full_beams[j], Y_AXIS);
444
445           the_beam.add_stencil (b);           
446         }
447           
448       if (lfliebertjes.size () || rfliebertjes.size ())
449         {
450           Real nw_f;
451
452           if (st)
453             {
454               int t = Stem::duration_log (st); 
455
456               SCM proc = me->get_property ("flag-width-function");
457               SCM result = scm_call_1 (proc, scm_int2num (t));
458               nw_f = scm_to_double (result);
459             }
460           else
461             nw_f = break_overshoot / 2;
462               
463           /* Half beam should be one note-width,
464              but let's make sure two half-beams never touch */
465           Real lw = nw_f;
466           Real rw = nw_f;
467           if (i > 0)
468             rw = nw_f <? ( (xposn - last_xposn) / 2);
469           else
470             /*
471               TODO: 0.5 is a guess.
472             */
473             rw = xposn - me->get_bound (LEFT)->extent (xcommon, X_AXIS)[RIGHT]
474               - 0.5;
475           
476           if (st)
477             lw = nw_f <? ( (xposn - last_xposn) / 2);
478           else
479             lw = me->get_bound (RIGHT)->relative_coordinate (xcommon, X_AXIS)
480               - last_xposn;
481
482           Stencil rhalf = Lookup::beam (slope, rw, thick, blot);
483           Stencil lhalf = Lookup::beam (slope, lw, thick, blot);
484           for (int j = lfliebertjes.size (); j--;)
485             {
486               Stencil b (lhalf);
487               b.translate_axis (last_xposn -  x0, X_AXIS);
488               b.translate_axis (slope * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
489               the_beam.add_stencil (b);       
490             }
491           for (int j = rfliebertjes.size (); j--;)
492             {
493               Stencil b (rhalf);
494               b.translate_axis (xposn -  x0 - rw , X_AXIS);
495               b.translate_axis (slope * (xposn-x0 -rw) + bdy * rfliebertjes[j], Y_AXIS);
496               the_beam.add_stencil (b);       
497             }
498         }
499
500
501       last_xposn = xposn;
502       last_stem_width = stem_width;
503       last_beaming = this_beaming;
504     }
505
506   the_beam.translate_axis (x0 - me->relative_coordinate (xcommon, X_AXIS), X_AXIS);
507   the_beam.translate_axis (pos[LEFT], Y_AXIS);
508
509 #if (DEBUG_QUANTING)
510   SCM quant_score = me->get_property ("quant-score");
511   if (to_boolean (me->get_paper ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting")))
512       && scm_is_string (quant_score))
513     {
514       
515       /*
516         This code prints the demerits for each beam. Perhaps this
517         should be switchable for those who want to twiddle with the
518         parameters.
519       */
520       String str;
521       SCM properties = Font_interface::text_font_alist_chain (me);
522
523       Direction stem_dir = stems.size() ? to_dir (stems[0]->get_property ("direction")) : UP;
524     
525       Stencil tm = *unsmob_stencil (Text_item::interpret_markup
526          (me->get_paper ()->self_scm (), properties, quant_score));
527       the_beam.add_at_edge (Y_AXIS, stem_dir, tm, 1.0, 0);
528     }
529 #endif
530   
531   return the_beam.smobbed_copy ();
532 }
533   
534
535
536
537 Direction
538 Beam::get_default_dir (Grob *me) 
539 {
540   Drul_array<int> total;
541   total[UP]  = total[DOWN] = 0;
542   Drul_array<int> count; 
543   count[UP]  = count[DOWN] = 0;
544   Direction d = DOWN;
545
546   Link_array<Grob> stems=
547         Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
548
549   for (int i=0; i < stems.size (); i++)
550     do {
551       Grob *s = stems[i];
552       Direction sd = get_grob_direction (s);
553
554       int center_distance = int (- d * Stem::head_positions (s) [-d]) >? 0;
555       int current = sd  ? (1 + d * sd)/2 : center_distance;
556
557       if (current)
558         {
559           total[d] += current;
560           count[d] ++;
561         }
562     } while (flip (&d) != DOWN);
563   
564   SCM func = me->get_property ("dir-function");
565   SCM s = scm_call_2 (func,
566                     scm_cons (scm_int2num (count[UP]),
567                              scm_int2num (count[DOWN])),
568                     scm_cons (scm_int2num (total[UP]),
569                              scm_int2num (total[DOWN])));
570
571   if (scm_is_number (s) && scm_to_int (s))
572     return to_dir (s);
573   
574   /* If dir is not determined: get default */
575   return to_dir (me->get_property ("neutral-direction"));
576 }
577
578
579 /* Set all stems with non-forced direction to beam direction.
580    Urg: non-forced should become `without/with unforced' direction,
581    once stem gets cleaned-up. */
582 void
583 Beam::set_stem_directions (Grob *me, Direction d)
584 {
585   Link_array<Grob> stems
586     =Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
587   
588   for (int i=0; i <stems.size (); i++)
589     {
590       Grob *s = stems[i];
591   
592       SCM forcedir = s->get_property ("direction");
593       if (!to_dir (forcedir))
594         set_grob_direction (s,  d);
595     }
596 }
597
598 /*
599   A union of intervals in the real line.
600
601   Abysmal performance (quadratic) for large N, hopefully we don't have
602   that large N. In any case, this should probably be rewritten to use
603   a balanced tree.
604  */
605 struct Int_set
606 {
607   Array<Interval> allowed_regions_;
608
609   Int_set ()
610   {
611     set_full ();
612   }
613
614   void set_full ()
615   {
616     allowed_regions_.clear ();
617     Interval s;
618     s.set_full ();
619     allowed_regions_.push (s);
620   }
621
622   void remove_interval (Interval rm)
623   {
624     for (int i = 0; i < allowed_regions_.size (); )
625       {
626         Interval s = rm;
627
628         s.intersect (allowed_regions_[i]);
629
630         if (!s.is_empty ())
631           {
632             Interval before = allowed_regions_[i];
633             Interval after = allowed_regions_[i];
634
635             before[RIGHT] = s[LEFT];
636             after[LEFT] = s[RIGHT];
637
638             if (!before.is_empty () && before.length () > 0.0)
639               {
640                 allowed_regions_.insert (before, i);
641                 i++;
642               }
643             allowed_regions_.del (i);
644             if (!after.is_empty () && after.length () > 0.0)
645               {
646                 allowed_regions_.insert (after, i);
647                 i++;
648               }
649           }
650         else
651           i++;
652       }
653   }
654 };
655
656
657 /*
658   Only try horizontal beams for knees.  No reliable detection of
659   anything else is possible here, since we don't know funky-beaming
660   settings, or X-distances (slopes!)  People that want sloped
661   knee-beams, should set the directions manually.
662  */
663 void
664 Beam::consider_auto_knees (Grob* me)
665 {
666   SCM scm = me->get_property ("auto-knee-gap");
667   if (!scm_is_number (scm))
668     return ;
669
670   Real threshold = scm_to_double (scm);
671   
672   Int_set gaps;
673
674   gaps.set_full ();
675
676   Link_array<Grob> stems=
677     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
678       
679   Grob *common = common_refpoint_of_array (stems, me,  Y_AXIS);
680   Real staff_space = Staff_symbol_referencer::staff_space (me);
681   
682   Array<Interval> hps_array;  
683   for (int i=0; i < stems.size (); i++)
684     {
685       Grob* stem = stems[i];
686       if (Stem::is_invisible (stem))
687         continue;
688
689       Interval hps = Stem::head_positions (stem);
690       if (!hps.is_empty ())
691         {
692           hps[LEFT] += -1;
693           hps[RIGHT] += 1; 
694           hps *= staff_space * 0.5 ;
695
696           /*
697             We could subtract beam Y position, but this routine only
698             sets stem directions, a constant shift does not have an
699             influence.
700             
701            */
702           hps += stem->relative_coordinate (common, Y_AXIS);
703
704           if (to_dir (stem->get_property ("direction")))
705             {
706               Direction stemdir = to_dir (stem->get_property ("direction"));
707               hps[-stemdir] = - stemdir * infinity_f;
708             }
709         }
710       hps_array.push (hps);
711
712       gaps.remove_interval (hps);
713     }
714
715   Interval max_gap;
716   Real max_gap_len =0.0;
717
718   for (int i  = gaps.allowed_regions_.size () -1;  i >=  0 ; i--)
719     {
720       Interval gap = gaps.allowed_regions_[i];
721
722       /*
723         the outer gaps are not knees.
724        */
725       if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
726         continue;
727       
728       if (gap.length () >= max_gap_len)
729         {
730           max_gap_len = gap.length ();
731           max_gap = gap;
732         }
733     }
734
735   if (max_gap_len > threshold)
736     {
737       int j = 0;
738       for (int i = 0; i < stems.size (); i++)
739         {
740           Grob* stem = stems[i];
741           if (Stem::is_invisible (stem))
742             continue;
743
744           Interval hps = hps_array[j++];
745
746
747           Direction d =  (hps.center () < max_gap.center ()) ?
748             UP : DOWN ;
749           
750           stem->set_property ("direction", scm_int2num (d));
751           
752           hps.intersect (max_gap);
753           assert (hps.is_empty () || hps.length () < 1e-6 );
754         }
755     }
756 }
757
758
759
760 /* Set stem's shorten property if unset.
761
762  TODO:
763    take some y-position (chord/beam/nearest?) into account
764    scmify forced-fraction
765  
766   This is done in beam because the shorten has to be uniform over the
767   entire beam.
768
769 */
770 void
771 Beam::set_stem_shorten (Grob *me)
772 {
773   /*
774     shortening looks silly for x staff beams
775    */
776   if (is_knee (me))
777     return ;
778   
779   Real forced_fraction = 1.0 * forced_stem_count (me)
780     / visible_stem_count (me);
781
782   int beam_count = get_beam_count (me);
783
784   SCM shorten_list = me->get_property ("beamed-stem-shorten");
785   if (shorten_list == SCM_EOL)
786     return;
787
788   Real staff_space = Staff_symbol_referencer::staff_space (me);
789   
790   SCM shorten_elt =
791     robust_list_ref (beam_count -1, shorten_list);
792   Real shorten_f = scm_to_double (shorten_elt) * staff_space;
793
794   /* your similar cute comment here */
795   shorten_f *= forced_fraction;
796
797   if (shorten_f)
798     me->set_property ("shorten", scm_make_real (shorten_f));
799 }
800
801 /*  Call list of y-dy-callbacks, that handle setting of
802     grob-properties
803
804 */
805 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
806 SCM
807 Beam::after_line_breaking (SCM smob)
808 {
809   Grob *me = unsmob_grob (smob);
810
811   position_beam (me);
812   return SCM_UNSPECIFIED;
813 }
814
815 void
816 Beam::position_beam (Grob *me)
817 {
818   if (to_boolean (me->get_property ("positioning-done")))
819     return ;
820
821   me->set_property ("positioning-done", SCM_BOOL_T);
822
823   /* Copy to mutable list. */
824   SCM s = ly_deep_copy (me->get_property ("positions"));
825   me->set_property ("positions", s);
826
827   if (ly_car (s) == SCM_BOOL_F)
828     {
829       // one wonders if such genericity is necessary  --hwn.
830       SCM callbacks = me->get_property ("position-callbacks");
831       for (SCM i = callbacks; ly_c_pair_p (i); i = ly_cdr (i))
832         scm_call_1 (ly_car (i), me->self_scm ());
833     }
834
835   set_stem_lengths (me);  
836 }
837
838
839 void
840 set_minimum_dy (Grob *me, Real * dy)
841 {
842   if (*dy)
843     {
844       /*
845         If dy is smaller than the smallest quant, we
846         get absurd direction-sign penalties. 
847       */
848           
849       Real ss = Staff_symbol_referencer::staff_space (me);
850       Real thickness = Beam::get_thickness (me) / ss ;
851       Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
852       Real sit = (thickness - slt) / 2;
853       Real inter = 0.5;
854       Real hang = 1.0 - (thickness - slt) / 2;
855           
856       *dy = sign (*dy) * (fabs (*dy)
857                         >?
858                         (sit <? inter <? hang));
859     }
860 }
861
862 /*
863   Compute  a first approximation to the beam slope.
864  */
865 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
866 SCM
867 Beam::least_squares (SCM smob)
868 {
869   Grob *me = unsmob_grob (smob);
870
871   int count = visible_stem_count (me);
872   Interval pos (0, 0);
873   
874   if (count < 1)
875     {
876       me->set_property ("positions", ly_interval2scm (pos));
877       return SCM_UNSPECIFIED;
878     }
879
880
881   Array<Real> x_posns ;
882   Link_array<Grob> stems=
883     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
884   Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
885   Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);  
886
887   Real my_y = me->relative_coordinate (commony, Y_AXIS);
888   
889   Grob *fvs  = first_visible_stem (me);
890   Grob *lvs  = last_visible_stem (me);
891   
892   Interval ideal (Stem::get_stem_info (fvs).ideal_y_
893                   + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
894                   Stem::get_stem_info (lvs).ideal_y_
895                   + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
896   
897   Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
898   for (int i=0; i < stems.size (); i++)
899     {
900       Grob* s = stems[i];
901
902       Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
903       x_posns.push (x);
904     }
905   Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
906
907   
908   Real y =0;  
909   Real slope = 0;
910   Real dy = 0;
911   
912   if (!ideal.delta ())
913     {
914       Interval chord (Stem::chord_start_y (first_visible_stem (me)),
915                       Stem::chord_start_y (last_visible_stem (me)));
916
917       /* Simple beams (2 stems) on middle line should be allowed to be
918          slightly sloped.
919          
920          However, if both stems reach middle line,
921          ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
922
923          For that case, we apply artificial slope */
924       if (!ideal[LEFT] && chord.delta () && count == 2)
925         {
926           /* FIXME. -> UP */
927           Direction d = (Direction) (sign (chord.delta ()) * UP);
928           pos[d] = get_thickness (me) / 2;
929           pos[-d] = - pos[d];
930         }
931       else
932         {
933           pos = ideal;
934         }
935
936       /*
937         For broken beams this doesn't work well. In this case, the
938         slope esp. of the first part of a broken beam should predict
939         where the second part goes.
940       */
941       me->set_property ("least-squares-dy",
942                         scm_make_real (pos[RIGHT] - pos[LEFT]));
943     }
944   else
945     {
946       Array<Offset> ideals;
947       for (int i=0; i < stems.size (); i++)
948         {
949           Grob* s = stems[i];
950           if (Stem::is_invisible (s))
951             continue;
952           ideals.push (Offset (x_posns[i],
953                                Stem::get_stem_info (s).ideal_y_
954                                + s->relative_coordinate (commony, Y_AXIS)
955                                - my_y));
956         }
957       
958       minimise_least_squares (&slope, &y, ideals);
959
960       dy = slope * dx;
961
962       set_minimum_dy (me,&dy);
963       me->set_property ("least-squares-dy", scm_make_real (dy));
964       pos = Interval (y, (y+dy));
965     }
966
967   /*
968     "position" is relative to the staff.
969    */
970   scale_drul (&pos,  1/ Staff_symbol_referencer::staff_space (me)); 
971   
972   me->set_property ("positions", ly_interval2scm (pos));
973  
974   return SCM_UNSPECIFIED;
975 }
976
977
978 /*
979   We can't combine with previous function, since check concave and
980   slope damping comes first.
981
982   TODO: we should use the concaveness to control the amount of damping
983   applied.
984   
985 */
986 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
987 SCM
988 Beam::shift_region_to_valid (SCM grob)
989 {
990   Grob *me = unsmob_grob (grob);
991   /*
992     Code dup.
993    */
994   Array<Real> x_posns ;
995   Link_array<Grob> stems=
996     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
997   Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
998   Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);  
999
1000   Grob *fvs = first_visible_stem (me);
1001
1002   if (!fvs)
1003     return SCM_UNSPECIFIED;
1004     
1005   Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
1006   for (int i=0; i < stems.size (); i++)
1007     {
1008       Grob* s = stems[i];
1009
1010       Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
1011       x_posns.push (x);
1012     }
1013
1014   Grob *lvs = last_visible_stem (me);
1015   if (!lvs)
1016     return SCM_UNSPECIFIED;
1017   
1018   Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
1019
1020   Drul_array<Real> pos = ly_scm2interval ( me->get_property ("positions"));
1021
1022   scale_drul (&pos,  Staff_symbol_referencer::staff_space (me));
1023   
1024   Real dy = pos[RIGHT] - pos[LEFT];
1025   Real y = pos[LEFT];
1026   Real slope =dy/dx;
1027
1028   
1029   /*
1030     Shift the positions so that we have a chance of finding good
1031     quants (i.e. no short stem failures.)
1032    */
1033   Interval feasible_left_point;
1034   feasible_left_point.set_full ();
1035   for (int i=0; i < stems.size (); i++)
1036     {
1037       Grob* s = stems[i];
1038       if (Stem::is_invisible (s))
1039         continue;
1040
1041       Direction d = Stem::get_direction (s);
1042
1043       Real left_y =
1044         Stem::get_stem_info (s).shortest_y_
1045         - slope * x_posns [i];
1046
1047       /*
1048         left_y is now relative to the stem S. We want relative to
1049         ourselves, so translate:
1050        */
1051       left_y += 
1052         + s->relative_coordinate (commony, Y_AXIS)
1053         - me->relative_coordinate (commony, Y_AXIS);
1054
1055       Interval flp ;
1056       flp.set_full ();
1057       flp[-d] = left_y;
1058
1059       feasible_left_point.intersect (flp);
1060     }
1061       
1062   if (feasible_left_point.is_empty ())
1063     warning (_ ("no viable initial configuration found: may not find good beam slope"));
1064   else if (!feasible_left_point.contains (y))
1065     {
1066       if (isinf (feasible_left_point[DOWN]))
1067         y = feasible_left_point[UP] - REGION_SIZE;
1068       else if (isinf (feasible_left_point[UP]))
1069         y = feasible_left_point[DOWN]+ REGION_SIZE;
1070       else
1071         y = feasible_left_point.center ();
1072     }
1073   
1074   pos = Drul_array<Real> (y, (y+dy));
1075   scale_drul (&pos, 1/ Staff_symbol_referencer::staff_space (me));
1076   
1077   me->set_property ("positions", ly_interval2scm (pos));
1078   return SCM_UNSPECIFIED;
1079 }
1080
1081 /* This neat trick is by Werner Lemberg,
1082    damped = tanh (slope)
1083    corresponds with some tables in [Wanske] CHECKME */
1084 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1085 SCM
1086 Beam::slope_damping (SCM smob)
1087 {
1088   Grob *me = unsmob_grob (smob);
1089
1090   if (visible_stem_count (me) <= 1)
1091     return SCM_UNSPECIFIED;
1092
1093   SCM s = me->get_property ("damping"); 
1094   Real damping = scm_to_double (s);
1095
1096   if (damping)
1097     {
1098       Drul_array<Real> pos = ly_scm2interval (me->get_property ("positions"));
1099       scale_drul (&pos,  Staff_symbol_referencer::staff_space (me));
1100       
1101       Real dy = pos[RIGHT] - pos[LEFT];
1102
1103       Grob *fvs  = first_visible_stem (me);
1104       Grob *lvs  = last_visible_stem (me);
1105
1106       Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1107
1108
1109       Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1110         - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1111
1112       Real slope = dy && dx ? dy/dx : 0;
1113
1114       Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0);
1115       
1116       slope = 0.6 * tanh (slope) / (damping + concaveness);
1117
1118       Real damped_dy = slope * dx;
1119
1120       set_minimum_dy (me, &damped_dy);
1121       
1122       pos[LEFT] += (dy - damped_dy) / 2;
1123       pos[RIGHT] -= (dy - damped_dy) / 2;
1124
1125       scale_drul (&pos, 1/Staff_symbol_referencer::staff_space (me));
1126       
1127       me->set_property ("positions", ly_interval2scm (pos));
1128     }
1129   return SCM_UNSPECIFIED;
1130 }
1131
1132 /*
1133   Report slice containing the numbers that are both in (car BEAMING)
1134   and (cdr BEAMING)
1135  */
1136 Slice
1137 where_are_the_whole_beams (SCM beaming)
1138 {
1139   Slice l; 
1140   
1141   for ( SCM s = ly_car (beaming); ly_c_pair_p (s) ; s = ly_cdr (s))
1142     {
1143       if (scm_c_memq (ly_car (s), ly_cdr (beaming)) != SCM_BOOL_F)
1144         
1145         l.add_point (scm_to_int (ly_car (s)));
1146     }
1147
1148   return l;
1149 }
1150
1151 /* Return the Y position of the stem-end, given the Y-left, Y-right
1152    in POS for stem S.  This Y position is relative to S. */
1153 Real
1154 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1155                    Real xl, Real xr,
1156                    Drul_array<Real> pos, bool french) 
1157 {
1158   Real beam_translation = get_beam_translation (me);
1159
1160     
1161   Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1162   Real dy = pos[RIGHT] - pos[LEFT];
1163   Real dx = xr - xl;
1164   Real stem_y_beam0 = (dy && dx
1165                        ? r / dx
1166                        * dy
1167                        : 0) + pos[LEFT];
1168   
1169   Direction my_dir = get_grob_direction (s);
1170   SCM beaming = s->get_property ("beaming");
1171  
1172   Real stem_y = stem_y_beam0;
1173   if (french)
1174     {
1175       Slice bm = where_are_the_whole_beams (beaming);
1176       if (!bm.is_empty ())
1177         stem_y += beam_translation * bm[-my_dir];
1178     }
1179   else
1180     {
1181       Slice bm = Stem::beam_multiplicity (s);
1182       if (!bm.is_empty ())
1183         stem_y +=bm[my_dir] * beam_translation;
1184     }
1185   
1186   Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1187     - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1188   
1189   return stem_y + id;
1190 }
1191
1192 /*
1193   Hmm.  At this time, beam position and slope are determined.  Maybe,
1194   stem directions and length should set to relative to the chord's
1195   position of the beam.  */
1196 void
1197 Beam::set_stem_lengths (Grob *me)
1198 {
1199   Link_array<Grob> stems=
1200     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1201
1202   if (!stems.size ())
1203     return;
1204   
1205   Grob *common[2];
1206   for (int a = 2; a--;)
1207     common[a] = common_refpoint_of_array (stems, me, Axis (a));
1208   
1209   Drul_array<Real> pos = ly_scm2realdrul (me->get_property ("positions"));
1210   Real staff_space = Staff_symbol_referencer::staff_space (me);
1211   scale_drul (&pos,  staff_space);
1212
1213   bool gap = false;
1214   Real thick =0.0;
1215   if (scm_is_number (me->get_property ("gap-count"))
1216       &&scm_to_int (me->get_property ("gap-count")))
1217     {
1218       gap = true;
1219       thick = get_thickness (me);
1220     }
1221       
1222   // ugh -> use commonx
1223   Grob * fvs = first_visible_stem (me);
1224   Grob *lvs = last_visible_stem (me);
1225     
1226   Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1227   Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1228   
1229   for (int i=0; i < stems.size (); i++)
1230     {
1231       Grob* s = stems[i];
1232       if (Stem::is_invisible (s))
1233         continue;
1234
1235       bool french = to_boolean (s->get_property ("french-beaming"));
1236       Real stem_y = calc_stem_y (me, s, common,
1237                                  xl, xr,
1238                                  pos, french && s != lvs && s!= fvs);
1239
1240       /*
1241         Make the stems go up to the end of the beam. This doesn't matter
1242         for normal beams, but for tremolo beams it looks silly otherwise.
1243        */
1244       if (gap)
1245         stem_y += thick * 0.5 * get_grob_direction (s);
1246
1247       Stem::set_stemend (s, 2* stem_y / staff_space);
1248     }
1249 }
1250
1251 void
1252 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1253 {
1254   Link_array<Grob> stems=
1255     Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1256   
1257   Direction d = LEFT;
1258   for (int i=0; i  < stems.size (); i++)
1259     {
1260       /*
1261         Don't overwrite user settings.
1262        */
1263       
1264       do
1265         {
1266           /* Don't set beaming for outside of outer stems */      
1267           if ( (d == LEFT && i == 0)
1268               || (d == RIGHT && i == stems.size () -1))
1269             continue;
1270
1271           Grob *st =  stems[i];
1272           SCM beaming_prop = st->get_property ("beaming");
1273           if (beaming_prop == SCM_EOL ||
1274               index_get_cell (beaming_prop, d) == SCM_EOL)
1275             {
1276               int b = beaming->infos_.elem (i).beams_i_drul_[d];
1277               if (i>0
1278                   && i < stems.size () -1
1279                   && Stem::is_invisible (st))
1280                 b = b <? beaming->infos_.elem (i).beams_i_drul_[-d];
1281               
1282               Stem::set_beaming (st, b, d);
1283             }
1284         }
1285       while (flip (&d) != LEFT);
1286     }
1287 }
1288
1289 int
1290 Beam::forced_stem_count (Grob *me) 
1291 {
1292   Link_array<Grob>stems = 
1293     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1294   int f = 0;
1295   for (int i=0; i < stems.size (); i++)
1296     {
1297       Grob *s = stems[i];
1298
1299       if (Stem::is_invisible (s))
1300         continue;
1301
1302       /* I can imagine counting those boundaries as a half forced stem,
1303          but let's count them full for now. */
1304       if (abs (Stem::chord_start_y (s)) > 0.1
1305         && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1306         f++;
1307     }
1308   return f;
1309 }
1310
1311
1312
1313
1314 int
1315 Beam::visible_stem_count (Grob *me) 
1316 {
1317   Link_array<Grob>stems = 
1318     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1319   int c = 0;
1320   for (int i = stems.size (); i--;)
1321     {
1322       if (!Stem::is_invisible (stems[i]))
1323         c++;
1324     }
1325   return c;
1326 }
1327
1328 Grob*
1329 Beam::first_visible_stem (Grob *me) 
1330 {
1331   Link_array<Grob>stems = 
1332     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1333   
1334   for (int i = 0; i < stems.size (); i++)
1335     {
1336       if (!Stem::is_invisible (stems[i]))
1337         return stems[i];
1338     }
1339   return 0;
1340 }
1341
1342 Grob*
1343 Beam::last_visible_stem (Grob *me) 
1344 {
1345   Link_array<Grob>stems = 
1346     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1347   for (int i = stems.size (); i--;)
1348     {
1349       if (!Stem::is_invisible (stems[i]))
1350         return stems[i];
1351     }
1352   return 0;
1353 }
1354
1355
1356 /*
1357   [TODO]
1358   
1359   handle rest under beam (do_post: beams are calculated now)
1360   what about combination of collisions and rest under beam.
1361
1362   Should lookup
1363     
1364     rest -> stem -> beam -> interpolate_y_position ()
1365 */
1366 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1367 SCM
1368 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1369 {
1370   Grob *rest = unsmob_grob (element_smob);
1371   Axis a = (Axis) scm_to_int (axis);
1372
1373   if (scm_is_number (rest->get_property ("staff-position")))
1374     return scm_int2num (0);
1375   
1376   assert (a == Y_AXIS);
1377
1378   Grob *st = unsmob_grob (rest->get_property ("stem"));
1379   Grob *stem = st;
1380   if (!stem)
1381     return scm_make_real (0.0);
1382   Grob *beam = unsmob_grob (stem->get_property ("beam"));
1383   if (!beam
1384       || !Beam::has_interface (beam)
1385       || !Beam::visible_stem_count (beam))
1386     return scm_make_real (0.0);
1387
1388   Drul_array<Real> pos (0, 0);
1389   SCM s = beam->get_property ("positions");
1390   if (ly_c_pair_p (s) && scm_is_number (ly_car (s)))
1391     pos = ly_scm2interval (s);
1392   Real staff_space = Staff_symbol_referencer::staff_space (rest);
1393
1394   scale_drul (&pos, staff_space);
1395   
1396
1397   Real dy = pos[RIGHT] - pos[LEFT];
1398   
1399   // ugh -> use commonx
1400   Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1401   Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1402   Real slope = dy && dx ? dy/dx : 0;
1403   
1404   Direction d = Stem::get_direction (stem);
1405   Real stem_y = pos[LEFT] + (stem->relative_coordinate (0, X_AXIS) - x0) * slope;
1406   
1407   Real beam_translation = get_beam_translation (beam);
1408   Real beam_thickness = Beam::get_thickness (beam);
1409   
1410   int beam_count = get_direction_beam_count (beam, d);
1411   Real height_of_my_beams = beam_thickness / 2
1412     + (beam_count - 1) * beam_translation;
1413   Real beam_y = stem_y - d * height_of_my_beams;
1414
1415   Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1416
1417   Real rest_dim = rest->extent (common_y, Y_AXIS)[d];
1418   Real minimum_distance =
1419     staff_space * robust_scm2double (rest->get_property ("minimum-distance"), 0.0);
1420
1421   Real shift = d * ( ((beam_y - d * minimum_distance) - rest_dim) * d  <? 0.0);
1422
1423   shift /= staff_space;
1424   Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1425
1426   /* Always move discretely by half spaces */
1427   shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1428
1429   /* Inside staff, move by whole spaces*/
1430   if ( (rest->extent (common_y, Y_AXIS)[d] + staff_space * shift) * d
1431       < rad
1432       || (rest->extent (common_y, Y_AXIS)[-d] + staff_space * shift) * -d
1433       < rad)
1434     shift = ceil (fabs (shift)) *sign (shift);
1435
1436   return scm_make_real (staff_space * shift);
1437 }
1438
1439 bool
1440 Beam::is_knee (Grob* me)
1441 {
1442   SCM k = me->get_property ("knee");
1443   if (scm_is_bool (k))
1444     return ly_scm2bool (k);
1445
1446   bool knee = false;
1447   int d = 0;
1448   for (SCM s = me->get_property ("stems"); ly_c_pair_p (s); s = ly_cdr (s))
1449     {
1450       Direction dir = get_grob_direction (unsmob_grob (ly_car (s)));
1451       if (d && d != dir)
1452         {
1453           knee = true;
1454           break;
1455         }
1456       d = dir;
1457     }
1458   
1459   me->set_property ("knee", ly_bool2scm (knee));
1460
1461   return knee;
1462 }
1463
1464 int
1465 Beam::get_direction_beam_count (Grob *me, Direction d )
1466 {
1467   Link_array<Grob>stems = 
1468     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1469   int bc = 0;
1470   
1471   for (int i = stems.size (); i--;)
1472     {
1473       /*
1474         Should we take invisible stems into account?
1475        */
1476       if (Stem::get_direction (stems[i]) == d)
1477         bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1478     }
1479
1480   return bc;
1481 }
1482
1483
1484 ADD_INTERFACE (Beam, "beam-interface",
1485                "A beam. \n\n"
1486                "The @code{thickness} property is the weight of beams, and is measured "
1487                "in  staffspace"
1488                ,
1489                "knee positioning-done position-callbacks "
1490                "concaveness dir-function quant-score auto-knee-gap gap "
1491                "gap-count chord-tremolo beamed-stem-shorten shorten least-squares-dy "
1492                "damping inspect-quants flag-width-function neutral-direction positions space-function "
1493                "thickness");
1494
1495