]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
Merge branch 'lilypond/translation' of ssh://thsoft@git.sv.gnu.org/srv/git/lilypond...
[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   vector<Grob*> filtered;
1186   /*
1187     We only update these for objects that are too large for quanting
1188     to find a workaround.  Typically, these are notes with
1189     stems, and timesig/keysig/clef, which take out the entire area
1190     inside the staff as feasible.
1191
1192     The code below disregards the thickness and multiplicity of the
1193     beam.  This should not be a problem, as the beam quanting will
1194     take care of computing the impact those exactly.
1195   */
1196   Real min_y_size = 2.0;
1197
1198   // A list of intervals into which beams may not fall
1199   vector<Interval> forbidden_intervals;
1200
1201   for (vsize i = 0; i < covered.size(); i++)
1202     {
1203       if (!covered[i]->is_live())
1204         continue;
1205
1206       if (Beam::has_interface (covered[i]) && is_cross_staff (covered[i]))
1207         continue;
1208
1209       Box b;
1210       for (Axis a = X_AXIS; a < NO_AXES; incr (a))
1211         b[a] = covered[i]->extent (common[a], a);
1212
1213       if (b[X_AXIS].is_empty () || b[Y_AXIS].is_empty ())
1214         continue;
1215
1216       if (intersection (b[X_AXIS], x_span).is_empty ())
1217         continue;
1218
1219       filtered.push_back (covered[i]);
1220       Grob *head_stem = Rhythmic_head::get_stem (covered[i]);
1221       if (head_stem && Stem::is_normal_stem (head_stem)
1222           && Note_head::has_interface (covered[i]))
1223         {
1224           if (Stem::get_beam (head_stem))
1225             {
1226               /*
1227                 We must assume that stems are infinitely long in this
1228                 case, as asking for the length of the stem typically
1229                 leads to circular dependencies.
1230
1231                 This strategy assumes that we don't want to handle the
1232                 collision of beams in opposite non-forced directions
1233                 with this code, where shortening the stems of both
1234                 would resolve the problem, eg.
1235
1236                  x    x
1237                 |    | 
1238                 =====
1239
1240                 =====
1241                 |   |  
1242                 x   x
1243                 
1244                 Such beams would need a coordinating grob to resolve
1245                 the collision, since both will likely want to occupy
1246                 the centerline.
1247               */
1248               Direction stemdir = get_grob_direction (head_stem);
1249               b[Y_AXIS][stemdir] = stemdir * infinity_f;
1250             }
1251           else
1252             {
1253               // TODO - should we include the extent of the stem here?
1254             }
1255         }
1256
1257       if (b[Y_AXIS].length () < min_y_size)
1258         continue;
1259
1260       Direction d = LEFT;
1261       do
1262         {
1263           Real x = b[X_AXIS][d] - x_span[LEFT];
1264           Real dy = slope * x;
1265
1266           Direction yd = DOWN;
1267           Interval disallowed;
1268           do
1269             {
1270               Real left_y = b[Y_AXIS][yd];
1271
1272               left_y -= dy;
1273
1274               // Translate back to beam as ref point.
1275               left_y -= me->relative_coordinate (common[Y_AXIS], Y_AXIS);
1276
1277               disallowed[yd] = left_y;
1278             }
1279           while (flip (&yd) != DOWN);
1280
1281           forbidden_intervals.push_back (disallowed);
1282         }
1283       while (flip (&d) != LEFT);
1284     }
1285
1286   Grob_array *arr =
1287     Pointer_group_interface::get_grob_array (me,
1288                                              ly_symbol2scm ("covered-grobs"));
1289   arr->set_array (filtered);
1290
1291   vector_sort (forbidden_intervals, Interval::left_less);
1292   Real epsilon = 1.0e-10;
1293   Interval feasible_beam_placements (beam_left_y, beam_left_y);
1294
1295   /*
1296     forbidden_intervals contains a vector of intervals in which
1297     the beam cannot start.  it iterates through these intervals,
1298     pushing feasible_beam_placements epsilon over or epsilon under a
1299     collision.  when this type of change happens, the loop is marked
1300     as "dirty" and re-iterated.
1301
1302     TODO: figure out a faster ways that this loop can happen via
1303     a better search algorithm and/or OOP.
1304   */
1305
1306   bool dirty = false;
1307   do
1308     {
1309       dirty = false;
1310       for (vsize i = 0; i < forbidden_intervals.size (); i++)
1311         {
1312           Direction d = DOWN;
1313           do
1314             {
1315               if (forbidden_intervals[i][d] == d * infinity_f)
1316                 feasible_beam_placements[d] = d * infinity_f;
1317               else if (forbidden_intervals[i].contains (feasible_beam_placements[d]))
1318                 {
1319                   feasible_beam_placements[d] = d * epsilon + forbidden_intervals[i][d];
1320                   dirty = true;
1321                 }
1322             }
1323           while (flip (&d) != DOWN);
1324         }
1325     }
1326   while (dirty);
1327
1328   // if the beam placement falls out of the feasible region, we push it
1329   // to infinity so that it can never be a feasible candidate below
1330   Direction d = DOWN;
1331   do
1332     {
1333       if (!feasible_left_point.contains (feasible_beam_placements[d]))
1334         feasible_beam_placements[d] = d*infinity_f;
1335     }
1336   while (flip (&d) != DOWN);
1337
1338   if ((feasible_beam_placements[UP] == infinity_f && feasible_beam_placements[DOWN] == -infinity_f) && !feasible_left_point.is_empty ())
1339     {
1340       // We are somewhat screwed: we have a collision, but at least
1341       // there is a way to satisfy stem length constraints.
1342       beam_left_y = point_in_interval (feasible_left_point, 2.0);
1343     }
1344   else if (!feasible_left_point.is_empty ())
1345     {
1346       // Only one of them offers is feasible solution. Pick that one.
1347       if (abs (beam_left_y - feasible_beam_placements[DOWN]) > abs (beam_left_y - feasible_beam_placements[UP]))
1348         beam_left_y = feasible_beam_placements[UP];
1349       else
1350         beam_left_y = feasible_beam_placements[DOWN];
1351     }
1352   else
1353     {
1354       // We are completely screwed.
1355       me->warning (_ ("no viable initial configuration found: may not find good beam slope"));
1356     }
1357
1358   pos = Drul_array<Real> (beam_left_y, (beam_left_y + beam_dy));
1359   scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1360
1361   return ly_interval2scm (pos);
1362 }
1363
1364 /* This neat trick is by Werner Lemberg,
1365    damped = tanh (slope)
1366    corresponds with some tables in [Wanske] CHECKME */
1367 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 2);
1368 SCM
1369 Beam::slope_damping (SCM smob, SCM posns)
1370 {
1371   Grob *me = unsmob_grob (smob);
1372   Drul_array<Real> pos = ly_scm2interval (posns);
1373
1374   if (normal_stem_count (me) <= 1)
1375     return posns;
1376
1377   SCM s = me->get_property ("damping");
1378   Real damping = scm_to_double (s);
1379   Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0);
1380   if (concaveness >= 10000)
1381     {
1382       pos[LEFT] = pos[RIGHT];
1383       me->set_property ("least-squares-dy", scm_from_double (0));
1384       damping = 0;
1385     }
1386
1387   if (damping)
1388     {
1389       scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1390
1391       Real dy = pos[RIGHT] - pos[LEFT];
1392
1393       Grob *fvs = first_normal_stem (me);
1394       Grob *lvs = last_normal_stem (me);
1395
1396       Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1397
1398       Real dx = last_normal_stem (me)->relative_coordinate (commonx, X_AXIS)
1399         - first_normal_stem (me)->relative_coordinate (commonx, X_AXIS);
1400
1401       Real slope = dy && dx ? dy / dx : 0;
1402
1403       slope = 0.6 * tanh (slope) / (damping + concaveness);
1404
1405       Real damped_dy = slope * dx;
1406
1407       set_minimum_dy (me, &damped_dy);
1408
1409       pos[LEFT] += (dy - damped_dy) / 2;
1410       pos[RIGHT] -= (dy - damped_dy) / 2;
1411
1412       scale_drul (&pos, 1 / Staff_symbol_referencer::staff_space (me));
1413     }
1414
1415   return ly_interval2scm (pos);
1416 }
1417
1418
1419 MAKE_SCHEME_CALLBACK (Beam, quanting, 2);
1420 SCM
1421 Beam::quanting (SCM smob, SCM posns)
1422 {
1423   Grob *me = unsmob_grob (smob);
1424   Drul_array<Real> ys(0, 0);
1425   ys = robust_scm2drul (posns, ys);
1426   Beam_scoring_problem problem (me, ys);
1427
1428   ys = problem.solve ();
1429   return ly_interval2scm (ys);
1430 }
1431
1432
1433 /*
1434   Report slice containing the numbers that are both in (car BEAMING)
1435   and (cdr BEAMING)
1436 */
1437 Slice
1438 where_are_the_whole_beams (SCM beaming)
1439 {
1440   Slice l;
1441
1442   for (SCM s = scm_car (beaming); scm_is_pair (s); s = scm_cdr (s))
1443     {
1444       if (scm_c_memq (scm_car (s), scm_cdr (beaming)) != SCM_BOOL_F)
1445
1446         l.add_point (scm_to_int (scm_car (s)));
1447     }
1448
1449   return l;
1450 }
1451
1452 /* Return the Y position of the stem-end, given the Y-left, Y-right
1453    in POS for stem S.  This Y position is relative to S. */
1454 Real
1455 Beam::calc_stem_y (Grob *me, Grob *stem, Grob **common,
1456                    Real xl, Real xr, Direction feather_dir,
1457                    Drul_array<Real> pos, bool french)
1458 {
1459   Real beam_translation = get_beam_translation (me);
1460   Direction stem_dir = get_grob_direction (stem);
1461
1462   Real dx = xr - xl;
1463   Real relx = dx ? (stem->relative_coordinate (common[X_AXIS], X_AXIS) - xl)/dx : 0;
1464   Real xdir = 2*relx-1;
1465
1466   Real stem_y = linear_combination(pos, xdir);
1467
1468   SCM beaming = stem->get_property ("beaming");
1469
1470   Slice beam_slice (french
1471                     ? where_are_the_whole_beams (beaming)
1472                     : Stem::beam_multiplicity (stem));
1473   if (beam_slice.is_empty ())
1474     beam_slice = Slice (0,0);
1475   Interval beam_multiplicity(beam_slice[LEFT],
1476                              beam_slice[RIGHT]);
1477
1478   /*
1479     feather dir = 1 , relx 0->1 : factor 0 -> 1
1480     feather dir = 0 , relx 0->1 : factor 1 -> 1
1481     feather dir = -1, relx 0->1 : factor 1 -> 0
1482    */
1483   Real feather_factor = 1;
1484   if (feather_dir > 0)
1485     feather_factor = relx;
1486   else if (feather_dir < 0)
1487     feather_factor = 1 - relx;
1488
1489   stem_y += feather_factor * beam_translation
1490     * beam_multiplicity[Direction(((french) ? DOWN : UP)*stem_dir)];
1491   Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1492     - stem->relative_coordinate (common[Y_AXIS], Y_AXIS);
1493
1494   return stem_y + id;
1495 }
1496
1497 /*
1498   Hmm.  At this time, beam position and slope are determined.  Maybe,
1499   stem directions and length should set to relative to the chord's
1500   position of the beam.  */
1501 MAKE_SCHEME_CALLBACK (Beam, set_stem_lengths, 1);
1502 SCM
1503 Beam::set_stem_lengths (SCM smob)
1504 {
1505   Grob *me = unsmob_grob (smob);
1506
1507   /* trigger callbacks. */
1508   (void) me->get_property ("direction");
1509   (void) me->get_property ("beaming");
1510
1511   SCM posns = me->get_property ("positions");
1512
1513   extract_grob_set (me, "stems", stems);
1514   if (!stems.size ())
1515     return posns;
1516
1517   Grob *common[2];
1518   for (int a = 2; a--;)
1519     common[a] = common_refpoint_of_array (stems, me, Axis (a));
1520
1521   Drul_array<Real> pos = ly_scm2realdrul (posns);
1522   Real staff_space = Staff_symbol_referencer::staff_space (me);
1523   scale_drul (&pos, staff_space);
1524
1525   bool gap = false;
1526   Real thick = 0.0;
1527   if (robust_scm2int (me->get_property ("gap-count"), 0))
1528     {
1529       gap = true;
1530       thick = get_beam_thickness (me);
1531     }
1532
1533   Grob *fvs = first_normal_stem (me);
1534   Grob *lvs = last_normal_stem (me);
1535
1536   Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1537   Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1538   Direction feather_dir = to_dir (me->get_property ("grow-direction"));
1539
1540   for (vsize i = 0; i < stems.size (); i++)
1541     {
1542       Grob *s = stems[i];
1543
1544       bool french = to_boolean (s->get_property ("french-beaming"));
1545       Real stem_y = calc_stem_y (me, s, common,
1546                                  xl, xr, feather_dir,
1547                                  pos, french && s != lvs && s!= fvs);
1548
1549       /*
1550         Make the stems go up to the end of the beam. This doesn't matter
1551         for normal beams, but for tremolo beams it looks silly otherwise.
1552       */
1553       if (gap
1554           && !Stem::is_invisible (s))
1555         stem_y += thick * 0.5 * get_grob_direction (s);
1556
1557       /*
1558         Do set_stemend for invisible stems too, so tuplet brackets
1559         have a reference point for sloping
1560        */
1561       Stem::set_stemend (s, 2 * stem_y / staff_space);
1562     }
1563
1564   return posns;
1565 }
1566
1567 void
1568 Beam::set_beaming (Grob *me, Beaming_pattern const *beaming)
1569 {
1570   extract_grob_set (me, "stems", stems);
1571
1572   Direction d = LEFT;
1573   for (vsize i = 0; i < stems.size (); i++)
1574     {
1575       /*
1576         Don't overwrite user settings.
1577       */
1578       do
1579         {
1580           Grob *stem = stems[i];
1581           SCM beaming_prop = stem->get_property ("beaming");
1582           if (beaming_prop == SCM_EOL
1583               || index_get_cell (beaming_prop, d) == SCM_EOL)
1584             {
1585               int count = beaming->beamlet_count (i, d);
1586               if (i > 0
1587                   && i + 1 < stems.size ()
1588                   && Stem::is_invisible (stem))
1589                 count = min (count, beaming->beamlet_count (i,-d));
1590
1591               if ( ((i == 0 && d == LEFT)
1592                     || (i == stems.size ()-1 && d == RIGHT))
1593                    && stems.size () > 1
1594                    && to_boolean (me->get_property ("clip-edges")))
1595                 count = 0;
1596
1597               Stem::set_beaming (stem, count, d);
1598             }
1599         }
1600       while (flip (&d) != LEFT);
1601     }
1602 }
1603
1604 int
1605 Beam::forced_stem_count (Grob *me)
1606 {
1607   extract_grob_set (me, "normal-stems", stems);
1608
1609   int f = 0;
1610   for (vsize i = 0; i < stems.size (); i++)
1611     {
1612       Grob *s = stems[i];
1613
1614       /* I can imagine counting those boundaries as a half forced stem,
1615          but let's count them full for now. */
1616       Direction defdir = to_dir (s->get_property ("default-direction"));
1617
1618       if (abs (Stem::chord_start_y (s)) > 0.1
1619           && defdir
1620           && get_grob_direction (s) != defdir)
1621         f++;
1622     }
1623   return f;
1624 }
1625
1626 int
1627 Beam::normal_stem_count (Grob *me)
1628 {
1629   extract_grob_set (me, "normal-stems", stems);
1630   return stems.size ();
1631 }
1632
1633 Grob *
1634 Beam::first_normal_stem (Grob *me)
1635 {
1636   extract_grob_set (me, "normal-stems", stems);
1637   return stems.size () ? stems[0] : 0;
1638 }
1639
1640 Grob *
1641 Beam::last_normal_stem (Grob *me)
1642 {
1643   extract_grob_set (me, "normal-stems", stems);
1644   return stems.size () ? stems.back () : 0;
1645 }
1646
1647 /*
1648   [TODO]
1649
1650   handle rest under beam (do_post: beams are calculated now)
1651   what about combination of collisions and rest under beam.
1652
1653   Should lookup
1654
1655   rest -> stem -> beam -> interpolate_y_position ()
1656 */
1657 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Beam, rest_collision_callback, 2, 1, "");
1658 SCM
1659 Beam::rest_collision_callback (SCM smob, SCM prev_offset)
1660 {
1661   Grob *rest = unsmob_grob (smob);
1662   if (scm_is_number (rest->get_property ("staff-position")))
1663     return scm_from_int (0);
1664
1665   Real offset = robust_scm2double (prev_offset, 0.0);
1666
1667   Grob *st = unsmob_grob (rest->get_object ("stem"));
1668   Grob *stem = st;
1669   if (!stem)
1670     return scm_from_double (0.0);
1671   Grob *beam = unsmob_grob (stem->get_object ("beam"));
1672   if (!beam
1673       || !Beam::has_interface (beam)
1674       || !Beam::normal_stem_count (beam))
1675     return scm_from_double (0.0);
1676
1677   Drul_array<Real> pos (robust_scm2drul (beam->get_property ("positions"),
1678                                          Drul_array<Real> (0,0)));
1679
1680   Real staff_space = Staff_symbol_referencer::staff_space (rest);
1681
1682   scale_drul (&pos, staff_space);
1683
1684   Real dy = pos[RIGHT] - pos[LEFT];
1685
1686   Drul_array<Grob*> visible_stems (first_normal_stem (beam),
1687                                    last_normal_stem (beam));
1688   extract_grob_set (beam, "stems", stems);
1689
1690   Grob *common = common_refpoint_of_array (stems, beam, X_AXIS);
1691
1692   Real x0 = visible_stems[LEFT]->relative_coordinate (common, X_AXIS);
1693   Real dx = visible_stems[RIGHT]->relative_coordinate (common, X_AXIS) - x0;
1694   Real slope = dy && dx ? dy / dx : 0;
1695
1696   Direction d = get_grob_direction (stem);
1697   Real stem_y = pos[LEFT]
1698     + (stem->relative_coordinate (common, X_AXIS) - x0) * slope;
1699
1700   Real beam_translation = get_beam_translation (beam);
1701   Real beam_thickness = Beam::get_beam_thickness (beam);
1702
1703   /*
1704     TODO: this is not strictly correct for 16th knee beams.
1705   */
1706   int beam_count
1707     = Stem::beam_multiplicity (stem).length () + 1;
1708
1709   Real height_of_my_beams = beam_thickness / 2
1710     + (beam_count - 1) * beam_translation;
1711   Real beam_y = stem_y - d * height_of_my_beams;
1712
1713   Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1714
1715   Interval rest_extent = rest->extent (rest, Y_AXIS);
1716   rest_extent.translate (offset + rest->get_parent (Y_AXIS)->relative_coordinate (common_y, Y_AXIS));
1717
1718   Real rest_dim = rest_extent[d];
1719   Real minimum_distance
1720     = staff_space * (robust_scm2double (stem->get_property ("stemlet-length"), 0.0)
1721                      + robust_scm2double (rest->get_property ("minimum-distance"), 0.0));
1722
1723   Real shift = d * min (d * (beam_y - d * minimum_distance - rest_dim), 0.0);
1724
1725   shift /= staff_space;
1726   Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1727
1728   /* Always move discretely by half spaces */
1729   shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1730
1731   /* Inside staff, move by whole spaces*/
1732   if ((rest_extent[d] + staff_space * shift) * d
1733       < rad
1734       || (rest_extent[-d] + staff_space * shift) * -d
1735       < rad)
1736     shift = ceil (fabs (shift)) * sign (shift);
1737
1738   return scm_from_double (offset + staff_space * shift);
1739 }
1740
1741 bool
1742 Beam::is_knee (Grob *me)
1743 {
1744   SCM k = me->get_property ("knee");
1745   if (scm_is_bool (k))
1746     return ly_scm2bool (k);
1747
1748   bool knee = false;
1749   int d = 0;
1750   extract_grob_set (me, "stems", stems);
1751   for (vsize i = stems.size (); i--;)
1752     {
1753       Direction dir = get_grob_direction (stems[i]);
1754       if (d && d != dir)
1755         {
1756           knee = true;
1757           break;
1758         }
1759       d = dir;
1760     }
1761
1762   me->set_property ("knee", ly_bool2scm (knee));
1763
1764   return knee;
1765 }
1766
1767 bool
1768 Beam::is_cross_staff (Grob *me)
1769 {
1770   extract_grob_set (me, "stems", stems);
1771   Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
1772   for (vsize i = 0; i < stems.size (); i++)
1773     if (Staff_symbol_referencer::get_staff_symbol (stems[i]) != staff_symbol)
1774       return true;
1775   return false;
1776 }
1777
1778 MAKE_SCHEME_CALLBACK (Beam, calc_cross_staff, 1)
1779 SCM
1780 Beam::calc_cross_staff (SCM smob)
1781 {
1782   return scm_from_bool (is_cross_staff (unsmob_grob (smob)));
1783 }
1784
1785 int
1786 Beam::get_direction_beam_count (Grob *me, Direction d)
1787 {
1788   extract_grob_set (me, "stems", stems);
1789   int bc = 0;
1790
1791   for (vsize i = stems.size (); i--;)
1792     {
1793       /*
1794         Should we take invisible stems into account?
1795       */
1796       if (get_grob_direction (stems[i]) == d)
1797         bc = max (bc, (Stem::beam_multiplicity (stems[i]).length () + 1));
1798     }
1799
1800   return bc;
1801 }
1802
1803 ADD_INTERFACE (Beam,
1804                "A beam.\n"
1805                "\n"
1806                "The @code{beam-thickness} property is the weight of beams,"
1807                " measured in staffspace.  The @code{direction} property is"
1808                " not user-serviceable.  Use the @code{direction} property"
1809                " of @code{Stem} instead.\n"
1810                "\n"
1811                "The following properties may be set in the @code{details}"
1812                " list.\n"
1813                "\n"
1814                "@table @code\n"
1815                "@item stem-length-demerit-factor\n"
1816                "Demerit factor used for inappropriate stem lengths.\n"
1817                "@item secondary-beam-demerit\n"
1818                "Demerit used in quanting calculations for multiple"
1819                " beams.\n"
1820                "@item region-size\n"
1821                "Size of region for checking quant scores.\n"
1822                "@item beam-eps\n"
1823                "Epsilon for beam quant code to check for presence"
1824                " in gap.\n"
1825                "@item stem-length-limit-penalty\n"
1826                "Penalty for differences in stem lengths on a beam.\n"
1827                "@item damping-direction-penalty\n"
1828                "Demerit penalty applied when beam direction is different"
1829                " from damping direction.\n"
1830                "@item hint-direction-penalty\n"
1831                "Demerit penalty applied when beam direction is different"
1832                " from damping direction, but damping slope is"
1833                " <= @code{round-to-zero-slope}.\n"
1834                "@item musical-direction-factor\n"
1835                "Demerit scaling factor for difference between"
1836                " beam slope and music slope.\n"
1837                "@item ideal-slope-factor\n"
1838                "Demerit scaling factor for difference between"
1839                " beam slope and damping slope.\n"
1840                "@item round-to-zero-slope\n"
1841                "Damping slope which is considered zero for purposes of"
1842                " calculating direction penalties.\n"
1843                "@end table\n",
1844
1845                /* properties */
1846                "annotation "
1847                "auto-knee-gap "
1848                "beamed-stem-shorten "
1849                "beaming "
1850                "beam-thickness "
1851                "break-overshoot "
1852                "clip-edges "
1853                "concaveness "
1854                "collision-interfaces "
1855                "collision-voice-only "
1856                "covered-grobs "
1857                "damping "
1858                "details "
1859                "direction "
1860                "gap "
1861                "gap-count "
1862                "grow-direction "
1863                "inspect-quants "
1864                "knee "
1865                "length-fraction "
1866                "least-squares-dy "
1867                "neutral-direction "
1868                "normal-stems "
1869                "positions "
1870                "quantized-positions "
1871                "shorten "
1872                "stems "
1873                );