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