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>
17 * Use Number_pair i.s.o Interval to represent (yl, yr).
19 - Determine auto knees based on positions if it's set by the user.
25 - Stems run to the Y-center of the beam.
27 - beam_space is the offset between Y centers of the beam.
32 #include <math.h> // tanh.
34 #include "molecule.hh"
35 #include "directional-element-interface.hh"
39 #include "least-squares.hh"
41 #include "paper-def.hh"
43 #include "group-interface.hh"
44 #include "staff-symbol-referencer.hh"
50 #define DEBUG_QUANTING 0
54 #include "text-item.hh" // debug output.
55 #include "font-interface.hh" // debug output.
60 Beam::add_stem (Grob *me, Grob *s)
62 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
64 s->add_dependency (me);
66 assert (!Stem::beam_l (s));
67 s->set_grob_property ("beam", me->self_scm ());
69 add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
73 Beam::get_beam_space (Grob *me)
75 SCM func = me->get_grob_property ("space-function");
76 SCM s = gh_call2 (func, me->self_scm (), gh_int2scm (get_beam_count (me)));
77 return gh_scm2double (s);
84 Beam::get_beam_count (Grob *me)
87 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
89 Grob *sc = unsmob_grob (ly_car (s));
91 m = m >? (Stem::beam_multiplicity (sc).length () + 1);
96 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
98 Beam::space_function (SCM smob, SCM beam_count)
100 Grob *me = unsmob_grob (smob);
102 Real staff_space = Staff_symbol_referencer::staff_space (me);
103 Real line = me->paper_l ()->get_var ("linethickness");
104 Real thickness = gh_scm2double (me->get_grob_property ("thickness"))
107 Real beam_space = gh_scm2int (beam_count) < 4
108 ? (2*staff_space + line - thickness) / 2.0
109 : (3*staff_space + line - thickness) / 3.0;
111 return gh_double2scm (beam_space);
115 /* After pre-processing all directions should be set.
116 Several post-processing routines (stem, slur, script) need stem/beam
118 Currenly, this means that beam has set all stem's directions.
119 [Alternatively, stems could set its own directions, according to
120 their beam, during 'final-pre-processing'.] */
121 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
123 Beam::before_line_breaking (SCM smob)
125 Grob *me = unsmob_grob (smob);
127 /* Beams with less than 2 two stems don't make much sense, but could happen
132 For a beam that only has one stem, we try to do some disappearance magic:
133 we revert the flag, and move on to The Eternal Engraving Fields. */
135 int count = visible_stem_count (me);
138 me->warning (_ ("beam has less than two visible stems"));
140 SCM stems = me->get_grob_property ("stems");
141 if (scm_ilength (stems) == 1)
143 me->warning (_ ("Beam has less than two stems. Removing beam."));
145 unsmob_grob (gh_car (stems))->remove_grob_property ("beam");
148 return SCM_UNSPECIFIED;
150 else if (scm_ilength (stems) == 0)
153 return SCM_UNSPECIFIED;
158 Direction d = get_default_dir (me);
160 consider_auto_knees (me, d);
161 set_stem_directions (me, d);
165 set_stem_shorten (me);
174 Beam::connect_beams (Grob *me)
176 Link_array<Grob> stems=
177 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
180 last_int.set_empty();
181 for (int i = 0; i< stems.size(); i++)
183 Grob *this_stem = stems[i];
184 SCM this_beaming = this_stem->get_grob_property ("beaming");
186 Direction this_dir = Directional_element_interface::get(this_stem);
189 int start_point = last_int [this_dir];
195 if (d == RIGHT && i == stems.size()-1)
198 new_slice.set_empty();
199 SCM s = index_get_cell (this_beaming, d);
200 for (; gh_pair_p (s); s = gh_cdr (s))
203 start_point - this_dir * gh_scm2int (gh_car (s));
205 new_slice.add_point (new_beam_pos);
206 gh_set_car_x (s, gh_int2scm (new_beam_pos));
209 while (flip (&d) != LEFT);
211 if (!new_slice.empty_b())
212 last_int = new_slice;
216 SCM s = gh_cdr (this_beaming);
217 for (; gh_pair_p (s); s = gh_cdr (s))
219 int np = - this_dir * gh_scm2int (gh_car(s));
220 gh_set_car_x (s, gh_int2scm (np));
221 last_int.add_point (np);
227 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
229 Beam::brew_molecule (SCM grob)
231 Grob *me = unsmob_grob (grob);
232 Link_array<Grob> stems=
233 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
234 Grob* xcommon = common_refpoint_of_array (stems, me, X_AXIS);
237 if (visible_stem_count (me))
239 // ugh -> use commonx
240 x0 = first_visible_stem (me)->relative_coordinate (xcommon, X_AXIS);
241 dx = last_visible_stem (me)->relative_coordinate (xcommon, X_AXIS) - x0;
245 x0 = stems[0]->relative_coordinate (xcommon, X_AXIS);
246 dx = stems.top ()->relative_coordinate (xcommon, X_AXIS) - x0;
249 SCM posns = me->get_grob_property ("positions");
251 if (!ly_number_pair_p (posns))
253 programming_error ("No beam posns");
254 pos = Interval (0,0);
257 pos= ly_scm2interval (posns);
259 Real dy = pos.delta ();
260 Real dydx = dy && dx ? dy/dx : 0;
262 Real thick = gh_scm2double (me->get_grob_property ("thickness"));
263 Real bdy = get_beam_space (me);
265 SCM last_beaming = SCM_EOL;;
266 Real last_xposn = -1;
267 Real last_width = -1 ;
270 SCM gap = me->get_grob_property ("gap");
272 Real lt = me->paper_l ()->get_var ("linethickness");
273 for (int i = 0; i< stems.size(); i++)
277 SCM this_beaming = st->get_grob_property ("beaming");
278 Real xposn = st->relative_coordinate (xcommon, X_AXIS);
279 Real stem_width = gh_scm2double (st->get_grob_property ("thickness")) *lt;
283 SCM left = gh_cdr (last_beaming);
284 SCM right = gh_car (this_beaming);
286 Array<int> fullbeams;
287 Array<int> lfliebertjes;
288 Array<int> rfliebertjes;
291 gh_pair_p (s); s =gh_cdr (s))
293 int b = gh_scm2int (gh_car (s));
294 if (scm_memq (gh_car(s), right) != SCM_BOOL_F)
300 lfliebertjes.push (b);
304 gh_pair_p (s); s =gh_cdr (s))
306 int b = gh_scm2int (gh_car (s));
307 if (scm_memq (gh_car(s), left) == SCM_BOOL_F)
309 rfliebertjes.push (b);
314 Real w = xposn - last_xposn;
315 Real stem_offset = 0.0;
316 Real width_corr = 0.0;
319 stem_offset -= last_width/2;
320 width_corr += last_width/2;
323 if (i == stems.size() -1)
325 width_corr += stem_width/2;
328 if (gh_number_p (gap))
330 Real g = gh_scm2double (gap);
335 Molecule whole = Lookup::beam (dydx, w + width_corr, thick);
336 for (int j = fullbeams.size(); j--;)
339 b.translate_axis (last_xposn - x0 + stem_offset, X_AXIS);
340 b.translate_axis (dydx * (last_xposn - x0) + bdy * fullbeams[j], Y_AXIS);
341 the_beam.add_molecule (b);
344 if (lfliebertjes.size() || rfliebertjes.size())
348 if (!Stem::first_head (st))
352 int t = Stem::duration_log (st);
354 SCM proc = me->get_grob_property ("flag-width-function");
355 SCM result = gh_call1 (proc, gh_int2scm (t));
356 nw_f = gh_scm2double (result);
359 /* Half beam should be one note-width,
360 but let's make sure two half-beams never touch */
362 Real w = xposn - last_xposn;
365 Molecule half = Lookup::beam (dydx, w, thick);
366 for (int j = lfliebertjes.size(); j--;)
369 b.translate_axis (last_xposn - x0, X_AXIS);
370 b.translate_axis (dydx * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
371 the_beam.add_molecule (b);
373 for (int j = rfliebertjes.size(); j--;)
376 b.translate_axis (xposn - x0 - w , X_AXIS);
377 b.translate_axis (dydx * (xposn-x0 -w) + bdy * rfliebertjes[j], Y_AXIS);
378 the_beam.add_molecule (b);
384 last_width = stem_width;
385 last_beaming = this_beaming;
388 the_beam.translate_axis (x0 - me->relative_coordinate (xcommon, X_AXIS), X_AXIS);
389 the_beam.translate_axis (pos[LEFT], Y_AXIS);
394 This code prints the demerits for each beam. Perhaps this
395 should be switchable for those who want to twiddle with the
401 str += to_str (gh_scm2int (me->get_grob_property ("best-idx")));
404 str += to_str (gh_scm2double (me->get_grob_property ("quant-score")),
407 SCM properties = Font_interface::font_alist_chain (me);
410 Molecule tm = Text_item::text2molecule (me, ly_str02scm (str.ch_C ()), properties);
411 the_beam.add_at_edge (Y_AXIS, UP, tm, 5.0);
417 return the_beam.smobbed_copy();
424 Beam::get_default_dir (Grob *me)
426 Drul_array<int> total;
427 total[UP] = total[DOWN] = 0;
428 Drul_array<int> count;
429 count[UP] = count[DOWN] = 0;
432 Link_array<Item> stems=
433 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
435 for (int i=0; i <stems.size (); i++)
438 Direction sd = Directional_element_interface::get (s);
440 int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
441 int current = sd ? (1 + d * sd)/2 : center_distance;
448 } while (flip (&d) != DOWN);
450 SCM func = me->get_grob_property ("dir-function");
451 SCM s = gh_call2 (func,
452 gh_cons (gh_int2scm (count[UP]),
453 gh_int2scm (count[DOWN])),
454 gh_cons (gh_int2scm (total[UP]),
455 gh_int2scm (total[DOWN])));
457 if (gh_number_p (s) && gh_scm2int (s))
460 /* If dir is not determined: get default */
461 return to_dir (me->get_grob_property ("neutral-direction"));
465 /* Set all stems with non-forced direction to beam direction.
466 Urg: non-forced should become `without/with unforced' direction,
467 once stem gets cleaned-up. */
469 Beam::set_stem_directions (Grob *me, Direction d)
471 Link_array<Item> stems
472 =Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
474 for (int i=0; i <stems.size (); i++)
477 SCM force = s->remove_grob_property ("dir-forced");
478 if (!gh_boolean_p (force) || !gh_scm2bool (force))
479 Directional_element_interface::set (s, d);
483 /* Simplistic auto-knees; only consider vertical gap between two
486 `Forced' stem directions are ignored. If you don't want auto-knees,
487 don't set, or unset auto-knee-gap. */
489 Beam::consider_auto_knees (Grob *me, Direction d)
491 SCM scm = me->get_grob_property ("auto-knee-gap");
493 if (gh_number_p (scm))
497 Real staff_space = Staff_symbol_referencer::staff_space (me);
498 Real gap = gh_scm2double (scm) / staff_space;
500 Link_array<Grob> stems=
501 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
503 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
506 for (int i=1; i < stems.size (); i++)
508 if (!Stem::invisible_b (stems[i-1]))
510 if (Stem::invisible_b (stems[l]))
512 if (Stem::invisible_b (stems[i]))
515 Real left = Stem::extremal_heads (stems[l])[d]
516 ->relative_coordinate (common, Y_AXIS);
517 Real right = Stem::extremal_heads (stems[i])[-d]
518 ->relative_coordinate (common, Y_AXIS);
520 Real dy = right - left;
524 knee_y = (right + left) / 2;
532 for (int i=0; i < stems.size (); i++)
535 if (Stem::invisible_b (s) ||
536 s->get_grob_property ("dir-forced") == SCM_BOOL_T)
538 Real y = Stem::extremal_heads (stems[i])[d]
539 ->relative_coordinate (common, Y_AXIS);
541 Directional_element_interface::set (s, y < knee_y ? UP : DOWN);
542 s->set_grob_property ("dir-forced", SCM_BOOL_T);
548 /* Set stem's shorten property if unset.
551 take some y-position (chord/beam/nearest?) into account
552 scmify forced-fraction
556 why is shorten stored in beam, and not directly in stem?
560 Beam::set_stem_shorten (Grob *m)
562 Spanner*me = dynamic_cast<Spanner*> (m);
564 Real forced_fraction = forced_stem_count (me) / visible_stem_count (me);
566 int beam_count = get_beam_count (me);
568 SCM shorten = me->get_grob_property ("beamed-stem-shorten");
569 if (shorten == SCM_EOL)
572 int sz = scm_ilength (shorten);
574 Real staff_space = Staff_symbol_referencer::staff_space (me);
575 SCM shorten_elt = scm_list_ref (shorten,
576 gh_int2scm (beam_count <? (sz - 1)));
577 Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
579 /* your similar cute comment here */
580 shorten_f *= forced_fraction;
583 me->set_grob_property ("shorten", gh_double2scm (shorten_f));
586 /* Call list of y-dy-callbacks, that handle setting of
590 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
592 Beam::after_line_breaking (SCM smob)
594 Grob *me = unsmob_grob (smob);
596 /* Copy to mutable list. */
597 SCM s = ly_deep_copy (me->get_grob_property ("positions"));
598 me->set_grob_property ("positions", s);
600 if (ly_car (s) == SCM_BOOL_F)
603 // one wonders if such genericity is necessary --hwn.
604 SCM callbacks = me->get_grob_property ("position-callbacks");
605 for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
606 gh_call1 (ly_car (i), smob);
609 set_stem_lengths (me);
610 return SCM_UNSPECIFIED;
613 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
615 Beam::least_squares (SCM smob)
617 Grob *me = unsmob_grob (smob);
619 int count = visible_stem_count (me);
624 me->set_grob_property ("positions", ly_interval2scm (pos));
625 return SCM_UNSPECIFIED;
629 Array<Real> x_posns ;
630 Link_array<Grob> stems=
631 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
632 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
633 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
635 Real my_y = me->relative_coordinate (commony, Y_AXIS);
637 Grob *fvs = first_visible_stem (me);
638 Grob *lvs = last_visible_stem (me);
640 Interval ideal (Stem::calc_stem_info (fvs).ideal_y_
641 + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
642 Stem::calc_stem_info (lvs).ideal_y_
643 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
645 Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
646 for (int i=0; i < stems.size (); i++)
650 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
653 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
661 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
662 Stem::chord_start_y (last_visible_stem (me)));
666 TODO -- use scoring for this.
668 complicated, because we take stem-info.ideal for determining
671 /* Make simple beam on middle line have small tilt */
672 if (!ideal[LEFT] && chord.delta () && count == 2)
678 Direction d = (Direction) (sign (chord.delta ()) * UP);
679 pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
694 Array<Offset> ideals;
695 for (int i=0; i < stems.size (); i++)
698 if (Stem::invisible_b (s))
700 ideals.push (Offset (x_posns[i],
701 Stem::calc_stem_info (s).ideal_y_
702 + s->relative_coordinate (commony, Y_AXIS)
705 minimise_least_squares (&dydx, &y, ideals);
708 me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
709 pos = Interval (y, (y+dy));
712 me->set_grob_property ("positions", ly_interval2scm (pos));
714 return SCM_UNSPECIFIED;
719 We can't combine with previous function, since check concave and
720 slope damping comes first.
722 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
724 Beam::shift_region_to_valid (SCM grob)
726 Grob *me = unsmob_grob (grob);
730 Array<Real> x_posns ;
731 Link_array<Grob> stems=
732 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
733 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
734 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
736 Grob *fvs = first_visible_stem (me);
739 return SCM_UNSPECIFIED;
741 Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
742 for (int i=0; i < stems.size (); i++)
746 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
750 Grob *lvs = last_visible_stem (me);
752 return SCM_UNSPECIFIED;
754 Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
756 Interval pos = ly_scm2interval ( me->get_grob_property ("positions"));
757 Real dy = pos.delta();
763 Shift the positions so that we have a chance of finding good
764 quants (i.e. no short stem failures.)
766 Interval feasible_left_point;
767 feasible_left_point.set_full ();
768 for (int i=0; i < stems.size (); i++)
771 if (Stem::invisible_b (s))
774 Direction d = Stem::get_direction (s);
777 Stem::calc_stem_info (s).shortest_y_
778 - dydx * x_posns [i];
781 left_y is now relative to the stem S. We want relative to
782 ourselves, so translate:
785 + s->relative_coordinate (commony, Y_AXIS)
786 - me->relative_coordinate (commony, Y_AXIS);
792 feasible_left_point.intersect (flp);
795 if (feasible_left_point.empty_b())
797 warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
799 else if (!feasible_left_point.elem_b(y))
801 if (isinf (feasible_left_point[DOWN]))
802 y = feasible_left_point[UP] - REGION_SIZE;
803 else if (isinf (feasible_left_point[UP]))
804 y = feasible_left_point[DOWN]+ REGION_SIZE;
806 y = feasible_left_point.center ();
808 pos = Interval (y, (y+dy));
809 me->set_grob_property ("positions", ly_interval2scm (pos));
810 return SCM_UNSPECIFIED;
814 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
816 Beam::check_concave (SCM smob)
818 Grob *me = unsmob_grob (smob);
820 Link_array<Grob> stems =
821 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
823 for (int i = 0; i < stems.size ();)
825 if (Stem::invisible_b (stems[i]))
831 if (stems.size () < 3)
832 return SCM_UNSPECIFIED;
835 /* Concaveness #1: If distance of an inner notehead to line between
836 two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
837 beam is concave (Heinz Stolba).
839 In the case of knees, the line connecting outer heads is often
840 not related to the beam slope (it may even go in the other
841 direction). Skip the check when the outer stems point in
842 different directions. --hwn
845 bool concaveness1 = false;
846 SCM gap = me->get_grob_property ("concaveness-gap");
847 if (gh_number_p (gap)
848 && Stem::get_direction(stems.top ())
849 == Stem::get_direction(stems[0]))
851 Real r1 = gh_scm2double (gap);
852 Real dy = Stem::chord_start_y (stems.top ())
853 - Stem::chord_start_y (stems[0]);
856 Real slope = dy / (stems.size () - 1);
858 Real y0 = Stem::chord_start_y (stems[0]);
859 for (int i = 1; i < stems.size () - 1; i++)
861 Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
871 /* Concaveness #2: Sum distances of inner noteheads that fall
872 outside the interval of the two outer noteheads.
874 We only do this for beams where first and last stem have the same
878 Note that "convex" stems compensate for "concave" stems.
879 (is that intentional?) --hwn.
882 Real concaveness2 = 0;
883 SCM thresh = me->get_grob_property ("concaveness-threshold");
884 Real r2 = infinity_f;
885 if (!concaveness1 && gh_number_p (thresh)
886 && Stem::get_direction(stems.top ())
887 == Stem::get_direction(stems[0]))
889 r2 = gh_scm2double (thresh);
891 Direction dir = Stem::get_direction(stems.top ());
893 Interval iv (Stem::chord_start_y (stems[0]),
894 Stem::chord_start_y (stems.top ()));
896 if (iv[MAX] < iv[MIN])
899 for (int i = 1; i < stems.size () - 1; i++)
901 Real f = Stem::chord_start_y (stems[i]);
902 concave += ((f - iv[MAX] ) >? 0) +
903 ((f - iv[MIN] ) <? 0);
906 concaveness2 = concave / (stems.size () - 2);
908 /* ugh: this is the a kludge to get
909 input/regression/beam-concave.ly to behave as
913 huh? we're dividing twice (which is not scalable) meaning that
914 the longer the beam, the more unlikely it will be
915 concave. Maybe you would even expect the other way around??
920 concaveness2 /= (stems.size () - 2);
923 /* TODO: some sort of damping iso -> plain horizontal */
924 if (concaveness1 || concaveness2 > r2)
926 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
927 Real r = pos.linear_combination (0);
928 me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
929 me->set_grob_property ("least-squares-dy", gh_double2scm (0));
932 return SCM_UNSPECIFIED;
935 /* This neat trick is by Werner Lemberg,
936 damped = tanh (slope)
937 corresponds with some tables in [Wanske] CHECKME */
938 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
940 Beam::slope_damping (SCM smob)
942 Grob *me = unsmob_grob (smob);
944 if (visible_stem_count (me) <= 1)
945 return SCM_UNSPECIFIED;
947 SCM s = me->get_grob_property ("damping");
948 int damping = gh_scm2int (s);
952 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
953 Real dy = pos.delta ();
955 Grob *fvs = first_visible_stem (me);
956 Grob *lvs = last_visible_stem (me);
958 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
961 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
962 - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
963 Real dydx = dy && dx ? dy/dx : 0;
964 dydx = 0.6 * tanh (dydx) / damping;
966 Real damped_dy = dydx * dx;
967 pos[LEFT] += (dy - damped_dy) / 2;
968 pos[RIGHT] -= (dy - damped_dy) / 2;
970 me->set_grob_property ("positions", ly_interval2scm (pos));
972 return SCM_UNSPECIFIED;
976 where_are_the_whole_beams(SCM beaming)
980 for( SCM s = gh_car (beaming); gh_pair_p (s) ; s = gh_cdr (s))
982 if (scm_memq (gh_car (s), gh_cdr (beaming)) != SCM_BOOL_F)
984 l.add_point (gh_scm2int (gh_car (s)));
991 Calculate the Y position of the stem-end, given the Y-left, Y-right
992 in POS for stem S. This Y position is relative to S.
995 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
997 Interval pos, bool french)
999 Real beam_space = get_beam_space (me);
1002 Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1003 Real dy = pos.delta ();
1005 Real stem_y_beam0 = (dy && dx
1010 Direction my_dir = Directional_element_interface::get (s);
1011 SCM beaming = s->get_grob_property ("beaming");
1013 Real stem_y = stem_y_beam0;
1016 Slice bm = where_are_the_whole_beams (beaming);
1018 stem_y += beam_space * bm[-my_dir];
1022 Slice bm = Stem::beam_multiplicity(s);
1024 stem_y +=bm[my_dir] * beam_space;
1027 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1028 - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1034 Hmm. At this time, beam position and slope are determined. Maybe,
1035 stem directions and length should set to relative to the chord's
1036 position of the beam. */
1038 Beam::set_stem_lengths (Grob *me)
1040 Link_array<Grob> stems=
1041 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1043 if (stems.size () <= 1)
1047 for (int a = 2; a--;)
1048 common[a] = common_refpoint_of_array (stems, me, Axis(a));
1050 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1051 Real staff_space = Staff_symbol_referencer::staff_space (me);
1053 bool french = to_boolean (me->get_grob_property ("french-beaming"));
1056 // ugh -> use commonx
1057 Grob * fvs = first_visible_stem (me);
1058 Grob *lvs = last_visible_stem (me);
1060 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1061 Real xr = fvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1063 for (int i=0; i < stems.size (); i++)
1066 if (Stem::invisible_b (s))
1069 Real stem_y = calc_stem_y (me, s, common,
1071 pos, french && i > 0&& (i < stems.size () -1));
1073 Stem::set_stemend (s, 2* stem_y / staff_space);
1078 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1080 Link_array<Grob> stems=
1081 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1084 for (int i=0; i < stems.size (); i++)
1087 Don't overwrite user settings.
1092 /* Don't set beaming for outside of outer stems */
1093 if ((d == LEFT && i == 0)
1094 ||(d == RIGHT && i == stems.size () -1))
1098 SCM beaming_prop = stems[i]->get_grob_property ("beaming");
1099 if (beaming_prop == SCM_EOL ||
1100 index_get_cell (beaming_prop, d) == SCM_EOL)
1102 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1103 Stem::set_beaming (stems[i], b, d);
1106 while (flip (&d) != LEFT);
1111 Beam::forced_stem_count (Grob *me)
1113 Link_array<Grob>stems =
1114 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1116 for (int i=0; i < stems.size (); i++)
1120 if (Stem::invisible_b (s))
1123 if (((int)Stem::chord_start_y (s))
1124 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1134 Beam::visible_stem_count (Grob *me)
1136 Link_array<Grob>stems =
1137 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1139 for (int i = stems.size (); i--;)
1141 if (!Stem::invisible_b (stems[i]))
1148 Beam::first_visible_stem (Grob *me)
1150 Link_array<Grob>stems =
1151 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1153 for (int i = 0; i < stems.size (); i++)
1155 if (!Stem::invisible_b (stems[i]))
1162 Beam::last_visible_stem (Grob *me)
1164 Link_array<Grob>stems =
1165 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1166 for (int i = stems.size (); i--;)
1168 if (!Stem::invisible_b (stems[i]))
1178 handle rest under beam (do_post: beams are calculated now)
1179 what about combination of collisions and rest under beam.
1183 rest -> stem -> beam -> interpolate_y_position ()
1185 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1187 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1189 Grob *rest = unsmob_grob (element_smob);
1190 Axis a = (Axis) gh_scm2int (axis);
1192 assert (a == Y_AXIS);
1194 Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1197 return gh_double2scm (0.0);
1198 Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1200 || !Beam::has_interface (beam)
1201 || !Beam::visible_stem_count (beam))
1202 return gh_double2scm (0.0);
1204 // make callback for rest from this.
1205 // todo: make sure this calced already.
1207 // Interval pos = ly_scm2interval (beam->get_grob_property ("positions"));
1208 Interval pos (0, 0);
1209 SCM s = beam->get_grob_property ("positions");
1210 if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1211 pos = ly_scm2interval (s);
1213 Real dy = pos.delta ();
1214 // ugh -> use commonx
1215 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1216 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1217 Real dydx = dy && dx ? dy/dx : 0;
1219 Direction d = Stem::get_direction (stem);
1220 Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + pos[LEFT];
1222 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1225 Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space; // refp??
1228 = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1230 minimum_dist + -d * (beamy - rest_dim) >? 0;
1232 int stafflines = Staff_symbol_referencer::line_count (rest);
1234 // move discretely by half spaces.
1235 int discrete_dist = int (ceil (dist));
1237 // move by whole spaces inside the staff.
1238 if (discrete_dist < stafflines+1)
1239 discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1241 return gh_double2scm (-d * discrete_dist);
1247 ADD_INTERFACE (Beam, "beam-interface",
1250 #'thickness= weight of beams, in staffspace
1253 We take the least squares line through the ideal-length stems, and
1254 then damp that using
1256 damped = tanh (slope)
1258 this gives an unquantized left and right position for the beam end.
1259 Then we take all combinations of quantings near these left and right
1260 positions, and give them a score (according to how close they are to
1261 the ideal slope, how close the result is to the ideal stems, etc.). We
1262 take the best scoring combination.
1265 "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");