]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
Take collisions into account in Beam::shift_region_to_valid().
[lilypond.git] / lily / beam.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--2011 Han-Wen Nienhuys <hanwen@xs4all.nl>
5   Jan Nieuwenhuizen <janneke@gnu.org>
6
7   LilyPond is free software: you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation, either version 3 of the License, or
10   (at your option) any later version.
11
12   LilyPond is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 /*
22   TODO:
23
24   - Determine auto knees based on positions if it's set by the user.
25
26   - the code is littered with * and / staff_space calls for
27   #'positions. Consider moving to real-world coordinates?
28
29   Problematic issue is user tweaks (user tweaks are in staff-coordinates.)
30
31   Notes:
32
33   - Stems run to the Y-center of the beam.
34
35   - beam_translation is the offset between Y centers of the beam.
36 */
37
38 #include "beam.hh"
39
40 #include "beam-scoring-problem.hh"
41 #include "beaming-pattern.hh"
42 #include "directional-element-interface.hh"
43 #include "grob-array.hh"
44 #include "international.hh"
45 #include "interval-set.hh"
46 #include "item.hh"
47 #include "least-squares.hh"
48 #include "lookup.hh"
49 #include "main.hh"
50 #include "misc.hh"
51 #include "note-head.hh"
52 #include "output-def.hh"
53 #include "pointer-group-interface.hh"
54 #include "rhythmic-head.hh"
55 #include "spanner.hh"
56 #include "staff-symbol-referencer.hh"
57 #include "stem.hh"
58 #include "warn.hh"
59
60 #if DEBUG_BEAM_SCORING
61 #include "text-interface.hh" // debug output.
62 #include "font-interface.hh" // debug output.
63 #endif
64
65 #include <map>
66
67
68 Beam_stem_segment::Beam_stem_segment ()
69 {
70   max_connect_ = 1000;          // infinity
71   stem_ = 0;
72   width_ = 0.0;
73   stem_x_ = 0.0;
74   rank_ = 0;
75   stem_index_ = 0;
76   dir_ = CENTER;
77 }
78
79 bool
80 beam_segment_less (Beam_segment const& a, Beam_segment const& b)
81 {
82   return a.horizontal_[LEFT] < b.horizontal_[LEFT];
83 }
84
85 Beam_segment::Beam_segment ()
86 {
87   vertical_count_ = 0;
88 }
89
90 void
91 Beam::add_stem (Grob *me, Grob *s)
92 {
93   if (Stem::get_beam (s))
94     {
95       programming_error ("Stem already has beam");
96       return ;
97     }
98
99   Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
100   s->set_object ("beam", me->self_scm ());
101   add_bound_item (dynamic_cast<Spanner *> (me), dynamic_cast<Item *> (s));
102 }
103
104 Real
105 Beam::get_beam_thickness (Grob *me)
106 {
107   return robust_scm2double (me->get_property ("beam-thickness"), 0)
108     * Staff_symbol_referencer::staff_space (me);
109 }
110
111 /* Return the translation between 2 adjoining beams. */
112 Real
113 Beam::get_beam_translation (Grob *me)
114 {
115   int beam_count = get_beam_count (me);
116   Real staff_space = Staff_symbol_referencer::staff_space (me);
117   Real line = Staff_symbol_referencer::line_thickness (me);
118   Real beam_thickness = get_beam_thickness (me);
119   Real fract = robust_scm2double (me->get_property ("length-fraction"), 1.0);
120
121   Real beam_translation = beam_count < 4
122     ? (2 * staff_space + line - beam_thickness) / 2.0
123     : (3 * staff_space + line - beam_thickness) / 3.0;
124
125   return fract * beam_translation;
126 }
127
128 /* Maximum beam_count. */
129 int
130 Beam::get_beam_count (Grob *me)
131 {
132   int m = 0;
133
134   extract_grob_set (me, "stems", stems);
135   for (vsize i = 0; i < stems.size (); i++)
136     {
137       Grob *stem = stems[i];
138       m = max (m, (Stem::beam_multiplicity (stem).length () + 1));
139     }
140   return m;
141 }
142
143 MAKE_SCHEME_CALLBACK (Beam, calc_normal_stems, 1);
144 SCM
145 Beam::calc_normal_stems (SCM smob)
146 {
147   Grob *me = unsmob_grob (smob);
148
149   extract_grob_set (me, "stems", stems);
150   SCM val = Grob_array::make_array ();
151   Grob_array *ga = unsmob_grob_array (val);
152   for (vsize i = 0; i < stems.size ();  i++)
153     if (Stem::is_normal_stem (stems[i]))
154       ga->add (stems[i]);
155
156   return val;
157 }
158
159 MAKE_SCHEME_CALLBACK (Beam, calc_direction, 1);
160 SCM
161 Beam::calc_direction (SCM smob)
162 {
163   Grob *me = unsmob_grob (smob);
164
165   /* Beams with less than 2 two stems don't make much sense, but could happen
166      when you do
167
168      r8[ c8 r8]
169
170   */
171
172   Direction dir = CENTER;
173
174   int count = normal_stem_count (me);
175   if (count < 2)
176     {
177       extract_grob_set (me, "stems", stems);
178       if (stems.size () == 0)
179         {
180           me->warning (_ ("removing beam with no stems"));
181           me->suicide ();
182
183           return SCM_UNSPECIFIED;
184         }
185       else
186         {
187           Grob *stem = first_normal_stem (me);
188
189           /*
190             This happens for chord tremolos.
191           */
192           if (!stem)
193             stem = stems[0];
194
195           if (is_direction (stem->get_property_data ("direction")))
196             dir = to_dir (stem->get_property_data ("direction"));
197           else
198             dir = to_dir (stem->get_property ("default-direction"));
199         }
200     }
201
202   if (count >= 1)
203     {
204       if (!dir)
205         dir = get_default_dir (me);
206
207       consider_auto_knees (me);
208     }
209
210   if (dir)
211     {
212       set_stem_directions (me, dir);
213     }
214
215   return scm_from_int (dir);
216 }
217
218
219
220 /* We want a maximal number of shared beams, but if there is choice, we
221  * take the one that is closest to the end of the stem. This is for
222  * situations like
223  *
224  *        x
225  *       |
226  *       |
227  *   |===|
228  *   |=
229  *   |
230  *  x
231  */
232 int
233 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
234                                     Direction left_dir,
235                                     Direction right_dir)
236 {
237   Slice lslice = int_list_to_slice (scm_cdr (left_beaming));
238
239   int best_count = 0;
240   int best_start = 0;
241   for (int i = lslice[-left_dir];
242        (i - lslice[left_dir]) * left_dir <= 0; i += left_dir)
243     {
244       int count = 0;
245       for (SCM s = scm_car (right_beaming); scm_is_pair (s); s = scm_cdr (s))
246         {
247           int k = -right_dir * scm_to_int (scm_car (s)) + i;
248           if (scm_c_memq (scm_from_int (k), left_beaming) != SCM_BOOL_F)
249             count++;
250         }
251
252       if (count >= best_count)
253         {
254           best_count = count;
255           best_start = i;
256         }
257     }
258
259   return best_start;
260 }
261
262 MAKE_SCHEME_CALLBACK (Beam, calc_beaming, 1)
263 SCM
264 Beam::calc_beaming (SCM smob)
265 {
266   Grob *me = unsmob_grob (smob);
267
268   extract_grob_set (me, "stems", stems);
269
270   Slice last_int;
271   last_int.set_empty ();
272
273   SCM last_beaming = scm_cons (SCM_EOL, scm_list_1 (scm_from_int (0)));
274   Direction last_dir = CENTER;
275   for (vsize i = 0; i < stems.size (); i++)
276     {
277       Grob *this_stem = stems[i];
278       SCM this_beaming = this_stem->get_property ("beaming");
279
280       Direction this_dir = get_grob_direction (this_stem);
281       if (scm_is_pair (last_beaming) && scm_is_pair (this_beaming))
282         {
283           int start_point = position_with_maximal_common_beams
284             (last_beaming, this_beaming,
285              last_dir ? last_dir : this_dir,
286              this_dir);
287
288           Direction d = LEFT;
289           Slice new_slice;
290           do
291             {
292               new_slice.set_empty ();
293               SCM s = index_get_cell (this_beaming, d);
294               for (; scm_is_pair (s); s = scm_cdr (s))
295                 {
296                   int new_beam_pos
297                     = start_point - this_dir * scm_to_int (scm_car (s));
298
299                   new_slice.add_point (new_beam_pos);
300                   scm_set_car_x (s, scm_from_int (new_beam_pos));
301                 }
302             }
303           while (flip (&d) != LEFT);
304
305           if (!new_slice.is_empty ())
306             last_int = new_slice;
307         }
308       else
309         {
310           /*
311             FIXME: what's this for?
312            */
313           SCM s = scm_cdr (this_beaming);
314           for (; scm_is_pair (s); s = scm_cdr (s))
315             {
316               int np = -this_dir * scm_to_int (scm_car (s));
317               scm_set_car_x (s, scm_from_int (np));
318               last_int.add_point (np);
319             }
320         }
321
322       if (scm_ilength (scm_cdr (this_beaming)) > 0)
323         {
324           last_beaming = this_beaming;
325           last_dir = this_dir;
326         }
327     }
328
329   return SCM_EOL;
330 }
331
332 bool
333 operator <(Beam_stem_segment const &a,
334            Beam_stem_segment const &b)
335 {
336   return a.rank_ < b.rank_;
337 }
338
339 typedef map<int, vector<Beam_stem_segment> >  Position_stem_segments_map;
340
341 // TODO - should store result in a property?
342 vector<Beam_segment>
343 Beam::get_beam_segments (Grob *me_grob, Grob **common)
344 {
345   /* ugh, this has a side-effect that we need to ensure that
346      Stem #'beaming is correct */
347   (void) me_grob->get_property ("beaming");
348
349   Spanner *me = dynamic_cast<Spanner*> (me_grob);
350
351   extract_grob_set (me, "stems", stems);
352   Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
353
354   commonx = me->get_bound (LEFT)->common_refpoint (commonx, X_AXIS);
355   commonx = me->get_bound (RIGHT)->common_refpoint (commonx, X_AXIS);
356
357   *common = commonx;
358
359   int gap_count = robust_scm2int (me->get_property ("gap-count"), 0);
360   Real gap_length = robust_scm2double (me->get_property ("gap"), 0.0);
361
362   Position_stem_segments_map stem_segments;
363   Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
364
365   /* There are two concepts of "rank" that are used in the following code.
366      The beam_rank is the vertical position of the beam (larger numbers are
367      closer to the noteheads). Beam_stem_segment.rank_, on the other hand,
368      is the horizontal position of the segment (this is incremented by two
369      for each stem; the beam segment on the right side of the stem has
370      a higher rank (by one) than its neighbour to the left). */
371   Slice ranks;
372   for (vsize i = 0; i < stems.size (); i++)
373     {
374       Grob *stem = stems[i];
375       Real stem_width = robust_scm2double (stem->get_property ("thickness"), 1.0) * lt;
376       Real stem_x = stem->relative_coordinate (commonx, X_AXIS);
377       SCM beaming = stem->get_property ("beaming");
378       Direction d = LEFT;
379       do
380         {
381           // Find the maximum and minimum beam ranks.
382           // Given that RANKS is never reset to empty, the interval will always be
383           // smallest for the left beamlet of the first stem, and then it might grow.
384           // Do we really want this? (It only affects the tremolo gaps) --jneem
385           for (SCM s = index_get_cell (beaming, d);
386                scm_is_pair (s); s = scm_cdr (s))
387             {
388               if (!scm_is_integer (scm_car (s)))
389                 continue;
390
391               int beam_rank = scm_to_int (scm_car (s));
392               ranks.add_point (beam_rank);
393             }
394
395           for (SCM s = index_get_cell (beaming, d);
396                scm_is_pair (s); s = scm_cdr (s))
397             {
398               if (!scm_is_integer (scm_car (s)))
399                 continue;
400
401               int beam_rank = scm_to_int (scm_car (s));
402               Beam_stem_segment seg;
403               seg.stem_ = stem;
404               seg.stem_x_ = stem_x;
405               seg.rank_ = 2 * i + (d+1)/2;
406               seg.width_ = stem_width;
407               seg.stem_index_ = i;
408               seg.dir_ = d;
409               seg.max_connect_ = robust_scm2int (stem->get_property ("max-beam-connect"), 1000);
410
411               Direction stem_dir = get_grob_direction (stem);
412
413               seg.gapped_
414                 = (stem_dir * beam_rank < (stem_dir * ranks[-stem_dir] + gap_count));
415               stem_segments[beam_rank].push_back (seg);
416             }
417         }
418       while (flip (&d) != LEFT);
419     }
420
421   Drul_array<Real> break_overshoot
422     = robust_scm2drul (me->get_property ("break-overshoot"),
423                        Drul_array<Real> (-0.5, 0.0));
424
425   vector<Beam_segment> segments;
426   for (Position_stem_segments_map::const_iterator i (stem_segments.begin ());
427        i != stem_segments.end (); i++)
428     {
429       vector<Beam_stem_segment> segs = (*i).second;
430       vector_sort (segs, less<Beam_stem_segment> ());
431
432       Beam_segment current;
433
434       // Iterate over all of the segments of the current beam rank,
435       // merging the adjacent Beam_stem_segments into one Beam_segment
436       // when appropriate.
437       int vertical_count =  (*i).first;
438       for (vsize j = 0; j < segs.size (); j++)
439         {
440           // Keeping track of the different directions here is a little tricky.
441           // segs[j].dir_ is the direction of the beam segment relative to the stem
442           // (ie. segs[j].dir_ == LEFT if the beam segment sticks out to the left of
443           // its stem) whereas event_dir refers to the edge of the beam segment that
444           // we are currently looking at (ie. if segs[j].dir_ == event_dir then we
445           // are looking at that edge of the beam segment that is furthest from its
446           // stem).
447           Direction event_dir = LEFT;
448           Beam_stem_segment const& seg = segs[j];
449           do
450             {
451               Beam_stem_segment const& neighbor_seg = segs[j + event_dir];
452               // TODO: make names clearer? --jneem
453               // on_line_bound: whether the current segment is on the boundary of the WHOLE beam
454               // on_beam_bound: whether the current segment is on the boundary of just that part
455               //   of the beam with the current beam_rank
456               bool on_line_bound = (seg.dir_ == LEFT) ? seg.stem_index_ == 0
457                 : seg.stem_index_ == stems.size() - 1;
458               bool on_beam_bound = (event_dir == LEFT) ? j == 0 :
459                 j == segs.size () - 1;
460               bool inside_stem = (event_dir == LEFT)
461                 ? seg.stem_index_ > 0
462                 : seg.stem_index_ + 1 < stems.size () ;
463
464               bool event = on_beam_bound
465                 || abs (seg.rank_ - neighbor_seg.rank_) > 1
466                 || (abs (vertical_count) >= seg.max_connect_
467                     || abs (vertical_count) >= neighbor_seg.max_connect_);
468
469               if (!event)
470                 // Then this edge of the current segment is irrelevent because it will
471                 // be connected with the next segment in the event_dir direction.
472                 continue;
473
474               current.vertical_count_ = vertical_count;
475               current.horizontal_[event_dir] = seg.stem_x_;
476               if (seg.dir_ == event_dir)
477                 // then we are examining the edge of a beam segment that is furthest
478                 // from its stem.
479                 {
480                   if (on_line_bound
481                       && me->get_bound (event_dir)->break_status_dir ())
482                     {
483                       current.horizontal_[event_dir]
484                         = (robust_relative_extent (me->get_bound (event_dir),
485                                                    commonx, X_AXIS)[RIGHT]
486                            + event_dir * break_overshoot[event_dir]);
487                     }
488                   else
489                     {
490                       Grob *stem = stems[seg.stem_index_];
491                       Drul_array<Real> beamlet_length =
492                         robust_scm2interval (stem->get_property ("beamlet-default-length"), Interval (1.1, 1.1));
493                       Drul_array<Real> max_proportion =
494                         robust_scm2interval (stem->get_property ("beamlet-max-length-proportion"), Interval (0.75, 0.75));
495                       Real length = beamlet_length[seg.dir_];
496
497                       if (inside_stem)
498                         {
499                           Grob *neighbor_stem = stems[seg.stem_index_ + event_dir];
500                           Real neighbor_stem_x = neighbor_stem->relative_coordinate (commonx, X_AXIS);
501
502                           length = min (length,
503                                         fabs (neighbor_stem_x - seg.stem_x_) * max_proportion[seg.dir_]);
504                         }
505                       current.horizontal_[event_dir] += event_dir * length;
506                     }
507                 }
508               else
509                 // we are examining the edge of a beam segment that is closest
510                 // (ie. touching, unless there is a gap) its stem.
511                 {
512                   current.horizontal_[event_dir] += event_dir * seg.width_/2;
513                   if (seg.gapped_)
514                     {
515                       current.horizontal_[event_dir] -= event_dir * gap_length;
516
517                       if (Stem::is_invisible (seg.stem_))
518                         {
519                           /*
520                             Need to do this in case of whole notes. We don't want the
521                             heads to collide with the beams.
522                            */
523                           extract_grob_set (seg.stem_, "note-heads", heads);
524
525                           for (vsize k = 0; k < heads.size (); k ++)
526                             current.horizontal_[event_dir]
527                               = event_dir * min  (event_dir * current.horizontal_[event_dir],
528                                                   - gap_length/2
529                                                   + event_dir
530                                                     * heads[k]->extent (commonx,
531                                                                         X_AXIS)[-event_dir]);
532                         }
533                     }
534                 }
535
536               if (event_dir == RIGHT)
537                 {
538                   segments.push_back (current);
539                   current = Beam_segment ();
540                 }
541             }
542           while (flip (&event_dir) != LEFT);
543         }
544
545     }
546
547   return segments;
548 }
549
550 MAKE_SCHEME_CALLBACK (Beam, print, 1);
551 SCM
552 Beam::print (SCM grob)
553 {
554   Spanner *me = unsmob_spanner (grob);
555   Grob *commonx = 0;
556   vector<Beam_segment> segments = get_beam_segments (me, &commonx);
557
558   Interval span;
559   if (normal_stem_count (me))
560     {
561       span[LEFT] = first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
562       span[RIGHT] = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
563     }
564   else
565     {
566       extract_grob_set (me, "stems", stems);
567       span[LEFT] = stems[0]->relative_coordinate (commonx, X_AXIS);
568       span[RIGHT] = stems.back ()->relative_coordinate (commonx, X_AXIS);
569     }
570
571   Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
572
573   SCM posns = me->get_property ("quantized-positions");
574   Interval pos;
575   if (!is_number_pair (posns))
576     {
577       programming_error ("no beam positions?");
578       pos = Interval (0, 0);
579     }
580   else
581     pos = ly_scm2realdrul (posns);
582
583   scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
584
585   Real dy = pos[RIGHT] - pos[LEFT];
586   Real slope = (dy && span.length ()) ? dy / span.length ()  : 0;
587
588   Real beam_thickness = get_beam_thickness (me);
589   Real beam_dy = get_beam_translation (me);
590
591   Direction feather_dir = to_dir (me->get_property ("grow-direction"));
592
593   Stencil the_beam;
594   for (vsize i = 0; i < segments.size (); i ++)
595     {
596       Real local_slope = slope;
597       if (feather_dir)
598         {
599           local_slope += feather_dir * segments[i].vertical_count_ * beam_dy / span.length ();
600         }
601
602       Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
603
604       b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
605
606       b.translate_axis (local_slope
607                         * (segments[i].horizontal_[LEFT] - span.linear_combination (feather_dir))
608                         + pos.linear_combination (feather_dir)
609                         + beam_dy * segments[i].vertical_count_, Y_AXIS);
610       the_beam.add_stencil (b);
611     }
612
613 #if (DEBUG_BEAM_SCORING)
614   SCM annotation = me->get_property ("annotation");
615   if (scm_is_string (annotation))
616     {
617       extract_grob_set (me, "stems", stems);
618
619       /*
620         This code prints the demerits for each beam. Perhaps this
621         should be switchable for those who want to twiddle with the
622         parameters.
623       */
624       string str;
625       SCM properties = Font_interface::text_font_alist_chain (me);
626
627       properties = scm_cons(scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL),
628                             properties);
629       
630       Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
631
632       Stencil score = *unsmob_stencil (Text_interface::interpret_markup
633                                        (me->layout ()->self_scm (), properties, annotation));
634
635       if (!score.is_empty ())
636         {
637           score.translate_axis (me->relative_coordinate(commonx, X_AXIS), X_AXIS);
638           the_beam.add_at_edge (Y_AXIS, stem_dir, score, 1.0);
639         }
640     }
641 #endif
642
643   the_beam.translate_axis (-me->relative_coordinate (commonx, X_AXIS), X_AXIS);
644   return the_beam.smobbed_copy ();
645 }
646
647 Direction
648 Beam::get_default_dir (Grob *me)
649 {
650   extract_grob_set (me, "stems", stems);
651
652   Drul_array<Real> extremes (0.0, 0.0);
653   for (iterof (s, stems); s != stems.end (); s++)
654     {
655       Interval positions = Stem::head_positions (*s);
656       Direction d = DOWN;
657       do
658         {
659           if (sign (positions[d]) == d)
660             extremes[d] = d * max (d * positions[d], d * extremes[d]);
661         }
662       while (flip (&d) != DOWN);
663     }
664
665   Drul_array<int> total (0, 0);
666   Drul_array<int> count (0, 0);
667
668   bool force_dir = false;
669   for (vsize i = 0; i < stems.size (); i++)
670     {
671       Grob *s = stems[i];
672       Direction stem_dir = CENTER;
673       SCM stem_dir_scm = s->get_property_data ("direction");
674       if (is_direction (stem_dir_scm))
675         {
676           stem_dir = to_dir (stem_dir_scm);
677           force_dir = true;
678         }
679       else
680         stem_dir = to_dir (s->get_property ("default-direction"));
681
682       if (!stem_dir)
683         stem_dir = to_dir (s->get_property ("neutral-direction"));
684
685       if (stem_dir)
686         {
687           count[stem_dir] ++;
688           total[stem_dir] += max (int (- stem_dir * Stem::head_positions (s) [-stem_dir]), 0);
689         }
690     }
691
692
693   if (!force_dir)
694     {
695       if (abs (extremes[UP]) > -extremes[DOWN])
696         return DOWN;
697       else if (extremes[UP] < -extremes[DOWN])
698         return UP;
699     }
700
701   Direction dir = CENTER;
702   Direction d = CENTER;
703   if ((d = (Direction) sign (count[UP] - count[DOWN])))
704     dir = d;
705   else if (count[UP]
706            && count[DOWN]
707            && (d = (Direction)  sign (total[UP] / count[UP] - total[DOWN]/count[DOWN])))
708     dir = d;
709   else if ((d = (Direction)  sign (total[UP] - total[DOWN])))
710     dir = d;
711   else
712     dir = to_dir (me->get_property ("neutral-direction"));
713
714   return dir;
715 }
716
717 /* Set all stems with non-forced direction to beam direction.
718    Urg: non-forced should become `without/with unforced' direction,
719    once stem gets cleaned-up. */
720 void
721 Beam::set_stem_directions (Grob *me, Direction d)
722 {
723   extract_grob_set (me, "stems", stems);
724
725   for (vsize i = 0; i < stems.size (); i++)
726     {
727       Grob *s = stems[i];
728
729       SCM forcedir = s->get_property_data ("direction");
730       if (!to_dir (forcedir))
731         set_grob_direction (s, d);
732     }
733 }
734
735 /*
736   Only try horizontal beams for knees.  No reliable detection of
737   anything else is possible here, since we don't know funky-beaming
738   settings, or X-distances (slopes!)  People that want sloped
739   knee-beams, should set the directions manually.
740
741
742   TODO:
743
744   this routine should take into account the stemlength scoring
745   of a possible knee/nonknee beam.
746 */
747 void
748 Beam::consider_auto_knees (Grob *me)
749 {
750   SCM scm = me->get_property ("auto-knee-gap");
751   if (!scm_is_number (scm))
752     return;
753
754   Interval_set gaps;
755
756   gaps.set_full ();
757
758   extract_grob_set (me, "normal-stems", stems);
759
760   Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
761   Real staff_space = Staff_symbol_referencer::staff_space (me);
762
763   vector<Interval> head_extents_array;
764   for (vsize i = 0; i < stems.size (); i++)
765     {
766       Grob *stem = stems[i];
767
768       Interval head_extents = Stem::head_positions (stem);
769       if (!head_extents.is_empty ())
770         {
771           head_extents[LEFT] += -1;
772           head_extents[RIGHT] += 1;
773           head_extents *= staff_space * 0.5;
774
775           /*
776             We could subtract beam Y position, but this routine only
777             sets stem directions, a constant shift does not have an
778             influence.
779           */
780           head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
781
782           if (to_dir (stem->get_property_data ("direction")))
783             {
784               Direction stemdir = to_dir (stem->get_property ("direction"));
785               head_extents[-stemdir] = -stemdir * infinity_f;
786             }
787         }
788       head_extents_array.push_back (head_extents);
789
790       gaps.remove_interval (head_extents);
791     }
792
793   Interval max_gap;
794   Real max_gap_len = 0.0;
795
796   for (vsize i = gaps.allowed_regions_.size () -1; i != VPOS ;i--)
797     {
798       Interval gap = gaps.allowed_regions_[i];
799
800       /*
801         the outer gaps are not knees.
802       */
803       if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
804         continue;
805
806       if (gap.length () >= max_gap_len)
807         {
808           max_gap_len = gap.length ();
809           max_gap = gap;
810         }
811     }
812
813   Real beam_translation = get_beam_translation (me);
814   Real beam_thickness = Beam::get_beam_thickness (me);
815   int beam_count = Beam::get_beam_count (me);
816   Real height_of_beams = beam_thickness / 2
817     + (beam_count - 1) * beam_translation;
818   Real threshold = scm_to_double (scm) + height_of_beams;
819
820   if (max_gap_len > threshold)
821     {
822       int j = 0;
823       for (vsize i = 0; i < stems.size (); i++)
824         {
825           Grob *stem = stems[i];
826           Interval head_extents = head_extents_array[j++];
827
828           Direction d = (head_extents.center () < max_gap.center ())
829             ? UP : DOWN;
830
831           stem->set_property ("direction", scm_from_int (d));
832
833           head_extents.intersect (max_gap);
834           assert (head_extents.is_empty () || head_extents.length () < 1e-6);
835         }
836     }
837 }
838
839 /* Set stem's shorten property if unset.
840
841 TODO:
842 take some y-position (chord/beam/nearest?) into account
843 scmify forced-fraction
844
845 This is done in beam because the shorten has to be uniform over the
846 entire beam.
847 */
848
849
850
851 void
852 set_minimum_dy (Grob *me, Real *dy)
853 {
854   if (*dy)
855     {
856       /*
857         If dy is smaller than the smallest quant, we
858         get absurd direction-sign penalties.
859       */
860
861       Real ss = Staff_symbol_referencer::staff_space (me);
862       Real beam_thickness = Beam::get_beam_thickness (me) / ss;
863       Real slt = Staff_symbol_referencer::line_thickness (me) / ss;
864       Real sit = (beam_thickness - slt) / 2;
865       Real inter = 0.5;
866       Real hang = 1.0 - (beam_thickness - slt) / 2;
867
868       *dy = sign (*dy) * max (fabs (*dy),
869                               min (min (sit, inter), hang));
870     }
871 }
872
873
874
875 MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1)
876 SCM
877 Beam::calc_stem_shorten (SCM smob)
878 {
879   Grob *me = unsmob_grob (smob);
880
881   /*
882     shortening looks silly for x staff beams
883   */
884   if (is_knee (me))
885     return scm_from_int (0);
886
887   Real forced_fraction = 1.0 * forced_stem_count (me)
888     / normal_stem_count (me);
889
890   int beam_count = get_beam_count (me);
891
892   SCM shorten_list = me->get_property ("beamed-stem-shorten");
893   if (shorten_list == SCM_EOL)
894     return scm_from_int (0);
895
896   Real staff_space = Staff_symbol_referencer::staff_space (me);
897
898   SCM shorten_elt
899     = robust_list_ref (beam_count -1, shorten_list);
900   Real shorten = scm_to_double (shorten_elt) * staff_space;
901
902   shorten *= forced_fraction;
903
904
905   if (shorten)
906     return scm_from_double (shorten);
907
908   return scm_from_double (0.0);
909 }
910
911
912 Interval
913 Beam::no_visible_stem_positions (Grob *me, Interval default_value)
914 {
915   extract_grob_set (me, "stems", stems);
916   if (stems.empty ())
917     return default_value;
918
919   Interval head_positions;
920   Slice multiplicity;
921   for (vsize i = 0; i < stems.size(); i++)
922     {
923       head_positions.unite (Stem::head_positions (stems[i]));
924       multiplicity.unite (Stem::beam_multiplicity (stems[i]));
925     }
926
927   Direction dir = get_grob_direction (me);
928   Real y = head_positions[dir]
929     * 0.5 * Staff_symbol_referencer::staff_space (me)
930     + dir * get_beam_translation (me) * (multiplicity.length () + 1);
931
932   y /= Staff_symbol_referencer::staff_space (me);
933   return Interval (y,y);
934 }
935
936
937 /*
938   Compute a first approximation to the beam slope.
939 */
940 MAKE_SCHEME_CALLBACK (Beam, calc_least_squares_positions, 2);
941 SCM
942 Beam::calc_least_squares_positions (SCM smob, SCM /* posns */)
943 {
944   Grob *me = unsmob_grob (smob);
945
946   int count = normal_stem_count (me);
947   Interval pos (0,0);
948   if (count < 1)
949     return ly_interval2scm (no_visible_stem_positions (me, pos));
950
951   vector<Real> x_posns;
952   extract_grob_set (me, "normal-stems", stems);
953   Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
954   Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
955
956   Real my_y = me->relative_coordinate (commony, Y_AXIS);
957
958   Grob *fvs = first_normal_stem (me);
959   Grob *lvs = last_normal_stem (me);
960
961   Interval ideal (Stem::get_stem_info (fvs).ideal_y_
962                   + fvs->relative_coordinate (commony, Y_AXIS) - my_y,
963                   Stem::get_stem_info (lvs).ideal_y_
964                   + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
965
966   Real x0 = first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
967   for (vsize i = 0; i < stems.size (); i++)
968     {
969       Grob *s = stems[i];
970
971       Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
972       x_posns.push_back (x);
973     }
974   Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
975
976   Real y = 0;
977   Real slope = 0;
978   Real dy = 0;
979   Real ldy = 0.0;
980   if (!ideal.delta ())
981     {
982       Interval chord (Stem::chord_start_y (stems[0]),
983                       Stem::chord_start_y (stems.back ()));
984
985       /* Simple beams (2 stems) on middle line should be allowed to be
986          slightly sloped.
987
988          However, if both stems reach middle line,
989          ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
990
991          For that case, we apply artificial slope */
992       if (!ideal[LEFT] && chord.delta () && count == 2)
993         {
994           /* FIXME. -> UP */
995           Direction d = (Direction) (sign (chord.delta ()) * UP);
996           pos[d] = get_beam_thickness (me) / 2;
997           pos[-d] = -pos[d];
998         }
999       else
1000         pos = ideal;
1001
1002       /*
1003         For broken beams this doesn't work well. In this case, the
1004         slope esp. of the first part of a broken beam should predict
1005         where the second part goes.
1006       */
1007       ldy = pos[RIGHT] - pos[LEFT];
1008     }
1009   else
1010     {
1011       vector<Offset> ideals;
1012       for (vsize i = 0; i < stems.size (); i++)
1013         {
1014           Grob *s = stems[i];
1015           ideals.push_back (Offset (x_posns[i],
1016                                Stem::get_stem_info (s).ideal_y_
1017                                + s->relative_coordinate (commony, Y_AXIS)
1018                                - my_y));
1019         }
1020
1021       minimise_least_squares (&slope, &y, ideals);
1022
1023       dy = slope * dx;
1024
1025       set_minimum_dy (me, &dy);
1026
1027       ldy = dy;
1028       pos = Interval (y, (y + dy));
1029     }
1030
1031   /*
1032     "position" is relative to the staff.
1033   */
1034   scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1035
1036   me->set_property ("least-squares-dy",  scm_from_double (ldy));
1037   return ly_interval2scm (pos);
1038 }
1039
1040
1041 // Assuming V is not empty, pick a 'reasonable' point inside V.
1042 static Real
1043 point_in_interval (Interval v, Real dist)
1044 {
1045   if (isinf (v[DOWN]))
1046     return v[UP] - dist;
1047   else if (isinf (v[UP]))
1048     return v[DOWN] + dist;
1049   else
1050     return v.center ();
1051 }
1052
1053 /*
1054   We can't combine with previous function, since check concave and
1055   slope damping comes first.
1056
1057   TODO: we should use the concaveness to control the amount of damping
1058   applied.
1059 */
1060 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 2);
1061 SCM
1062 Beam::shift_region_to_valid (SCM grob, SCM posns)
1063 {
1064   Grob *me = unsmob_grob (grob);
1065
1066   /*
1067     Code dup.
1068   */
1069   vector<Real> x_posns;
1070   extract_grob_set (me, "stems", stems);
1071   extract_grob_set (me, "covered-grobs", covered);
1072
1073   Grob *common[NO_AXES] = { me, me };
1074   for (Axis a = X_AXIS; a < NO_AXES; incr (a)) {
1075     common[a] = common_refpoint_of_array (stems, me, a);
1076     common[a] = common_refpoint_of_array (covered, common[a], a);
1077   }
1078   Grob *fvs = first_normal_stem (me);
1079
1080   if (!fvs)
1081     return posns;
1082   Interval x_span;
1083   x_span[LEFT] = fvs->relative_coordinate (common[X_AXIS], X_AXIS);
1084   for (vsize i = 0; i < stems.size (); i++)
1085     {
1086       Grob *s = stems[i];
1087
1088       Real x = s->relative_coordinate (common[X_AXIS], X_AXIS) - x_span[LEFT];
1089       x_posns.push_back (x);
1090     }
1091   
1092   Grob *lvs = last_normal_stem (me);
1093   x_span[RIGHT] = lvs->relative_coordinate (common[X_AXIS], X_AXIS);
1094
1095   Drul_array<Real> pos = ly_scm2interval (posns);
1096
1097   scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1098
1099   Real beam_dy = pos[RIGHT] - pos[LEFT];
1100   Real beam_left_y = pos[LEFT];
1101   Real slope = x_span.delta () ? (beam_dy / x_span.delta ()) : 0.0;
1102
1103   /*
1104     Shift the positions so that we have a chance of finding good
1105     quants (i.e. no short stem failures.)
1106   */
1107   Interval feasible_left_point;
1108   feasible_left_point.set_full ();
1109
1110   for (vsize i = 0; i < stems.size (); i++)
1111     {
1112       Grob *s = stems[i];
1113       if (Stem::is_invisible (s))
1114         continue;
1115
1116       Direction d = get_grob_direction (s);
1117       Real left_y
1118         = Stem::get_stem_info (s).shortest_y_
1119         - slope * x_posns [i];
1120
1121       /*
1122         left_y is now relative to the stem S. We want relative to
1123         ourselves, so translate:
1124       */
1125       left_y
1126         += + s->relative_coordinate (common[Y_AXIS], Y_AXIS)
1127         - me->relative_coordinate (common[Y_AXIS], Y_AXIS);
1128
1129       Interval flp;
1130       flp.set_full ();
1131       flp[-d] = left_y;
1132
1133       feasible_left_point.intersect (flp);
1134     }
1135
1136   /*
1137     We have two intervals here, one for the up variant (beams goes
1138     over the collision) one for the down.
1139   */
1140   Drul_array<Interval> collision_free (feasible_left_point,
1141                                        feasible_left_point);
1142
1143   vector<Grob*> filtered;
1144   /*
1145     We only update these for objects that are too large for quanting
1146     to find a workaround.  Typically, these are notes with
1147     stems, and timesig/keysig/clef, which take out the entire area
1148     inside the staff as feasible.
1149
1150     The code below disregards the thickness and multiplicity of the
1151     beam.  This should not be a problem, as the beam quanting will
1152     take care of computing the impact those exactly.
1153   */
1154   Real min_y_size = 2.0;
1155   for (vsize i = 0; i < covered.size(); i++)
1156     {
1157       if (!covered[i]->is_live())
1158         continue;
1159       
1160       Box b;
1161       for (Axis a = X_AXIS; a < NO_AXES; incr (a))
1162         b[a] = covered[i]->extent (common[a], a);
1163
1164       if (b[X_AXIS].is_empty () || b[Y_AXIS].is_empty ())
1165         continue;
1166
1167       if (intersection (b[X_AXIS], x_span).is_empty ())
1168         continue;
1169
1170       filtered.push_back (covered[i]);
1171       Grob *head_stem = Rhythmic_head::get_stem (covered[i]);
1172       if (head_stem && Stem::is_normal_stem (head_stem)
1173           && Note_head::has_interface (covered[i]))
1174         {
1175           if (Stem::get_beam (head_stem))
1176             {
1177               /*
1178                 We must assume that stems are infinitely long in this
1179                 case, as asking for the length of the stem typically
1180                 leads to circular dependencies.
1181
1182                 This strategy assumes that we don't want to handle the
1183                 collision of beams in opposite non-forced directions
1184                 with this code, where shortening the stems of both
1185                 would resolve the problem, eg.
1186
1187                  x    x
1188                 |    | 
1189                 =====
1190
1191                 =====
1192                 |   |  
1193                 x   x
1194                 
1195                 Such beams would need a coordinating grob to resolve
1196                 the collision, since both will likely want to occupy
1197                 the centerline.
1198               */
1199               Direction stemdir = get_grob_direction (head_stem);
1200               b[Y_AXIS][stemdir] = stemdir * infinity_f; 
1201             }
1202           else
1203             {
1204               // TODO - should we include the extent of the stem here?
1205             }
1206         }
1207
1208       if (b[Y_AXIS].length () < min_y_size)
1209         continue;
1210
1211       Direction d = LEFT;
1212       do
1213         {
1214           Real x = b[X_AXIS][d] - x_span[LEFT];
1215           Real dy = slope * x;
1216
1217           Direction yd = DOWN;
1218           do
1219             {
1220               Real left_y = b[Y_AXIS][yd];
1221
1222               if (left_y == yd * infinity_f)
1223                 {
1224                   collision_free[yd].set_empty ();
1225                   continue;
1226                 }
1227
1228               left_y -= dy;
1229
1230               // Translate back to beam as ref point.
1231               left_y -= -me->relative_coordinate (common[Y_AXIS], Y_AXIS);
1232             
1233               Interval allowed;
1234               allowed.set_full ();
1235
1236               allowed[-yd] = left_y;
1237               collision_free[yd].intersect (allowed);
1238             }
1239           while (flip (&yd) != DOWN);
1240         }
1241       while (flip (&d) != LEFT);
1242     }
1243
1244   Grob_array *arr = 
1245     Pointer_group_interface::get_grob_array (me,
1246                                              ly_symbol2scm ("covered-grobs"));
1247   arr->set_array (filtered);
1248
1249   if (collision_free[DOWN].contains (beam_left_y)
1250       || collision_free[UP].contains (beam_left_y))
1251     {
1252       // We're good to go. Do nothing.
1253     }
1254   else if (!collision_free[DOWN].is_empty ()
1255            || !collision_free[UP].is_empty ())
1256     {
1257       // We have space above or below collisions (or, no collisions at
1258       // all).
1259       Interval best =  
1260         (collision_free[DOWN].length () > collision_free[UP].length ()) ?
1261         collision_free[DOWN] : collision_free[UP];
1262
1263       beam_left_y = point_in_interval (best, 2.0);
1264     }
1265   else if (!feasible_left_point.is_empty ())
1266     {
1267       // We are somewhat screwed: we have a collision, but at least
1268       // there is a way to satisfy stem length constraints.
1269       beam_left_y = point_in_interval (feasible_left_point, 2.0);
1270     }
1271   else
1272     {
1273       // We are completely screwed.
1274       warning (_ ("no viable initial configuration found: may not find good beam slope"));
1275     }
1276   
1277   pos = Drul_array<Real> (beam_left_y, (beam_left_y + beam_dy));
1278   scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1279
1280   return ly_interval2scm (pos);
1281 }
1282
1283 /* This neat trick is by Werner Lemberg,
1284    damped = tanh (slope)
1285    corresponds with some tables in [Wanske] CHECKME */
1286 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 2);
1287 SCM
1288 Beam::slope_damping (SCM smob, SCM posns)
1289 {
1290   Grob *me = unsmob_grob (smob);
1291   Drul_array<Real> pos = ly_scm2interval (posns);
1292
1293   if (normal_stem_count (me) <= 1)
1294     return posns;
1295
1296   SCM s = me->get_property ("damping");
1297   Real damping = scm_to_double (s);
1298   Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0);
1299   if (concaveness >= 10000)
1300     {
1301       pos[LEFT] = pos[RIGHT];
1302       me->set_property ("least-squares-dy", scm_from_double (0));
1303       damping = 0;
1304     }
1305
1306   if (damping)
1307     {
1308       scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1309
1310       Real dy = pos[RIGHT] - pos[LEFT];
1311
1312       Grob *fvs = first_normal_stem (me);
1313       Grob *lvs = last_normal_stem (me);
1314
1315       Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1316
1317       Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS)
1318         - first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
1319
1320       Real slope = dy && dx ? dy / dx : 0;
1321
1322       slope = 0.6 * tanh (slope) / (damping + concaveness);
1323
1324       Real damped_dy = slope * dx;
1325
1326       set_minimum_dy (me, &damped_dy);
1327
1328       pos[LEFT] += (dy - damped_dy) / 2;
1329       pos[RIGHT] -= (dy - damped_dy) / 2;
1330
1331       scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1332     }
1333
1334   return ly_interval2scm (pos);
1335 }
1336
1337
1338 MAKE_SCHEME_CALLBACK (Beam, quanting, 2);
1339 SCM
1340 Beam::quanting (SCM smob, SCM posns)
1341 {
1342   Grob *me = unsmob_grob (smob);
1343   Drul_array<Real> ys(0, 0);
1344   ys = robust_scm2drul (posns, ys);
1345   Beam_scoring_problem problem (me, ys);
1346
1347   ys = problem.solve ();
1348   return ly_interval2scm (ys);
1349 }
1350
1351
1352 /*
1353   Report slice containing the numbers that are both in (car BEAMING)
1354   and (cdr BEAMING)
1355 */
1356 Slice
1357 where_are_the_whole_beams (SCM beaming)
1358 {
1359   Slice l;
1360
1361   for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1362     {
1363       if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1364
1365         l.add_point (scm_to_int (scm_car (s)));
1366     }
1367
1368   return l;
1369 }
1370
1371 /* Return the Y position of the stem-end, given the Y-left, Y-right
1372    in POS for stem S.  This Y position is relative to S. */
1373 Real
1374 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
1375                    Real xl, Real xr, Direction feather_dir,
1376                    Drul_array<Real> pos, bool french)
1377 {
1378   Real beam_translation = get_beam_translation (me);
1379   Direction stem_dir = get_grob_direction (stem);
1380
1381   Real dx = xr - xl;
1382   Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl)/dx : 0;
1383   Real xdir = 2*relx-1;
1384
1385   Real stem_y = linear_combination(pos, xdir);
1386
1387   SCM beaming = stem->get_property ("beaming");
1388
1389   Slice beam_slice (french
1390                     ? where_are_the_whole_beams (beaming)
1391                     : Stem::beam_multiplicity (stem));
1392   if (beam_slice.is_empty ())
1393     beam_slice = Slice (0,0);
1394   Interval beam_multiplicity(beam_slice[LEFT],
1395                              beam_slice[RIGHT]);
1396
1397   /*
1398     feather dir = 1 , relx 0->1 : factor 0 -> 1
1399     feather dir = 0 , relx 0->1 : factor 1 -> 1
1400     feather dir = -1, relx 0->1 : factor 1 -> 0
1401    */
1402   Real feather_factor = 1;
1403   if (feather_dir > 0)
1404     feather_factor = relx;
1405   else if (feather_dir < 0)
1406     feather_factor = 1 - relx;
1407
1408   stem_y += feather_factor * beam_translation
1409     * beam_multiplicity[Direction(((french) ? DOWN : UP)*stem_dir)];
1410   Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1411     - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
1412
1413   return stem_y + id;
1414 }
1415
1416 /*
1417   Hmm.  At this time, beam position and slope are determined.  Maybe,
1418   stem directions and length should set to relative to the chord's
1419   position of the beam.  */
1420 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1421 SCM
1422 Beam::set_stem_lengths (SCM smob)
1423 {
1424   Grob *me = unsmob_grob (smob);
1425
1426   /* trigger callbacks. */
1427   (void) me->get_property ("direction");
1428   (void) me->get_property ("beaming");
1429
1430   SCM posns = me->get_property ("positions");
1431
1432   extract_grob_set (me, "stems", stems);
1433   if (!stems.size ())
1434     return posns;
1435
1436   Grob *common[2];
1437   for (int a = 2; a--;)
1438     common[a] = common_refpoint_of_array (stems, me, Axis (a));
1439
1440   Drul_array<Real> pos = ly_scm2realdrul (posns);
1441   Real staff_space = Staff_symbol_referencer::staff_space (me);
1442   scale_drul (&pos, staff_space);
1443
1444   bool gap = false;
1445   Real thick = 0.0;
1446   if (robust_scm2int (me->get_property ("gap-count"), 0))
1447     {
1448       gap = true;
1449       thick = get_beam_thickness (me);
1450     }
1451
1452   Grob *fvs = first_normal_stem (me);
1453   Grob *lvs = last_normal_stem (me);
1454
1455   Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1456   Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1457   Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1458
1459   for (vsize i = 0; i < stems.size (); i++)
1460     {
1461       Grob *s = stems[i];
1462
1463       bool french = to_boolean (s->get_property ("french-beaming"));
1464       Real stem_y = calc_stem_y (me, s, common,
1465                                  xl, xr, feather_dir,
1466                                  pos, french && s != lvs && s!= fvs);
1467
1468       /*
1469         Make the stems go up to the end of the beam. This doesn't matter
1470         for normal beams, but for tremolo beams it looks silly otherwise.
1471       */
1472       if (gap
1473           && !Stem::is_invisible (s))
1474         stem_y += thick * 0.5 * get_grob_direction (s);
1475
1476       /*
1477         Do set_stemend for invisible stems too, so tuplet brackets
1478         have a reference point for sloping
1479        */
1480       Stem::set_stemend (s, 2 * stem_y / staff_space);
1481     }
1482
1483   return posns;
1484 }
1485
1486 void
1487 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1488 {
1489   extract_grob_set (me, "stems", stems);
1490
1491   Direction d = LEFT;
1492   for (vsize i = 0; i < stems.size (); i++)
1493     {
1494       /*
1495         Don't overwrite user settings.
1496       */
1497       do
1498         {
1499           Grob *stem = stems[i];
1500           SCM beaming_prop = stem->get_property ("beaming");
1501           if (beaming_prop == SCM_EOL
1502               || index_get_cell (beaming_prop, d) == SCM_EOL)
1503             {
1504               int count = beaming->beamlet_count (i, d);
1505               if (i > 0
1506                   && i + 1 < stems.size ()
1507                   && Stem::is_invisible (stem))
1508                 count = min (count, beaming->beamlet_count (i,-d));
1509
1510               if ( ((i == 0 && d == LEFT)
1511                     || (i == stems.size ()-1 && d == RIGHT))
1512                    && stems.size () > 1
1513                    && to_boolean (me->get_property ("clip-edges")))
1514                 count = 0;
1515
1516               Stem::set_beaming (stem, count, d);
1517             }
1518         }
1519       while (flip (&d) != LEFT);
1520     }
1521 }
1522
1523 int
1524 Beam::forced_stem_count (Grob *me)
1525 {
1526   extract_grob_set (me, "normal-stems", stems);
1527
1528   int f = 0;
1529   for (vsize i = 0; i < stems.size (); i++)
1530     {
1531       Grob *s = stems[i];
1532
1533       /* I can imagine counting those boundaries as a half forced stem,
1534          but let's count them full for now. */
1535       Direction defdir = to_dir (s->get_property ("default-direction"));
1536
1537       if (abs (Stem::chord_start_y (s)) > 0.1
1538           && defdir
1539           && get_grob_direction (s) != defdir)
1540         f++;
1541     }
1542   return f;
1543 }
1544
1545 int
1546 Beam::normal_stem_count (Grob *me)
1547 {
1548   extract_grob_set (me, "normal-stems", stems);
1549   return stems.size ();
1550 }
1551
1552 Grob *
1553 Beam::first_normal_stem (Grob *me)
1554 {
1555   extract_grob_set (me, "normal-stems", stems);
1556   return stems.size () ? stems[0] : 0;
1557 }
1558
1559 Grob *
1560 Beam::last_normal_stem (Grob *me)
1561 {
1562   extract_grob_set (me, "normal-stems", stems);
1563   return stems.size () ? stems.back () : 0;
1564 }
1565
1566 /*
1567   [TODO]
1568
1569   handle rest under beam (do_post: beams are calculated now)
1570   what about combination of collisions and rest under beam.
1571
1572   Should lookup
1573
1574   rest -> stem -> beam -> interpolate_y_position ()
1575 */
1576 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1577 SCM
1578 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1579 {
1580   Grob *rest = unsmob_grob (smob);
1581   if (scm_is_number (rest->get_property ("staff-position")))
1582     return scm_from_int (0);
1583
1584   Real offset = robust_scm2double (prev_offset, 0.0);
1585
1586   Grob *st = unsmob_grob (rest->get_object ("stem"));
1587   Grob *stem = st;
1588   if (!stem)
1589     return scm_from_double (0.0);
1590   Grob *beam = unsmob_grob (stem->get_object ("beam"));
1591   if (!beam
1592       || !Beam::has_interface (beam)
1593       || !Beam::normal_stem_count (beam))
1594     return scm_from_double (0.0);
1595
1596   Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1597                                          Drul_array<Real> (0,0)));
1598
1599   Real staff_space = Staff_symbol_referencer::staff_space (rest);
1600
1601   scale_drul (&pos, staff_space);
1602
1603   Real dy = pos[RIGHT] - pos[LEFT];
1604
1605   Drul_array<Grob*> visible_stems (first_normal_stem (beam),
1606                                    last_normal_stem (beam));
1607   extract_grob_set (beam, "stems", stems);
1608
1609   Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1610
1611   Real x0 = visible_stems[LEFT]->relative_coordinate (common, X_AXIS);
1612   Real dx = visible_stems[RIGHT]->relative_coordinate (common, X_AXIS) - x0;
1613   Real slope = dy && dx ? dy / dx : 0;
1614
1615   Direction d = get_grob_direction (stem);
1616   Real stem_y = pos[LEFT]
1617     + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1618
1619   Real beam_translation = get_beam_translation (beam);
1620   Real beam_thickness = Beam::get_beam_thickness (beam);
1621
1622   /*
1623     TODO: this is not strictly correct for 16th knee beams.
1624   */
1625   int beam_count
1626     = Stem::beam_multiplicity (stem).length () + 1;
1627
1628   Real height_of_my_beams = beam_thickness / 2
1629     + (beam_count - 1) * beam_translation;
1630   Real beam_y = stem_y - d * height_of_my_beams;
1631
1632   Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1633
1634   Interval rest_extent = rest->extent (rest, Y_AXIS);
1635   rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1636
1637   Real rest_dim = rest_extent[d];
1638   Real minimum_distance
1639     = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1640                      + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1641
1642   Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1643
1644   shift /= staff_space;
1645   Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1646
1647   /* Always move discretely by half spaces */
1648   shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1649
1650   /* Inside staff, move by whole spaces*/
1651   if ((rest_extent[d] + staff_space * shift) * d
1652       < rad
1653       || (rest_extent[-d] + staff_space * shift) * -d
1654       < rad)
1655     shift = ceil (fabs (shift)) * sign (shift);
1656
1657   return scm_from_double (offset + staff_space * shift);
1658 }
1659
1660 bool
1661 Beam::is_knee (Grob *me)
1662 {
1663   SCM k = me->get_property ("knee");
1664   if (scm_is_bool (k))
1665     return ly_scm2bool (k);
1666
1667   bool knee = false;
1668   int d = 0;
1669   extract_grob_set (me, "stems", stems);
1670   for (vsize i = stems.size (); i--;)
1671     {
1672       Direction dir = get_grob_direction (stems[i]);
1673       if (d && d != dir)
1674         {
1675           knee = true;
1676           break;
1677         }
1678       d = dir;
1679     }
1680
1681   me->set_property ("knee", ly_bool2scm (knee));
1682
1683   return knee;
1684 }
1685
1686 bool
1687 Beam::is_cross_staff (Grob *me)
1688 {
1689   extract_grob_set (me, "stems", stems);
1690   Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1691   for (vsize i = 0; i < stems.size (); i++)
1692     if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1693       return true;
1694   return false;
1695 }
1696
1697 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1698 SCM
1699 Beam::calc_cross_staff (SCM smob)
1700 {
1701   return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1702 }
1703
1704 int
1705 Beam::get_direction_beam_count (Grob *me, Direction d)
1706 {
1707   extract_grob_set (me, "stems", stems);
1708   int bc = 0;
1709
1710   for (vsize i = stems.size (); i--;)
1711     {
1712       /*
1713         Should we take invisible stems into account?
1714       */
1715       if (get_grob_direction (stems[i]) == d)
1716         bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1717     }
1718
1719   return bc;
1720 }
1721
1722 ADD_INTERFACE (Beam,
1723                "A beam.\n"
1724                "\n"
1725                "The @code{beam-thickness} property is the weight of beams,"
1726                " measured in staffspace.  The @code{direction} property is"
1727                " not user-serviceable.  Use the @code{direction} property"
1728                " of @code{Stem} instead.\n"
1729                "\n"
1730                "The following properties may be set in the @code{details}"
1731                " list.\n"
1732                "\n"
1733                "@table @code\n"
1734                "@item stem-length-demerit-factor\n"
1735                "Demerit factor used for inappropriate stem lengths.\n"
1736                "@item secondary-beam-demerit\n"
1737                "Demerit used in quanting calculations for multiple"
1738                " beams.\n"
1739                "@item region-size\n"
1740                "Size of region for checking quant scores.\n"
1741                "@item beam-eps\n"
1742                "Epsilon for beam quant code to check for presence"
1743                " in gap.\n"
1744                "@item stem-length-limit-penalty\n"
1745                "Penalty for differences in stem lengths on a beam.\n"
1746                "@item damping-direction-penalty\n"
1747                "Demerit penalty applied when beam direction is different"
1748                " from damping direction.\n"
1749                "@item hint-direction-penalty\n"
1750                "Demerit penalty applied when beam direction is different"
1751                " from damping direction, but damping slope is"
1752                " <= @code{round-to-zero-slope}.\n"
1753                "@item musical-direction-factor\n"
1754                "Demerit scaling factor for difference between"
1755                " beam slope and music slope.\n"
1756                "@item ideal-slope-factor\n"
1757                "Demerit scaling factor for difference between"
1758                " beam slope and damping slope.\n"
1759                "@item round-to-zero-slope\n"
1760                "Damping slope which is considered zero for purposes of"
1761                " calculating direction penalties.\n"
1762                "@end table\n",
1763
1764                /* properties */
1765                "annotation "
1766                "auto-knee-gap "
1767                "beamed-stem-shorten "
1768                "beaming "
1769                "beam-thickness "
1770                "break-overshoot "
1771                "clip-edges "
1772                "concaveness "
1773                "covered-grobs "
1774                "damping "
1775                "details "
1776                "direction "
1777                "gap "
1778                "gap-count "
1779                "grow-direction "
1780                "inspect-quants "
1781                "knee "
1782                "length-fraction "
1783                "least-squares-dy "
1784                "neutral-direction "
1785                "normal-stems "
1786                "positions "
1787                "quantized-positions "
1788                "shorten "
1789                "stems "
1790                );