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");
338 for (int i = 0; i< stems.size(); i++)
342 SCM this_beaming = st->get_grob_property ("beaming");
343 Real xposn = st->relative_coordinate (xcommon, X_AXIS);
344 Real stem_width = gh_scm2double (st->get_grob_property ("thickness")) *lt;
348 SCM left = gh_cdr (last_beaming);
349 SCM right = gh_car (this_beaming);
351 Array<int> fullbeams;
352 Array<int> lfliebertjes;
353 Array<int> rfliebertjes;
356 gh_pair_p (s); s =gh_cdr (s))
358 int b = gh_scm2int (gh_car (s));
359 if (scm_memq (gh_car(s), right) != SCM_BOOL_F)
365 lfliebertjes.push (b);
369 gh_pair_p (s); s =gh_cdr (s))
371 int b = gh_scm2int (gh_car (s));
372 if (scm_memq (gh_car(s), left) == SCM_BOOL_F)
374 rfliebertjes.push (b);
379 Real w = xposn - last_xposn;
380 Real stem_offset = 0.0;
381 Real width_corr = 0.0;
384 stem_offset -= last_width/2;
385 width_corr += last_width/2;
388 if (i == stems.size() -1)
390 width_corr += stem_width/2;
393 if (gh_number_p (gap))
395 Real g = gh_scm2double (gap);
400 Molecule whole = Lookup::beam (dydx, w + width_corr, thick);
401 for (int j = fullbeams.size(); j--;)
404 b.translate_axis (last_xposn - x0 + stem_offset, X_AXIS);
405 b.translate_axis (dydx * (last_xposn - x0) + bdy * fullbeams[j], Y_AXIS);
406 the_beam.add_molecule (b);
409 if (lfliebertjes.size() || rfliebertjes.size())
413 if (!Stem::first_head (st))
417 int t = Stem::duration_log (st);
419 SCM proc = me->get_grob_property ("flag-width-function");
420 SCM result = gh_call1 (proc, scm_int2num (t));
421 nw_f = gh_scm2double (result);
424 /* Half beam should be one note-width,
425 but let's make sure two half-beams never touch */
427 Real w = xposn - last_xposn;
430 Molecule half = Lookup::beam (dydx, w, thick);
431 for (int j = lfliebertjes.size(); j--;)
434 b.translate_axis (last_xposn - x0, X_AXIS);
435 b.translate_axis (dydx * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
436 the_beam.add_molecule (b);
438 for (int j = rfliebertjes.size(); j--;)
441 b.translate_axis (xposn - x0 - w , X_AXIS);
442 b.translate_axis (dydx * (xposn-x0 -w) + bdy * rfliebertjes[j], Y_AXIS);
443 the_beam.add_molecule (b);
449 last_width = stem_width;
450 last_beaming = this_beaming;
453 the_beam.translate_axis (x0 - me->relative_coordinate (xcommon, X_AXIS), X_AXIS);
454 the_beam.translate_axis (pos[LEFT], Y_AXIS);
459 This code prints the demerits for each beam. Perhaps this
460 should be switchable for those who want to twiddle with the
466 str += to_string (gh_scm2int (me->get_grob_property ("best-idx")));
469 str += to_string (gh_scm2double (me->get_grob_property ("quant-score")),
472 SCM properties = Font_interface::font_alist_chain (me);
475 Molecule tm = Text_item::text2molecule (me, scm_makfrom0str (str.to_str0 ()), properties);
476 the_beam.add_at_edge (Y_AXIS, UP, tm, 5.0);
482 return the_beam.smobbed_copy();
489 Beam::get_default_dir (Grob *me)
491 Drul_array<int> total;
492 total[UP] = total[DOWN] = 0;
493 Drul_array<int> count;
494 count[UP] = count[DOWN] = 0;
497 Link_array<Grob> stems=
498 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
500 for (int i=0; i <stems.size (); i++)
503 Direction sd = Directional_element_interface::get (s);
505 int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
506 int current = sd ? (1 + d * sd)/2 : center_distance;
513 } while (flip (&d) != DOWN);
515 SCM func = me->get_grob_property ("dir-function");
516 SCM s = gh_call2 (func,
517 gh_cons (scm_int2num (count[UP]),
518 scm_int2num (count[DOWN])),
519 gh_cons (scm_int2num (total[UP]),
520 scm_int2num (total[DOWN])));
522 if (gh_number_p (s) && gh_scm2int (s))
525 /* If dir is not determined: get default */
526 return to_dir (me->get_grob_property ("neutral-direction"));
530 /* Set all stems with non-forced direction to beam direction.
531 Urg: non-forced should become `without/with unforced' direction,
532 once stem gets cleaned-up. */
534 Beam::set_stem_directions (Grob *me, Direction d)
536 Link_array<Grob> stems
537 =Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
539 for (int i=0; i <stems.size (); i++)
543 SCM forcedir = s->get_grob_property ("direction");
544 if (!to_dir (forcedir))
545 Directional_element_interface::set (s, d);
550 A union of intervals in the real line.
552 Abysmal performance (quadratic) for large N, hopefully we don't have
553 that large N. In any case, this should probably be rewritten to use
558 Array<Interval> allowed_regions_;
567 allowed_regions_.clear();
570 allowed_regions_.push (s);
573 void remove_interval (Interval rm)
575 for (int i = 0; i < allowed_regions_.size(); )
579 s.intersect (allowed_regions_[i]);
583 Interval before = allowed_regions_[i];
584 Interval after = allowed_regions_[i];
586 before[RIGHT] = s[LEFT];
587 after[LEFT] = s[RIGHT];
589 if (!before.empty_b() && before.length () > 0.0)
591 allowed_regions_.insert (before, i);
594 allowed_regions_.del (i);
595 if (!after.empty_b () && after.length () > 0.0)
597 allowed_regions_.insert (after, i);
609 Only try horizontal beams for knees. No reliable detection of
610 anything else is possible here, since we don't know funky-beaming
611 settings, or X-distances (slopes!) People that want sloped
612 knee-beams, should set the directions manually.
615 Beam::consider_auto_knees (Grob* me)
617 SCM scm = me->get_grob_property ("auto-knee-gap");
618 if (!gh_number_p (scm))
621 Real threshold = gh_scm2double (scm);
627 Link_array<Grob> stems=
628 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
630 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
631 Real staff_space = Staff_symbol_referencer::staff_space (me);
633 Array<Interval> hps_array;
634 for (int i=0; i < stems.size (); i++)
636 Grob* stem = stems[i];
637 if (Stem::invisible_b (stem))
640 Interval hps = Stem::head_positions (stem);
645 hps *= staff_space * 0.5 ;
648 We could subtract beam Y position, but this routine only
649 sets stem directions, a constant shift does not have an
653 hps += stem->relative_coordinate (common, Y_AXIS);
655 if (to_dir (stem->get_grob_property ("direction")))
657 Direction stemdir = to_dir (stem->get_grob_property ("direction"));
658 hps[-stemdir] = - stemdir * infinity_f;
661 hps_array.push (hps);
663 gaps.remove_interval (hps);
667 Real max_gap_len =0.0;
669 for (int i = gaps.allowed_regions_.size() -1; i >= 0 ; i--)
671 Interval gap = gaps.allowed_regions_[i];
674 the outer gaps are not knees.
676 if (isinf (gap[LEFT]) || isinf(gap[RIGHT]))
679 if (gap.length () >= max_gap_len)
681 max_gap_len = gap.length();
686 if (max_gap_len > threshold)
689 for (int i = 0; i < stems.size(); i++)
691 Grob* stem = stems[i];
692 if (Stem::invisible_b (stem))
695 Interval hps = hps_array[j++];
698 Direction d = (hps.center () < max_gap.center()) ?
701 stem->set_grob_property ("direction", scm_int2num (d));
703 hps.intersect (max_gap);
704 assert (hps.empty_b () || hps.length () < 1e-6 );
711 /* Set stem's shorten property if unset.
714 take some y-position (chord/beam/nearest?) into account
715 scmify forced-fraction
717 This is done in beam because the shorten has to be uniform over the
722 Beam::set_stem_shorten (Grob *me)
725 shortening looks silly for x staff beams
730 Real forced_fraction = 1.0 * forced_stem_count (me)
731 / visible_stem_count (me);
733 int beam_count = get_beam_count (me);
735 SCM shorten_list = me->get_grob_property ("beamed-stem-shorten");
736 if (shorten_list == SCM_EOL)
739 Real staff_space = Staff_symbol_referencer::staff_space (me);
742 robust_list_ref (beam_count -1, shorten_list);
743 Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
745 /* your similar cute comment here */
746 shorten_f *= forced_fraction;
749 me->set_grob_property ("shorten", gh_double2scm (shorten_f));
752 /* Call list of y-dy-callbacks, that handle setting of
756 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
758 Beam::after_line_breaking (SCM smob)
760 Grob *me = unsmob_grob (smob);
762 /* Copy to mutable list. */
763 SCM s = ly_deep_copy (me->get_grob_property ("positions"));
764 me->set_grob_property ("positions", s);
766 if (ly_car (s) == SCM_BOOL_F)
769 // one wonders if such genericity is necessary --hwn.
770 SCM callbacks = me->get_grob_property ("position-callbacks");
771 for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
772 gh_call1 (ly_car (i), smob);
775 set_stem_lengths (me);
776 return SCM_UNSPECIFIED;
779 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
781 Beam::least_squares (SCM smob)
783 Grob *me = unsmob_grob (smob);
785 int count = visible_stem_count (me);
790 me->set_grob_property ("positions", ly_interval2scm (pos));
791 return SCM_UNSPECIFIED;
795 Array<Real> x_posns ;
796 Link_array<Grob> stems=
797 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
798 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
799 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
801 Real my_y = me->relative_coordinate (commony, Y_AXIS);
803 Grob *fvs = first_visible_stem (me);
804 Grob *lvs = last_visible_stem (me);
806 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
807 + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
808 Stem::get_stem_info (lvs).ideal_y_
809 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
811 Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
812 for (int i=0; i < stems.size (); i++)
816 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
819 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
827 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
828 Stem::chord_start_y (last_visible_stem (me)));
830 /* Simple beams (2 stems) on middle line should be allowed to be
833 However, if both stems reach middle line,
834 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
836 For that case, we apply artificial slope */
837 if (!ideal[LEFT] && chord.delta () && count == 2)
840 Direction d = (Direction) (sign (chord.delta ()) * UP);
841 pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
855 Array<Offset> ideals;
856 for (int i=0; i < stems.size (); i++)
859 if (Stem::invisible_b (s))
861 ideals.push (Offset (x_posns[i],
862 Stem::get_stem_info (s).ideal_y_
863 + s->relative_coordinate (commony, Y_AXIS)
866 minimise_least_squares (&dydx, &y, ideals);
869 me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
870 pos = Interval (y, (y+dy));
873 me->set_grob_property ("positions", ly_interval2scm (pos));
875 return SCM_UNSPECIFIED;
880 We can't combine with previous function, since check concave and
881 slope damping comes first.
883 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
885 Beam::shift_region_to_valid (SCM grob)
887 Grob *me = unsmob_grob (grob);
891 Array<Real> x_posns ;
892 Link_array<Grob> stems=
893 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
894 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
895 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
897 Grob *fvs = first_visible_stem (me);
900 return SCM_UNSPECIFIED;
902 Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
903 for (int i=0; i < stems.size (); i++)
907 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
911 Grob *lvs = last_visible_stem (me);
913 return SCM_UNSPECIFIED;
915 Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
917 Interval pos = ly_scm2interval ( me->get_grob_property ("positions"));
918 Real dy = pos.delta();
924 Shift the positions so that we have a chance of finding good
925 quants (i.e. no short stem failures.)
927 Interval feasible_left_point;
928 feasible_left_point.set_full ();
929 for (int i=0; i < stems.size (); i++)
932 if (Stem::invisible_b (s))
935 Direction d = Stem::get_direction (s);
938 Stem::get_stem_info (s).shortest_y_
939 - dydx * x_posns [i];
942 left_y is now relative to the stem S. We want relative to
943 ourselves, so translate:
946 + s->relative_coordinate (commony, Y_AXIS)
947 - me->relative_coordinate (commony, Y_AXIS);
953 feasible_left_point.intersect (flp);
956 if (feasible_left_point.empty_b())
958 warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
960 else if (!feasible_left_point.elem_b(y))
962 if (isinf (feasible_left_point[DOWN]))
963 y = feasible_left_point[UP] - REGION_SIZE;
964 else if (isinf (feasible_left_point[UP]))
965 y = feasible_left_point[DOWN]+ REGION_SIZE;
967 y = feasible_left_point.center ();
969 pos = Interval (y, (y+dy));
970 me->set_grob_property ("positions", ly_interval2scm (pos));
971 return SCM_UNSPECIFIED;
975 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
977 Beam::check_concave (SCM smob)
979 Grob *me = unsmob_grob (smob);
981 Link_array<Grob> stems =
982 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
984 for (int i = 0; i < stems.size ();)
986 if (Stem::invisible_b (stems[i]))
992 if (stems.size () < 3)
993 return SCM_UNSPECIFIED;
996 /* Concaveness #1: If distance of an inner notehead to line between
997 two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
998 beam is concave (Heinz Stolba).
1000 In the case of knees, the line connecting outer heads is often
1001 not related to the beam slope (it may even go in the other
1002 direction). Skip the check when the outer stems point in
1003 different directions. --hwn
1006 bool concaveness1 = false;
1007 SCM gap = me->get_grob_property ("concaveness-gap");
1008 if (gh_number_p (gap)
1009 && Stem::get_direction(stems.top ())
1010 == Stem::get_direction(stems[0]))
1012 Real r1 = gh_scm2double (gap);
1013 Real dy = Stem::chord_start_y (stems.top ())
1014 - Stem::chord_start_y (stems[0]);
1017 Real slope = dy / (stems.size () - 1);
1019 Real y0 = Stem::chord_start_y (stems[0]);
1020 for (int i = 1; i < stems.size () - 1; i++)
1022 Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
1025 concaveness1 = true;
1032 /* Concaveness #2: Sum distances of inner noteheads that fall
1033 outside the interval of the two outer noteheads.
1035 We only do this for beams where first and last stem have the same
1039 Note that "convex" stems compensate for "concave" stems.
1040 (is that intentional?) --hwn.
1043 Real concaveness2 = 0;
1044 SCM thresh = me->get_grob_property ("concaveness-threshold");
1045 Real r2 = infinity_f;
1046 if (!concaveness1 && gh_number_p (thresh)
1047 && Stem::get_direction(stems.top ())
1048 == Stem::get_direction(stems[0]))
1050 r2 = gh_scm2double (thresh);
1052 Direction dir = Stem::get_direction(stems.top ());
1054 Interval iv (Stem::chord_start_y (stems[0]),
1055 Stem::chord_start_y (stems.top ()));
1057 if (iv[MAX] < iv[MIN])
1060 for (int i = 1; i < stems.size () - 1; i++)
1062 Real f = Stem::chord_start_y (stems[i]);
1063 concave += ((f - iv[MAX] ) >? 0) +
1064 ((f - iv[MIN] ) <? 0);
1067 concaveness2 = concave / (stems.size () - 2);
1069 /* ugh: this is the a kludge to get
1070 input/regression/beam-concave.ly to behave as
1074 huh? we're dividing twice (which is not scalable) meaning that
1075 the longer the beam, the more unlikely it will be
1076 concave. Maybe you would even expect the other way around??
1081 concaveness2 /= (stems.size () - 2);
1084 /* TODO: some sort of damping iso -> plain horizontal */
1085 if (concaveness1 || concaveness2 > r2)
1087 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1088 Real r = pos.linear_combination (0);
1089 me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
1090 me->set_grob_property ("least-squares-dy", gh_double2scm (0));
1093 return SCM_UNSPECIFIED;
1096 /* This neat trick is by Werner Lemberg,
1097 damped = tanh (slope)
1098 corresponds with some tables in [Wanske] CHECKME */
1099 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1101 Beam::slope_damping (SCM smob)
1103 Grob *me = unsmob_grob (smob);
1105 if (visible_stem_count (me) <= 1)
1106 return SCM_UNSPECIFIED;
1108 SCM s = me->get_grob_property ("damping");
1109 int damping = gh_scm2int (s);
1113 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1114 Real dy = pos.delta ();
1116 Grob *fvs = first_visible_stem (me);
1117 Grob *lvs = last_visible_stem (me);
1119 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1122 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1123 - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1124 Real dydx = dy && dx ? dy/dx : 0;
1125 dydx = 0.6 * tanh (dydx) / damping;
1127 Real damped_dy = dydx * dx;
1128 pos[LEFT] += (dy - damped_dy) / 2;
1129 pos[RIGHT] -= (dy - damped_dy) / 2;
1131 me->set_grob_property ("positions", ly_interval2scm (pos));
1133 return SCM_UNSPECIFIED;
1137 Report slice containing the numbers that are both in (car BEAMING)
1141 where_are_the_whole_beams(SCM beaming)
1145 for( SCM s = gh_car (beaming); gh_pair_p (s) ; s = gh_cdr (s))
1147 if (scm_memq (gh_car (s), gh_cdr (beaming)) != SCM_BOOL_F)
1149 l.add_point (gh_scm2int (gh_car (s)));
1155 /* Return the Y position of the stem-end, given the Y-left, Y-right
1156 in POS for stem S. This Y position is relative to S. */
1158 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1160 Interval pos, bool french)
1162 Real beam_translation = get_beam_translation (me);
1165 Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1166 Real dy = pos.delta ();
1168 Real stem_y_beam0 = (dy && dx
1173 Direction my_dir = Directional_element_interface::get (s);
1174 SCM beaming = s->get_grob_property ("beaming");
1176 Real stem_y = stem_y_beam0;
1179 Slice bm = where_are_the_whole_beams (beaming);
1181 stem_y += beam_translation * bm[-my_dir];
1185 Slice bm = Stem::beam_multiplicity(s);
1187 stem_y +=bm[my_dir] * beam_translation;
1190 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1191 - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1197 Hmm. At this time, beam position and slope are determined. Maybe,
1198 stem directions and length should set to relative to the chord's
1199 position of the beam. */
1201 Beam::set_stem_lengths (Grob *me)
1203 Link_array<Grob> stems=
1204 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1206 if (stems.size () <= 1)
1210 for (int a = 2; a--;)
1211 common[a] = common_refpoint_of_array (stems, me, Axis(a));
1213 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1214 Real staff_space = Staff_symbol_referencer::staff_space (me);
1216 bool french = to_boolean (me->get_grob_property ("french-beaming"));
1221 if (gh_number_p (me->get_grob_property ("gap"))
1222 &&gh_scm2double (me->get_grob_property ("gap")))
1225 thick = get_thickness(me);
1228 // ugh -> use commonx
1229 Grob * fvs = first_visible_stem (me);
1230 Grob *lvs = last_visible_stem (me);
1232 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1233 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1235 for (int i=0; i < stems.size (); i++)
1238 if (Stem::invisible_b (s))
1241 Real stem_y = calc_stem_y (me, s, common,
1243 pos, french && i > 0&& (i < stems.size () -1));
1246 Make the stems go up to the end of the beam. This doesn't matter
1247 for normal beams, but for tremolo beams it looks silly otherwise.
1250 stem_y += thick * 0.5 * Directional_element_interface::get(s);
1252 Stem::set_stemend (s, 2* stem_y / staff_space);
1257 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1259 Link_array<Grob> stems=
1260 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1263 for (int i=0; i < stems.size (); i++)
1266 Don't overwrite user settings.
1271 /* Don't set beaming for outside of outer stems */
1272 if ((d == LEFT && i == 0)
1273 ||(d == RIGHT && i == stems.size () -1))
1277 SCM beaming_prop = stems[i]->get_grob_property ("beaming");
1278 if (beaming_prop == SCM_EOL ||
1279 index_get_cell (beaming_prop, d) == SCM_EOL)
1281 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1282 Stem::set_beaming (stems[i], b, d);
1285 while (flip (&d) != LEFT);
1290 Beam::forced_stem_count (Grob *me)
1292 Link_array<Grob>stems =
1293 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1295 for (int i=0; i < stems.size (); i++)
1299 if (Stem::invisible_b (s))
1302 /* I can imagine counting those boundaries as a half forced stem,
1303 but let's count them full for now. */
1304 if (abs (Stem::chord_start_y (s)) > 0.1
1305 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1315 Beam::visible_stem_count (Grob *me)
1317 Link_array<Grob>stems =
1318 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1320 for (int i = stems.size (); i--;)
1322 if (!Stem::invisible_b (stems[i]))
1329 Beam::first_visible_stem (Grob *me)
1331 Link_array<Grob>stems =
1332 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1334 for (int i = 0; i < stems.size (); i++)
1336 if (!Stem::invisible_b (stems[i]))
1343 Beam::last_visible_stem (Grob *me)
1345 Link_array<Grob>stems =
1346 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1347 for (int i = stems.size (); i--;)
1349 if (!Stem::invisible_b (stems[i]))
1359 handle rest under beam (do_post: beams are calculated now)
1360 what about combination of collisions and rest under beam.
1364 rest -> stem -> beam -> interpolate_y_position ()
1366 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1368 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1370 Grob *rest = unsmob_grob (element_smob);
1371 Axis a = (Axis) gh_scm2int (axis);
1373 assert (a == Y_AXIS);
1375 Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1378 return gh_double2scm (0.0);
1379 Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1381 || !Beam::has_interface (beam)
1382 || !Beam::visible_stem_count (beam))
1383 return gh_double2scm (0.0);
1385 Interval pos (0, 0);
1386 SCM s = beam->get_grob_property ("positions");
1387 if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1388 pos = ly_scm2interval (s);
1390 Real dy = pos.delta ();
1391 // ugh -> use commonx
1392 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1393 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1394 Real dydx = dy && dx ? dy/dx : 0;
1396 Direction d = Stem::get_direction (stem);
1397 Real stem_y = (pos[LEFT]
1398 + (stem->relative_coordinate (0, X_AXIS) - x0) * dydx)
1401 Real beam_translation = get_beam_translation (beam);
1402 Real beam_thickness = gh_scm2double (beam->get_grob_property ("thickness"));
1403 int beam_count = get_direction_beam_count (beam, d);
1404 Real height_of_my_beams = beam_thickness
1405 + (beam_count - 1) * beam_translation;
1406 Real beam_y = stem_y - height_of_my_beams + beam_thickness / 2.0;
1408 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1410 /* Better calculate relative-distance directly, rather than using
1412 Grob *common_x = rest->common_refpoint (beam, Y_AXIS);
1413 Real rest_dim = rest->extent (common_x, Y_AXIS)[d] / staff_space * d;
1415 Real minimum_distance = gh_scm2double
1416 (rest->get_grob_property ("minimum-beam-collision-distance"));
1418 Real distance = beam_y - rest_dim;
1421 shift = minimum_distance - distance;
1422 else if (minimum_distance > distance)
1423 shift = minimum_distance - distance;
1425 int stafflines = Staff_symbol_referencer::line_count (rest);
1427 /* Always move discretely by half spaces */
1428 Real discrete_shift = ceil (shift * 2.0) / 2.0;
1430 /* Inside staff, move by whole spaces*/
1431 if ((rest->extent (common_x, Y_AXIS)[d] + discrete_shift) * d
1433 ||(rest->extent (common_x, Y_AXIS)[-d] + discrete_shift) * -d
1435 discrete_shift = ceil (discrete_shift);
1437 return gh_double2scm (-d * discrete_shift);
1441 Beam::knee_b (Grob* me)
1443 SCM k = me->get_grob_property ("knee");
1444 if (gh_boolean_p (k))
1445 return gh_scm2bool (k);
1449 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
1451 Direction dir = Directional_element_interface::get
1452 (unsmob_grob (ly_car (s)));
1461 me->set_grob_property ("knee", gh_bool2scm (knee));
1467 Beam::get_direction_beam_count (Grob *me, Direction d )
1469 Link_array<Grob>stems =
1470 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1473 for (int i = stems.size (); i--;)
1476 Should we take invisible stems into account?
1478 if (Stem::get_direction (stems[i]) == d)
1479 bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1486 ADD_INTERFACE (Beam, "beam-interface",
1489 "#'thickness= weight of beams, in staffspace "
1492 "We take the least squares line through the ideal-length stems, and "
1493 "then damp that using "
1495 " damped = tanh (slope) \n"
1497 "this gives an unquantized left and right position for the beam end. "
1498 "Then we take all combinations of quantings near these left and right "
1499 "positions, and give them a score (according to how close they are to "
1500 "the ideal slope, how close the result is to the ideal stems, etc.). We "
1501 "take the best scoring combination. "
1503 "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");