2 beam.cc -- implement Beam
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
14 * Use Number_pair i.s.o Interval to represent (yl, yr).
16 - Determine auto knees based on positions if it's set by the user.
22 - Stems run to the Y-center of the beam.
24 - beam_translation is the offset between Y centers of the beam.
29 #include <math.h> // tanh.
31 #include "molecule.hh"
32 #include "directional-element-interface.hh"
36 #include "least-squares.hh"
38 #include "paper-def.hh"
40 #include "group-interface.hh"
41 #include "staff-symbol-referencer.hh"
47 #define DEBUG_QUANTING 0
51 #include "text-item.hh" // debug output.
52 #include "font-interface.hh" // debug output.
57 Beam::add_stem (Grob *me, Grob *s)
59 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
61 s->add_dependency (me);
63 assert (!Stem::get_beam (s));
64 s->set_grob_property ("beam", me->self_scm ());
66 add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
71 Beam::get_thickness (Grob * me)
73 SCM th = me->get_grob_property ("thickness");
75 return gh_scm2double (th)* Staff_symbol_referencer::staff_space (me);
80 /* Return the translation between 2 adjoining beams. */
82 Beam::get_beam_translation (Grob *me)
84 SCM func = me->get_grob_property ("space-function");
85 SCM s = gh_call2 (func, me->self_scm (), scm_int2num (get_beam_count (me)));
86 return gh_scm2double (s);
89 /* Maximum beam_count. */
91 Beam::get_beam_count (Grob *me)
94 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
96 Grob *stem = unsmob_grob (ly_car (s));
97 m = m >? (Stem::beam_multiplicity (stem).length () + 1);
102 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
104 Beam::space_function (SCM smob, SCM beam_count)
106 Grob *me = unsmob_grob (smob);
108 Real staff_space = Staff_symbol_referencer::staff_space (me);
109 Real line = me->get_paper ()->get_var ("linethickness");
110 Real thickness = get_thickness (me);
112 Real beam_translation = gh_scm2int (beam_count) < 4
113 ? (2*staff_space + line - thickness) / 2.0
114 : (3*staff_space + line - thickness) / 3.0;
116 return gh_double2scm (beam_translation);
120 /* After pre-processing all directions should be set.
121 Several post-processing routines (stem, slur, script) need stem/beam
123 Currenly, this means that beam has set all stem's directions.
124 [Alternatively, stems could set its own directions, according to
125 their beam, during 'final-pre-processing'.] */
126 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
128 Beam::before_line_breaking (SCM smob)
130 Grob *me = unsmob_grob (smob);
132 /* Beams with less than 2 two stems don't make much sense, but could happen
137 For a beam that only has one stem, we try to do some disappearance magic:
138 we revert the flag, and move on to The Eternal Engraving Fields. */
140 int count = visible_stem_count (me);
143 me->warning (_ ("beam has less than two visible stems"));
145 SCM stems = me->get_grob_property ("stems");
146 if (scm_ilength (stems) == 1)
148 me->warning (_ ("Beam has less than two stems. Removing beam."));
150 unsmob_grob (gh_car (stems))->set_grob_property ("beam", SCM_EOL);
153 return SCM_UNSPECIFIED;
155 else if (scm_ilength (stems) == 0)
158 return SCM_UNSPECIFIED;
163 Direction d = get_default_dir (me);
165 consider_auto_knees (me);
166 set_stem_directions (me, d);
170 set_stem_shorten (me);
178 We want a maximal number of shared beams, but if there is choice, we
179 take the one that is closest to the end of the stem. This is for situations like
192 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
196 Slice lslice = int_list_to_slice (gh_cdr (left_beaming));
200 for (int i = lslice[-left_dir];
201 (i - lslice[left_dir])* left_dir <= 0 ; i+= left_dir)
204 for ( SCM s = gh_car (right_beaming); gh_pair_p (s); s = gh_cdr (s))
206 int k = - right_dir * gh_scm2int (gh_car (s)) + i;
207 if (scm_memq (scm_int2num (k), left_beaming) != SCM_BOOL_F)
211 if (count >= best_count)
222 Beam::connect_beams (Grob *me)
224 Link_array<Grob> stems=
225 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
228 last_int.set_empty();
229 SCM last_beaming = SCM_EOL;
230 Direction last_dir = CENTER;
231 for (int i = 0; i< stems.size(); i++)
233 Grob *this_stem = stems[i];
234 SCM this_beaming = this_stem->get_grob_property ("beaming");
236 Direction this_dir = Directional_element_interface::get(this_stem);
239 int start_point = position_with_maximal_common_beams
240 (last_beaming, this_beaming,
247 if (d == RIGHT && i == stems.size()-1)
250 new_slice.set_empty();
251 SCM s = index_get_cell (this_beaming, d);
252 for (; gh_pair_p (s); s = gh_cdr (s))
255 start_point - this_dir * gh_scm2int (gh_car (s));
257 new_slice.add_point (new_beam_pos);
258 gh_set_car_x (s, scm_int2num (new_beam_pos));
263 while (flip (&d) != LEFT);
265 if (!new_slice.empty_b())
266 last_int = new_slice;
270 gh_set_car_x ( this_beaming, SCM_EOL);
271 SCM s = gh_cdr (this_beaming);
272 for (; gh_pair_p (s); s = gh_cdr (s))
274 int np = - this_dir * gh_scm2int (gh_car(s));
275 gh_set_car_x (s, scm_int2num (np));
276 last_int.add_point (np);
280 if (i == stems.size () -1)
282 gh_set_cdr_x (this_beaming, SCM_EOL);
285 if (scm_ilength (gh_cdr (this_beaming)) > 0)
287 last_beaming = this_beaming;
293 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
295 Beam::brew_molecule (SCM grob)
297 Grob *me = unsmob_grob (grob);
298 Link_array<Grob> stems=
299 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
300 Grob* xcommon = common_refpoint_of_array (stems, me, X_AXIS);
303 if (visible_stem_count (me))
305 // ugh -> use commonx
306 x0 = first_visible_stem (me)->relative_coordinate (xcommon, X_AXIS);
307 dx = last_visible_stem (me)->relative_coordinate (xcommon, X_AXIS) - x0;
311 x0 = stems[0]->relative_coordinate (xcommon, X_AXIS);
312 dx = stems.top ()->relative_coordinate (xcommon, X_AXIS) - x0;
315 SCM posns = me->get_grob_property ("positions");
317 if (!ly_number_pair_p (posns))
319 programming_error ("No beam posns");
320 pos = Interval (0,0);
323 pos= ly_scm2interval (posns);
325 Real dy = pos.delta ();
326 Real dydx = dy && dx ? dy/dx : 0;
328 Real thick = get_thickness (me);
329 Real bdy = get_beam_translation (me);
331 SCM last_beaming = SCM_EOL;;
332 Real last_xposn = -1;
333 Real last_width = -1 ;
336 SCM gap = me->get_grob_property ("gap");
338 Real lt = me->get_paper ()->get_var ("linethickness");
339 for (int i = 0; i< stems.size(); i++)
343 SCM this_beaming = st->get_grob_property ("beaming");
344 Real xposn = st->relative_coordinate (xcommon, X_AXIS);
345 Real stem_width = gh_scm2double (st->get_grob_property ("thickness")) *lt;
349 SCM left = gh_cdr (last_beaming);
350 SCM right = gh_car (this_beaming);
352 Array<int> fullbeams;
353 Array<int> lfliebertjes;
354 Array<int> rfliebertjes;
357 gh_pair_p (s); s =gh_cdr (s))
359 int b = gh_scm2int (gh_car (s));
360 if (scm_memq (gh_car(s), right) != SCM_BOOL_F)
366 lfliebertjes.push (b);
370 gh_pair_p (s); s =gh_cdr (s))
372 int b = gh_scm2int (gh_car (s));
373 if (scm_memq (gh_car(s), left) == SCM_BOOL_F)
375 rfliebertjes.push (b);
380 Real w = xposn - last_xposn;
381 Real stem_offset = 0.0;
382 Real width_corr = 0.0;
385 stem_offset -= last_width/2;
386 width_corr += last_width/2;
389 if (i == stems.size() -1)
391 width_corr += stem_width/2;
394 if (gh_number_p (gap))
396 Real g = gh_scm2double (gap);
401 Molecule whole = Lookup::beam (dydx, w + width_corr, thick);
402 for (int j = fullbeams.size(); j--;)
405 b.translate_axis (last_xposn - x0 + stem_offset, X_AXIS);
406 b.translate_axis (dydx * (last_xposn - x0) + bdy * fullbeams[j], Y_AXIS);
407 the_beam.add_molecule (b);
410 if (lfliebertjes.size() || rfliebertjes.size())
414 if (!Stem::first_head (st))
418 int t = Stem::duration_log (st);
420 SCM proc = me->get_grob_property ("flag-width-function");
421 SCM result = gh_call1 (proc, scm_int2num (t));
422 nw_f = gh_scm2double (result);
425 /* Half beam should be one note-width,
426 but let's make sure two half-beams never touch */
428 Real w = xposn - last_xposn;
431 Molecule half = Lookup::beam (dydx, w, thick);
432 for (int j = lfliebertjes.size(); j--;)
435 b.translate_axis (last_xposn - x0, X_AXIS);
436 b.translate_axis (dydx * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
437 the_beam.add_molecule (b);
439 for (int j = rfliebertjes.size(); j--;)
442 b.translate_axis (xposn - x0 - w , X_AXIS);
443 b.translate_axis (dydx * (xposn-x0 -w) + bdy * rfliebertjes[j], Y_AXIS);
444 the_beam.add_molecule (b);
450 last_width = stem_width;
451 last_beaming = this_beaming;
454 the_beam.translate_axis (x0 - me->relative_coordinate (xcommon, X_AXIS), X_AXIS);
455 the_beam.translate_axis (pos[LEFT], Y_AXIS);
460 This code prints the demerits for each beam. Perhaps this
461 should be switchable for those who want to twiddle with the
467 str += to_string (gh_scm2int (me->get_grob_property ("best-idx")));
470 str += to_string (gh_scm2double (me->get_grob_property ("quant-score")),
473 SCM properties = Font_interface::font_alist_chain (me);
476 Molecule tm = Text_item::text2molecule (me, scm_makfrom0str (str.to_str0 ()), properties);
477 the_beam.add_at_edge (Y_AXIS, UP, tm, 5.0);
483 return the_beam.smobbed_copy();
490 Beam::get_default_dir (Grob *me)
492 Drul_array<int> total;
493 total[UP] = total[DOWN] = 0;
494 Drul_array<int> count;
495 count[UP] = count[DOWN] = 0;
498 Link_array<Grob> stems=
499 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
501 for (int i=0; i <stems.size (); i++)
504 Direction sd = Directional_element_interface::get (s);
506 int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
507 int current = sd ? (1 + d * sd)/2 : center_distance;
514 } while (flip (&d) != DOWN);
516 SCM func = me->get_grob_property ("dir-function");
517 SCM s = gh_call2 (func,
518 gh_cons (scm_int2num (count[UP]),
519 scm_int2num (count[DOWN])),
520 gh_cons (scm_int2num (total[UP]),
521 scm_int2num (total[DOWN])));
523 if (gh_number_p (s) && gh_scm2int (s))
526 /* If dir is not determined: get default */
527 return to_dir (me->get_grob_property ("neutral-direction"));
531 /* Set all stems with non-forced direction to beam direction.
532 Urg: non-forced should become `without/with unforced' direction,
533 once stem gets cleaned-up. */
535 Beam::set_stem_directions (Grob *me, Direction d)
537 Link_array<Grob> stems
538 =Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
540 for (int i=0; i <stems.size (); i++)
544 SCM forcedir = s->get_grob_property ("direction");
545 if (!to_dir (forcedir))
546 Directional_element_interface::set (s, d);
551 A union of intervals in the real line.
553 Abysmal performance (quadratic) for large N, hopefully we don't have
554 that large N. In any case, this should probably be rewritten to use
559 Array<Interval> allowed_regions_;
568 allowed_regions_.clear();
571 allowed_regions_.push (s);
574 void remove_interval (Interval rm)
576 for (int i = 0; i < allowed_regions_.size(); )
580 s.intersect (allowed_regions_[i]);
584 Interval before = allowed_regions_[i];
585 Interval after = allowed_regions_[i];
587 before[RIGHT] = s[LEFT];
588 after[LEFT] = s[RIGHT];
590 if (!before.empty_b() && before.length () > 0.0)
592 allowed_regions_.insert (before, i);
595 allowed_regions_.del (i);
596 if (!after.empty_b () && after.length () > 0.0)
598 allowed_regions_.insert (after, i);
610 Only try horizontal beams for knees. No reliable detection of
611 anything else is possible here, since we don't know funky-beaming
612 settings, or X-distances (slopes!) People that want sloped
613 knee-beams, should set the directions manually.
616 Beam::consider_auto_knees (Grob* me)
618 SCM scm = me->get_grob_property ("auto-knee-gap");
619 if (!gh_number_p (scm))
622 Real threshold = gh_scm2double (scm);
628 Link_array<Grob> stems=
629 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
631 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
632 Real staff_space = Staff_symbol_referencer::staff_space (me);
634 Array<Interval> hps_array;
635 for (int i=0; i < stems.size (); i++)
637 Grob* stem = stems[i];
638 if (Stem::invisible_b (stem))
641 Interval hps = Stem::head_positions (stem);
646 hps *= staff_space * 0.5 ;
649 We could subtract beam Y position, but this routine only
650 sets stem directions, a constant shift does not have an
654 hps += stem->relative_coordinate (common, Y_AXIS);
656 if (to_dir (stem->get_grob_property ("direction")))
658 Direction stemdir = to_dir (stem->get_grob_property ("direction"));
659 hps[-stemdir] = - stemdir * infinity_f;
662 hps_array.push (hps);
664 gaps.remove_interval (hps);
668 Real max_gap_len =0.0;
670 for (int i = gaps.allowed_regions_.size() -1; i >= 0 ; i--)
672 Interval gap = gaps.allowed_regions_[i];
675 the outer gaps are not knees.
677 if (isinf (gap[LEFT]) || isinf(gap[RIGHT]))
680 if (gap.length () >= max_gap_len)
682 max_gap_len = gap.length();
687 if (max_gap_len > threshold)
690 for (int i = 0; i < stems.size(); i++)
692 Grob* stem = stems[i];
693 if (Stem::invisible_b (stem))
696 Interval hps = hps_array[j++];
699 Direction d = (hps.center () < max_gap.center()) ?
702 stem->set_grob_property ("direction", scm_int2num (d));
704 hps.intersect (max_gap);
705 assert (hps.empty_b () || hps.length () < 1e-6 );
712 /* Set stem's shorten property if unset.
715 take some y-position (chord/beam/nearest?) into account
716 scmify forced-fraction
718 This is done in beam because the shorten has to be uniform over the
723 Beam::set_stem_shorten (Grob *me)
726 shortening looks silly for x staff beams
731 Real forced_fraction = 1.0 * forced_stem_count (me)
732 / visible_stem_count (me);
734 int beam_count = get_beam_count (me);
736 SCM shorten_list = me->get_grob_property ("beamed-stem-shorten");
737 if (shorten_list == SCM_EOL)
740 Real staff_space = Staff_symbol_referencer::staff_space (me);
743 robust_list_ref (beam_count -1, shorten_list);
744 Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
746 /* your similar cute comment here */
747 shorten_f *= forced_fraction;
750 me->set_grob_property ("shorten", gh_double2scm (shorten_f));
753 /* Call list of y-dy-callbacks, that handle setting of
757 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
759 Beam::after_line_breaking (SCM smob)
761 Grob *me = unsmob_grob (smob);
763 /* Copy to mutable list. */
764 SCM s = ly_deep_copy (me->get_grob_property ("positions"));
765 me->set_grob_property ("positions", s);
767 if (ly_car (s) == SCM_BOOL_F)
770 // one wonders if such genericity is necessary --hwn.
771 SCM callbacks = me->get_grob_property ("position-callbacks");
772 for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
773 gh_call1 (ly_car (i), smob);
776 set_stem_lengths (me);
777 return SCM_UNSPECIFIED;
780 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
782 Beam::least_squares (SCM smob)
784 Grob *me = unsmob_grob (smob);
786 int count = visible_stem_count (me);
791 me->set_grob_property ("positions", ly_interval2scm (pos));
792 return SCM_UNSPECIFIED;
796 Array<Real> x_posns ;
797 Link_array<Grob> stems=
798 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
799 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
800 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
802 Real my_y = me->relative_coordinate (commony, Y_AXIS);
804 Grob *fvs = first_visible_stem (me);
805 Grob *lvs = last_visible_stem (me);
807 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
808 + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
809 Stem::get_stem_info (lvs).ideal_y_
810 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
812 Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
813 for (int i=0; i < stems.size (); i++)
817 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
820 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
828 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
829 Stem::chord_start_y (last_visible_stem (me)));
831 /* Simple beams (2 stems) on middle line should be allowed to be
834 However, if both stems reach middle line,
835 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
837 For that case, we apply artificial slope */
838 if (!ideal[LEFT] && chord.delta () && count == 2)
841 Direction d = (Direction) (sign (chord.delta ()) * UP);
842 pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
856 Array<Offset> ideals;
857 for (int i=0; i < stems.size (); i++)
860 if (Stem::invisible_b (s))
862 ideals.push (Offset (x_posns[i],
863 Stem::get_stem_info (s).ideal_y_
864 + s->relative_coordinate (commony, Y_AXIS)
867 minimise_least_squares (&dydx, &y, ideals);
870 me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
871 pos = Interval (y, (y+dy));
874 me->set_grob_property ("positions", ly_interval2scm (pos));
876 return SCM_UNSPECIFIED;
881 We can't combine with previous function, since check concave and
882 slope damping comes first.
884 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
886 Beam::shift_region_to_valid (SCM grob)
888 Grob *me = unsmob_grob (grob);
892 Array<Real> x_posns ;
893 Link_array<Grob> stems=
894 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
895 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
896 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
898 Grob *fvs = first_visible_stem (me);
901 return SCM_UNSPECIFIED;
903 Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
904 for (int i=0; i < stems.size (); i++)
908 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
912 Grob *lvs = last_visible_stem (me);
914 return SCM_UNSPECIFIED;
916 Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
918 Interval pos = ly_scm2interval ( me->get_grob_property ("positions"));
919 Real dy = pos.delta();
925 Shift the positions so that we have a chance of finding good
926 quants (i.e. no short stem failures.)
928 Interval feasible_left_point;
929 feasible_left_point.set_full ();
930 for (int i=0; i < stems.size (); i++)
933 if (Stem::invisible_b (s))
936 Direction d = Stem::get_direction (s);
939 Stem::get_stem_info (s).shortest_y_
940 - dydx * x_posns [i];
943 left_y is now relative to the stem S. We want relative to
944 ourselves, so translate:
947 + s->relative_coordinate (commony, Y_AXIS)
948 - me->relative_coordinate (commony, Y_AXIS);
954 feasible_left_point.intersect (flp);
957 if (feasible_left_point.empty_b())
959 warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
961 else if (!feasible_left_point.elem_b(y))
963 if (isinf (feasible_left_point[DOWN]))
964 y = feasible_left_point[UP] - REGION_SIZE;
965 else if (isinf (feasible_left_point[UP]))
966 y = feasible_left_point[DOWN]+ REGION_SIZE;
968 y = feasible_left_point.center ();
970 pos = Interval (y, (y+dy));
971 me->set_grob_property ("positions", ly_interval2scm (pos));
972 return SCM_UNSPECIFIED;
976 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
978 Beam::check_concave (SCM smob)
980 Grob *me = unsmob_grob (smob);
982 Link_array<Grob> stems =
983 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
985 for (int i = 0; i < stems.size ();)
987 if (Stem::invisible_b (stems[i]))
993 if (stems.size () < 3)
994 return SCM_UNSPECIFIED;
997 /* Concaveness #1: If distance of an inner notehead to line between
998 two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
999 beam is concave (Heinz Stolba).
1001 In the case of knees, the line connecting outer heads is often
1002 not related to the beam slope (it may even go in the other
1003 direction). Skip the check when the outer stems point in
1004 different directions. --hwn
1007 bool concaveness1 = false;
1008 SCM gap = me->get_grob_property ("concaveness-gap");
1009 if (gh_number_p (gap)
1010 && Stem::get_direction(stems.top ())
1011 == Stem::get_direction(stems[0]))
1013 Real r1 = gh_scm2double (gap);
1014 Real dy = Stem::chord_start_y (stems.top ())
1015 - Stem::chord_start_y (stems[0]);
1018 Real slope = dy / (stems.size () - 1);
1020 Real y0 = Stem::chord_start_y (stems[0]);
1021 for (int i = 1; i < stems.size () - 1; i++)
1023 Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
1026 concaveness1 = true;
1033 /* Concaveness #2: Sum distances of inner noteheads that fall
1034 outside the interval of the two outer noteheads.
1036 We only do this for beams where first and last stem have the same
1040 Note that "convex" stems compensate for "concave" stems.
1041 (is that intentional?) --hwn.
1044 Real concaveness2 = 0;
1045 SCM thresh = me->get_grob_property ("concaveness-threshold");
1046 Real r2 = infinity_f;
1047 if (!concaveness1 && gh_number_p (thresh)
1048 && Stem::get_direction(stems.top ())
1049 == Stem::get_direction(stems[0]))
1051 r2 = gh_scm2double (thresh);
1053 Direction dir = Stem::get_direction(stems.top ());
1055 Interval iv (Stem::chord_start_y (stems[0]),
1056 Stem::chord_start_y (stems.top ()));
1058 if (iv[MAX] < iv[MIN])
1061 for (int i = 1; i < stems.size () - 1; i++)
1063 Real f = Stem::chord_start_y (stems[i]);
1064 concave += ((f - iv[MAX] ) >? 0) +
1065 ((f - iv[MIN] ) <? 0);
1068 concaveness2 = concave / (stems.size () - 2);
1070 /* ugh: this is the a kludge to get
1071 input/regression/beam-concave.ly to behave as
1075 huh? we're dividing twice (which is not scalable) meaning that
1076 the longer the beam, the more unlikely it will be
1077 concave. Maybe you would even expect the other way around??
1082 concaveness2 /= (stems.size () - 2);
1085 /* TODO: some sort of damping iso -> plain horizontal */
1086 if (concaveness1 || concaveness2 > r2)
1088 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1089 Real r = pos.linear_combination (0);
1090 me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
1091 me->set_grob_property ("least-squares-dy", gh_double2scm (0));
1094 return SCM_UNSPECIFIED;
1097 /* This neat trick is by Werner Lemberg,
1098 damped = tanh (slope)
1099 corresponds with some tables in [Wanske] CHECKME */
1100 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1102 Beam::slope_damping (SCM smob)
1104 Grob *me = unsmob_grob (smob);
1106 if (visible_stem_count (me) <= 1)
1107 return SCM_UNSPECIFIED;
1109 SCM s = me->get_grob_property ("damping");
1110 int damping = gh_scm2int (s);
1114 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1115 Real dy = pos.delta ();
1117 Grob *fvs = first_visible_stem (me);
1118 Grob *lvs = last_visible_stem (me);
1120 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1123 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1124 - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1125 Real dydx = dy && dx ? dy/dx : 0;
1126 dydx = 0.6 * tanh (dydx) / damping;
1128 Real damped_dy = dydx * dx;
1129 pos[LEFT] += (dy - damped_dy) / 2;
1130 pos[RIGHT] -= (dy - damped_dy) / 2;
1132 me->set_grob_property ("positions", ly_interval2scm (pos));
1134 return SCM_UNSPECIFIED;
1138 Report slice containing the numbers that are both in (car BEAMING)
1142 where_are_the_whole_beams(SCM beaming)
1146 for( SCM s = gh_car (beaming); gh_pair_p (s) ; s = gh_cdr (s))
1148 if (scm_memq (gh_car (s), gh_cdr (beaming)) != SCM_BOOL_F)
1150 l.add_point (gh_scm2int (gh_car (s)));
1156 /* Return the Y position of the stem-end, given the Y-left, Y-right
1157 in POS for stem S. This Y position is relative to S. */
1159 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1161 Interval pos, bool french)
1163 Real beam_translation = get_beam_translation (me);
1166 Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1167 Real dy = pos.delta ();
1169 Real stem_y_beam0 = (dy && dx
1174 Direction my_dir = Directional_element_interface::get (s);
1175 SCM beaming = s->get_grob_property ("beaming");
1177 Real stem_y = stem_y_beam0;
1180 Slice bm = where_are_the_whole_beams (beaming);
1182 stem_y += beam_translation * bm[-my_dir];
1186 Slice bm = Stem::beam_multiplicity(s);
1188 stem_y +=bm[my_dir] * beam_translation;
1191 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1192 - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1198 Hmm. At this time, beam position and slope are determined. Maybe,
1199 stem directions and length should set to relative to the chord's
1200 position of the beam. */
1202 Beam::set_stem_lengths (Grob *me)
1204 Link_array<Grob> stems=
1205 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1207 if (stems.size () <= 1)
1211 for (int a = 2; a--;)
1212 common[a] = common_refpoint_of_array (stems, me, Axis(a));
1214 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1215 Real staff_space = Staff_symbol_referencer::staff_space (me);
1217 bool french = to_boolean (me->get_grob_property ("french-beaming"));
1222 if (gh_number_p (me->get_grob_property ("gap"))
1223 &&gh_scm2double (me->get_grob_property ("gap")))
1226 thick = get_thickness(me);
1229 // ugh -> use commonx
1230 Grob * fvs = first_visible_stem (me);
1231 Grob *lvs = last_visible_stem (me);
1233 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1234 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1236 for (int i=0; i < stems.size (); i++)
1239 if (Stem::invisible_b (s))
1242 Real stem_y = calc_stem_y (me, s, common,
1244 pos, french && i > 0&& (i < stems.size () -1));
1247 Make the stems go up to the end of the beam. This doesn't matter
1248 for normal beams, but for tremolo beams it looks silly otherwise.
1251 stem_y += thick * 0.5 * Directional_element_interface::get(s);
1253 Stem::set_stemend (s, 2* stem_y / staff_space);
1258 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1260 Link_array<Grob> stems=
1261 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1264 for (int i=0; i < stems.size (); i++)
1267 Don't overwrite user settings.
1272 /* Don't set beaming for outside of outer stems */
1273 if ((d == LEFT && i == 0)
1274 ||(d == RIGHT && i == stems.size () -1))
1278 SCM beaming_prop = stems[i]->get_grob_property ("beaming");
1279 if (beaming_prop == SCM_EOL ||
1280 index_get_cell (beaming_prop, d) == SCM_EOL)
1282 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1283 Stem::set_beaming (stems[i], b, d);
1286 while (flip (&d) != LEFT);
1291 Beam::forced_stem_count (Grob *me)
1293 Link_array<Grob>stems =
1294 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1296 for (int i=0; i < stems.size (); i++)
1300 if (Stem::invisible_b (s))
1303 /* I can imagine counting those boundaries as a half forced stem,
1304 but let's count them full for now. */
1305 if (abs (Stem::chord_start_y (s)) > 0.1
1306 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1316 Beam::visible_stem_count (Grob *me)
1318 Link_array<Grob>stems =
1319 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1321 for (int i = stems.size (); i--;)
1323 if (!Stem::invisible_b (stems[i]))
1330 Beam::first_visible_stem (Grob *me)
1332 Link_array<Grob>stems =
1333 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1335 for (int i = 0; i < stems.size (); i++)
1337 if (!Stem::invisible_b (stems[i]))
1344 Beam::last_visible_stem (Grob *me)
1346 Link_array<Grob>stems =
1347 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1348 for (int i = stems.size (); i--;)
1350 if (!Stem::invisible_b (stems[i]))
1360 handle rest under beam (do_post: beams are calculated now)
1361 what about combination of collisions and rest under beam.
1365 rest -> stem -> beam -> interpolate_y_position ()
1367 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1369 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1371 Grob *rest = unsmob_grob (element_smob);
1372 Axis a = (Axis) gh_scm2int (axis);
1374 assert (a == Y_AXIS);
1376 Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1379 return gh_double2scm (0.0);
1380 Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1382 || !Beam::has_interface (beam)
1383 || !Beam::visible_stem_count (beam))
1384 return gh_double2scm (0.0);
1386 // make callback for rest from this.
1387 // todo: make sure this calced already.
1389 // Interval pos = ly_scm2interval (beam->get_grob_property ("positions"));
1390 Interval pos (0, 0);
1391 SCM s = beam->get_grob_property ("positions");
1392 if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1393 pos = ly_scm2interval (s);
1395 Real dy = pos.delta ();
1396 // ugh -> use commonx
1397 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1398 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1399 Real dydx = dy && dx ? dy/dx : 0;
1401 Direction d = Stem::get_direction (stem);
1402 Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + pos[LEFT];
1404 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1407 Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space; // refp??
1410 = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1412 minimum_dist + -d * (beamy - rest_dim) >? 0;
1414 int stafflines = Staff_symbol_referencer::line_count (rest);
1416 // move discretely by half spaces.
1417 int discrete_dist = int (ceil (dist));
1419 // move by whole spaces inside the staff.
1420 if (discrete_dist < stafflines+1)
1421 discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1423 return gh_double2scm (-d * discrete_dist);
1427 Beam::knee_b (Grob* me)
1429 SCM k = me->get_grob_property ("knee");
1430 if (gh_boolean_p (k))
1431 return gh_scm2bool (k);
1435 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
1437 Direction dir = Directional_element_interface::get
1438 (unsmob_grob (ly_car (s)));
1447 me->set_grob_property ("knee", gh_bool2scm (knee));
1453 Beam::get_direction_beam_count (Grob *me, Direction d )
1455 Link_array<Grob>stems =
1456 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1459 for (int i = stems.size (); i--;)
1462 Should we take invisible stems into account?
1464 if (Stem::get_direction (stems[i]) == d)
1465 bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1472 ADD_INTERFACE (Beam, "beam-interface",
1475 #'thickness= weight of beams, in staffspace
1478 We take the least squares line through the ideal-length stems, and
1479 then damp that using
1481 damped = tanh (slope)
1483 this gives an unquantized left and right position for the beam end.
1484 Then we take all combinations of quantings near these left and right
1485 positions, and give them a score (according to how close they are to
1486 the ideal slope, how close the result is to the ideal stems, etc.). We
1487 take the best scoring combination.
1490 "knee french-beaming 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");