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