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>
13 * Use Number_pair i.s.o Interval to represent (yl, yr).
15 - Determine auto knees based on positions if it's set by the user.
21 - Stems run to the Y-center of the beam.
23 - beam_translation is the offset between Y centers of the beam.
28 #include <math.h> // tanh.
30 #include "molecule.hh"
31 #include "directional-element-interface.hh"
35 #include "least-squares.hh"
37 #include "paper-def.hh"
39 #include "group-interface.hh"
40 #include "staff-symbol-referencer.hh"
46 #define DEBUG_QUANTING 0
50 #include "text-item.hh" // debug output.
51 #include "font-interface.hh" // debug output.
56 Beam::add_stem (Grob *me, Grob *s)
58 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
60 s->add_dependency (me);
62 assert (!Stem::get_beam (s));
63 s->set_grob_property ("beam", me->self_scm ());
65 add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
70 Beam::get_thickness (Grob * me)
72 SCM th = me->get_grob_property ("thickness");
74 return gh_scm2double (th)* Staff_symbol_referencer::staff_space (me);
79 /* Return the translation between 2 adjoining beams. */
81 Beam::get_beam_translation (Grob *me)
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);
88 /* Maximum beam_count. */
90 Beam::get_beam_count (Grob *me)
93 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
95 Grob *stem = unsmob_grob (ly_car (s));
96 m = m >? (Stem::beam_multiplicity (stem).length () + 1);
101 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
103 Beam::space_function (SCM smob, SCM beam_count)
105 Grob *me = unsmob_grob (smob);
107 Real staff_space = Staff_symbol_referencer::staff_space (me);
108 Real line = me->get_paper ()->get_var ("linethickness");
109 Real thickness = get_thickness (me);
111 Real beam_translation = gh_scm2int (beam_count) < 4
112 ? (2*staff_space + line - thickness) / 2.0
113 : (3*staff_space + line - thickness) / 3.0;
115 return gh_double2scm (beam_translation);
119 /* After pre-processing all directions should be set.
120 Several post-processing routines (stem, slur, script) need stem/beam
122 Currenly, this means that beam has set all stem's directions.
123 [Alternatively, stems could set its own directions, according to
124 their beam, during 'final-pre-processing'.] */
125 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
127 Beam::before_line_breaking (SCM smob)
129 Grob *me = unsmob_grob (smob);
131 /* Beams with less than 2 two stems don't make much sense, but could happen
136 For a beam that only has one stem, we try to do some disappearance magic:
137 we revert the flag, and move on to The Eternal Engraving Fields. */
139 int count = visible_stem_count (me);
142 me->warning (_ ("beam has less than two visible stems"));
144 SCM stems = me->get_grob_property ("stems");
145 if (scm_ilength (stems) == 1)
147 me->warning (_ ("Beam has less than two stems. Removing beam."));
149 unsmob_grob (gh_car (stems))->set_grob_property ("beam", SCM_EOL);
152 return SCM_UNSPECIFIED;
154 else if (scm_ilength (stems) == 0)
157 return SCM_UNSPECIFIED;
162 Direction d = get_default_dir (me);
164 consider_auto_knees (me);
165 set_stem_directions (me, d);
169 set_stem_shorten (me);
177 We want a maximal number of shared beams, but if there is choice, we
178 take the one that is closest to the end of the stem. This is for situations like
191 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
195 Slice lslice = int_list_to_slice (gh_cdr (left_beaming));
199 for (int i = lslice[-left_dir];
200 (i - lslice[left_dir])* left_dir <= 0 ; i+= left_dir)
203 for ( SCM s = gh_car (right_beaming); gh_pair_p (s); s = gh_cdr (s))
205 int k = - right_dir * gh_scm2int (gh_car (s)) + i;
206 if (scm_memq (scm_int2num (k), left_beaming) != SCM_BOOL_F)
210 if (count >= best_count)
221 Beam::connect_beams (Grob *me)
223 Link_array<Grob> stems=
224 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
227 last_int.set_empty();
228 SCM last_beaming = SCM_EOL;
229 Direction last_dir = CENTER;
230 for (int i = 0; i< stems.size(); i++)
232 Grob *this_stem = stems[i];
233 SCM this_beaming = this_stem->get_grob_property ("beaming");
235 Direction this_dir = Directional_element_interface::get(this_stem);
236 if (gh_pair_p (last_beaming) && gh_pair_p (this_beaming))
238 int start_point = position_with_maximal_common_beams
239 (last_beaming, this_beaming,
246 if (d == RIGHT && i == stems.size()-1)
249 new_slice.set_empty();
250 SCM s = index_get_cell (this_beaming, d);
251 for (; gh_pair_p (s); s = gh_cdr (s))
254 start_point - this_dir * gh_scm2int (gh_car (s));
256 new_slice.add_point (new_beam_pos);
257 gh_set_car_x (s, scm_int2num (new_beam_pos));
262 while (flip (&d) != LEFT);
264 if (!new_slice.empty_b())
265 last_int = new_slice;
269 gh_set_car_x ( this_beaming, SCM_EOL);
270 SCM s = gh_cdr (this_beaming);
271 for (; gh_pair_p (s); s = gh_cdr (s))
273 int np = - this_dir * gh_scm2int (gh_car(s));
274 gh_set_car_x (s, scm_int2num (np));
275 last_int.add_point (np);
279 if (i == stems.size () -1)
281 gh_set_cdr_x (this_beaming, SCM_EOL);
284 if (scm_ilength (gh_cdr (this_beaming)) > 0)
286 last_beaming = this_beaming;
292 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
294 Beam::brew_molecule (SCM grob)
296 Grob *me = unsmob_grob (grob);
297 Link_array<Grob> stems=
298 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
299 Grob* xcommon = common_refpoint_of_array (stems, me, X_AXIS);
302 if (visible_stem_count (me))
304 // ugh -> use commonx
305 x0 = first_visible_stem (me)->relative_coordinate (xcommon, X_AXIS);
306 dx = last_visible_stem (me)->relative_coordinate (xcommon, X_AXIS) - x0;
310 x0 = stems[0]->relative_coordinate (xcommon, X_AXIS);
311 dx = stems.top ()->relative_coordinate (xcommon, X_AXIS) - x0;
314 SCM posns = me->get_grob_property ("positions");
316 if (!ly_number_pair_p (posns))
318 programming_error ("No beam posns");
319 pos = Interval (0,0);
322 pos= ly_scm2interval (posns);
324 Real dy = pos.delta ();
325 Real dydx = dy && dx ? dy/dx : 0;
327 Real thick = get_thickness (me);
328 Real bdy = get_beam_translation (me);
330 SCM last_beaming = SCM_EOL;;
331 Real last_xposn = -1;
332 Real last_width = -1 ;
335 SCM gap = me->get_grob_property ("gap");
337 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;
348 We do the space left of ST, with lfliebertjes pointing to the
349 right from the left stem, and rfliebertjes pointing left from
354 SCM left = gh_cdr (last_beaming);
355 SCM right = gh_car (this_beaming);
357 Array<int> fullbeams;
358 Array<int> lfliebertjes;
359 Array<int> rfliebertjes;
362 gh_pair_p (s); s =gh_cdr (s))
364 int b = gh_scm2int (gh_car (s));
365 if (scm_memq (gh_car(s), right) != SCM_BOOL_F)
371 lfliebertjes.push (b);
375 gh_pair_p (s); s =gh_cdr (s))
377 int b = gh_scm2int (gh_car (s));
378 if (scm_memq (gh_car(s), left) == SCM_BOOL_F)
380 rfliebertjes.push (b);
385 Real w = xposn - last_xposn;
386 Real stem_offset = 0.0;
387 Real width_corr = 0.0;
390 stem_offset -= last_width/2;
391 width_corr += last_width/2;
394 if (i == stems.size() -1)
396 width_corr += stem_width/2;
399 if (gh_number_p (gap))
401 Real g = gh_scm2double (gap);
406 Molecule whole = Lookup::beam (dydx, w + width_corr, thick);
407 for (int j = fullbeams.size(); j--;)
410 b.translate_axis (last_xposn - x0 + stem_offset, X_AXIS);
411 b.translate_axis (dydx * (last_xposn - x0) + bdy * fullbeams[j], Y_AXIS);
412 the_beam.add_molecule (b);
415 if (lfliebertjes.size() || rfliebertjes.size())
419 int t = Stem::duration_log (st);
421 SCM proc = me->get_grob_property ("flag-width-function");
422 SCM result = gh_call1 (proc, scm_int2num (t));
423 nw_f = gh_scm2double (result);
426 /* Half beam should be one note-width,
427 but let's make sure two half-beams never touch */
429 Real w = xposn - last_xposn;
432 Molecule half = Lookup::beam (dydx, w, thick);
433 for (int j = lfliebertjes.size(); j--;)
436 b.translate_axis (last_xposn - x0, X_AXIS);
437 b.translate_axis (dydx * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
438 the_beam.add_molecule (b);
440 for (int j = rfliebertjes.size(); j--;)
443 b.translate_axis (xposn - x0 - w , X_AXIS);
444 b.translate_axis (dydx * (xposn-x0 -w) + bdy * rfliebertjes[j], Y_AXIS);
445 the_beam.add_molecule (b);
451 last_width = stem_width;
452 last_beaming = this_beaming;
455 the_beam.translate_axis (x0 - me->relative_coordinate (xcommon, X_AXIS), X_AXIS);
456 the_beam.translate_axis (pos[LEFT], Y_AXIS);
461 This code prints the demerits for each beam. Perhaps this
462 should be switchable for those who want to twiddle with the
468 str += to_string (gh_scm2int (me->get_grob_property ("best-idx")));
471 str += to_string (gh_scm2double (me->get_grob_property ("quant-score")),
474 SCM properties = Font_interface::font_alist_chain (me);
477 Molecule tm = Text_item::text2molecule (me, scm_makfrom0str (str.to_str0 ()), properties);
478 the_beam.add_at_edge (Y_AXIS, UP, tm, 5.0);
484 return the_beam.smobbed_copy();
491 Beam::get_default_dir (Grob *me)
493 Drul_array<int> total;
494 total[UP] = total[DOWN] = 0;
495 Drul_array<int> count;
496 count[UP] = count[DOWN] = 0;
499 Link_array<Grob> stems=
500 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
502 for (int i=0; i <stems.size (); i++)
505 Direction sd = Directional_element_interface::get (s);
507 int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
508 int current = sd ? (1 + d * sd)/2 : center_distance;
515 } while (flip (&d) != DOWN);
517 SCM func = me->get_grob_property ("dir-function");
518 SCM s = gh_call2 (func,
519 gh_cons (scm_int2num (count[UP]),
520 scm_int2num (count[DOWN])),
521 gh_cons (scm_int2num (total[UP]),
522 scm_int2num (total[DOWN])));
524 if (gh_number_p (s) && gh_scm2int (s))
527 /* If dir is not determined: get default */
528 return to_dir (me->get_grob_property ("neutral-direction"));
532 /* Set all stems with non-forced direction to beam direction.
533 Urg: non-forced should become `without/with unforced' direction,
534 once stem gets cleaned-up. */
536 Beam::set_stem_directions (Grob *me, Direction d)
538 Link_array<Grob> stems
539 =Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
541 for (int i=0; i <stems.size (); i++)
545 SCM forcedir = s->get_grob_property ("direction");
546 if (!to_dir (forcedir))
547 Directional_element_interface::set (s, d);
552 A union of intervals in the real line.
554 Abysmal performance (quadratic) for large N, hopefully we don't have
555 that large N. In any case, this should probably be rewritten to use
560 Array<Interval> allowed_regions_;
569 allowed_regions_.clear();
572 allowed_regions_.push (s);
575 void remove_interval (Interval rm)
577 for (int i = 0; i < allowed_regions_.size(); )
581 s.intersect (allowed_regions_[i]);
585 Interval before = allowed_regions_[i];
586 Interval after = allowed_regions_[i];
588 before[RIGHT] = s[LEFT];
589 after[LEFT] = s[RIGHT];
591 if (!before.empty_b() && before.length () > 0.0)
593 allowed_regions_.insert (before, i);
596 allowed_regions_.del (i);
597 if (!after.empty_b () && after.length () > 0.0)
599 allowed_regions_.insert (after, i);
611 Only try horizontal beams for knees. No reliable detection of
612 anything else is possible here, since we don't know funky-beaming
613 settings, or X-distances (slopes!) People that want sloped
614 knee-beams, should set the directions manually.
617 Beam::consider_auto_knees (Grob* me)
619 SCM scm = me->get_grob_property ("auto-knee-gap");
620 if (!gh_number_p (scm))
623 Real threshold = gh_scm2double (scm);
629 Link_array<Grob> stems=
630 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
632 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
633 Real staff_space = Staff_symbol_referencer::staff_space (me);
635 Array<Interval> hps_array;
636 for (int i=0; i < stems.size (); i++)
638 Grob* stem = stems[i];
639 if (Stem::invisible_b (stem))
642 Interval hps = Stem::head_positions (stem);
647 hps *= staff_space * 0.5 ;
650 We could subtract beam Y position, but this routine only
651 sets stem directions, a constant shift does not have an
655 hps += stem->relative_coordinate (common, Y_AXIS);
657 if (to_dir (stem->get_grob_property ("direction")))
659 Direction stemdir = to_dir (stem->get_grob_property ("direction"));
660 hps[-stemdir] = - stemdir * infinity_f;
663 hps_array.push (hps);
665 gaps.remove_interval (hps);
669 Real max_gap_len =0.0;
671 for (int i = gaps.allowed_regions_.size() -1; i >= 0 ; i--)
673 Interval gap = gaps.allowed_regions_[i];
676 the outer gaps are not knees.
678 if (isinf (gap[LEFT]) || isinf(gap[RIGHT]))
681 if (gap.length () >= max_gap_len)
683 max_gap_len = gap.length();
688 if (max_gap_len > threshold)
691 for (int i = 0; i < stems.size(); i++)
693 Grob* stem = stems[i];
694 if (Stem::invisible_b (stem))
697 Interval hps = hps_array[j++];
700 Direction d = (hps.center () < max_gap.center()) ?
703 stem->set_grob_property ("direction", scm_int2num (d));
705 hps.intersect (max_gap);
706 assert (hps.empty_b () || hps.length () < 1e-6 );
713 /* Set stem's shorten property if unset.
716 take some y-position (chord/beam/nearest?) into account
717 scmify forced-fraction
719 This is done in beam because the shorten has to be uniform over the
724 Beam::set_stem_shorten (Grob *me)
727 shortening looks silly for x staff beams
732 Real forced_fraction = 1.0 * forced_stem_count (me)
733 / visible_stem_count (me);
735 int beam_count = get_beam_count (me);
737 SCM shorten_list = me->get_grob_property ("beamed-stem-shorten");
738 if (shorten_list == SCM_EOL)
741 Real staff_space = Staff_symbol_referencer::staff_space (me);
744 robust_list_ref (beam_count -1, shorten_list);
745 Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
747 /* your similar cute comment here */
748 shorten_f *= forced_fraction;
751 me->set_grob_property ("shorten", gh_double2scm (shorten_f));
754 /* Call list of y-dy-callbacks, that handle setting of
758 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
760 Beam::after_line_breaking (SCM smob)
762 Grob *me = unsmob_grob (smob);
764 /* Copy to mutable list. */
765 SCM s = ly_deep_copy (me->get_grob_property ("positions"));
766 me->set_grob_property ("positions", s);
768 if (ly_car (s) == SCM_BOOL_F)
771 // one wonders if such genericity is necessary --hwn.
772 SCM callbacks = me->get_grob_property ("position-callbacks");
773 for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
774 gh_call1 (ly_car (i), smob);
777 set_stem_lengths (me);
778 return SCM_UNSPECIFIED;
781 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
783 Beam::least_squares (SCM smob)
785 Grob *me = unsmob_grob (smob);
787 int count = visible_stem_count (me);
792 me->set_grob_property ("positions", ly_interval2scm (pos));
793 return SCM_UNSPECIFIED;
797 Array<Real> x_posns ;
798 Link_array<Grob> stems=
799 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
800 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
801 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
803 Real my_y = me->relative_coordinate (commony, Y_AXIS);
805 Grob *fvs = first_visible_stem (me);
806 Grob *lvs = last_visible_stem (me);
808 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
809 + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
810 Stem::get_stem_info (lvs).ideal_y_
811 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
813 Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
814 for (int i=0; i < stems.size (); i++)
818 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
821 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
829 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
830 Stem::chord_start_y (last_visible_stem (me)));
832 /* Simple beams (2 stems) on middle line should be allowed to be
835 However, if both stems reach middle line,
836 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
838 For that case, we apply artificial slope */
839 if (!ideal[LEFT] && chord.delta () && count == 2)
842 Direction d = (Direction) (sign (chord.delta ()) * UP);
843 pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
857 Array<Offset> ideals;
858 for (int i=0; i < stems.size (); i++)
861 if (Stem::invisible_b (s))
863 ideals.push (Offset (x_posns[i],
864 Stem::get_stem_info (s).ideal_y_
865 + s->relative_coordinate (commony, Y_AXIS)
868 minimise_least_squares (&dydx, &y, ideals);
871 me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
872 pos = Interval (y, (y+dy));
875 me->set_grob_property ("positions", ly_interval2scm (pos));
877 return SCM_UNSPECIFIED;
882 We can't combine with previous function, since check concave and
883 slope damping comes first.
885 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
887 Beam::shift_region_to_valid (SCM grob)
889 Grob *me = unsmob_grob (grob);
893 Array<Real> x_posns ;
894 Link_array<Grob> stems=
895 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
896 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
897 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
899 Grob *fvs = first_visible_stem (me);
902 return SCM_UNSPECIFIED;
904 Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
905 for (int i=0; i < stems.size (); i++)
909 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
913 Grob *lvs = last_visible_stem (me);
915 return SCM_UNSPECIFIED;
917 Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
919 Interval pos = ly_scm2interval ( me->get_grob_property ("positions"));
920 Real dy = pos.delta();
926 Shift the positions so that we have a chance of finding good
927 quants (i.e. no short stem failures.)
929 Interval feasible_left_point;
930 feasible_left_point.set_full ();
931 for (int i=0; i < stems.size (); i++)
934 if (Stem::invisible_b (s))
937 Direction d = Stem::get_direction (s);
940 Stem::get_stem_info (s).shortest_y_
941 - dydx * x_posns [i];
944 left_y is now relative to the stem S. We want relative to
945 ourselves, so translate:
948 + s->relative_coordinate (commony, Y_AXIS)
949 - me->relative_coordinate (commony, Y_AXIS);
955 feasible_left_point.intersect (flp);
958 if (feasible_left_point.empty_b())
960 warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
962 else if (!feasible_left_point.elem_b(y))
964 if (isinf (feasible_left_point[DOWN]))
965 y = feasible_left_point[UP] - REGION_SIZE;
966 else if (isinf (feasible_left_point[UP]))
967 y = feasible_left_point[DOWN]+ REGION_SIZE;
969 y = feasible_left_point.center ();
971 pos = Interval (y, (y+dy));
972 me->set_grob_property ("positions", ly_interval2scm (pos));
973 return SCM_UNSPECIFIED;
977 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
979 Beam::check_concave (SCM smob)
981 Grob *me = unsmob_grob (smob);
983 Link_array<Grob> stems =
984 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
986 for (int i = 0; i < stems.size ();)
988 if (Stem::invisible_b (stems[i]))
994 if (stems.size () < 3)
995 return SCM_UNSPECIFIED;
998 /* Concaveness #1: If distance of an inner notehead to line between
999 two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
1000 beam is concave (Heinz Stolba).
1002 In the case of knees, the line connecting outer heads is often
1003 not related to the beam slope (it may even go in the other
1004 direction). Skip the check when the outer stems point in
1005 different directions. --hwn
1008 bool concaveness1 = false;
1009 SCM gap = me->get_grob_property ("concaveness-gap");
1010 if (gh_number_p (gap)
1011 && Stem::get_direction(stems.top ())
1012 == Stem::get_direction(stems[0]))
1014 Real r1 = gh_scm2double (gap);
1015 Real dy = Stem::chord_start_y (stems.top ())
1016 - Stem::chord_start_y (stems[0]);
1019 Real slope = dy / (stems.size () - 1);
1021 Real y0 = Stem::chord_start_y (stems[0]);
1022 for (int i = 1; i < stems.size () - 1; i++)
1024 Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
1027 concaveness1 = true;
1034 /* Concaveness #2: Sum distances of inner noteheads that fall
1035 outside the interval of the two outer noteheads.
1037 We only do this for beams where first and last stem have the same
1041 Note that "convex" stems compensate for "concave" stems.
1042 (is that intentional?) --hwn.
1045 Real concaveness2 = 0;
1046 SCM thresh = me->get_grob_property ("concaveness-threshold");
1047 Real r2 = infinity_f;
1048 if (!concaveness1 && gh_number_p (thresh)
1049 && Stem::get_direction(stems.top ())
1050 == Stem::get_direction(stems[0]))
1052 r2 = gh_scm2double (thresh);
1054 Direction dir = Stem::get_direction(stems.top ());
1056 Interval iv (Stem::chord_start_y (stems[0]),
1057 Stem::chord_start_y (stems.top ()));
1059 if (iv[MAX] < iv[MIN])
1062 for (int i = 1; i < stems.size () - 1; i++)
1064 Real f = Stem::chord_start_y (stems[i]);
1065 concave += ((f - iv[MAX] ) >? 0) +
1066 ((f - iv[MIN] ) <? 0);
1069 concaveness2 = concave / (stems.size () - 2);
1071 /* ugh: this is the a kludge to get
1072 input/regression/beam-concave.ly to behave as
1076 huh? we're dividing twice (which is not scalable) meaning that
1077 the longer the beam, the more unlikely it will be
1078 concave. Maybe you would even expect the other way around??
1083 concaveness2 /= (stems.size () - 2);
1086 /* TODO: some sort of damping iso -> plain horizontal */
1087 if (concaveness1 || concaveness2 > r2)
1089 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1090 Real r = pos.linear_combination (0);
1091 me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
1092 me->set_grob_property ("least-squares-dy", gh_double2scm (0));
1095 return SCM_UNSPECIFIED;
1098 /* This neat trick is by Werner Lemberg,
1099 damped = tanh (slope)
1100 corresponds with some tables in [Wanske] CHECKME */
1101 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1103 Beam::slope_damping (SCM smob)
1105 Grob *me = unsmob_grob (smob);
1107 if (visible_stem_count (me) <= 1)
1108 return SCM_UNSPECIFIED;
1110 SCM s = me->get_grob_property ("damping");
1111 int damping = gh_scm2int (s);
1115 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1116 Real dy = pos.delta ();
1118 Grob *fvs = first_visible_stem (me);
1119 Grob *lvs = last_visible_stem (me);
1121 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1124 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1125 - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1126 Real dydx = dy && dx ? dy/dx : 0;
1127 dydx = 0.6 * tanh (dydx) / damping;
1129 Real damped_dy = dydx * dx;
1130 pos[LEFT] += (dy - damped_dy) / 2;
1131 pos[RIGHT] -= (dy - damped_dy) / 2;
1133 me->set_grob_property ("positions", ly_interval2scm (pos));
1135 return SCM_UNSPECIFIED;
1139 Report slice containing the numbers that are both in (car BEAMING)
1143 where_are_the_whole_beams(SCM beaming)
1147 for( SCM s = gh_car (beaming); gh_pair_p (s) ; s = gh_cdr (s))
1149 if (scm_memq (gh_car (s), gh_cdr (beaming)) != SCM_BOOL_F)
1151 l.add_point (gh_scm2int (gh_car (s)));
1157 /* Return the Y position of the stem-end, given the Y-left, Y-right
1158 in POS for stem S. This Y position is relative to S. */
1160 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1162 Interval pos, bool french)
1164 Real beam_translation = get_beam_translation (me);
1167 Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1168 Real dy = pos.delta ();
1170 Real stem_y_beam0 = (dy && dx
1175 Direction my_dir = Directional_element_interface::get (s);
1176 SCM beaming = s->get_grob_property ("beaming");
1178 Real stem_y = stem_y_beam0;
1181 Slice bm = where_are_the_whole_beams (beaming);
1183 stem_y += beam_translation * bm[-my_dir];
1187 Slice bm = Stem::beam_multiplicity(s);
1189 stem_y +=bm[my_dir] * beam_translation;
1192 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1193 - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1199 Hmm. At this time, beam position and slope are determined. Maybe,
1200 stem directions and length should set to relative to the chord's
1201 position of the beam. */
1203 Beam::set_stem_lengths (Grob *me)
1205 Link_array<Grob> stems=
1206 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1208 if (stems.size () <= 1)
1212 for (int a = 2; a--;)
1213 common[a] = common_refpoint_of_array (stems, me, Axis(a));
1215 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1216 Real staff_space = Staff_symbol_referencer::staff_space (me);
1218 bool french = to_boolean (me->get_grob_property ("french-beaming"));
1223 if (gh_number_p (me->get_grob_property ("gap"))
1224 &&gh_scm2double (me->get_grob_property ("gap")))
1227 thick = get_thickness(me);
1230 // ugh -> use commonx
1231 Grob * fvs = first_visible_stem (me);
1232 Grob *lvs = last_visible_stem (me);
1234 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1235 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1237 for (int i=0; i < stems.size (); i++)
1240 if (Stem::invisible_b (s))
1243 Real stem_y = calc_stem_y (me, s, common,
1245 pos, french && i > 0&& (i < stems.size () -1));
1248 Make the stems go up to the end of the beam. This doesn't matter
1249 for normal beams, but for tremolo beams it looks silly otherwise.
1252 stem_y += thick * 0.5 * Directional_element_interface::get(s);
1254 Stem::set_stemend (s, 2* stem_y / staff_space);
1259 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1261 Link_array<Grob> stems=
1262 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1265 for (int i=0; i < stems.size (); i++)
1268 Don't overwrite user settings.
1273 /* Don't set beaming for outside of outer stems */
1274 if ((d == LEFT && i == 0)
1275 ||(d == RIGHT && i == stems.size () -1))
1278 Grob *st = stems[i];
1279 SCM beaming_prop = st->get_grob_property ("beaming");
1280 if (beaming_prop == SCM_EOL ||
1281 index_get_cell (beaming_prop, d) == SCM_EOL)
1283 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1285 && i < stems.size() -1
1286 && Stem::invisible_b (st))
1287 b = b <? beaming->infos_.elem(i).beams_i_drul_[-d];
1289 Stem::set_beaming (st, b, d);
1292 while (flip (&d) != LEFT);
1297 Beam::forced_stem_count (Grob *me)
1299 Link_array<Grob>stems =
1300 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1302 for (int i=0; i < stems.size (); i++)
1306 if (Stem::invisible_b (s))
1309 /* I can imagine counting those boundaries as a half forced stem,
1310 but let's count them full for now. */
1311 if (abs (Stem::chord_start_y (s)) > 0.1
1312 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1322 Beam::visible_stem_count (Grob *me)
1324 Link_array<Grob>stems =
1325 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1327 for (int i = stems.size (); i--;)
1329 if (!Stem::invisible_b (stems[i]))
1336 Beam::first_visible_stem (Grob *me)
1338 Link_array<Grob>stems =
1339 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1341 for (int i = 0; i < stems.size (); i++)
1343 if (!Stem::invisible_b (stems[i]))
1350 Beam::last_visible_stem (Grob *me)
1352 Link_array<Grob>stems =
1353 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1354 for (int i = stems.size (); i--;)
1356 if (!Stem::invisible_b (stems[i]))
1366 handle rest under beam (do_post: beams are calculated now)
1367 what about combination of collisions and rest under beam.
1371 rest -> stem -> beam -> interpolate_y_position ()
1373 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1375 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1377 Grob *rest = unsmob_grob (element_smob);
1378 Axis a = (Axis) gh_scm2int (axis);
1380 assert (a == Y_AXIS);
1382 Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1385 return gh_double2scm (0.0);
1386 Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1388 || !Beam::has_interface (beam)
1389 || !Beam::visible_stem_count (beam))
1390 return gh_double2scm (0.0);
1392 Interval pos (0, 0);
1393 SCM s = beam->get_grob_property ("positions");
1394 if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1395 pos = ly_scm2interval (s);
1397 Real dy = pos.delta ();
1398 // ugh -> use commonx
1399 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1400 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1401 Real dydx = dy && dx ? dy/dx : 0;
1403 Direction d = Stem::get_direction (stem);
1404 Real stem_y = (pos[LEFT]
1405 + (stem->relative_coordinate (0, X_AXIS) - x0) * dydx)
1408 Real beam_translation = get_beam_translation (beam);
1409 Real beam_thickness = gh_scm2double (beam->get_grob_property ("thickness"));
1410 int beam_count = get_direction_beam_count (beam, d);
1411 Real height_of_my_beams = beam_thickness
1412 + (beam_count - 1) * beam_translation;
1413 Real beam_y = stem_y - height_of_my_beams + beam_thickness / 2.0;
1415 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1417 /* Better calculate relative-distance directly, rather than using
1419 Grob *common_x = rest->common_refpoint (beam, Y_AXIS);
1420 Real rest_dim = rest->extent (common_x, Y_AXIS)[d] / staff_space * d;
1422 Real minimum_distance = gh_scm2double
1423 (rest->get_grob_property ("minimum-beam-collision-distance"));
1425 Real distance = beam_y - rest_dim;
1428 shift = minimum_distance - distance;
1429 else if (minimum_distance > distance)
1430 shift = minimum_distance - distance;
1432 int stafflines = Staff_symbol_referencer::line_count (rest);
1434 /* Always move discretely by half spaces */
1435 Real discrete_shift = ceil (shift * 2.0) / 2.0;
1437 /* Inside staff, move by whole spaces*/
1438 if ((rest->extent (common_x, Y_AXIS)[d] + discrete_shift) * d
1440 ||(rest->extent (common_x, Y_AXIS)[-d] + discrete_shift) * -d
1442 discrete_shift = ceil (discrete_shift);
1444 return gh_double2scm (-d * discrete_shift);
1448 Beam::knee_b (Grob* me)
1450 SCM k = me->get_grob_property ("knee");
1451 if (gh_boolean_p (k))
1452 return gh_scm2bool (k);
1456 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
1458 Direction dir = Directional_element_interface::get
1459 (unsmob_grob (ly_car (s)));
1468 me->set_grob_property ("knee", gh_bool2scm (knee));
1474 Beam::get_direction_beam_count (Grob *me, Direction d )
1476 Link_array<Grob>stems =
1477 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1480 for (int i = stems.size (); i--;)
1483 Should we take invisible stems into account?
1485 if (Stem::get_direction (stems[i]) == d)
1486 bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1493 ADD_INTERFACE (Beam, "beam-interface",
1496 "#'thickness= weight of beams, in staffspace "
1499 "We take the least squares line through the ideal-length stems, and "
1500 "then damp that using "
1502 " damped = tanh (slope) \n"
1504 "this gives an unquantized left and right position for the beam end. "
1505 "Then we take all combinations of quantings near these left and right "
1506 "positions, and give them a score (according to how close they are to "
1507 "the ideal slope, how close the result is to the ideal stems, etc.). We "
1508 "take the best scoring combination. "
1510 "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");