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