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