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