]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
dbe22733dee2d3734d3ae9d0f963f082aa63b6df
[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 /*
176   We want a maximal number of shared beams, but if there is choice, we
177   take the one that is closest to the end of the stem. This is for 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 void
590 Beam::consider_auto_knees (Grob *me)
591 {
592   SCM scm = me->get_property ("auto-knee-gap");
593   if (!scm_is_number (scm))
594     return;
595
596   Interval_set gaps;
597
598   gaps.set_full ();
599
600   Link_array<Grob> stems
601     = extract_grob_array (me, ly_symbol2scm ("stems"));
602
603   Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
604   Real staff_space = Staff_symbol_referencer::staff_space (me);
605
606   Array<Interval> head_positions_array;
607   for (int i = 0; i < stems.size (); i++)
608     {
609       Grob *stem = stems[i];
610       if (Stem::is_invisible (stem))
611         continue;
612
613       Interval head_positions = Stem::head_positions (stem);
614       if (!head_positions.is_empty ())
615         {
616           head_positions[LEFT] += -1;
617           head_positions[RIGHT] += 1;
618           head_positions *= staff_space * 0.5;
619
620           /*
621             We could subtract beam Y position, but this routine only
622             sets stem directions, a constant shift does not have an
623             influence.
624           */
625           head_positions += stem->relative_coordinate (common, Y_AXIS);
626
627           if (to_dir (stem->get_property ("direction")))
628             {
629               Direction stemdir = to_dir (stem->get_property ("direction"));
630               head_positions[-stemdir] = -stemdir * infinity_f;
631             }
632         }
633       head_positions_array.push (head_positions);
634
635       gaps.remove_interval (head_positions);
636     }
637
638   Interval max_gap;
639   Real max_gap_len = 0.0;
640
641   for (int i = gaps.allowed_regions_.size () -1; i >= 0; i--)
642     {
643       Interval gap = gaps.allowed_regions_[i];
644
645       /*
646         the outer gaps are not knees.
647       */
648       if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
649         continue;
650
651       if (gap.length () >= max_gap_len)
652         {
653           max_gap_len = gap.length ();
654           max_gap = gap;
655         }
656     }
657
658   Real beam_translation = get_beam_translation (me);
659   Real beam_thickness = Beam::get_thickness (me);
660   int beam_count = Beam::get_beam_count (me);
661   Real height_of_beams = beam_thickness / 2
662     + (beam_count - 1) * beam_translation;
663   Real threshold = scm_to_double (scm) + height_of_beams;
664
665   if (max_gap_len > threshold)
666     {
667       int j = 0;
668       for (int i = 0; i < stems.size (); i++)
669         {
670           Grob *stem = stems[i];
671           if (Stem::is_invisible (stem))
672             continue;
673
674           Interval head_positions = head_positions_array[j++];
675
676           Direction d = (head_positions.center () < max_gap.center ()) ?
677             UP : DOWN;
678
679           stem->set_property ("direction", scm_int2num (d));
680
681           head_positions.intersect (max_gap);
682           assert (head_positions.is_empty () || head_positions.length () < 1e-6);
683         }
684     }
685 }
686
687 /* Set stem's shorten property if unset.
688
689 TODO:
690 take some y-position (chord/beam/nearest?) into account
691 scmify forced-fraction
692
693 This is done in beam because the shorten has to be uniform over the
694 entire beam.
695 */
696 void
697 Beam::set_stem_shorten (Grob *me)
698 {
699   /*
700     shortening looks silly for x staff beams
701   */
702   if (is_knee (me))
703     return;
704
705   Real forced_fraction = 1.0 * forced_stem_count (me)
706     / visible_stem_count (me);
707
708   int beam_count = get_beam_count (me);
709
710   SCM shorten_list = me->get_property ("beamed-stem-shorten");
711   if (shorten_list == SCM_EOL)
712     return;
713
714   Real staff_space = Staff_symbol_referencer::staff_space (me);
715
716   SCM shorten_elt
717     = robust_list_ref (beam_count -1, shorten_list);
718   Real shorten_f = scm_to_double (shorten_elt) * staff_space;
719
720   /* your similar cute comment here */
721   shorten_f *= forced_fraction;
722
723   if (shorten_f)
724     me->set_property ("shorten", scm_make_real (shorten_f));
725 }
726
727 /*  Call list of y-dy-callbacks, that handle setting of
728     grob-properties
729 */
730 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
731 SCM
732 Beam::after_line_breaking (SCM smob)
733 {
734   Grob *me = unsmob_grob (smob);
735
736   position_beam (me);
737   return SCM_UNSPECIFIED;
738 }
739
740 void
741 Beam::position_beam (Grob *me)
742 {
743   if (!me->is_live ())
744     return;
745   if (to_boolean (me->get_property ("positioning-done")))
746     return;
747
748   me->set_property ("positioning-done", SCM_BOOL_T);
749
750   /* Copy to mutable list. */
751   SCM s = ly_deep_copy (me->get_property ("positions"));
752   me->set_property ("positions", s);
753
754   if (scm_car (s) == SCM_BOOL_F)
755     {
756       // one wonders if such genericity is necessary  --hwn.
757       SCM callbacks = me->get_property ("position-callbacks");
758       for (SCM i = callbacks; scm_is_pair (i); i = scm_cdr (i))
759         scm_call_1 (scm_car (i), me->self_scm ());
760     }
761
762   set_stem_lengths (me);
763 }
764
765 void
766 set_minimum_dy (Grob *me, Real *dy)
767 {
768   if (*dy)
769     {
770       /*
771         If dy is smaller than the smallest quant, we
772         get absurd direction-sign penalties.
773       */
774
775       Real ss = Staff_symbol_referencer::staff_space (me);
776       Real thickness = Beam::get_thickness (me) / ss;
777       Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
778       Real sit = (thickness - slt) / 2;
779       Real inter = 0.5;
780       Real hang = 1.0 - (thickness - slt) / 2;
781
782       *dy = sign (*dy) * (fabs (*dy)
783                           >?
784                           (sit <? inter <? hang));
785     }
786 }
787
788 /*
789   Compute  a first approximation to the beam slope.
790 */
791 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
792 SCM
793 Beam::least_squares (SCM smob)
794 {
795   Grob *me = unsmob_grob (smob);
796
797   int count = visible_stem_count (me);
798   Interval pos (0, 0);
799
800   if (count < 1)
801     {
802       me->set_property ("positions", ly_interval2scm (pos));
803       return SCM_UNSPECIFIED;
804     }
805
806   Array<Real> x_posns;
807   Link_array<Grob> stems
808     = extract_grob_array (me, ly_symbol2scm ("stems"));
809   Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
810   Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
811
812   Real my_y = me->relative_coordinate (commony, Y_AXIS);
813
814   Grob *fvs = first_visible_stem (me);
815   Grob *lvs = last_visible_stem (me);
816
817   Interval ideal (Stem::get_stem_info (fvs).ideal_y_
818                   + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
819                   Stem::get_stem_info (lvs).ideal_y_
820                   + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
821
822   Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
823   for (int i = 0; i < stems.size (); i++)
824     {
825       Grob *s = stems[i];
826
827       Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
828       x_posns.push (x);
829     }
830   Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
831
832   Real y = 0;
833   Real slope = 0;
834   Real dy = 0;
835
836   if (!ideal.delta ())
837     {
838       Interval chord (Stem::chord_start_y (first_visible_stem (me)),
839                       Stem::chord_start_y (last_visible_stem (me)));
840
841       /* Simple beams (2 stems) on middle line should be allowed to be
842          slightly sloped.
843
844          However, if both stems reach middle line,
845          ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
846
847          For that case, we apply artificial slope */
848       if (!ideal[LEFT] && chord.delta () && count == 2)
849         {
850           /* FIXME. -> UP */
851           Direction d = (Direction) (sign (chord.delta ()) * UP);
852           pos[d] = get_thickness (me) / 2;
853           pos[-d] = -pos[d];
854         }
855       else
856         {
857           pos = ideal;
858         }
859
860       /*
861         For broken beams this doesn't work well. In this case, the
862         slope esp. of the first part of a broken beam should predict
863         where the second part goes.
864       */
865       me->set_property ("least-squares-dy",
866                         scm_make_real (pos[RIGHT] - pos[LEFT]));
867     }
868   else
869     {
870       Array<Offset> ideals;
871       for (int i = 0; i < stems.size (); i++)
872         {
873           Grob *s = stems[i];
874           if (Stem::is_invisible (s))
875             continue;
876           ideals.push (Offset (x_posns[i],
877                                Stem::get_stem_info (s).ideal_y_
878                                + s->relative_coordinate (commony, Y_AXIS)
879                                - my_y));
880         }
881
882       minimise_least_squares (&slope, &y, ideals);
883
884       dy = slope * dx;
885
886       set_minimum_dy (me, &dy);
887       me->set_property ("least-squares-dy", scm_make_real (dy));
888       pos = Interval (y, (y + dy));
889     }
890
891   /*
892     "position" is relative to the staff.
893   */
894   scale_drul (&pos, 1/ Staff_symbol_referencer::staff_space (me));
895
896   me->set_property ("positions", ly_interval2scm (pos));
897
898   return SCM_UNSPECIFIED;
899 }
900
901 /*
902   We can't combine with previous function, since check concave and
903   slope damping comes first.
904
905   TODO: we should use the concaveness to control the amount of damping
906   applied.
907 */
908 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
909 SCM
910 Beam::shift_region_to_valid (SCM grob)
911 {
912   Grob *me = unsmob_grob (grob);
913   /*
914     Code dup.
915   */
916   Array<Real> x_posns;
917   Link_array<Grob> stems
918     = extract_grob_array (me, ly_symbol2scm ("stems"));
919   Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
920   Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
921
922   Grob *fvs = first_visible_stem (me);
923
924   if (!fvs)
925     return SCM_UNSPECIFIED;
926
927   Real x0 = fvs->relative_coordinate (commonx, X_AXIS);
928   for (int i = 0; i < stems.size (); i++)
929     {
930       Grob *s = stems[i];
931
932       Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
933       x_posns.push (x);
934     }
935
936   Grob *lvs = last_visible_stem (me);
937   if (!lvs)
938     return SCM_UNSPECIFIED;
939
940   Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
941
942   Drul_array<Real> pos = ly_scm2interval (me->get_property ("positions"));
943
944   scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
945
946   Real dy = pos[RIGHT] - pos[LEFT];
947   Real y = pos[LEFT];
948   Real slope = dy / dx;
949
950   /*
951     Shift the positions so that we have a chance of finding good
952     quants (i.e. no short stem failures.)
953   */
954   Interval feasible_left_point;
955   feasible_left_point.set_full ();
956   for (int i = 0; i < stems.size (); i++)
957     {
958       Grob *s = stems[i];
959       if (Stem::is_invisible (s))
960         continue;
961
962       Direction d = Stem::get_direction (s);
963
964       Real left_y
965         = Stem::get_stem_info (s).shortest_y_
966         - slope * x_posns [i];
967
968       /*
969         left_y is now relative to the stem S. We want relative to
970         ourselves, so translate:
971       */
972       left_y
973         += + s->relative_coordinate (commony, Y_AXIS)
974         - me->relative_coordinate (commony, Y_AXIS);
975
976       Interval flp;
977       flp.set_full ();
978       flp[-d] = left_y;
979
980       feasible_left_point.intersect (flp);
981     }
982
983   if (feasible_left_point.is_empty ())
984     warning (_ ("no viable initial configuration found: may not find good beam slope"));
985   else if (!feasible_left_point.contains (y))
986     {
987       if (isinf (feasible_left_point[DOWN]))
988         y = feasible_left_point[UP] - REGION_SIZE;
989       else if (isinf (feasible_left_point[UP]))
990         y = feasible_left_point[DOWN]+ REGION_SIZE;
991       else
992         y = feasible_left_point.center ();
993     }
994
995   pos = Drul_array<Real> (y, (y + dy));
996   scale_drul (&pos, 1/ Staff_symbol_referencer::staff_space (me));
997
998   me->set_property ("positions", ly_interval2scm (pos));
999   return SCM_UNSPECIFIED;
1000 }
1001
1002 /* This neat trick is by Werner Lemberg,
1003    damped = tanh (slope)
1004    corresponds with some tables in [Wanske] CHECKME */
1005 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1006 SCM
1007 Beam::slope_damping (SCM smob)
1008 {
1009   Grob *me = unsmob_grob (smob);
1010
1011   if (visible_stem_count (me) <= 1)
1012     return SCM_UNSPECIFIED;
1013
1014   SCM s = me->get_property ("damping");
1015   Real damping = scm_to_double (s);
1016
1017   if (damping)
1018     {
1019       Drul_array<Real> pos = ly_scm2interval (me->get_property ("positions"));
1020       scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1021
1022       Real dy = pos[RIGHT] - pos[LEFT];
1023
1024       Grob *fvs = first_visible_stem (me);
1025       Grob *lvs = last_visible_stem (me);
1026
1027       Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1028
1029       Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1030         - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1031
1032       Real slope = dy && dx ? dy / dx : 0;
1033
1034       Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0);
1035
1036       slope = 0.6 * tanh (slope) / (damping + concaveness);
1037
1038       Real damped_dy = slope * dx;
1039
1040       set_minimum_dy (me, &damped_dy);
1041
1042       pos[LEFT] += (dy - damped_dy) / 2;
1043       pos[RIGHT] -= (dy - damped_dy) / 2;
1044
1045       scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1046
1047       me->set_property ("positions", ly_interval2scm (pos));
1048     }
1049   return SCM_UNSPECIFIED;
1050 }
1051
1052 /*
1053   Report slice containing the numbers that are both in (car BEAMING)
1054   and (cdr BEAMING)
1055 */
1056 Slice
1057 where_are_the_whole_beams (SCM beaming)
1058 {
1059   Slice l;
1060
1061   for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1062     {
1063       if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1064
1065         l.add_point (scm_to_int (scm_car (s)));
1066     }
1067
1068   return l;
1069 }
1070
1071 /* Return the Y position of the stem-end, given the Y-left, Y-right
1072    in POS for stem S.  This Y position is relative to S. */
1073 Real
1074 Beam::calc_stem_y (Grob *me, Grob *s, Grob ** common,
1075                    Real xl, Real xr,
1076                    Drul_array<Real> pos, bool french)
1077 {
1078   Real beam_translation = get_beam_translation (me);
1079
1080   Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1081   Real dy = pos[RIGHT] - pos[LEFT];
1082   Real dx = xr - xl;
1083   Real stem_y_beam0 = (dy && dx
1084                        ? r / dx
1085                        * dy
1086                        : 0) + pos[LEFT];
1087
1088   Direction my_dir = get_grob_direction (s);
1089   SCM beaming = s->get_property ("beaming");
1090
1091   Real stem_y = stem_y_beam0;
1092   if (french)
1093     {
1094       Slice bm = where_are_the_whole_beams (beaming);
1095       if (!bm.is_empty ())
1096         stem_y += beam_translation * bm[-my_dir];
1097     }
1098   else
1099     {
1100       Slice bm = Stem::beam_multiplicity (s);
1101       if (!bm.is_empty ())
1102         stem_y += bm[my_dir] * beam_translation;
1103     }
1104
1105   Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1106     - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1107
1108   return stem_y + id;
1109 }
1110
1111 /*
1112   Hmm.  At this time, beam position and slope are determined.  Maybe,
1113   stem directions and length should set to relative to the chord's
1114   position of the beam.  */
1115 void
1116 Beam::set_stem_lengths (Grob *me)
1117 {
1118   Link_array<Grob> stems
1119     = extract_grob_array (me, ly_symbol2scm ("stems"));
1120
1121   if (!stems.size ())
1122     return;
1123
1124   Grob *common[2];
1125   for (int a = 2; a--;)
1126     common[a] = common_refpoint_of_array (stems, me, Axis (a));
1127
1128   Drul_array<Real> pos = ly_scm2realdrul (me->get_property ("positions"));
1129   Real staff_space = Staff_symbol_referencer::staff_space (me);
1130   scale_drul (&pos, staff_space);
1131
1132   bool gap = false;
1133   Real thick = 0.0;
1134   if (scm_is_number (me->get_property ("gap-count"))
1135       &&scm_to_int (me->get_property ("gap-count")))
1136     {
1137       gap = true;
1138       thick = get_thickness (me);
1139     }
1140
1141   // ugh -> use commonx
1142   Grob *fvs = first_visible_stem (me);
1143   Grob *lvs = last_visible_stem (me);
1144
1145   Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1146   Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1147
1148   for (int i = 0; i < stems.size (); i++)
1149     {
1150       Grob *s = stems[i];
1151       if (Stem::is_invisible (s))
1152         continue;
1153
1154       bool french = to_boolean (s->get_property ("french-beaming"));
1155       Real stem_y = calc_stem_y (me, s, common,
1156                                  xl, xr,
1157                                  pos, french && s != lvs && s!= fvs);
1158
1159       /*
1160         Make the stems go up to the end of the beam. This doesn't matter
1161         for normal beams, but for tremolo beams it looks silly otherwise.
1162       */
1163       if (gap)
1164         stem_y += thick * 0.5 * get_grob_direction (s);
1165
1166       Stem::set_stemend (s, 2* stem_y / staff_space);
1167     }
1168 }
1169
1170 void
1171 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1172 {
1173   Link_array<Grob> stems
1174     = extract_grob_array (me, ly_symbol2scm ("stems"));
1175
1176   Direction d = LEFT;
1177   for (int i = 0; i < stems.size (); i++)
1178     {
1179       /*
1180         Don't overwrite user settings.
1181       */
1182
1183       do
1184         {
1185           /* Don't set beaming for outside of outer stems */
1186           if ((d == LEFT && i == 0)
1187               || (d == RIGHT && i == stems.size () -1))
1188             continue;
1189
1190           Grob *st = stems[i];
1191           SCM beaming_prop = st->get_property ("beaming");
1192           if (beaming_prop == SCM_EOL
1193               || index_get_cell (beaming_prop, d) == SCM_EOL)
1194             {
1195               int b = beaming->infos_.elem (i).beams_i_drul_[d];
1196               if (i > 0
1197                   && i < stems.size () -1
1198                   && Stem::is_invisible (st))
1199                 b = b <? beaming->infos_.elem (i).beams_i_drul_[-d];
1200
1201               Stem::set_beaming (st, b, d);
1202             }
1203         }
1204       while (flip (&d) != LEFT);
1205     }
1206 }
1207
1208 int
1209 Beam::forced_stem_count (Grob *me)
1210 {
1211   Link_array<Grob> stems
1212     = extract_grob_array (me, ly_symbol2scm ("stems"));
1213   int f = 0;
1214   for (int i = 0; i < stems.size (); i++)
1215     {
1216       Grob *s = stems[i];
1217
1218       if (Stem::is_invisible (s))
1219         continue;
1220
1221       /* I can imagine counting those boundaries as a half forced stem,
1222          but let's count them full for now. */
1223       if (abs (Stem::chord_start_y (s)) > 0.1
1224           && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1225         f++;
1226     }
1227   return f;
1228 }
1229
1230
1231 int
1232 Beam::visible_stem_count (Grob *me)
1233 {
1234   Link_array<Grob> stems
1235     = extract_grob_array (me, ly_symbol2scm ("stems"));
1236   int c = 0;
1237   for (int i = stems.size (); i--;)
1238     {
1239       if (!Stem::is_invisible (stems[i]))
1240         c++;
1241     }
1242   return c;
1243 }
1244
1245 Grob *
1246 Beam::first_visible_stem (Grob *me)
1247 {
1248   Link_array<Grob> stems
1249     = extract_grob_array (me, ly_symbol2scm ("stems"));
1250
1251   for (int i = 0; i < stems.size (); i++)
1252     {
1253       if (!Stem::is_invisible (stems[i]))
1254         return stems[i];
1255     }
1256   return 0;
1257 }
1258
1259 Grob *
1260 Beam::last_visible_stem (Grob *me)
1261 {
1262   Link_array<Grob> stems
1263     = extract_grob_array (me, ly_symbol2scm ("stems"));
1264   for (int i = stems.size (); i--;)
1265     {
1266       if (!Stem::is_invisible (stems[i]))
1267         return stems[i];
1268     }
1269   return 0;
1270 }
1271
1272 /*
1273   [TODO]
1274
1275   handle rest under beam (do_post: beams are calculated now)
1276   what about combination of collisions and rest under beam.
1277
1278   Should lookup
1279
1280   rest -> stem -> beam -> interpolate_y_position ()
1281 */
1282 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1283 SCM
1284 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1285 {
1286   Grob *rest = unsmob_grob (element_smob);
1287   Axis a = (Axis) scm_to_int (axis);
1288
1289   if (scm_is_number (rest->get_property ("staff-position")))
1290     return scm_int2num (0);
1291
1292   assert (a == Y_AXIS);
1293
1294   Grob *st = unsmob_grob (rest->get_property ("stem"));
1295   Grob *stem = st;
1296   if (!stem)
1297     return scm_make_real (0.0);
1298   Grob *beam = unsmob_grob (stem->get_property ("beam"));
1299   if (!beam
1300       || !Beam::has_interface (beam)
1301       || !Beam::visible_stem_count (beam))
1302     return scm_make_real (0.0);
1303
1304   Drul_array<Real> pos (0, 0);
1305   SCM s = beam->get_property ("positions");
1306   if (scm_is_pair (s) && scm_is_number (scm_car (s)))
1307     pos = ly_scm2interval (s);
1308   Real staff_space = Staff_symbol_referencer::staff_space (rest);
1309
1310   scale_drul (&pos, staff_space);
1311
1312   Real dy = pos[RIGHT] - pos[LEFT];
1313
1314   // ugh -> use commonx
1315   Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1316   Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1317   Real slope = dy && dx ? dy / dx : 0;
1318
1319   Direction d = Stem::get_direction (stem);
1320   Real stem_y = pos[LEFT] + (stem->relative_coordinate (0, X_AXIS) - x0) * slope;
1321
1322   Real beam_translation = get_beam_translation (beam);
1323   Real beam_thickness = Beam::get_thickness (beam);
1324
1325   /*
1326     TODO: this is not strictly correct for 16th knee beams.
1327   */
1328   int beam_count
1329     = Stem::beam_multiplicity (stem).length () + 1;
1330
1331   Real height_of_my_beams = beam_thickness / 2
1332     + (beam_count - 1) * beam_translation;
1333   Real beam_y = stem_y - d * height_of_my_beams;
1334
1335   Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1336
1337   Real rest_dim = rest->extent (common_y, Y_AXIS)[d];
1338   Real minimum_distance
1339     = + staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1340                        + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1341
1342   Real shift = d * (((beam_y - d * minimum_distance) - rest_dim) * d <? 0.0);
1343
1344   shift /= staff_space;
1345   Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1346
1347   /* Always move discretely by half spaces */
1348   shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1349
1350   /* Inside staff, move by whole spaces*/
1351   if ((rest->extent (common_y, Y_AXIS)[d] + staff_space * shift) * d
1352       < rad
1353       || (rest->extent (common_y, Y_AXIS)[-d] + staff_space * shift) * -d
1354       < rad)
1355     shift = ceil (fabs (shift)) *sign (shift);
1356
1357   return scm_make_real (staff_space * shift);
1358 }
1359
1360 bool
1361 Beam::is_knee (Grob *me)
1362 {
1363   SCM k = me->get_property ("knee");
1364   if (scm_is_bool (k))
1365     return ly_scm2bool (k);
1366
1367   bool knee = false;
1368   int d = 0;
1369   for (SCM s = me->get_property ("stems"); scm_is_pair (s); s = scm_cdr (s))
1370     {
1371       Direction dir = get_grob_direction (unsmob_grob (scm_car (s)));
1372       if (d && d != dir)
1373         {
1374           knee = true;
1375           break;
1376         }
1377       d = dir;
1378     }
1379
1380   me->set_property ("knee", ly_bool2scm (knee));
1381
1382   return knee;
1383 }
1384
1385 int
1386 Beam::get_direction_beam_count (Grob *me, Direction d)
1387 {
1388   Link_array<Grob> stems
1389     = extract_grob_array (me, ly_symbol2scm ("stems"));
1390   int bc = 0;
1391
1392   for (int i = stems.size (); i--;)
1393     {
1394       /*
1395         Should we take invisible stems into account?
1396       */
1397       if (Stem::get_direction (stems[i]) == d)
1398         bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1399     }
1400
1401   return bc;
1402 }
1403
1404 ADD_INTERFACE (Beam, "beam-interface",
1405                "A beam. \n\n"
1406                "The @code{thickness} property is the weight of beams, and is measured "
1407                "in  staffspace",
1408                "knee positioning-done position-callbacks "
1409                "concaveness dir-function quant-score auto-knee-gap gap "
1410                "gap-count chord-tremolo beamed-stem-shorten shorten least-squares-dy "
1411                "damping inspect-quants flag-width-function neutral-direction positions space-function "
1412                "thickness");
1413