]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
064f5d7b665ef440e15857d9611071b9c535e7b9
[lilypond.git] / lily / beam.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--2015 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 "axis-group-interface.hh"
41 #include "align-interface.hh"
42 #include "beam-scoring-problem.hh"
43 #include "beaming-pattern.hh"
44 #include "directional-element-interface.hh"
45 #include "grob-array.hh"
46 #include "international.hh"
47 #include "interval-set.hh"
48 #include "item.hh"
49 #include "lookup.hh"
50 #include "main.hh"
51 #include "misc.hh"
52 #include "note-head.hh"
53 #include "output-def.hh"
54 #include "pointer-group-interface.hh"
55 #include "rhythmic-head.hh"
56 #include "spanner.hh"
57 #include "staff-symbol.hh"
58 #include "staff-symbol-referencer.hh"
59 #include "stem.hh"
60 #include "warn.hh"
61
62 #if DEBUG_BEAM_SCORING
63 #include "text-interface.hh" // debug output.
64 #include "font-interface.hh" // debug output.
65 #endif
66
67 #include <map>
68
69 Beam_stem_segment::Beam_stem_segment ()
70 {
71   max_connect_ = 1000;          // infinity
72   stem_ = 0;
73   width_ = 0.0;
74   stem_x_ = 0.0;
75   rank_ = 0;
76   stem_index_ = 0;
77   dir_ = CENTER;
78 }
79
80 bool
81 beam_segment_less (Beam_segment const &a, Beam_segment const &b)
82 {
83   return a.horizontal_[LEFT] < b.horizontal_[LEFT];
84 }
85
86 Beam_segment::Beam_segment ()
87 {
88   vertical_count_ = 0;
89 }
90
91 void
92 Beam::add_stem (Grob *me, Grob *s)
93 {
94   if (Stem::get_beam (s))
95     {
96       programming_error ("Stem already has beam");
97       return;
98     }
99
100   Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
101   s->set_object ("beam", me->self_scm ());
102   add_bound_item (dynamic_cast<Spanner *> (me), dynamic_cast<Item *> (s));
103 }
104
105 Real
106 Beam::get_beam_thickness (Grob *me)
107 {
108   return robust_scm2double (me->get_property ("beam-thickness"), 0)
109          * Staff_symbol_referencer::staff_space (me);
110 }
111
112 /* Return the translation between 2 adjoining beams. */
113 Real
114 Beam::get_beam_translation (Grob *me)
115 {
116   int beam_count = get_beam_count (me);
117   Real staff_space = Staff_symbol_referencer::staff_space (me);
118   Real line = Staff_symbol_referencer::line_thickness (me);
119   Real beam_thickness = get_beam_thickness (me);
120   Real fract = robust_scm2double (me->get_property ("length-fraction"), 1.0);
121
122   Real beam_translation = beam_count < 4
123                           ? (2 * staff_space + line - beam_thickness) / 2.0
124                           : (3 * staff_space + line - beam_thickness) / 3.0;
125
126   return fract * beam_translation;
127 }
128
129 /* Maximum beam_count. */
130 int
131 Beam::get_beam_count (Grob *me)
132 {
133   int m = 0;
134
135   extract_grob_set (me, "stems", stems);
136   for (vsize i = 0; i < stems.size (); i++)
137     {
138       Grob *stem = stems[i];
139       m = max (m, (Stem::beam_multiplicity (stem).length () + 1));
140     }
141   return m;
142 }
143
144 MAKE_SCHEME_CALLBACK (Beam, calc_normal_stems, 1);
145 SCM
146 Beam::calc_normal_stems (SCM smob)
147 {
148   Grob *me = unsmob<Grob> (smob);
149
150   extract_grob_set (me, "stems", stems);
151   SCM val = Grob_array::make_array ();
152   Grob_array *ga = unsmob<Grob_array> (val);
153   for (vsize i = 0; i < stems.size (); i++)
154     if (Stem::is_normal_stem (stems[i]))
155       ga->add (stems[i]);
156
157   return val;
158 }
159
160 MAKE_SCHEME_CALLBACK (Beam, calc_direction, 1);
161 SCM
162 Beam::calc_direction (SCM smob)
163 {
164   Grob *me = unsmob<Grob> (smob);
165
166   /* Beams with less than 2 two stems don't make much sense, but could happen
167      when you do
168
169      r8[ c8 r8]
170
171   */
172
173   Direction dir = CENTER;
174
175   int count = normal_stem_count (me);
176   if (count < 2)
177     {
178       extract_grob_set (me, "stems", stems);
179       if (stems.size () == 0)
180         {
181           me->warning (_ ("removing beam with no stems"));
182           me->suicide ();
183
184           return SCM_UNSPECIFIED;
185         }
186       else
187         {
188           Grob *stem = first_normal_stem (me);
189
190           /*
191             This happens for chord tremolos.
192           */
193           if (!stem)
194             stem = stems[0];
195
196           if (is_direction (stem->get_property_data ("direction")))
197             dir = to_dir (stem->get_property_data ("direction"));
198           else
199             dir = to_dir (stem->get_property ("default-direction"));
200
201           extract_grob_set (stem, "note-heads", heads);
202           /* default position of Kievan heads with beams is down
203              placing this here avoids warnings downstream */
204           if (heads.size())
205             {
206                if (scm_is_eq (heads[0]->get_property ("style"),
207                               ly_symbol2scm ("kievan")))
208                  {
209                     if (dir == CENTER)
210                       dir = DOWN;
211                  }
212             }
213         }
214     }
215
216   if (count >= 1)
217     {
218       if (!dir)
219         dir = get_default_dir (me);
220
221       consider_auto_knees (me);
222     }
223
224   if (dir)
225     {
226       set_stem_directions (me, dir);
227     }
228
229   return scm_from_int (dir);
230 }
231
232 /* We want a maximal number of shared beams, but if there is choice, we
233  * take the one that is closest to the end of the stem. This is for
234  * situations like
235  *
236  *        x
237  *       |
238  *       |
239  *   |===|
240  *   |=
241  *   |
242  *  x
243  */
244 int
245 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
246                                     Direction left_dir,
247                                     Direction right_dir)
248 {
249   Slice lslice = int_list_to_slice (scm_cdr (left_beaming));
250
251   int best_count = 0;
252   int best_start = 0;
253   for (int i = lslice[-left_dir];
254        (i - lslice[left_dir]) * left_dir <= 0; i += left_dir)
255     {
256       int count = 0;
257       for (SCM s = scm_car (right_beaming); scm_is_pair (s); s = scm_cdr (s))
258         {
259           int k = -right_dir * scm_to_int (scm_car (s)) + i;
260           if (scm_is_true (scm_c_memq (scm_from_int (k), left_beaming)))
261             count++;
262         }
263
264       if (count >= best_count)
265         {
266           best_count = count;
267           best_start = i;
268         }
269     }
270
271   return best_start;
272 }
273
274 MAKE_SCHEME_CALLBACK (Beam, calc_beaming, 1)
275 SCM
276 Beam::calc_beaming (SCM smob)
277 {
278   Grob *me = unsmob<Grob> (smob);
279
280   extract_grob_set (me, "stems", stems);
281
282   Slice last_int;
283   last_int.set_empty ();
284
285   SCM last_beaming = scm_cons (SCM_EOL, scm_list_1 (scm_from_int (0)));
286   Direction last_dir = CENTER;
287   for (vsize i = 0; i < stems.size (); i++)
288     {
289       Grob *this_stem = stems[i];
290       SCM this_beaming = this_stem->get_property ("beaming");
291
292       Direction this_dir = get_grob_direction (this_stem);
293       if (scm_is_pair (last_beaming) && scm_is_pair (this_beaming))
294         {
295           int start_point = position_with_maximal_common_beams
296                             (last_beaming, this_beaming,
297                              last_dir ? last_dir : this_dir,
298                              this_dir);
299
300           Slice new_slice;
301           for (LEFT_and_RIGHT (d))
302             {
303               new_slice.set_empty ();
304               SCM s = index_get_cell (this_beaming, d);
305               for (; scm_is_pair (s); s = scm_cdr (s))
306                 {
307                   int new_beam_pos
308                     = start_point - this_dir * scm_to_int (scm_car (s));
309
310                   new_slice.add_point (new_beam_pos);
311                   scm_set_car_x (s, scm_from_int (new_beam_pos));
312                 }
313             }
314
315           if (!new_slice.is_empty ())
316             last_int = new_slice;
317         }
318       else
319         {
320           /*
321             FIXME: what's this for?
322            */
323           SCM s = scm_cdr (this_beaming);
324           for (; scm_is_pair (s); s = scm_cdr (s))
325             {
326               int np = -this_dir * scm_to_int (scm_car (s));
327               scm_set_car_x (s, scm_from_int (np));
328               last_int.add_point (np);
329             }
330         }
331
332       if (scm_ilength (scm_cdr (this_beaming)) > 0)
333         {
334           last_beaming = this_beaming;
335           last_dir = this_dir;
336         }
337     }
338
339   return SCM_EOL;
340 }
341
342 bool
343 operator <(Beam_stem_segment const &a,
344            Beam_stem_segment const &b)
345 {
346   return a.rank_ < b.rank_;
347 }
348
349 typedef map<int, vector<Beam_stem_segment> > Position_stem_segments_map;
350
351 MAKE_SCHEME_CALLBACK (Beam, calc_beam_segments, 1);
352 SCM
353 Beam::calc_beam_segments (SCM smob)
354 {
355   /* ugh, this has a side-effect that we need to ensure that
356      Stem #'beaming is correct */
357   Grob *me_grob = unsmob<Grob> (smob);
358   (void) me_grob->get_property ("beaming");
359
360   Spanner *me = dynamic_cast<Spanner *> (me_grob);
361
362   extract_grob_set (me, "stems", stems);
363
364   Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
365   for (LEFT_and_RIGHT (d))
366     commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS);
367
368   int gap_count = robust_scm2int (me->get_property ("gap-count"), 0);
369   Real gap_length = robust_scm2double (me->get_property ("gap"), 0.0);
370
371   Position_stem_segments_map stem_segments;
372   Real lt = me->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
373
374   /* There are two concepts of "rank" that are used in the following code.
375      The beam_rank is the vertical position of the beam (larger numbers are
376      closer to the noteheads). Beam_stem_segment.rank_, on the other hand,
377      is the horizontal position of the segment (this is incremented by two
378      for each stem; the beam segment on the right side of the stem has
379      a higher rank (by one) than its neighbour to the left). */
380   Slice ranks;
381   for (vsize i = 0; i < stems.size (); i++)
382     {
383       Grob *stem = stems[i];
384       Real stem_width = robust_scm2double (stem->get_property ("thickness"), 1.0) * lt;
385       Real stem_x = stem->relative_coordinate (commonx, X_AXIS);
386       SCM beaming = stem->get_property ("beaming");
387
388       for (LEFT_and_RIGHT (d))
389         {
390           // Find the maximum and minimum beam ranks.
391           // Given that RANKS is never reset to empty, the interval will always be
392           // smallest for the left beamlet of the first stem, and then it might grow.
393           // Do we really want this? (It only affects the tremolo gaps) --jneem
394           for (SCM s = index_get_cell (beaming, d);
395                scm_is_pair (s); s = scm_cdr (s))
396             {
397               if (!scm_is_integer (scm_car (s)))
398                 continue;
399
400               int beam_rank = scm_to_int (scm_car (s));
401               ranks.add_point (beam_rank);
402             }
403
404           for (SCM s = index_get_cell (beaming, d);
405                scm_is_pair (s); s = scm_cdr (s))
406             {
407               if (!scm_is_integer (scm_car (s)))
408                 continue;
409
410               int beam_rank = scm_to_int (scm_car (s));
411               Beam_stem_segment seg;
412               seg.stem_ = stem;
413               seg.stem_x_ = stem_x;
414               seg.rank_ = 2 * i + (d + 1) / 2;
415               seg.width_ = stem_width;
416               seg.stem_index_ = i;
417               seg.dir_ = d;
418               seg.max_connect_ = robust_scm2int (stem->get_property ("max-beam-connect"), 1000);
419
420               Direction stem_dir = get_grob_direction (stem);
421
422               seg.gapped_
423                 = (stem_dir * beam_rank < (stem_dir * ranks[-stem_dir] + gap_count));
424               stem_segments[beam_rank].push_back (seg);
425             }
426         }
427     }
428
429   Drul_array<Real> break_overshoot
430     = robust_scm2drul (me->get_property ("break-overshoot"),
431                        Drul_array<Real> (-0.5, 0.0));
432
433   vector<Beam_segment> segments;
434   for (Position_stem_segments_map::const_iterator i (stem_segments.begin ());
435        i != stem_segments.end (); i++)
436     {
437       vector<Beam_stem_segment> segs = (*i).second;
438       vector_sort (segs, less<Beam_stem_segment> ());
439
440       Beam_segment current;
441
442       // Iterate over all of the segments of the current beam rank,
443       // merging the adjacent Beam_stem_segments into one Beam_segment
444       // when appropriate.
445       int vertical_count = (*i).first;
446       for (vsize j = 0; j < segs.size (); j++)
447         {
448           // Keeping track of the different directions here is a little tricky.
449           // segs[j].dir_ is the direction of the beam segment relative to the stem
450           // (ie. segs[j].dir_ == LEFT if the beam segment sticks out to the left of
451           // its stem) whereas event_dir refers to the edge of the beam segment that
452           // we are currently looking at (ie. if segs[j].dir_ == event_dir then we
453           // are looking at that edge of the beam segment that is furthest from its
454           // stem).
455           Beam_stem_segment const &seg = segs[j];
456           for (LEFT_and_RIGHT (event_dir))
457             {
458               Beam_stem_segment const &neighbor_seg = segs[j + event_dir];
459               // TODO: make names clearer? --jneem
460               // on_line_bound: whether the current segment is on the boundary of the WHOLE beam
461               // on_beam_bound: whether the current segment is on the boundary of just that part
462               //   of the beam with the current beam_rank
463               bool on_line_bound = (seg.dir_ == LEFT) ? seg.stem_index_ == 0
464                                    : seg.stem_index_ == stems.size () - 1;
465               bool on_beam_bound = (event_dir == LEFT) ? j == 0
466                                    : j == segs.size () - 1;
467               bool inside_stem = (event_dir == LEFT)
468                                  ? seg.stem_index_ > 0
469                                  : seg.stem_index_ + 1 < stems.size ();
470
471               bool event = on_beam_bound
472                            || abs (seg.rank_ - neighbor_seg.rank_) > 1
473                            || (abs (vertical_count) >= seg.max_connect_
474                                || abs (vertical_count) >= neighbor_seg.max_connect_);
475
476               if (!event)
477                 // Then this edge of the current segment is irrelevant because it will
478                 // be connected with the next segment in the event_dir direction.
479                 // If we skip the left edge here, the right edge of
480                 // the previous segment has already been skipped since
481                 // the conditions are symmetric
482                 continue;
483
484               current.vertical_count_ = vertical_count;
485               current.horizontal_[event_dir] = seg.stem_x_;
486               if (seg.dir_ == event_dir)
487                 // then we are examining the edge of a beam segment that is furthest
488                 // from its stem.
489                 {
490                   if (on_line_bound
491                       && me->get_bound (event_dir)->break_status_dir ())
492                     {
493                       current.horizontal_[event_dir]
494                         = (Axis_group_interface::generic_bound_extent (me->get_bound (event_dir),
495                                                                        commonx, X_AXIS)[RIGHT]
496                            + event_dir * break_overshoot[event_dir]);
497                     }
498                   else
499                     {
500                       Grob *stem = stems[seg.stem_index_];
501                       Drul_array<Real> beamlet_length
502                         = robust_scm2interval (stem->get_property ("beamlet-default-length"), Interval (1.1, 1.1));
503                       Drul_array<Real> max_proportion
504                         = robust_scm2interval (stem->get_property ("beamlet-max-length-proportion"), Interval (0.75, 0.75));
505                       Real length = beamlet_length[seg.dir_];
506
507                       if (inside_stem)
508                         {
509                           Grob *neighbor_stem = stems[seg.stem_index_ + event_dir];
510                           Real neighbor_stem_x = neighbor_stem->relative_coordinate (commonx, X_AXIS);
511
512                           length = min (length,
513                                         fabs (neighbor_stem_x - seg.stem_x_) * max_proportion[seg.dir_]);
514                         }
515                       current.horizontal_[event_dir] += event_dir * length;
516                     }
517                 }
518               else
519                 // we are examining the edge of a beam segment that is closest
520                 // (ie. touching, unless there is a gap) its stem.
521                 {
522                   current.horizontal_[event_dir] += event_dir * seg.width_ / 2;
523                   if (seg.gapped_)
524                     {
525                       current.horizontal_[event_dir] -= event_dir * gap_length;
526
527                       if (Stem::is_invisible (seg.stem_))
528                         {
529                           /*
530                             Need to do this in case of whole notes. We don't want the
531                             heads to collide with the beams.
532                            */
533                           extract_grob_set (seg.stem_, "note-heads", heads);
534
535                           for (vsize k = 0; k < heads.size (); k++)
536                             current.horizontal_[event_dir]
537                               = event_dir * min (event_dir * current.horizontal_[event_dir],
538                                                  - gap_length / 2
539                                                  + event_dir
540                                                  * heads[k]->extent (commonx,
541                                                                      X_AXIS)[-event_dir]);
542                         }
543                     }
544                 }
545
546               if (event_dir == RIGHT)
547                 {
548                   segments.push_back (current);
549                   current = Beam_segment ();
550                 }
551             }
552         }
553
554     }
555
556   SCM segments_scm = SCM_EOL;
557
558   for (vsize i = segments.size (); i--;)
559     {
560       segments_scm = scm_cons (scm_list_2 (scm_cons (ly_symbol2scm ("vertical-count"),
561                                                      scm_from_int (segments[i].vertical_count_)),
562                                            scm_cons (ly_symbol2scm ("horizontal"),
563                                                      ly_interval2scm (segments[i].horizontal_))),
564                                segments_scm);
565     }
566
567   return segments_scm;
568 }
569
570 MAKE_SCHEME_CALLBACK (Beam, calc_x_positions, 1);
571 SCM
572 Beam::calc_x_positions (SCM smob)
573 {
574   Spanner *me = unsmob<Spanner> (smob);
575   SCM segments = me->get_property ("beam-segments");
576   Interval x_positions;
577   x_positions.set_empty ();
578   for (SCM s = segments; scm_is_pair (s); s = scm_cdr (s))
579     x_positions.unite (robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"),
580                                                           scm_car (s),
581                                                           SCM_EOL),
582                                             Interval (0.0, 0.0)));
583
584   // Case for beams without segments (i.e. uniting two skips with a beam)
585   // TODO: should issue a warning?  warning likely issued downstream, but couldn't hurt...
586   if (x_positions.is_empty ())
587     {
588       extract_grob_set (me, "stems", stems);
589       Grob *common_x = common_refpoint_of_array (stems, me, X_AXIS);
590       for (LEFT_and_RIGHT (d))
591         x_positions[d] = me->relative_coordinate (common_x, X_AXIS);
592     }
593   return ly_interval2scm (x_positions);
594 }
595
596 vector<Beam_segment>
597 Beam::get_beam_segments (Grob *me)
598 {
599   SCM segments_scm = me->get_property ("beam-segments");
600   vector<Beam_segment> segments;
601   for (SCM s = segments_scm; scm_is_pair (s); s = scm_cdr (s))
602     {
603       segments.push_back (Beam_segment ());
604       segments.back ().vertical_count_ = robust_scm2int (ly_assoc_get (ly_symbol2scm ("vertical-count"), scm_car (s), SCM_EOL), 0);
605       segments.back ().horizontal_ = robust_scm2interval (ly_assoc_get (ly_symbol2scm ("horizontal"), scm_car (s), SCM_EOL), Interval (0.0, 0.0));
606     }
607
608   return segments;
609 }
610
611 MAKE_SCHEME_CALLBACK (Beam, print, 1);
612 SCM
613 Beam::print (SCM grob)
614 {
615   Spanner *me = unsmob<Spanner> (grob);
616   /*
617     TODO - mild code dup for all the commonx calls.
618     Some use just common_refpoint_of_array, some (in print and
619     calc_beam_segments) use this plus calls to get_bound.
620
621     Figure out if there is any particular reason for this and
622     consolidate in one Beam::get_common function.
623   */
624   extract_grob_set (me, "stems", stems);
625   Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
626   for (LEFT_and_RIGHT (d))
627     commonx = me->get_bound (d)->common_refpoint (commonx, X_AXIS);
628
629   vector<Beam_segment> segments = get_beam_segments (me);
630
631   if (!segments.size ())
632     return SCM_EOL;
633
634   Real blot = me->layout ()->get_dimension (ly_symbol2scm ("blot-diameter"));
635
636   SCM posns = me->get_property ("quantized-positions");
637   Interval span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0));
638   Interval pos;
639   if (!is_number_pair (posns))
640     {
641       programming_error ("no beam positions?");
642       pos = Interval (0, 0);
643     }
644   else
645     pos = ly_scm2realdrul (posns);
646
647   scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
648
649   Real dy = pos[RIGHT] - pos[LEFT];
650   Real slope = (dy && span.length ()) ? dy / span.length () : 0;
651
652   Real beam_thickness = get_beam_thickness (me);
653   Real beam_dy = get_beam_translation (me);
654
655   Direction feather_dir = to_dir (me->get_property ("grow-direction"));
656
657   Interval placements = robust_scm2interval (me->get_property ("normalized-endpoints"), Interval (0.0, 0.0));
658
659   Stencil the_beam;
660   vsize extreme = (segments[0].vertical_count_ == 0
661                    ? segments[0].vertical_count_
662                    : segments.back ().vertical_count_);
663
664   for (vsize i = 0; i < segments.size (); i++)
665     {
666       Real local_slope = slope;
667       /*
668         Makes local slope proportional to the ratio of the length of this beam
669         to the total length.
670       */
671       if (feather_dir)
672         local_slope += (feather_dir * segments[i].vertical_count_
673                         * beam_dy
674                         * placements.length ()
675                         / span.length ());
676
677       Stencil b = Lookup::beam (local_slope, segments[i].horizontal_.length (), beam_thickness, blot);
678
679       b.translate_axis (segments[i].horizontal_[LEFT], X_AXIS);
680       Real multiplier = feather_dir ? placements[LEFT] : 1.0;
681
682       Interval weights (1 - multiplier, multiplier);
683
684       if (feather_dir != LEFT)
685         weights.swap ();
686
687       // we need two translations: the normal one and
688       // the one of the lowest segment
689       size_t idx[] = {i, extreme};
690       Real translations[2];
691
692       for (int j = 0; j < 2; j++)
693         translations[j] = slope
694                           * (segments[idx[j]].horizontal_[LEFT] - span.linear_combination (CENTER))
695                           + pos.linear_combination (CENTER)
696                           + beam_dy * segments[idx[j]].vertical_count_;
697
698       Real weighted_average = translations[0] * weights[LEFT] + translations[1] * weights[RIGHT];
699
700       /*
701         Tricky.  The manipulation of the variable `weighted_average' below ensures
702         that beams with a RIGHT grow direction will start from the position of the
703         lowest segment at 0, and this error will decrease and decrease over the
704         course of the beam.  Something with a LEFT grow direction, on the other
705         hand, will always start in the correct place but progressively accrue
706         error at broken places.  This code shifts beams up given where they are
707         in the total span length (controlled by the variable `multiplier').  To
708         better understand what it does, try commenting it out: you'll see that
709         all of the RIGHT growing beams immediately start too low and get better
710         over line breaks, whereas all of the LEFT growing beams start just right
711         and get worse over line breaks.
712       */
713       Real factor = Interval (multiplier, 1 - multiplier).linear_combination (feather_dir);
714
715       if (segments[0].vertical_count_ < 0 && feather_dir)
716         weighted_average += beam_dy * (segments.size () - 1) * factor;
717
718       b.translate_axis (weighted_average, Y_AXIS);
719
720       the_beam.add_stencil (b);
721
722     }
723
724 #if (DEBUG_BEAM_SCORING)
725   SCM annotation = me->get_property ("annotation");
726   if (scm_is_string (annotation))
727     {
728       extract_grob_set (me, "stems", stems);
729
730       /*
731         This code prints the demerits for each beam. Perhaps this
732         should be switchable for those who want to twiddle with the
733         parameters.
734       */
735       string str;
736       SCM properties = Font_interface::text_font_alist_chain (me);
737
738       properties = scm_cons (scm_acons (ly_symbol2scm ("font-size"), scm_from_int (-5), SCM_EOL),
739                              properties);
740
741       Direction stem_dir = stems.size () ? to_dir (stems[0]->get_property ("direction")) : UP;
742
743       Stencil score = *unsmob<Stencil> (Text_interface::interpret_markup
744                                        (me->layout ()->self_scm (), properties, annotation));
745
746       if (!score.is_empty ())
747         {
748           score.translate_axis (me->relative_coordinate (commonx, X_AXIS), X_AXIS);
749           the_beam.add_at_edge (Y_AXIS, stem_dir, score, 1.0);
750         }
751     }
752 #endif
753
754   the_beam.translate_axis (-me->relative_coordinate (commonx, X_AXIS), X_AXIS);
755   return the_beam.smobbed_copy ();
756 }
757
758 Direction
759 Beam::get_default_dir (Grob *me)
760 {
761   extract_grob_set (me, "stems", stems);
762
763   Drul_array<Real> extremes (0.0, 0.0);
764   for (iterof (s, stems); s != stems.end (); s++)
765     {
766       Interval positions = Stem::head_positions (*s);
767       for (DOWN_and_UP (d))
768         {
769           if (sign (positions[d]) == d)
770             extremes[d] = d * max (d * positions[d], d * extremes[d]);
771         }
772     }
773
774   Drul_array<int> total (0, 0);
775   Drul_array<int> count (0, 0);
776
777   bool force_dir = false;
778   for (vsize i = 0; i < stems.size (); i++)
779     {
780       Grob *s = stems[i];
781       Direction stem_dir = CENTER;
782       SCM stem_dir_scm = s->get_property_data ("direction");
783       if (is_direction (stem_dir_scm))
784         {
785           stem_dir = to_dir (stem_dir_scm);
786           force_dir = true;
787         }
788       else
789         stem_dir = to_dir (s->get_property ("default-direction"));
790
791       if (!stem_dir)
792         stem_dir = to_dir (s->get_property ("neutral-direction"));
793
794       if (stem_dir)
795         {
796           count[stem_dir]++;
797           total[stem_dir] += max (int (- stem_dir * Stem::head_positions (s) [-stem_dir]), 0);
798         }
799     }
800
801   if (!force_dir)
802     {
803       if (abs (extremes[UP]) > -extremes[DOWN])
804         return DOWN;
805       else if (extremes[UP] < -extremes[DOWN])
806         return UP;
807     }
808
809   Direction dir = CENTER;
810   Direction d = CENTER;
811   if ((d = (Direction) sign (count[UP] - count[DOWN])))
812     dir = d;
813   else if (count[UP]
814            && count[DOWN]
815            && (d = (Direction) sign (total[UP] / count[UP] - total[DOWN] / count[DOWN])))
816     dir = d;
817   else if ((d = (Direction) sign (total[UP] - total[DOWN])))
818     dir = d;
819   else
820     dir = to_dir (me->get_property ("neutral-direction"));
821
822   return dir;
823 }
824
825 /* Set all stems with non-forced direction to beam direction.
826    Urg: non-forced should become `without/with unforced' direction,
827    once stem gets cleaned-up. */
828 void
829 Beam::set_stem_directions (Grob *me, Direction d)
830 {
831   extract_grob_set (me, "stems", stems);
832
833   for (vsize i = 0; i < stems.size (); i++)
834     {
835       Grob *s = stems[i];
836
837       SCM forcedir = s->get_property_data ("direction");
838       if (!to_dir (forcedir))
839         set_grob_direction (s, d);
840     }
841 }
842
843 /*
844   Only try horizontal beams for knees.  No reliable detection of
845   anything else is possible here, since we don't know funky-beaming
846   settings, or X-distances (slopes!)  People that want sloped
847   knee-beams, should set the directions manually.
848
849
850   TODO:
851
852   this routine should take into account the stemlength scoring
853   of a possible knee/nonknee beam.
854 */
855 void
856 Beam::consider_auto_knees (Grob *me)
857 {
858   SCM scm = me->get_property ("auto-knee-gap");
859   if (!scm_is_number (scm))
860     return;
861
862   vector<Interval> forbidden_intervals;
863
864   extract_grob_set (me, "normal-stems", stems);
865
866   Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
867   Real staff_space = Staff_symbol_referencer::staff_space (me);
868
869   vector<Interval> head_extents_array;
870   for (vsize i = 0; i < stems.size (); i++)
871     {
872       Grob *stem = stems[i];
873
874       Interval head_extents;
875       if (Stem::head_count (stem))
876         {
877           head_extents = Stem::head_positions (stem);
878           head_extents.widen (1);
879           head_extents *= staff_space * 0.5;
880
881           /*
882             We could subtract beam Y position, but this routine only
883             sets stem directions, a constant shift does not have an
884             influence.
885           */
886           head_extents += stem->pure_relative_y_coordinate (common, 0, INT_MAX);
887
888           if (to_dir (stem->get_property_data ("direction")))
889             {
890               Direction stemdir = to_dir (stem->get_property ("direction"));
891               head_extents[-stemdir] = -stemdir * infinity_f;
892             }
893         }
894       head_extents_array.push_back (head_extents);
895
896       forbidden_intervals.push_back (head_extents);
897     }
898
899   Interval max_gap;
900   Real max_gap_len = 0.0;
901
902   vector<Interval> allowed_regions
903     = Interval_set::interval_union (forbidden_intervals).complement ().intervals ();
904   for (vsize i = allowed_regions.size () - 1; i != VPOS; i--)
905     {
906       Interval gap = allowed_regions[i];
907
908       /*
909         the outer gaps are not knees.
910       */
911       if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
912         continue;
913
914       if (gap.length () >= max_gap_len)
915         {
916           max_gap_len = gap.length ();
917           max_gap = gap;
918         }
919     }
920
921   Real beam_translation = get_beam_translation (me);
922   Real beam_thickness = Beam::get_beam_thickness (me);
923   int beam_count = Beam::get_beam_count (me);
924   Real height_of_beams = beam_thickness / 2
925                          + (beam_count - 1) * beam_translation;
926   Real threshold = scm_to_double (scm) + height_of_beams;
927
928   if (max_gap_len > threshold)
929     {
930       int j = 0;
931       for (vsize i = 0; i < stems.size (); i++)
932         {
933           Grob *stem = stems[i];
934           Interval head_extents = head_extents_array[j++];
935
936           Direction d = (head_extents.center () < max_gap.center ())
937                         ? UP : DOWN;
938
939           stem->set_property ("direction", scm_from_int (d));
940
941           head_extents.intersect (max_gap);
942           assert (head_extents.is_empty () || head_extents.length () < 1e-6);
943         }
944     }
945 }
946
947 MAKE_SCHEME_CALLBACK (Beam, calc_stem_shorten, 1)
948 SCM
949 Beam::calc_stem_shorten (SCM smob)
950 {
951   Grob *me = unsmob<Grob> (smob);
952
953   /*
954     shortening looks silly for x staff beams
955   */
956   if (is_knee (me))
957     return scm_from_int (0);
958
959   Real forced_fraction = 1.0 * forced_stem_count (me)
960                          / normal_stem_count (me);
961
962   int beam_count = get_beam_count (me);
963
964   SCM shorten_list = me->get_property ("beamed-stem-shorten");
965   if (scm_is_null (shorten_list))
966     return scm_from_int (0);
967
968   Real staff_space = Staff_symbol_referencer::staff_space (me);
969
970   SCM shorten_elt
971     = robust_list_ref (beam_count - 1, shorten_list);
972   Real shorten = scm_to_double (shorten_elt) * staff_space;
973
974   shorten *= forced_fraction;
975
976   if (shorten)
977     return scm_from_double (shorten);
978
979   return scm_from_double (0.0);
980 }
981
982 MAKE_SCHEME_CALLBACK (Beam, quanting, 3);
983 SCM
984 Beam::quanting (SCM smob, SCM ys_scm, SCM align_broken_intos)
985 {
986   Grob *me = unsmob<Grob> (smob);
987   Drul_array<Real> ys = robust_scm2drul (ys_scm, Drul_array<Real> (infinity_f, -infinity_f));
988   bool cbs = to_boolean (align_broken_intos);
989
990   Beam_scoring_problem problem (me, ys, cbs);
991   ys = problem.solve ();
992
993   return ly_interval2scm (ys);
994 }
995
996 /*
997   Report slice containing the numbers that are both in (car BEAMING)
998   and (cdr BEAMING)
999 */
1000 Slice
1001 where_are_the_whole_beams (SCM beaming)
1002 {
1003   Slice l;
1004
1005   for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1006     {
1007       if (scm_is_true (scm_c_memq (scm_car (s), scm_cdr (beaming))))
1008
1009         l.add_point (scm_to_int (scm_car (s)));
1010     }
1011
1012   return l;
1013 }
1014
1015 /* Return the Y position of the stem-end, given the Y-left, Y-right
1016    in POS for stem S.  This Y position is relative to S. */
1017 Real
1018 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
1019                    Real xl, Real xr, Direction feather_dir,
1020                    Drul_array<Real> pos, bool french)
1021 {
1022   Real beam_translation = get_beam_translation (me);
1023   Direction stem_dir = get_grob_direction (stem);
1024
1025   Real dx = xr - xl;
1026   Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl) / dx : 0;
1027   Real xdir = 2 * relx - 1;
1028
1029   Real stem_y = linear_combination (pos, xdir);
1030
1031   SCM beaming = stem->get_property ("beaming");
1032
1033   Slice beam_slice (french
1034                     ? where_are_the_whole_beams (beaming)
1035                     : Stem::beam_multiplicity (stem));
1036   if (beam_slice.is_empty ())
1037     beam_slice = Slice (0, 0);
1038   Interval beam_multiplicity (beam_slice[LEFT],
1039                               beam_slice[RIGHT]);
1040
1041   /*
1042     feather dir = 1 , relx 0->1 : factor 0 -> 1
1043     feather dir = 0 , relx 0->1 : factor 1 -> 1
1044     feather dir = -1, relx 0->1 : factor 1 -> 0
1045    */
1046   Real feather_factor = 1;
1047   if (feather_dir > 0)
1048     feather_factor = relx;
1049   else if (feather_dir < 0)
1050     feather_factor = 1 - relx;
1051
1052   stem_y += feather_factor * beam_translation
1053             * beam_multiplicity[Direction (((french) ? DOWN : UP) * stem_dir)];
1054   Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1055             - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
1056
1057   return stem_y + id;
1058 }
1059
1060 /*
1061   Hmm.  At this time, beam position and slope are determined.  Maybe,
1062   stem directions and length should set to relative to the chord's
1063   position of the beam.  */
1064 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1065 SCM
1066 Beam::set_stem_lengths (SCM smob)
1067 {
1068   Grob *me = unsmob<Grob> (smob);
1069
1070   /* trigger callbacks. */
1071   (void) me->get_property ("direction");
1072   (void) me->get_property ("beaming");
1073
1074   SCM posns = me->get_property ("positions");
1075
1076   extract_grob_set (me, "stems", stems);
1077   if (!stems.size ())
1078     return posns;
1079
1080   Grob *common[2];
1081   for (int a = 2; a--;)
1082     common[a] = common_refpoint_of_array (stems, me, Axis (a));
1083
1084   Drul_array<Real> pos = ly_scm2realdrul (posns);
1085   Real staff_space = Staff_symbol_referencer::staff_space (me);
1086   scale_drul (&pos, staff_space);
1087
1088   bool gap = false;
1089   Real thick = 0.0;
1090   if (robust_scm2int (me->get_property ("gap-count"), 0))
1091     {
1092       gap = true;
1093       thick = get_beam_thickness (me);
1094     }
1095
1096   Grob *fvs = first_normal_stem (me);
1097   Grob *lvs = last_normal_stem (me);
1098
1099   Interval x_span = robust_scm2interval (me->get_property ("X-positions"), Interval (0, 0));
1100   Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1101
1102   for (vsize i = 0; i < stems.size (); i++)
1103     {
1104       Grob *s = stems[i];
1105
1106       bool french = to_boolean (s->get_property ("french-beaming"));
1107       Real stem_y = calc_stem_y (me, s, common,
1108                                  x_span[LEFT], x_span[RIGHT], feather_dir,
1109                                  pos, french && s != lvs && s != fvs);
1110
1111       /*
1112         Make the stems go up to the end of the beam. This doesn't matter
1113         for normal beams, but for tremolo beams it looks silly otherwise.
1114       */
1115       if (gap
1116           && !Stem::is_invisible (s))
1117         stem_y += thick * 0.5 * get_grob_direction (s);
1118
1119       /*
1120         Do set_stem_positions for invisible stems too, so tuplet brackets
1121         have a reference point for sloping
1122        */
1123       Stem::set_stem_positions (s, 2 * stem_y / staff_space);
1124     }
1125
1126   return posns;
1127 }
1128
1129 void
1130 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1131 {
1132   extract_grob_set (me, "stems", stems);
1133
1134   for (vsize i = 0; i < stems.size (); i++)
1135     {
1136       /*
1137         Don't overwrite user settings.
1138       */
1139       for (LEFT_and_RIGHT (d))
1140         {
1141           Grob *stem = stems[i];
1142           SCM beaming_prop = stem->get_property ("beaming");
1143           if (scm_is_null (beaming_prop)
1144               || scm_is_null (index_get_cell (beaming_prop, d)))
1145             {
1146               int count = beaming->beamlet_count (i, d);
1147               if (i > 0
1148                   && i + 1 < stems.size ()
1149                   && Stem::is_invisible (stem))
1150                 count = min (count, beaming->beamlet_count (i, -d));
1151
1152               if ( ((i == 0 && d == LEFT)
1153                     || (i == stems.size () - 1 && d == RIGHT))
1154                    && stems.size () > 1
1155                    && to_boolean (me->get_property ("clip-edges")))
1156                 count = 0;
1157
1158               Stem::set_beaming (stem, count, d);
1159             }
1160         }
1161     }
1162 }
1163
1164 int
1165 Beam::forced_stem_count (Grob *me)
1166 {
1167   extract_grob_set (me, "normal-stems", stems);
1168
1169   int f = 0;
1170   for (vsize i = 0; i < stems.size (); i++)
1171     {
1172       Grob *s = stems[i];
1173
1174       /* I can imagine counting those boundaries as a half forced stem,
1175          but let's count them full for now. */
1176       Direction defdir = to_dir (s->get_property ("default-direction"));
1177
1178       if (abs (Stem::chord_start_y (s)) > 0.1
1179           && defdir
1180           && get_grob_direction (s) != defdir)
1181         f++;
1182     }
1183   return f;
1184 }
1185
1186 int
1187 Beam::normal_stem_count (Grob *me)
1188 {
1189   extract_grob_set (me, "normal-stems", stems);
1190   return stems.size ();
1191 }
1192
1193 Grob *
1194 Beam::first_normal_stem (Grob *me)
1195 {
1196   extract_grob_set (me, "normal-stems", stems);
1197   return stems.size () ? stems[0] : 0;
1198 }
1199
1200 Grob *
1201 Beam::last_normal_stem (Grob *me)
1202 {
1203   extract_grob_set (me, "normal-stems", stems);
1204   return stems.size () ? stems.back () : 0;
1205 }
1206
1207 /*
1208   [TODO]
1209
1210   handle rest under beam (do_post: beams are calculated now)
1211   what about combination of collisions and rest under beam.
1212
1213   Should lookup
1214
1215   rest -> stem -> beam -> interpolate_y_position ()
1216 */
1217 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1218 SCM
1219 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1220 {
1221   Grob *rest = unsmob<Grob> (smob);
1222   if (scm_is_number (rest->get_property ("staff-position")))
1223     return scm_from_int (0);
1224
1225   Real offset = robust_scm2double (prev_offset, 0.0);
1226
1227   Grob *st = unsmob<Grob> (rest->get_object ("stem"));
1228   Grob *stem = st;
1229   if (!stem)
1230     return scm_from_double (0.0);
1231   Grob *beam = unsmob<Grob> (stem->get_object ("beam"));
1232   if (!beam
1233       || !Beam::has_interface (beam)
1234       || !Beam::normal_stem_count (beam))
1235     return scm_from_double (0.0);
1236
1237   Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1238
1239   Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1240                                          Drul_array<Real> (0, 0)));
1241
1242   for (LEFT_and_RIGHT (dir))
1243     pos[dir] += beam->relative_coordinate (common_y, Y_AXIS);
1244
1245   Real staff_space = Staff_symbol_referencer::staff_space (rest);
1246
1247   scale_drul (&pos, staff_space);
1248
1249   Real dy = pos[RIGHT] - pos[LEFT];
1250
1251   extract_grob_set (beam, "stems", stems);
1252   Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1253
1254   Interval x_span = robust_scm2interval (beam->get_property ("X-positions"),
1255                                          Interval (0.0, 0.0));
1256   Real x0 = x_span[LEFT];
1257   Real dx = x_span.length ();
1258   Real slope = dy && dx ? dy / dx : 0;
1259
1260   Direction d = get_grob_direction (stem);
1261   Real stem_y = pos[LEFT]
1262                 + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1263
1264   Real beam_translation = get_beam_translation (beam);
1265   Real beam_thickness = Beam::get_beam_thickness (beam);
1266
1267   /*
1268     TODO: this is not strictly correct for 16th knee beams.
1269   */
1270   int beam_count
1271     = Stem::beam_multiplicity (stem).length () + 1;
1272
1273   Real height_of_my_beams = beam_thickness / 2
1274                             + (beam_count - 1) * beam_translation;
1275   Real beam_y = stem_y - d * height_of_my_beams;
1276
1277   Interval rest_extent = rest->extent (rest, Y_AXIS);
1278   rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1279
1280   Real rest_dim = rest_extent[d];
1281   Real minimum_distance
1282     = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1283                      + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1284
1285   Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1286
1287   shift /= staff_space;
1288
1289   /* Always move discretely by half spaces */
1290   shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1291
1292   Interval staff_span = Staff_symbol_referencer::staff_span (rest);
1293   staff_span *= staff_space / 2;
1294
1295   /* Inside staff, move by whole spaces*/
1296   if (staff_span.contains (rest_extent[d] + staff_space * shift)
1297       || staff_span.contains (rest_extent[-d] + staff_space * shift))
1298     shift = ceil (fabs (shift)) * sign (shift);
1299
1300   return scm_from_double (offset + staff_space * shift);
1301 }
1302
1303 /*
1304   Estimate the position of a rest under a beam,
1305   using the average position of its neighboring heads.
1306 */
1307 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, pure_rest_collision_callback, 4, 1, "");
1308 SCM
1309 Beam::pure_rest_collision_callback (SCM smob,
1310                                     SCM, /* start */
1311                                     SCM, /* end */
1312                                     SCM prev_offset)
1313 {
1314   Real previous = robust_scm2double (prev_offset, 0.0);
1315
1316   Grob *me = unsmob<Grob> (smob);
1317   Grob *stem = unsmob<Grob> (me->get_object ("stem"));
1318   if (!stem)
1319     return scm_from_double (previous);
1320   Grob *beam = unsmob<Grob> (stem->get_object ("beam"));
1321   if (!beam
1322       || !Beam::normal_stem_count (beam)
1323       || !is_direction (beam->get_property_data ("direction")))
1324     return scm_from_double (previous);
1325
1326   Real ss = Staff_symbol_referencer::staff_space (me);
1327
1328   extract_grob_set (beam, "stems", stems);
1329   vector<Grob *> my_stems;
1330
1331   for (vsize i = 0; i < stems.size (); i++)
1332     if (Stem::head_count (stems[i]) || stems[i] == stem)
1333       my_stems.push_back (stems[i]);
1334
1335   vsize idx = -1;
1336
1337   for (vsize i = 0; i < my_stems.size (); i++)
1338     if (my_stems[i] == stem)
1339       {
1340         idx = i;
1341         break;
1342       }
1343   Grob *left;
1344   Grob *right;
1345
1346   if (idx == (vsize) - 1 || my_stems.size () == 1)
1347     return scm_from_double (previous);
1348   else if (idx == 0)
1349     left = right = my_stems[1];
1350   else if (idx == my_stems.size () - 1)
1351     left = right = my_stems[idx - 1];
1352   else
1353     {
1354       left = my_stems[idx - 1];
1355       right = my_stems[idx + 1];
1356     }
1357
1358   /* Estimate the closest beam to be four positions away from the heads, */
1359   Direction beamdir = get_grob_direction (beam);
1360   Real beam_pos = (Stem::head_positions (left)[beamdir]
1361                    + Stem::head_positions (right)[beamdir]) / 2.0
1362                   + 4.0 * beamdir; // four staff-positions
1363   /* and that the closest beam never crosses staff center by more than two positions */
1364   beam_pos = max (-2.0, beam_pos * beamdir) * beamdir;
1365
1366   Real minimum_distance
1367     = ss * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1368             + robust_scm2double (me->get_property ("minimum-distance"), 0.0));
1369   Real offset = beam_pos * ss / 2.0
1370                 - minimum_distance * beamdir
1371                 - me->extent (me, Y_AXIS)[beamdir];
1372
1373   /* Always move by a whole number of staff spaces, always away from the beam */
1374   offset = floor (min (0.0, (offset - previous) / ss * beamdir))
1375            * ss * beamdir + previous;
1376
1377   return scm_from_double (offset);
1378 }
1379
1380 bool
1381 Beam::is_knee (Grob *me)
1382 {
1383   SCM k = me->get_property ("knee");
1384   if (scm_is_bool (k))
1385     return ly_scm2bool (k);
1386
1387   bool knee = false;
1388   int d = 0;
1389   extract_grob_set (me, "stems", stems);
1390   for (vsize i = stems.size (); i--;)
1391     {
1392       Direction dir = get_grob_direction (stems[i]);
1393       if (d && d != dir)
1394         {
1395           knee = true;
1396           break;
1397         }
1398       d = dir;
1399     }
1400
1401   me->set_property ("knee", ly_bool2scm (knee));
1402
1403   return knee;
1404 }
1405
1406 bool
1407 Beam::is_cross_staff (Grob *me)
1408 {
1409   extract_grob_set (me, "stems", stems);
1410   Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1411   for (vsize i = 0; i < stems.size (); i++)
1412     if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1413       return true;
1414   return false;
1415 }
1416
1417 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1418 SCM
1419 Beam::calc_cross_staff (SCM smob)
1420 {
1421   return scm_from_bool (is_cross_staff (unsmob<Grob> (smob)));
1422 }
1423
1424 int
1425 Beam::get_direction_beam_count (Grob *me, Direction d)
1426 {
1427   extract_grob_set (me, "stems", stems);
1428   int bc = 0;
1429
1430   for (vsize i = stems.size (); i--;)
1431     {
1432       /*
1433         Should we take invisible stems into account?
1434       */
1435       if (get_grob_direction (stems[i]) == d)
1436         bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1437     }
1438
1439   return bc;
1440 }
1441
1442 ADD_INTERFACE (Beam,
1443                "A beam.\n"
1444                "\n"
1445                "The @code{beam-thickness} property is the weight of beams,"
1446                " measured in staffspace.  The @code{direction} property is"
1447                " not user-serviceable.  Use the @code{direction} property"
1448                " of @code{Stem} instead.\n"
1449                "The following properties may be set in the @code{details}"
1450                " list.\n"
1451                "\n"
1452                "@table @code\n"
1453                "@item stem-length-demerit-factor\n"
1454                "Demerit factor used for inappropriate stem lengths.\n"
1455                "@item secondary-beam-demerit\n"
1456                "Demerit used in quanting calculations for multiple"
1457                " beams.\n"
1458                "@item region-size\n"
1459                "Size of region for checking quant scores.\n"
1460                "@item beam-eps\n"
1461                "Epsilon for beam quant code to check for presence"
1462                " in gap.\n"
1463                "@item stem-length-limit-penalty\n"
1464                "Penalty for differences in stem lengths on a beam.\n"
1465                "@item damping-direction-penalty\n"
1466                "Demerit penalty applied when beam direction is different"
1467                " from damping direction.\n"
1468                "@item hint-direction-penalty\n"
1469                "Demerit penalty applied when beam direction is different"
1470                " from damping direction, but damping slope is"
1471                " <= @code{round-to-zero-slope}.\n"
1472                "@item musical-direction-factor\n"
1473                "Demerit scaling factor for difference between"
1474                " beam slope and music slope.\n"
1475                "@item ideal-slope-factor\n"
1476                "Demerit scaling factor for difference between"
1477                " beam slope and damping slope.\n"
1478                "@item round-to-zero-slope\n"
1479                "Damping slope which is considered zero for purposes of"
1480                " calculating direction penalties.\n"
1481                "@end table\n",
1482
1483                /* properties */
1484                "annotation "
1485                "auto-knee-gap "
1486                "beamed-stem-shorten "
1487                "beaming "
1488                "beam-segments "
1489                "beam-thickness "
1490                "break-overshoot "
1491                "clip-edges "
1492                "concaveness "
1493                "collision-interfaces "
1494                "collision-voice-only "
1495                "covered-grobs "
1496                "damping "
1497                "details "
1498                "direction "
1499                "gap "
1500                "gap-count "
1501                "grow-direction "
1502                "inspect-quants "
1503                "knee "
1504                "length-fraction "
1505                "least-squares-dy "
1506                "neutral-direction "
1507                "normal-stems "
1508                "positions "
1509                "quantized-positions "
1510                "shorten "
1511                "skip-quanting "
1512                "stems "
1513                "X-positions "
1514               );