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