]> git.donarmstrong.com Git - lilypond.git/blob - lily/beam.cc
(process_lilypond_blocks): opps.
[lilypond.git] / lily / beam.cc
1 /*
2   beam.cc -- implement Beam
3   
4   source file of the GNU LilyPond music typesetter
5   
6   (c)  1997--2003 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7   Jan Nieuwenhuizen <janneke@gnu.org>
8 */
9
10 /*
11 TODO:
12
13   * Use Number_pair i.s.o Interval to represent (yl, yr).
14   
15   - Determine auto knees based on positions if it's set by the user.
16
17
18 Notes:
19
20
21  - Stems run to the Y-center of the beam.
22   
23  - beam_translation is the offset between Y centers of the beam.
24
25 */
26
27
28 #include <math.h> // tanh.
29
30 #include "molecule.hh" 
31 #include "directional-element-interface.hh"
32 #include "beaming.hh"
33 #include "beam.hh"
34 #include "misc.hh"
35 #include "least-squares.hh"
36 #include "stem.hh"
37 #include "paper-def.hh"
38 #include "lookup.hh"
39 #include "group-interface.hh"
40 #include "staff-symbol-referencer.hh"
41 #include "item.hh"
42 #include "spanner.hh"
43 #include "warn.hh"
44
45
46 #define DEBUG_QUANTING 0
47
48
49 #if DEBUG_QUANTING
50 #include "text-item.hh"  // debug output.
51 #include "font-interface.hh"  // debug output.
52 #endif
53
54
55 void
56 Beam::add_stem (Grob *me, Grob *s)
57 {
58   Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
59   
60   s->add_dependency (me);
61
62   assert (!Stem::get_beam (s));
63   s->set_grob_property ("beam", me->self_scm ());
64
65   add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
66 }
67
68
69 Real
70 Beam::get_thickness (Grob * me)
71 {
72   SCM th = me->get_grob_property ("thickness");
73   if (gh_number_p (th))
74     return gh_scm2double (th)* Staff_symbol_referencer::staff_space (me);
75   else
76     return 0.0;
77 }
78
79 /* Return the translation between 2 adjoining beams. */
80 Real
81 Beam::get_beam_translation (Grob *me)
82 {
83   SCM func = me->get_grob_property ("space-function");
84   SCM s = gh_call2 (func, me->self_scm (), scm_int2num (get_beam_count (me)));
85   return gh_scm2double (s);
86 }
87
88 /* Maximum beam_count. */
89 int
90 Beam::get_beam_count (Grob *me) 
91 {
92   int m = 0;
93   for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
94     {
95       Grob *stem = unsmob_grob (ly_car (s));
96       m = m >? (Stem::beam_multiplicity (stem).length () + 1);
97     }
98   return m;
99 }
100
101
102 /*
103   Space return space between beams.
104  */
105 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
106 SCM
107 Beam::space_function (SCM smob, SCM beam_count)
108 {
109   Grob *me = unsmob_grob (smob);
110   
111   Real staff_space = Staff_symbol_referencer::staff_space (me);
112   Real line = me->get_paper ()->get_var ("linethickness");
113   Real thickness = get_thickness (me);
114   
115   Real beam_translation = gh_scm2int (beam_count) < 4
116     ? (2*staff_space + line - thickness) / 2.0
117     : (3*staff_space + line - thickness) / 3.0;
118   
119   return gh_double2scm (beam_translation);
120 }
121
122
123 /* After pre-processing all directions should be set.
124    Several post-processing routines (stem, slur, script) need stem/beam
125    direction.
126    Currenly, this means that beam has set all stem's directions.
127    [Alternatively, stems could set its own directions, according to
128    their beam, during 'final-pre-processing'.] */
129 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
130 SCM
131 Beam::before_line_breaking (SCM smob)
132 {
133   Grob *me =  unsmob_grob (smob);
134
135   /* Beams with less than 2 two stems don't make much sense, but could happen
136      when you do
137      
138      [r8 c8 r8].
139      
140     For a beam that  only has one stem, we try to do some disappearance magic:
141     we revert the flag, and move on to The Eternal Engraving Fields. */
142
143   int count = visible_stem_count (me);
144   if (count < 2)
145     {
146       me->warning (_ ("beam has less than two visible stems"));
147
148       SCM stems = me->get_grob_property ("stems");
149       if (scm_ilength (stems) == 1)
150         {
151           me->warning (_ ("Beam has less than two stems. Removing beam."));
152
153           unsmob_grob (gh_car (stems))->set_grob_property ("beam", SCM_EOL);
154           me->suicide ();
155
156           return SCM_UNSPECIFIED;
157         }
158       else if (scm_ilength (stems) == 0)
159         {
160           me->suicide ();
161           return SCM_UNSPECIFIED;         
162         }
163     }
164   if (count >= 1)
165     {
166       Direction d = get_default_dir (me);
167
168       consider_auto_knees (me);
169       set_stem_directions (me, d);
170
171       connect_beams (me);
172
173       set_stem_shorten (me);
174     }
175
176   return SCM_EOL;
177 }
178
179
180 /*
181   We want a maximal number of shared beams, but if there is choice, we
182   take the one that is closest to the end of the stem. This is for situations like
183
184        x
185       |
186       |
187   |===|
188   |=
189   |
190   x
191
192   
193  */
194 int
195 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
196                                     Direction left_dir,
197                                     Direction right_dir)
198 {
199   Slice lslice = int_list_to_slice (gh_cdr (left_beaming));
200
201   int best_count = 0;
202   int best_start = 0;
203   for (int i = lslice[-left_dir];
204        (i - lslice[left_dir])* left_dir <= 0 ; i+= left_dir) 
205     {
206       int count =0;
207       for ( SCM s = gh_car (right_beaming); gh_pair_p (s); s = gh_cdr (s))
208         {
209           int k = - right_dir * gh_scm2int (gh_car (s)) + i;
210           if (scm_memq (scm_int2num (k), left_beaming) != SCM_BOOL_F)
211             count ++;
212         }
213
214       if (count >= best_count)
215         {
216           best_count = count; 
217           best_start = i;
218         }
219     }
220
221   return best_start;
222 }
223
224 void
225 Beam::connect_beams (Grob *me)
226 {
227   Link_array<Grob> stems=
228     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
229
230   Slice last_int;
231   last_int.set_empty();
232   SCM last_beaming = SCM_EOL;
233   Direction last_dir = CENTER;
234   for (int i = 0; i< stems.size(); i++)
235     {
236       Grob *this_stem = stems[i];
237       SCM this_beaming = this_stem->get_grob_property ("beaming");
238
239       Direction this_dir = Directional_element_interface::get(this_stem);
240       if (gh_pair_p (last_beaming) && gh_pair_p (this_beaming))
241         {
242           int start_point = position_with_maximal_common_beams
243             (last_beaming, this_beaming,
244              last_dir, this_dir);
245           
246           Direction d = LEFT;
247           Slice new_slice ; 
248           do
249             {
250               if (d == RIGHT && i == stems.size()-1)
251                 continue;
252               
253               new_slice.set_empty();
254               SCM s = index_get_cell (this_beaming, d);
255               for (; gh_pair_p (s); s = gh_cdr (s))
256                 {
257                   int new_beam_pos =
258                     start_point - this_dir * gh_scm2int (gh_car (s));
259
260                   new_slice.add_point (new_beam_pos);
261                   gh_set_car_x (s, scm_int2num (new_beam_pos));
262                 }
263
264
265             }
266           while (flip (&d) != LEFT);
267
268           if (!new_slice.empty_b())
269             last_int =  new_slice;
270         }
271       else
272         {
273           gh_set_car_x ( this_beaming, SCM_EOL);
274           SCM s = gh_cdr (this_beaming);
275           for (; gh_pair_p (s); s = gh_cdr (s))
276             {
277               int np = - this_dir * gh_scm2int (gh_car(s));
278               gh_set_car_x (s, scm_int2num (np));
279               last_int.add_point (np);
280             }
281         }
282
283       if (i == stems.size () -1)
284         {
285           gh_set_cdr_x (this_beaming, SCM_EOL);
286         }
287
288       if (scm_ilength (gh_cdr (this_beaming)) > 0)
289         {
290           last_beaming = this_beaming;
291           last_dir = this_dir;
292         }
293     }
294  }
295
296 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
297 SCM
298 Beam::brew_molecule (SCM grob)
299 {
300   Grob *me = unsmob_grob (grob);
301   Link_array<Grob> stems=
302     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
303   Grob* xcommon = common_refpoint_of_array (stems, me, X_AXIS);
304
305   Real x0, dx;
306   if (visible_stem_count (me))
307     {
308       // ugh -> use commonx
309       x0 = first_visible_stem (me)->relative_coordinate (xcommon, X_AXIS);
310       dx = last_visible_stem (me)->relative_coordinate (xcommon, X_AXIS) - x0;
311     }
312   else
313     {
314       x0 = stems[0]->relative_coordinate (xcommon, X_AXIS);
315       dx = stems.top ()->relative_coordinate (xcommon, X_AXIS) - x0;
316     }
317
318   SCM posns = me->get_grob_property ("positions");
319   Interval pos;
320   if (!ly_number_pair_p (posns))
321     {
322       programming_error ("No beam posns");
323       pos = Interval (0,0);
324     }
325   else
326     pos= ly_scm2interval (posns);
327
328   Real dy = pos.delta ();
329   Real dydx = dy && dx ? dy/dx : 0;
330   
331   Real thick = get_thickness (me);
332   Real bdy = get_beam_translation (me);
333
334   SCM last_beaming = SCM_EOL;;
335   Real last_xposn = -1;
336   Real last_width = -1 ;
337
338
339   SCM gap = me->get_grob_property ("gap");
340   Molecule the_beam;
341   Real lt = me->get_paper ()->get_var ("linethickness");
342   
343   for (int i = 0; i<= stems.size(); i++)
344     {
345       Grob * st = (i < stems.size()) ? stems[i] : 0;
346       
347       SCM this_beaming = st ? st->get_grob_property ("beaming") : SCM_EOL;
348       Real xposn = st ? st->relative_coordinate (xcommon, X_AXIS) : 0.0;
349       Real stem_width = st ? gh_scm2double (st->get_grob_property ("thickness")) *lt : 0 ;
350
351       /*
352         We do the space left of ST, with lfliebertjes pointing to the
353         right from the left stem, and rfliebertjes pointing left from
354         right stem.
355        */
356       SCM left = (i>0) ? gh_cdr (last_beaming) : SCM_EOL;
357       SCM right = st ? gh_car (this_beaming) : SCM_EOL;
358
359       Array<int> fullbeams;
360       Array<int> lfliebertjes;
361       Array<int> rfliebertjes;    
362
363       for (SCM s = left;
364            gh_pair_p (s); s =gh_cdr (s))
365         {
366           int b = gh_scm2int (gh_car (s));
367           if (scm_memq (gh_car(s), right) != SCM_BOOL_F)
368             {
369               fullbeams.push (b);
370             }
371           else
372             {
373               lfliebertjes.push (b); 
374             }
375         }
376       for (SCM s = right;
377            gh_pair_p (s); s =gh_cdr (s))
378         {
379           int b = gh_scm2int (gh_car (s));
380           if (scm_memq (gh_car(s), left) == SCM_BOOL_F)
381             {
382               rfliebertjes.push (b);
383             }
384         }
385
386       /*
387         how much to stick out for beams across linebreaks
388        */
389       Real break_overshoot = 3.0;
390       Real w = (i>0 && st)? xposn - last_xposn : break_overshoot;
391       Real stem_offset = 0.0;
392       Real width_corr = 0.0;
393       if (i == 1)
394         {
395           stem_offset -= last_width/2;
396           width_corr += last_width/2;
397         }
398           
399       if (i == stems.size() -1)
400         {
401           width_corr += stem_width/2;
402         }
403
404       if (gh_number_p (gap))
405         {
406           Real g = gh_scm2double (gap);
407           stem_offset += g;
408           width_corr -= 2*g; 
409         }
410           
411       Molecule whole = Lookup::beam (dydx, w + width_corr, thick);
412       for (int j = fullbeams.size(); j--;)
413         {
414           Molecule b (whole);
415           b.translate_axis (last_xposn -  x0 + stem_offset, X_AXIS);
416           b.translate_axis (dydx * (last_xposn - x0) + bdy * fullbeams[j], Y_AXIS);
417           the_beam.add_molecule (b);          
418         }
419
420       if (lfliebertjes.size() || rfliebertjes.size())
421         {
422           Real nw_f;
423
424           if (st)
425             {
426               int t = Stem::duration_log (st); 
427
428               SCM proc = me->get_grob_property ("flag-width-function");
429               SCM result = gh_call1 (proc, scm_int2num (t));
430               nw_f = gh_scm2double (result);
431             }
432           else
433             nw_f = break_overshoot;
434               
435           /* Half beam should be one note-width,
436              but let's make sure two half-beams never touch */
437           Real w = (i>0 && st) ? (xposn - last_xposn) : break_overshoot;
438           w = w/2 <? nw_f;
439
440           Molecule half = Lookup::beam (dydx, w, thick);
441           for (int j = lfliebertjes.size(); j--;)
442             {
443               Molecule b (half);
444               b.translate_axis (last_xposn -  x0, X_AXIS);
445               b.translate_axis (dydx * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
446               the_beam.add_molecule (b);              
447             }
448           for (int j = rfliebertjes.size(); j--;)
449             {
450               Molecule b (half);
451               b.translate_axis (xposn -  x0 - w , X_AXIS);
452               b.translate_axis (dydx * (xposn-x0 -w) + bdy * rfliebertjes[j], Y_AXIS);
453               the_beam.add_molecule (b);              
454             }
455         }
456
457
458       last_xposn = xposn;
459       last_width = stem_width;
460       last_beaming = this_beaming;
461     }
462
463   the_beam.translate_axis (x0 - me->relative_coordinate (xcommon, X_AXIS), X_AXIS);
464   the_beam.translate_axis (pos[LEFT], Y_AXIS);
465
466 #if (DEBUG_QUANTING)
467     {
468       /*
469         This code prints the demerits for each beam. Perhaps this
470         should be switchable for those who want to twiddle with the
471         parameters.
472       */
473       String str;
474       if (1)
475         {
476           str += to_string (gh_scm2int (me->get_grob_property ("best-idx")));
477           str += ":";
478         }
479       str += to_string (gh_scm2double (me->get_grob_property ("quant-score")),
480                      "%.2f");
481
482       SCM properties = Font_interface::font_alist_chain (me);
483
484       
485       Molecule tm = Text_item::text2molecule (me, scm_makfrom0str (str.to_str0 ()), properties);
486       the_beam.add_at_edge (Y_AXIS, UP, tm, 5.0, 0);
487     }
488 #endif
489     
490   
491   
492   return the_beam.smobbed_copy();
493 }
494   
495
496
497
498 Direction
499 Beam::get_default_dir (Grob *me) 
500 {
501   Drul_array<int> total;
502   total[UP]  = total[DOWN] = 0;
503   Drul_array<int> count; 
504   count[UP]  = count[DOWN] = 0;
505   Direction d = DOWN;
506
507   Link_array<Grob> stems=
508         Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
509
510   for (int i=0; i <stems.size (); i++)
511     do {
512       Grob *s = stems[i];
513       Direction sd = Directional_element_interface::get (s);
514
515       int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
516       int current = sd  ? (1 + d * sd)/2 : center_distance;
517
518       if (current)
519         {
520           total[d] += current;
521           count[d] ++;
522         }
523     } while (flip (&d) != DOWN);
524   
525   SCM func = me->get_grob_property ("dir-function");
526   SCM s = gh_call2 (func,
527                     gh_cons (scm_int2num (count[UP]),
528                              scm_int2num (count[DOWN])),
529                     gh_cons (scm_int2num (total[UP]),
530                              scm_int2num (total[DOWN])));
531
532   if (gh_number_p (s) && gh_scm2int (s))
533     return to_dir (s);
534   
535   /* If dir is not determined: get default */
536   return to_dir (me->get_grob_property ("neutral-direction"));
537 }
538
539
540 /* Set all stems with non-forced direction to beam direction.
541    Urg: non-forced should become `without/with unforced' direction,
542    once stem gets cleaned-up. */
543 void
544 Beam::set_stem_directions (Grob *me, Direction d)
545 {
546   Link_array<Grob> stems
547     =Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
548   
549   for (int i=0; i <stems.size (); i++)
550     {
551       Grob *s = stems[i];
552   
553       SCM forcedir = s->get_grob_property ("direction");
554       if (!to_dir (forcedir))
555         Directional_element_interface::set (s, d);
556     }
557 }
558
559 /*
560   A union of intervals in the real line.
561
562   Abysmal performance (quadratic) for large N, hopefully we don't have
563   that large N. In any case, this should probably be rewritten to use
564   a balanced tree.
565  */
566 struct Int_set
567 {
568   Array<Interval> allowed_regions_;
569
570   Int_set()
571   {
572     set_full();
573   }
574
575   void set_full()
576   {
577     allowed_regions_.clear();
578     Interval s;
579     s.set_full ();
580     allowed_regions_.push (s);
581   }
582
583   void remove_interval (Interval rm)
584   {
585     for (int i = 0; i < allowed_regions_.size(); )
586       {
587         Interval s = rm;
588
589         s.intersect (allowed_regions_[i]);
590
591         if (!s.empty_b ())
592           {
593             Interval before = allowed_regions_[i];
594             Interval after = allowed_regions_[i];
595
596             before[RIGHT] = s[LEFT];
597             after[LEFT] = s[RIGHT];
598
599             if (!before.empty_b() && before.length () > 0.0)
600               {
601                 allowed_regions_.insert (before, i);
602                 i++;
603               }
604             allowed_regions_.del (i);
605             if (!after.empty_b () && after.length () > 0.0)
606               {
607                 allowed_regions_.insert (after, i);
608                 i++;
609               }
610           }
611         else
612           i++;
613       }
614   }
615 };
616
617
618 /*
619   Only try horizontal beams for knees.  No reliable detection of
620   anything else is possible here, since we don't know funky-beaming
621   settings, or X-distances (slopes!)  People that want sloped
622   knee-beams, should set the directions manually.
623  */
624 void
625 Beam::consider_auto_knees (Grob* me)
626 {
627   SCM scm = me->get_grob_property ("auto-knee-gap");
628   if (!gh_number_p (scm))
629     return ;
630
631   Real threshold = gh_scm2double (scm);
632   
633   Int_set gaps;
634
635   gaps.set_full ();
636
637   Link_array<Grob> stems=
638     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
639       
640   Grob *common = common_refpoint_of_array (stems, me,  Y_AXIS);
641   Real staff_space = Staff_symbol_referencer::staff_space (me);
642   
643   Array<Interval> hps_array;  
644   for (int i=0; i < stems.size (); i++)
645     {
646       Grob* stem = stems[i];
647       if (Stem::invisible_b (stem))
648         continue;
649
650       Interval hps = Stem::head_positions (stem);
651       if(!hps.empty_b())
652         {
653           hps[LEFT] += -1;
654           hps[RIGHT] += 1; 
655           hps *= staff_space * 0.5 ;
656
657           /*
658             We could subtract beam Y position, but this routine only
659             sets stem directions, a constant shift does not have an
660             influence.
661             
662            */
663           hps += stem->relative_coordinate (common, Y_AXIS);
664
665           if (to_dir (stem->get_grob_property ("direction")))
666             {
667               Direction stemdir = to_dir (stem->get_grob_property ("direction"));
668               hps[-stemdir] = - stemdir * infinity_f;
669             }
670         }
671       hps_array.push (hps);
672
673       gaps.remove_interval (hps);
674     }
675
676   Interval max_gap;
677   Real max_gap_len =0.0;
678
679   for (int i  = gaps.allowed_regions_.size() -1;  i >=  0 ; i--)
680     {
681       Interval gap = gaps.allowed_regions_[i];
682
683       /*
684         the outer gaps are not knees.
685        */
686       if (isinf (gap[LEFT]) || isinf(gap[RIGHT]))
687         continue;
688       
689       if (gap.length () >= max_gap_len)
690         {
691           max_gap_len = gap.length();
692           max_gap = gap;
693         }
694     }
695
696   if (max_gap_len > threshold)
697     {
698       int j = 0;
699       for (int i = 0; i < stems.size(); i++)
700         {
701           Grob* stem = stems[i];
702           if (Stem::invisible_b (stem))
703             continue;
704
705           Interval hps = hps_array[j++];
706
707
708           Direction d =  (hps.center () < max_gap.center()) ?
709             UP : DOWN ;
710           
711           stem->set_grob_property ("direction", scm_int2num (d));
712           
713           hps.intersect (max_gap);
714           assert (hps.empty_b () || hps.length () < 1e-6 );
715         }
716     }
717 }
718
719
720
721 /* Set stem's shorten property if unset.
722
723  TODO:
724    take some y-position (chord/beam/nearest?) into account
725    scmify forced-fraction
726  
727   This is done in beam because the shorten has to be uniform over the
728   entire beam.
729
730 */
731 void
732 Beam::set_stem_shorten (Grob *me)
733 {
734   /*
735     shortening looks silly for x staff beams
736    */
737   if (knee_b(me))
738     return ;
739   
740   Real forced_fraction = 1.0 * forced_stem_count (me)
741     / visible_stem_count (me);
742
743   int beam_count = get_beam_count (me);
744
745   SCM shorten_list = me->get_grob_property ("beamed-stem-shorten");
746   if (shorten_list == SCM_EOL)
747     return;
748
749   Real staff_space = Staff_symbol_referencer::staff_space (me);
750   
751   SCM shorten_elt =
752     robust_list_ref (beam_count -1, shorten_list);
753   Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
754
755   /* your similar cute comment here */
756   shorten_f *= forced_fraction;
757
758   if (shorten_f)
759     me->set_grob_property ("shorten", gh_double2scm (shorten_f));
760 }
761
762 /*  Call list of y-dy-callbacks, that handle setting of
763     grob-properties
764
765 */
766 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
767 SCM
768 Beam::after_line_breaking (SCM smob)
769 {
770   Grob *me = unsmob_grob (smob);
771   
772   /* Copy to mutable list. */
773   SCM s = ly_deep_copy (me->get_grob_property ("positions"));
774   me->set_grob_property ("positions", s);
775
776   if (ly_car (s) == SCM_BOOL_F)
777     {
778
779       // one wonders if such genericity is necessary  --hwn.
780       SCM callbacks = me->get_grob_property ("position-callbacks");
781       for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
782         gh_call1 (ly_car (i), smob);
783     }
784
785   set_stem_lengths (me);  
786   return SCM_UNSPECIFIED;
787 }
788
789
790 /*
791   Compute  a first approximation to the beam slope.
792  */
793 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
794 SCM
795 Beam::least_squares (SCM smob)
796 {
797   Grob *me = unsmob_grob (smob);
798
799   int count = visible_stem_count (me);
800   Interval pos (0, 0);
801   
802   if (count <= 1)
803     {
804       me->set_grob_property ("positions", ly_interval2scm (pos));
805       return SCM_UNSPECIFIED;
806     }
807
808
809   Array<Real> x_posns ;
810   Link_array<Grob> stems=
811     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
812   Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
813   Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);  
814
815   Real my_y = me->relative_coordinate (commony, Y_AXIS);
816   
817   Grob *fvs  = first_visible_stem (me);
818   Grob *lvs  = last_visible_stem (me);
819   
820   Interval ideal (Stem::get_stem_info (fvs).ideal_y_
821                   + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
822                   Stem::get_stem_info (lvs).ideal_y_
823                   + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
824   
825   Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
826   for (int i=0; i < stems.size (); i++)
827     {
828       Grob* s = stems[i];
829
830       Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
831       x_posns.push (x);
832     }
833   Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
834
835   Real y =0;  
836   Real dydx = 0;
837   Real dy = 0;
838   
839   if (!ideal.delta ())
840     {
841       Interval chord (Stem::chord_start_y (first_visible_stem (me)),
842                       Stem::chord_start_y (last_visible_stem (me)));
843
844       /* Simple beams (2 stems) on middle line should be allowed to be
845          slightly sloped.
846          
847          However, if both stems reach middle line,
848          ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
849
850          For that case, we apply artificial slope */
851       if (!ideal[LEFT] && chord.delta () && count == 2)
852         {
853           /* FIXME. -> UP */
854           Direction d = (Direction) (sign (chord.delta ()) * UP);
855           pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
856           pos[-d] = - pos[d];
857         }
858       else
859         {
860           pos = ideal;
861         }
862
863       y = pos[LEFT];
864       dy = pos[RIGHT]- y;
865       dydx = dy/dx;
866     }
867   else
868     {
869       Array<Offset> ideals;
870       for (int i=0; i < stems.size (); i++)
871         {
872           Grob* s = stems[i];
873           if (Stem::invisible_b (s))
874             continue;
875           ideals.push (Offset (x_posns[i],
876                                Stem::get_stem_info (s).ideal_y_
877                                + s->relative_coordinate (commony, Y_AXIS)
878                                - my_y));
879         }
880       minimise_least_squares (&dydx, &y, ideals);
881
882       dy = dydx * dx;
883       me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
884       pos = Interval (y, (y+dy));
885     }
886
887   me->set_grob_property ("positions", ly_interval2scm (pos));
888  
889   return SCM_UNSPECIFIED;
890 }
891
892
893 /*
894   We can't combine with previous function, since check concave and
895   slope damping comes first.
896  */
897 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
898 SCM
899 Beam::shift_region_to_valid (SCM grob)
900 {
901   Grob *me = unsmob_grob (grob);
902   /*
903     Code dup.
904    */
905   Array<Real> x_posns ;
906   Link_array<Grob> stems=
907     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
908   Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
909   Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);  
910
911   Grob *fvs = first_visible_stem (me);
912
913   if (!fvs)
914     return SCM_UNSPECIFIED;
915     
916   Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
917   for (int i=0; i < stems.size (); i++)
918     {
919       Grob* s = stems[i];
920
921       Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
922       x_posns.push (x);
923     }
924
925   Grob *lvs = last_visible_stem (me);
926   if (!lvs)
927     return SCM_UNSPECIFIED;
928   
929   Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
930
931   Interval pos = ly_scm2interval ( me->get_grob_property ("positions"));
932   Real dy = pos.delta();
933   Real y = pos[LEFT];
934   Real dydx =dy/dx;
935
936   
937   /*
938     Shift the positions so that we have a chance of finding good
939     quants (i.e. no short stem failures.)
940    */
941   Interval feasible_left_point;
942   feasible_left_point.set_full ();
943   for (int i=0; i < stems.size (); i++)
944     {
945       Grob* s = stems[i];
946       if (Stem::invisible_b (s))
947         continue;
948
949       Direction d = Stem::get_direction (s);
950
951       Real left_y =
952         Stem::get_stem_info (s).shortest_y_
953         - dydx * x_posns [i];
954
955       /*
956         left_y is now relative to the stem S. We want relative to
957         ourselves, so translate:
958        */
959       left_y += 
960         + s->relative_coordinate (commony, Y_AXIS)
961         - me->relative_coordinate (commony, Y_AXIS);
962
963       Interval flp ;
964       flp.set_full ();
965       flp[-d] = left_y;
966
967       feasible_left_point.intersect (flp);
968     }
969       
970   if (feasible_left_point.empty_b())
971     {
972       warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
973     }
974   else if (!feasible_left_point.elem_b(y))
975     {
976       if (isinf (feasible_left_point[DOWN]))
977         y = feasible_left_point[UP] - REGION_SIZE;
978       else if (isinf (feasible_left_point[UP]))
979         y = feasible_left_point[DOWN]+ REGION_SIZE;
980       else
981         y = feasible_left_point.center ();
982     }
983   pos = Interval (y, (y+dy));
984   me->set_grob_property ("positions", ly_interval2scm (pos));
985   return SCM_UNSPECIFIED;
986 }
987
988
989 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
990 SCM
991 Beam::check_concave (SCM smob)
992 {
993   Grob *me = unsmob_grob (smob);
994
995   Link_array<Grob> stems = 
996     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
997
998   for (int i = 0; i < stems.size ();)
999     {
1000       if (Stem::invisible_b (stems[i]))
1001         stems.del (i);
1002       else
1003         i++;
1004     }
1005   
1006   if (stems.size () < 3)
1007     return SCM_UNSPECIFIED;
1008
1009
1010   /* Concaveness #1: If distance of an inner notehead to line between
1011      two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
1012      beam is concave (Heinz Stolba).
1013
1014      In the case of knees, the line connecting outer heads is often
1015      not related to the beam slope (it may even go in the other
1016      direction). Skip the check when the outer stems point in
1017      different directions. --hwn
1018      
1019   */
1020   bool concaveness1 = false;
1021   SCM gap = me->get_grob_property ("concaveness-gap");
1022   if (gh_number_p (gap)
1023       && Stem::get_direction(stems.top ())
1024          == Stem::get_direction(stems[0]))
1025     {
1026       Real r1 = gh_scm2double (gap);
1027       Real dy = Stem::chord_start_y (stems.top ())
1028         - Stem::chord_start_y (stems[0]);
1029
1030       
1031       Real slope = dy / (stems.size () - 1);
1032       
1033       Real y0 = Stem::chord_start_y (stems[0]);
1034       for (int i = 1; i < stems.size () - 1; i++)
1035         {
1036           Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
1037           if (c > r1)
1038             {
1039               concaveness1 = true;
1040               break;
1041             }
1042         }
1043     }
1044
1045     
1046   /* Concaveness #2: Sum distances of inner noteheads that fall
1047      outside the interval of the two outer noteheads.
1048
1049      We only do this for beams where first and last stem have the same
1050      direction. --hwn.
1051
1052
1053      Note that "convex" stems compensate for "concave" stems.
1054      (is that intentional?) --hwn.
1055   */
1056   
1057   Real concaveness2 = 0;
1058   SCM thresh = me->get_grob_property ("concaveness-threshold");
1059   Real r2 = infinity_f;
1060   if (!concaveness1 && gh_number_p (thresh)
1061       && Stem::get_direction(stems.top ())
1062          == Stem::get_direction(stems[0]))
1063     {
1064       r2 = gh_scm2double (thresh);
1065
1066       Direction dir = Stem::get_direction(stems.top ());
1067       Real concave = 0;
1068       Interval iv (Stem::chord_start_y (stems[0]),
1069                    Stem::chord_start_y (stems.top ()));
1070       
1071       if (iv[MAX] < iv[MIN])
1072         iv.swap ();
1073       
1074       for (int i = 1; i < stems.size () - 1; i++)
1075         {
1076           Real f = Stem::chord_start_y (stems[i]);
1077           concave += ((f - iv[MAX] ) >? 0) +
1078             ((f - iv[MIN] ) <? 0);
1079         }
1080       concave *= dir;
1081       concaveness2 = concave / (stems.size () - 2);
1082       
1083       /*
1084
1085       ugh: this is the a kludge to get
1086       input/regression/beam-concave.ly to behave as
1087       baerenreiter.
1088
1089       */
1090
1091       /*
1092         huh? we're dividing twice (which is not scalable) meaning that
1093         the longer the beam, the more unlikely it will be
1094         concave. Maybe you would even expect the other way around??
1095
1096         --hwn.
1097         
1098        */
1099       concaveness2 /= (stems.size () - 2);
1100     }
1101   
1102   /* TODO: some sort of damping iso -> plain horizontal */
1103   if (concaveness1 || concaveness2 > r2)
1104     {
1105       Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1106       Real r = pos.linear_combination (0);
1107       me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
1108       me->set_grob_property ("least-squares-dy", gh_double2scm (0));
1109     }
1110
1111   return SCM_UNSPECIFIED;
1112 }
1113
1114 /* This neat trick is by Werner Lemberg,
1115    damped = tanh (slope)
1116    corresponds with some tables in [Wanske] CHECKME */
1117 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1118 SCM
1119 Beam::slope_damping (SCM smob)
1120 {
1121   Grob *me = unsmob_grob (smob);
1122
1123   if (visible_stem_count (me) <= 1)
1124     return SCM_UNSPECIFIED;
1125
1126   SCM s = me->get_grob_property ("damping"); 
1127   int damping = gh_scm2int (s);
1128
1129   if (damping)
1130     {
1131       Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1132       Real dy = pos.delta ();
1133
1134       Grob *fvs  = first_visible_stem (me);
1135       Grob *lvs  = last_visible_stem (me);
1136
1137       Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1138
1139
1140       Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1141         - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1142       Real dydx = dy && dx ? dy/dx : 0;
1143       dydx = 0.6 * tanh (dydx) / damping;
1144
1145       Real damped_dy = dydx * dx;
1146       pos[LEFT] += (dy - damped_dy) / 2;
1147       pos[RIGHT] -= (dy - damped_dy) / 2;
1148       
1149       me->set_grob_property ("positions", ly_interval2scm (pos));
1150     }
1151   return SCM_UNSPECIFIED;
1152 }
1153
1154 /*
1155   Report slice containing the numbers that are both in (car BEAMING)
1156   and (cdr BEAMING)
1157  */
1158 Slice
1159 where_are_the_whole_beams(SCM beaming)
1160 {
1161   Slice l; 
1162   
1163   for( SCM s = gh_car (beaming); gh_pair_p (s) ; s = gh_cdr (s))
1164     {
1165       if (scm_memq (gh_car (s), gh_cdr (beaming)) != SCM_BOOL_F)
1166         
1167         l.add_point (gh_scm2int (gh_car (s)));
1168     }
1169
1170   return l;
1171 }
1172
1173 /* Return the Y position of the stem-end, given the Y-left, Y-right
1174    in POS for stem S.  This Y position is relative to S. */
1175 Real
1176 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1177                    Real xl, Real xr,
1178                    Interval pos, bool french) 
1179 {
1180   Real beam_translation = get_beam_translation (me);
1181
1182     
1183   Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1184   Real dy = pos.delta ();
1185   Real dx = xr - xl;
1186   Real stem_y_beam0 = (dy && dx
1187                        ? r / dx
1188                        * dy
1189                        : 0) + pos[LEFT];
1190   
1191   Direction my_dir = Directional_element_interface::get (s);
1192   SCM beaming = s->get_grob_property ("beaming");
1193  
1194   Real stem_y = stem_y_beam0;
1195   if (french)
1196     {
1197       Slice bm = where_are_the_whole_beams (beaming);
1198       if (!bm.empty_b())
1199         stem_y += beam_translation * bm[-my_dir];
1200     }
1201   else
1202     {
1203       Slice bm = Stem::beam_multiplicity(s);
1204       if (!bm.empty_b())
1205         stem_y +=bm[my_dir] * beam_translation;
1206     }
1207   
1208   Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1209     - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1210   
1211   return stem_y + id;
1212 }
1213
1214 /*
1215   Hmm.  At this time, beam position and slope are determined.  Maybe,
1216   stem directions and length should set to relative to the chord's
1217   position of the beam.  */
1218 void
1219 Beam::set_stem_lengths (Grob *me)
1220 {
1221   Link_array<Grob> stems=
1222     Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1223
1224   if (stems.size () <= 1)
1225     return;
1226   
1227   Grob *common[2];
1228   for (int a = 2; a--;)
1229     common[a] = common_refpoint_of_array (stems, me, Axis(a));
1230   
1231   Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1232   Real staff_space = Staff_symbol_referencer::staff_space (me);
1233
1234   bool gap = false;
1235   Real thick =0.0;
1236   if (gh_number_p (me->get_grob_property ("gap"))
1237       &&gh_scm2double (me->get_grob_property ("gap")))
1238   {
1239     gap = true;
1240     thick = get_thickness(me);
1241   }
1242       
1243   // ugh -> use commonx
1244   Grob * fvs = first_visible_stem (me);
1245   Grob *lvs = last_visible_stem (me);
1246     
1247   Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1248   Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1249   
1250   for (int i=0; i < stems.size (); i++)
1251     {
1252       Grob* s = stems[i];
1253       if (Stem::invisible_b (s))
1254         continue;
1255
1256       
1257       bool french = to_boolean (s->get_grob_property ("french-beaming"));
1258       Real stem_y = calc_stem_y (me, s, common,
1259                                  xl, xr,
1260                                  pos, french && i > 0&& (i < stems.size  () -1));
1261
1262       /*
1263         Make the stems go up to the end of the beam. This doesn't matter
1264         for normal beams, but for tremolo beams it looks silly otherwise.
1265        */
1266       if (gap)
1267         stem_y += thick * 0.5 * Directional_element_interface::get(s);
1268       
1269       Stem::set_stemend (s, 2* stem_y / staff_space);
1270     }
1271 }
1272
1273 void
1274 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1275 {
1276   Link_array<Grob> stems=
1277     Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1278   
1279   Direction d = LEFT;
1280   for (int i=0; i  < stems.size (); i++)
1281     {
1282       /*
1283         Don't overwrite user settings.
1284        */
1285       
1286       do
1287         {
1288           /* Don't set beaming for outside of outer stems */      
1289           if ((d == LEFT && i == 0)
1290               ||(d == RIGHT && i == stems.size () -1))
1291             continue;
1292
1293           Grob *st =  stems[i];
1294           SCM beaming_prop = st->get_grob_property ("beaming");
1295           if (beaming_prop == SCM_EOL ||
1296               index_get_cell (beaming_prop, d) == SCM_EOL)
1297             {
1298               int b = beaming->infos_.elem (i).beams_i_drul_[d];
1299               if (i>0
1300                   && i < stems.size() -1
1301                   && Stem::invisible_b (st))
1302                 b = b <? beaming->infos_.elem(i).beams_i_drul_[-d];
1303               
1304               Stem::set_beaming (st, b, d);
1305             }
1306         }
1307       while (flip (&d) != LEFT);
1308     }
1309 }
1310
1311 int
1312 Beam::forced_stem_count (Grob *me) 
1313 {
1314   Link_array<Grob>stems = 
1315     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1316   int f = 0;
1317   for (int i=0; i < stems.size (); i++)
1318     {
1319       Grob *s = stems[i];
1320
1321       if (Stem::invisible_b (s))
1322         continue;
1323
1324       /* I can imagine counting those boundaries as a half forced stem,
1325          but let's count them full for now. */
1326       if (abs (Stem::chord_start_y (s)) > 0.1
1327         && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1328         f++;
1329     }
1330   return f;
1331 }
1332
1333
1334
1335
1336 int
1337 Beam::visible_stem_count (Grob *me) 
1338 {
1339   Link_array<Grob>stems = 
1340     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1341   int c = 0;
1342   for (int i = stems.size (); i--;)
1343     {
1344       if (!Stem::invisible_b (stems[i]))
1345         c++;
1346     }
1347   return c;
1348 }
1349
1350 Grob*
1351 Beam::first_visible_stem (Grob *me) 
1352 {
1353   Link_array<Grob>stems = 
1354     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1355   
1356   for (int i = 0; i < stems.size (); i++)
1357     {
1358       if (!Stem::invisible_b (stems[i]))
1359         return stems[i];
1360     }
1361   return 0;
1362 }
1363
1364 Grob*
1365 Beam::last_visible_stem (Grob *me) 
1366 {
1367   Link_array<Grob>stems = 
1368     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1369   for (int i = stems.size (); i--;)
1370     {
1371       if (!Stem::invisible_b (stems[i]))
1372         return stems[i];
1373     }
1374   return 0;
1375 }
1376
1377
1378 /*
1379   [TODO]
1380   
1381   handle rest under beam (do_post: beams are calculated now)
1382   what about combination of collisions and rest under beam.
1383
1384   Should lookup
1385     
1386     rest -> stem -> beam -> interpolate_y_position ()
1387 */
1388 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1389 SCM
1390 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1391 {
1392   Grob *rest = unsmob_grob (element_smob);
1393   Axis a = (Axis) gh_scm2int (axis);
1394   
1395   assert (a == Y_AXIS);
1396
1397   Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1398   Grob *stem = st;
1399   if (!stem)
1400     return gh_double2scm (0.0);
1401   Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1402   if (!beam
1403       || !Beam::has_interface (beam)
1404       || !Beam::visible_stem_count (beam))
1405     return gh_double2scm (0.0);
1406
1407   Interval pos (0, 0);
1408   SCM s = beam->get_grob_property ("positions");
1409   if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1410     pos = ly_scm2interval (s);
1411
1412   Real dy = pos.delta ();
1413   // ugh -> use commonx
1414   Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1415   Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1416   Real dydx = dy && dx ? dy/dx : 0;
1417   
1418   Direction d = Stem::get_direction (stem);
1419   Real stem_y = (pos[LEFT]
1420                  + (stem->relative_coordinate (0, X_AXIS) - x0) * dydx)
1421     * d;
1422   
1423   Real beam_translation = get_beam_translation (beam);
1424   Real beam_thickness = gh_scm2double (beam->get_grob_property ("thickness"));
1425   int beam_count = get_direction_beam_count (beam, d);
1426   Real height_of_my_beams = beam_thickness
1427     + (beam_count - 1) * beam_translation;
1428   Real beam_y = stem_y - height_of_my_beams + beam_thickness / 2.0;
1429
1430   Real staff_space = Staff_symbol_referencer::staff_space (rest);
1431
1432   /* Better calculate relative-distance directly, rather than using
1433      rest_dim? */
1434   Grob *common_x = rest->common_refpoint (beam, Y_AXIS);
1435   Real rest_dim = rest->extent (common_x, Y_AXIS)[d] / staff_space * d;
1436
1437   Real minimum_distance = gh_scm2double
1438     (rest->get_grob_property ("minimum-beam-collision-distance"));
1439
1440   Real distance = beam_y - rest_dim;
1441   Real shift = 0;
1442   if (distance < 0)
1443     shift = minimum_distance - distance;
1444   else if (minimum_distance > distance)
1445     shift = minimum_distance - distance;
1446       
1447   int stafflines = Staff_symbol_referencer::line_count (rest);
1448
1449   /* Always move discretely by half spaces */
1450   Real discrete_shift = ceil (shift * 2.0) / 2.0;
1451
1452   /* Inside staff, move by whole spaces*/
1453   if ((rest->extent (common_x, Y_AXIS)[d] + discrete_shift) * d
1454       < stafflines / 2.0
1455       ||(rest->extent (common_x, Y_AXIS)[-d] + discrete_shift) * -d
1456       < stafflines / 2.0)
1457     discrete_shift = ceil (discrete_shift);
1458
1459   return gh_double2scm (-d * discrete_shift);
1460 }
1461
1462 bool
1463 Beam::knee_b (Grob* me)
1464 {
1465   SCM k = me->get_grob_property ("knee");
1466   if (gh_boolean_p (k))
1467     return gh_scm2bool (k);
1468
1469   bool knee = false;
1470   int d = 0;
1471   for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
1472     {
1473       Direction dir = Directional_element_interface::get
1474         (unsmob_grob (ly_car (s)));
1475       if (d && d != dir)
1476         {
1477           knee = true;
1478           break;
1479         }
1480       d = dir;
1481     }
1482   
1483   me->set_grob_property ("knee", gh_bool2scm (knee));
1484
1485   return knee;
1486 }
1487
1488 int
1489 Beam::get_direction_beam_count (Grob *me, Direction d )
1490 {
1491   Link_array<Grob>stems = 
1492     Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1493   int bc = 0;
1494   
1495   for (int i = stems.size (); i--;)
1496     {
1497       /*
1498         Should we take invisible stems into account?
1499        */
1500       if (Stem::get_direction (stems[i]) == d)
1501         bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1502     }
1503
1504   return bc;
1505 }
1506
1507
1508 ADD_INTERFACE (Beam, "beam-interface",
1509   "A beam. \n\n"
1510 " "
1511 "#'thickness= weight of beams, in staffspace "
1512 " "
1513 " "
1514 "We take the least squares line through the ideal-length stems, and "
1515 "then damp that using "
1516 " \n"
1517 "       damped = tanh (slope) \n"
1518 " \n"
1519 "this gives an unquantized left and right position for the beam end. "
1520 "Then we take all combinations of quantings near these left and right "
1521 "positions, and give them a score (according to how close they are to "
1522 "the ideal slope, how close the result is to the ideal stems, etc.). We "
1523 "take the best scoring combination. "
1524 ,
1525   "knee position-callbacks concaveness-gap concaveness-threshold dir-function quant-score auto-knee-gap gap chord-tremolo beamed-stem-shorten shorten least-squares-dy damping flag-width-function neutral-direction positions space-function thickness");
1526
1527