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