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