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