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>
18 * Remove #'direction from beam. A beam has no direction per se.
19 It may only set directions for stems.
23 * Use Number_pair i.s.o Interval to represent (yl, yr).
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.
56 const int INTER_QUANT_PENALTY = 1000;
57 const int SECONDARY_BEAM_DEMERIT = 15;
58 const int STEM_LENGTH_DEMERIT_FACTOR = 5;
59 // possibly ridiculous, but too short stems just won't do
60 const int STEM_LENGTH_LIMIT_PENALTY = 5000;
61 const int DAMPING_DIRECTIION_PENALTY = 800;
62 const int MUSICAL_DIRECTION_FACTOR = 400;
63 const int IDEAL_SLOPE_FACTOR = 10;
64 const int REGION_SIZE = 2;
68 shrink_extra_weight (Real x)
70 return fabs (x) * ((x < 0) ? 1.5 : 1.0);
74 Beam::add_stem (Grob *me, Grob *s)
76 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
78 s->add_dependency (me);
80 assert (!Stem::beam_l (s));
81 s->set_grob_property ("beam", me->self_scm ());
83 add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
87 Beam::get_interbeam (Grob *me)
89 SCM func = me->get_grob_property ("space-function");
90 SCM s = gh_call2 (func, me->self_scm (), gh_int2scm (get_multiplicity (me)));
91 return gh_scm2double (s);
98 Beam::get_multiplicity (Grob *me)
101 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
103 Grob *sc = unsmob_grob (ly_car (s));
105 if (Stem::has_interface (sc))
106 m = m >? Stem::beam_count (sc, LEFT) >? Stem::beam_count (sc, RIGHT);
111 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
113 Beam::space_function (SCM smob, SCM multiplicity)
115 Grob *me = unsmob_grob (smob);
117 Real staff_space = Staff_symbol_referencer::staff_space (me);
118 Real line = me->paper_l ()->get_var ("linethickness");
119 Real thickness = gh_scm2double (me->get_grob_property ("thickness"))
122 Real interbeam = gh_scm2int (multiplicity) < 4
123 ? (2*staff_space + line - thickness) / 2.0
124 : (3*staff_space + line - thickness) / 3.0;
126 return gh_double2scm (interbeam);
130 /* After pre-processing all directions should be set.
131 Several post-processing routines (stem, slur, script) need stem/beam
133 Currenly, this means that beam has set all stem's directions.
134 [Alternatively, stems could set its own directions, according to
135 their beam, during 'final-pre-processing'.] */
136 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
138 Beam::before_line_breaking (SCM smob)
140 Grob *me = unsmob_grob (smob);
142 /* Beams with less than 2 two stems don't make much sense, but could happen
147 For a beam that only has one stem, we try to do some disappearance magic:
148 we revert the flag, and move on to The Eternal Engraving Fields. */
150 int count = visible_stem_count (me);
153 me->warning (_ ("beam has less than two visible stems"));
155 SCM stems = me->get_grob_property ("stems");
156 if (scm_ilength (stems) == 1)
158 me->warning (_ ("Beam has less than two stems. Removing beam."));
160 unsmob_grob (gh_car (stems))->remove_grob_property ("beam");
163 return SCM_UNSPECIFIED;
165 else if (scm_ilength (stems) == 0)
168 return SCM_UNSPECIFIED;
173 Direction d = get_default_dir (me);
175 consider_auto_knees (me, d);
176 set_stem_directions (me, d);
177 set_stem_shorten (me);
184 Beam::get_default_dir (Grob *me)
186 Drul_array<int> total;
187 total[UP] = total[DOWN] = 0;
188 Drul_array<int> count;
189 count[UP] = count[DOWN] = 0;
192 Link_array<Item> stems=
193 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
195 for (int i=0; i <stems.size (); i++)
198 Direction sd = Directional_element_interface::get (s);
200 int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
201 int current = sd ? (1 + d * sd)/2 : center_distance;
208 } while (flip (&d) != DOWN);
210 SCM func = me->get_grob_property ("dir-function");
211 SCM s = gh_call2 (func,
212 gh_cons (gh_int2scm (count[UP]),
213 gh_int2scm (count[DOWN])),
214 gh_cons (gh_int2scm (total[UP]),
215 gh_int2scm (total[DOWN])));
217 if (gh_number_p (s) && gh_scm2int (s))
220 /* If dir is not determined: get default */
221 return to_dir (me->get_grob_property ("neutral-direction"));
225 /* Set all stems with non-forced direction to beam direction.
226 Urg: non-forced should become `without/with unforced' direction,
227 once stem gets cleaned-up. */
229 Beam::set_stem_directions (Grob *me, Direction d)
231 Link_array<Item> stems
232 =Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
234 for (int i=0; i <stems.size (); i++)
237 SCM force = s->remove_grob_property ("dir-forced");
238 if (!gh_boolean_p (force) || !gh_scm2bool (force))
239 Directional_element_interface::set (s, d);
243 /* Simplistic auto-knees; only consider vertical gap between two
246 `Forced' stem directions are ignored. If you don't want auto-knees,
247 don't set, or unset auto-knee-gap. */
249 Beam::consider_auto_knees (Grob *me, Direction d)
251 SCM scm = me->get_grob_property ("auto-knee-gap");
253 if (gh_number_p (scm))
257 Real staff_space = Staff_symbol_referencer::staff_space (me);
258 Real gap = gh_scm2double (scm) / staff_space;
261 Link_array<Item> stems=
262 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
264 Grob *common = me->common_refpoint (stems[0], Y_AXIS);
265 for (int i=1; i < stems.size (); i++)
266 if (!Stem::invisible_b (stems[i]))
267 common = common->common_refpoint (stems[i], Y_AXIS);
270 for (int i=1; i < stems.size (); i++)
272 if (!Stem::invisible_b (stems[i-1]))
274 if (Stem::invisible_b (stems[l]))
276 if (Stem::invisible_b (stems[i]))
279 Real left = Stem::extremal_heads (stems[l])[d]
280 ->relative_coordinate (common, Y_AXIS);
281 Real right = Stem::extremal_heads (stems[i])[-d]
282 ->relative_coordinate (common, Y_AXIS);
284 Real dy = right - left;
288 knee_y = (right + left) / 2;
296 for (int i=0; i < stems.size (); i++)
298 if (Stem::invisible_b (stems[i]))
301 Real y = Stem::extremal_heads (stems[i])[d]
302 ->relative_coordinate (common, Y_AXIS);
304 Directional_element_interface::set (s, y < knee_y ? UP : DOWN);
305 s->set_grob_property ("dir-forced", SCM_BOOL_T);
311 /* Set stem's shorten property if unset.
314 take some y-position (chord/beam/nearest?) into account
315 scmify forced-fraction
319 why is shorten stored in beam, and not directly in stem?
323 Beam::set_stem_shorten (Grob *m)
325 Spanner*me = dynamic_cast<Spanner*> (m);
327 Real forced_fraction = forced_stem_count (me) / visible_stem_count (me);
329 int multiplicity = get_multiplicity (me);
331 SCM shorten = me->get_grob_property ("beamed-stem-shorten");
332 if (shorten == SCM_EOL)
335 int sz = scm_ilength (shorten);
337 Real staff_space = Staff_symbol_referencer::staff_space (me);
338 SCM shorten_elt = scm_list_ref (shorten,
339 gh_int2scm (multiplicity <? (sz - 1)));
340 Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
342 /* your similar cute comment here */
343 shorten_f *= forced_fraction;
346 me->set_grob_property ("shorten", gh_double2scm (shorten_f));
349 /* Call list of y-dy-callbacks, that handle setting of
353 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
355 Beam::after_line_breaking (SCM smob)
357 Grob *me = unsmob_grob (smob);
359 /* Copy to mutable list. */
360 SCM s = ly_deep_copy (me->get_grob_property ("positions"));
361 me->set_grob_property ("positions", s);
363 if (ly_car (s) != SCM_BOOL_F)
364 return SCM_UNSPECIFIED;
366 // one wonders if such genericity is necessary --hwn.
367 SCM callbacks = me->get_grob_property ("position-callbacks");
368 for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
369 gh_call1 (ly_car (i), smob);
371 set_stem_lengths (me);
372 return SCM_UNSPECIFIED;
386 - Make all demerits customisable
388 - One sensible check per demerit (what's this --hwn)
390 - Add demerits for quants per se, as to forbid a specific quant
394 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
396 Beam::quanting (SCM smob)
398 Grob *me = unsmob_grob (smob);
400 SCM s = me->get_grob_property ("positions");
401 Real yl = gh_scm2double (gh_car (s));
402 Real yr = gh_scm2double (gh_cdr (s));
404 Real ss = Staff_symbol_referencer::staff_space (me);
405 Real thickness = gh_scm2double (me->get_grob_property ("thickness")) / ss;
406 Real slt = me->paper_l ()->get_var ("linethickness") / ss;
409 SCM sdy = me->get_grob_property ("least-squares-dy");
410 Real dy_mus = gh_number_p (sdy) ? gh_scm2double (sdy) : 0.0;
413 Real sit = (thickness - slt) / 2;
415 Real hang = 1.0 - (thickness - slt) / 2;
416 Real quants [] = {straddle, sit, inter, hang };
418 int num_quants = int (sizeof (quants)/sizeof (Real));
423 going to REGION_SIZE == 2, yields another 0.6 second with
427 (result indexes between 70 and 575) ? --hwn.
434 Do stem computations. These depend on YL and YR linearly, so we can
435 precompute for every stem 2 factors.
437 Link_array<Grob> stems=
438 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
439 Array<Stem_info> stem_infos;
440 Array<Real> lbase_lengths;
441 Array<Real> rbase_lengths;
443 Drul_array<bool> dirs_found(0,0);
444 for (int i= 0; i < stems.size(); i++)
447 stem_infos.push (Stem::calc_stem_info (s));
448 dirs_found[stem_infos.top ().dir_] = true;
450 Real b = calc_stem_y (me, s, Interval (1,0), false);
451 lbase_lengths.push (b);
453 Real a = calc_stem_y (me, s, Interval (0,1), false);
454 rbase_lengths.push (a);
457 Direction ldir = Direction (stem_infos[0].dir_);
458 Direction rdir = Direction (stem_infos.top ().dir_);
459 bool knee_b = dirs_found[LEFT] && dirs_found[RIGHT];
462 int region_size = REGION_SIZE;
464 Knees are harder, lets try some more possibilities for knees.
469 for (int i = -region_size ; i < region_size; i++)
470 for (int j = 0; j < num_quants; j++)
472 quantsl.push (i + quants[j] + int (yl));
473 quantsr.push (i + quants[j] + int (yr));
476 Array<Quant_score> qscores;
478 for (int l =0; l < quantsl.size (); l++)
479 for (int r =0; r < quantsr.size (); r++)
491 This is a longish function, but we don't separate this out into
492 neat modular separate subfunctions, as the subfunctions would be
493 called for many values of YL, YR. By precomputing various
494 parameters outside of the loop, we can save a lot of time.
497 for (int i = qscores.size (); i--;)
498 if (qscores[i].demerits < 100)
501 += score_slopes_dy (me, qscores[i].yl, qscores[i].yr,
505 Real rad = Staff_symbol_referencer::staff_radius (me);
506 int multiplicity = get_multiplicity (me);
507 Real interbeam = multiplicity < 4
508 ? (2*ss + slt - thickness) / 2.0
509 : (3*ss + slt - thickness) / 3.0;
511 for (int i = qscores.size (); i--;)
512 if (qscores[i].demerits < 100)
515 += score_forbidden_quants (me, qscores[i].yl, qscores[i].yr,
516 rad, slt, thickness, interbeam,
517 multiplicity, ldir, rdir);
521 for (int i = qscores.size (); i--;)
522 if (qscores[i].demerits < 100)
525 += score_stem_lengths (stems, stem_infos,
526 lbase_lengths, rbase_lengths,
528 me, qscores[i].yl, qscores[i].yr);
534 for (int i = qscores.size (); i--;)
536 if (qscores[i].demerits < best)
538 best = qscores [i].demerits ;
544 me->set_grob_property ("positions",
545 gh_cons (gh_double2scm (qscores[best_idx].yl),
546 gh_double2scm (qscores[best_idx].yr))
552 me->set_grob_property ("quant-score",
553 gh_double2scm (qscores[best_idx].demerits));
554 me->set_grob_property ("best-idx", gh_int2scm (best_idx));
557 return SCM_UNSPECIFIED;
561 Beam::score_stem_lengths (Link_array<Grob>stems,
562 Array<Stem_info> stem_infos,
563 Array<Real> left_factor,
564 Array<Real> right_factor,
569 Real demerit_score = 0.0 ;
570 Real pen = STEM_LENGTH_LIMIT_PENALTY;
572 for (int i=0; i < stems.size (); i++)
575 if (Stem::invisible_b (s))
579 yl * left_factor[i] + right_factor[i]* yr;
581 Stem_info info = stem_infos[i];
582 Direction d = info.dir_;
585 * ( 0 >? (info.dir_ * (info.shortest_y_ - current_y)));
587 demerit_score += STEM_LENGTH_DEMERIT_FACTOR
588 * shrink_extra_weight (d * current_y - info.dir_ * info.ideal_y_);
591 demerit_score *= 2.0 / stems.size ();
593 return demerit_score;
597 Beam::score_slopes_dy (Grob *me,
599 Real dy_mus, Real dy_damp)
604 if (sign (dy_damp) != sign (dy))
606 dem += DAMPING_DIRECTIION_PENALTY;
609 dem += MUSICAL_DIRECTION_FACTOR * (0 >? (fabs (dy) - fabs (dy_mus)));
610 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy))* IDEAL_SLOPE_FACTOR;
618 return x - floor (x);
622 Beam::score_forbidden_quants (Grob*me,
626 Real thickness, Real interbeam,
628 Direction ldir, Direction rdir)
633 if (fabs (yl) < rad && fabs ( my_modf (yl) - 0.5) < 1e-3)
634 dem += INTER_QUANT_PENALTY;
635 if (fabs (yr) < rad && fabs ( my_modf (yr) - 0.5) < 1e-3)
636 dem += INTER_QUANT_PENALTY;
638 // todo: use multiplicity of outer stems.
639 if (multiplicity >= 2)
643 Real sit = (thickness - slt) / 2;
645 Real hang = 1.0 - (thickness - slt) / 2;
648 if (fabs (yl - ldir * interbeam) < rad
649 && fabs (my_modf (yl) - inter) < 1e-3)
650 dem += SECONDARY_BEAM_DEMERIT;
651 if (fabs (yr - rdir * interbeam) < rad
652 && fabs (my_modf (yr) - inter) < 1e-3)
653 dem += SECONDARY_BEAM_DEMERIT;
658 Can't we simply compute the distance between the nearest
659 staffline and the secondary beam? That would get rid of the
660 silly case analysis here (which is probably not when we have
661 different beam-thicknesses.)
667 // hmm, without Interval/Drul_array, you get ~ 4x same code...
668 if (fabs (yl - ldir * interbeam) < rad + inter)
670 if (ldir == UP && dy <= eps
671 && fabs (my_modf (yl) - sit) < eps)
672 dem += SECONDARY_BEAM_DEMERIT;
674 if (ldir == DOWN && dy >= eps
675 && fabs (my_modf (yl) - hang) < eps)
676 dem += SECONDARY_BEAM_DEMERIT;
679 if (fabs (yr - rdir * interbeam) < rad + inter)
681 if (rdir == UP && dy >= eps
682 && fabs (my_modf (yr) - sit) < eps)
683 dem += SECONDARY_BEAM_DEMERIT;
685 if (rdir == DOWN && dy <= eps
686 && fabs (my_modf (yr) - hang) < eps)
687 dem += SECONDARY_BEAM_DEMERIT;
690 if (multiplicity >= 3)
692 if (fabs (yl - 2 * ldir * interbeam) < rad + inter)
694 if (ldir == UP && dy <= eps
695 && fabs (my_modf (yl) - straddle) < eps)
696 dem += SECONDARY_BEAM_DEMERIT;
698 if (ldir == DOWN && dy >= eps
699 && fabs (my_modf (yl) - straddle) < eps)
700 dem += SECONDARY_BEAM_DEMERIT;
703 if (fabs (yr - 2 * rdir * interbeam) < rad + inter)
705 if (rdir == UP && dy >= eps
706 && fabs (my_modf (yr) - straddle) < eps)
707 dem += SECONDARY_BEAM_DEMERIT;
709 if (rdir == DOWN && dy <= eps
710 && fabs (my_modf (yr) - straddle) < eps)
711 dem += SECONDARY_BEAM_DEMERIT;
721 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
723 Beam::least_squares (SCM smob)
725 Grob *me = unsmob_grob (smob);
727 int count = visible_stem_count (me);
732 me->set_grob_property ("positions", ly_interval2scm (pos));
733 return SCM_UNSPECIFIED;
736 Interval ideal (Stem::calc_stem_info (first_visible_stem (me)).ideal_y_,
737 Stem::calc_stem_info (last_visible_stem (me)).ideal_y_);
741 Array<Real> x_posns ;
742 Link_array<Item> stems=
743 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
744 Grob *common = stems[0];
745 for (int i=1; i < stems.size (); i++)
746 common = stems[i]->common_refpoint (common, X_AXIS);
748 Real x0 = first_visible_stem (me)->relative_coordinate (common, X_AXIS);
749 for (int i=0; i < stems.size (); i++)
753 Real x = s->relative_coordinate (common, X_AXIS) - x0;
756 Real dx = last_visible_stem (me)->relative_coordinate (common, X_AXIS) - x0;
764 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
765 Stem::chord_start_y (last_visible_stem (me)));
769 TODO -- use scoring for this.
771 complicated, because we take stem-info.ideal for determining
774 /* Make simple beam on middle line have small tilt */
775 if (!ideal[LEFT] && chord.delta () && count == 2)
781 Direction d = (Direction) (sign (chord.delta ()) * UP);
782 pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
797 Array<Offset> ideals;
798 for (int i=0; i < stems.size (); i++)
801 if (Stem::invisible_b (s))
803 ideals.push (Offset (x_posns[i],
804 Stem::calc_stem_info (s).ideal_y_));
806 minimise_least_squares (&dydx, &y, ideals);
809 me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
810 pos = Interval (y, (y+dy));
813 me->set_grob_property ("positions", ly_interval2scm (pos));
815 return SCM_UNSPECIFIED;
820 We can't combine with previous function, since check concave and
821 slope damping comes first.
823 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
825 Beam::shift_region_to_valid (SCM grob)
827 Grob *me = unsmob_grob (grob);
831 Array<Real> x_posns ;
832 Link_array<Item> stems=
833 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
834 Grob *common = stems[0];
835 for (int i=1; i < stems.size (); i++)
836 common = stems[i]->common_refpoint (common, X_AXIS);
838 Grob *fvs = first_visible_stem (me);
841 return SCM_UNSPECIFIED;
843 Real x0 =fvs->relative_coordinate (common, X_AXIS);
844 for (int i=0; i < stems.size (); i++)
848 Real x = s->relative_coordinate (common, X_AXIS) - x0;
852 Grob *lvs = last_visible_stem (me);
854 return SCM_UNSPECIFIED;
856 Real dx = lvs->relative_coordinate (common, X_AXIS) - x0;
858 Interval pos = ly_scm2interval ( me->get_grob_property ("positions"));
859 Real dy = pos.delta();
865 Shift the positions so that we have a chance of finding good
866 quants (i.e. no short stem failures.)
868 Interval feasible_left_point;
869 feasible_left_point.set_full ();
870 for (int i=0; i < stems.size (); i++)
873 if (Stem::invisible_b (s))
877 Direction d = Stem::get_direction (s);
880 Real left_y = Stem::calc_stem_info (s).shortest_y_
881 - dydx * x_posns [i];
887 feasible_left_point.intersect (flp);
890 if (feasible_left_point.empty_b())
892 warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
894 else if (!feasible_left_point.elem_b(y))
896 if (isinf (feasible_left_point[DOWN]))
897 y = feasible_left_point[UP] - REGION_SIZE;
898 else if (isinf (feasible_left_point[UP]))
899 y = feasible_left_point[DOWN]+ REGION_SIZE;
901 y = feasible_left_point.center ();
903 pos = Interval (y, (y+dy));
904 me->set_grob_property ("positions", ly_interval2scm (pos));
905 return SCM_UNSPECIFIED;
909 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
911 Beam::check_concave (SCM smob)
913 Grob *me = unsmob_grob (smob);
915 Link_array<Item> stems =
916 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
918 for (int i = 0; i < stems.size ();)
920 if (Stem::invisible_b (stems[i]))
926 if (stems.size () < 3)
927 return SCM_UNSPECIFIED;
930 /* Concaveness #1: If distance of an inner notehead to line between
931 two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
932 beam is concave (Heinz Stolba).
934 In the case of knees, the line connecting outer heads is often
935 not related to the beam slope (it may even go in the other
936 direction). Skip the check when the outer stems point in
937 different directions. --hwn
940 bool concaveness1 = false;
941 SCM gap = me->get_grob_property ("concaveness-gap");
942 if (gh_number_p (gap)
943 && Stem::get_direction(stems.top ())
944 == Stem::get_direction(stems[0]))
946 Real r1 = gh_scm2double (gap);
947 Real dy = Stem::chord_start_y (stems.top ())
948 - Stem::chord_start_y (stems[0]);
951 Real slope = dy / (stems.size () - 1);
953 Real y0 = Stem::chord_start_y (stems[0]);
954 for (int i = 1; i < stems.size () - 1; i++)
956 Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
966 /* Concaveness #2: Sum distances of inner noteheads that fall
967 outside the interval of the two outer noteheads.
969 We only do this for beams where first and last stem have the same
973 Note that "convex" stems compensate for "concave" stems.
974 (is that intentional?) --hwn.
977 Real concaveness2 = 0;
978 SCM thresh = me->get_grob_property ("concaveness-threshold");
979 Real r2 = infinity_f;
980 if (!concaveness1 && gh_number_p (thresh)
981 && Stem::get_direction(stems.top ())
982 == Stem::get_direction(stems[0]))
984 r2 = gh_scm2double (thresh);
986 Direction dir = Stem::get_direction(stems.top ());
988 Interval iv (Stem::chord_start_y (stems[0]),
989 Stem::chord_start_y (stems.top ()));
991 if (iv[MAX] < iv[MIN])
994 for (int i = 1; i < stems.size () - 1; i++)
996 Real f = Stem::chord_start_y (stems[i]);
997 concave += ((f - iv[MAX] ) >? 0) +
998 ((f - iv[MIN] ) <? 0);
1001 concaveness2 = concave / (stems.size () - 2);
1003 /* ugh: this is the a kludge to get
1004 input/regression/beam-concave.ly to behave as
1008 huh? we're dividing twice (which is not scalable) meaning that
1009 the longer the beam, the more unlikely it will be
1010 concave. Maybe you would even expect the other way around??
1015 concaveness2 /= (stems.size () - 2);
1018 /* TODO: some sort of damping iso -> plain horizontal */
1019 if (concaveness1 || concaveness2 > r2)
1021 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1022 Real r = pos.linear_combination (0);
1023 me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
1024 me->set_grob_property ("least-squares-dy", gh_double2scm (0));
1027 return SCM_UNSPECIFIED;
1030 /* This neat trick is by Werner Lemberg,
1031 damped = tanh (slope)
1032 corresponds with some tables in [Wanske] CHECKME */
1033 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1035 Beam::slope_damping (SCM smob)
1037 Grob *me = unsmob_grob (smob);
1039 if (visible_stem_count (me) <= 1)
1040 return SCM_UNSPECIFIED;
1042 SCM s = me->get_grob_property ("damping");
1043 int damping = gh_scm2int (s);
1047 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1048 Real dy = pos.delta ();
1050 // ugh -> use commonx
1051 Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS)
1052 - first_visible_stem (me)->relative_coordinate (0, X_AXIS);
1053 Real dydx = dy && dx ? dy/dx : 0;
1054 dydx = 0.6 * tanh (dydx) / damping;
1056 Real damped_dy = dydx * dx;
1057 pos[LEFT] += (dy - damped_dy) / 2;
1058 pos[RIGHT] -= (dy - damped_dy) / 2;
1060 me->set_grob_property ("positions", ly_interval2scm (pos));
1062 return SCM_UNSPECIFIED;
1066 Calculate the Y position of the stem-end, given the Y-left, Y-right
1067 in POS, and for stem S.
1069 If CORRECT, correct for multiplicity of beam in case of knees.
1072 TODO: junk CORRECT from this.
1075 Beam::calc_stem_y (Grob *me, Grob* s, Interval pos, bool correct)
1077 int beam_multiplicity = get_multiplicity (me);
1078 int stem_multiplicity = (Stem::duration_log (s) - 2) >? 0;
1081 Real thick = gh_scm2double (me->get_grob_property ("thickness"));
1082 Real interbeam = get_interbeam (me);
1084 // ugh -> use commonx
1085 Grob * fvs = first_visible_stem (me);
1086 Grob *lvs = last_visible_stem (me);
1088 Real x0 = fvs ? fvs->relative_coordinate (0, X_AXIS) : 0.0;
1089 Real dx = fvs ? lvs->relative_coordinate (0, X_AXIS) - x0 : 0.0;
1090 Real r = s->relative_coordinate (0, X_AXIS) - x0;
1091 Real dy = pos.delta ();
1092 Real stem_y = (dy && dx
1097 Direction my_dir = Directional_element_interface::get (s);
1098 Direction first_dir = fvs? Directional_element_interface::get (fvs) : my_dir;
1100 if (correct && my_dir != first_dir)
1103 WTF is happening here ?
1105 It looks as if this is some kind of fixup for multiple kneed
1106 beams to get a piece of stem at the #.
1117 Rules for this kind of stuff are hairy. In any event, the
1118 current stem should look at the multiplicity of its
1125 // FIXME, hairy stuff
1126 stem_y += my_dir * (thick / 2 + (beam_multiplicity - 1) * interbeam);
1128 // huh, why not for first visible?
1131 What the heck is happening here??
1133 Grob *last_visible = last_visible_stem (me);
1136 if ( Staff_symbol_referencer::staff_symbol_l (s)
1137 != Staff_symbol_referencer::staff_symbol_l (last_visible))
1138 stem_y += Directional_element_interface::get (me)
1139 * (beam_multiplicity - stem_multiplicity) * interbeam;
1142 programming_error ("No last visible stem");
1148 Hmm. At this time, beam position and slope are determined. Maybe,
1149 stem directions and length should set to relative to the chord's
1150 position of the beam. */
1152 Beam::set_stem_lengths (Grob *me)
1154 Link_array<Item> stems=
1155 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
1157 if (stems.size () <= 1)
1160 Grob *common = me->common_refpoint (stems[0], Y_AXIS);
1161 for (int i=1; i < stems.size (); i++)
1162 if (!Stem::invisible_b (stems[i]))
1163 common = common->common_refpoint (stems[i], Y_AXIS);
1165 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1166 Real staff_space = Staff_symbol_referencer::staff_space (me);
1172 Real thick = gh_scm2double (me->get_grob_property ("thickness"));
1173 Direction dir = Directional_element_interface::get (me);
1174 bool ps_testing = to_boolean (ly_symbol2scm ("ps-testing"));
1177 for (int i=0; i < stems.size (); i++)
1180 if (Stem::invisible_b (s))
1183 Real stem_y = calc_stem_y (me, s, pos, true);
1186 // doesn't play well with dvips
1188 if (Stem::get_direction (s) == dir)
1189 stem_y += Stem::get_direction (s) * thick / 2;
1192 /* caution: stem measures in staff-positions */
1193 Real id = me->relative_coordinate (common, Y_AXIS)
1194 - stems[i]->relative_coordinate (common, Y_AXIS);
1195 Stem::set_stemend (s, (stem_y + id) / staff_space * 2);
1200 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1202 Link_array<Grob> stems=
1203 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1206 for (int i=0; i < stems.size (); i++)
1210 /* Don't overwrite user override (?) */
1211 if (Stem::beam_count (stems[i], d) == -1
1212 /* Don't set beaming for outside of outer stems */
1213 && ! (d == LEFT && i == 0)
1214 && ! (d == RIGHT && i == stems.size () -1))
1216 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1217 Stem::set_beaming (stems[i], b, d);
1220 while (flip (&d) != LEFT);
1227 beams to go with one stem.
1231 The beam should be constructed by one function that knows where the
1232 X and Y points are, and only inspects the stems to obtain
1233 multiplicity and stem directions.
1237 Beam::stem_beams (Grob *me, Item *here, Item *next, Item *prev, Real dydx)
1239 // ugh -> use commonx
1241 && ! (next->relative_coordinate (0, X_AXIS)
1242 > here->relative_coordinate (0, X_AXIS)))
1244 && ! (prev->relative_coordinate (0, X_AXIS)
1245 < here->relative_coordinate (0, X_AXIS))))
1246 programming_error ("Beams are not left-to-right");
1248 Real thick = gh_scm2double (me->get_grob_property ("thickness"));
1249 Real bdy = get_interbeam (me);
1252 Molecule rightbeams;
1255 if (!Stem::first_head (here))
1259 int t = Stem::duration_log (here);
1261 SCM proc = me->get_grob_property ("flag-width-function");
1262 SCM result = gh_call1 (proc, gh_int2scm (t));
1263 nw_f = gh_scm2double (result);
1267 /* [Tremolo] beams on whole notes may not have direction set? */
1268 Direction dir = Directional_element_interface::get (here);
1270 /* half beams extending to the left. */
1273 int lhalfs= lhalfs = Stem::beam_count (here, LEFT)
1274 - Stem::beam_count (prev, RIGHT);
1275 int lwholebeams= Stem::beam_count (here, LEFT)
1276 <? Stem::beam_count (prev, RIGHT);
1278 /* Half beam should be one note-width,
1279 but let's make sure two half-beams never touch */
1281 // FIXME: TODO (check) stem width / sloped beams
1282 Real w = here->relative_coordinate (0, X_AXIS)
1283 - prev->relative_coordinate (0, X_AXIS);
1284 Real stem_w = gh_scm2double (prev->get_grob_property ("thickness"))
1286 * me->paper_l ()->get_var ("linethickness");
1290 if (lhalfs) // generates warnings if not
1291 a = Lookup::beam (dydx, w + stem_w, thick);
1292 a.translate (Offset (-w, -w * dydx));
1293 a.translate_axis (-stem_w/2, X_AXIS);
1294 for (int j = 0; j < lhalfs; j++)
1297 b.translate_axis (-dir * bdy * (lwholebeams+j), Y_AXIS);
1298 leftbeams.add_molecule (b);
1304 int rhalfs = Stem::beam_count (here, RIGHT)
1305 - Stem::beam_count (next, LEFT);
1306 int rwholebeams= Stem::beam_count (here, RIGHT)
1307 <? Stem::beam_count (next, LEFT);
1309 Real w = next->relative_coordinate (0, X_AXIS)
1310 - here->relative_coordinate (0, X_AXIS);
1312 Real stem_w = gh_scm2double (next->get_grob_property ("thickness"))
1314 * me->paper_l ()->get_var ("linethickness");
1316 Molecule a = Lookup::beam (dydx, w + stem_w, thick);
1317 a.translate_axis (- stem_w/2, X_AXIS);
1321 SCM gap = me->get_grob_property ("gap");
1322 if (gh_number_p (gap))
1324 int gap_i = gh_scm2int ((gap));
1325 int nogap = rwholebeams - gap_i;
1327 for (; j < nogap; j++)
1330 b.translate_axis (-dir * bdy * j, Y_AXIS);
1331 rightbeams.add_molecule (b);
1333 if (Stem::invisible_b (here))
1338 a = Lookup::beam (dydx, w + stem_w, thick);
1341 for (; j < rwholebeams; j++)
1345 if (Stem::invisible_b (here))
1346 // ugh, see chord-tremolo.ly
1347 tx = (-dir + 1) / 2 * nw_f * 1.5 + gap_f/4;
1350 b.translate (Offset (tx, -dir * bdy * j));
1351 rightbeams.add_molecule (b);
1356 a = Lookup::beam (dydx, w, thick);
1358 for (; j < rwholebeams + rhalfs; j++)
1361 b.translate_axis (- dir * bdy * j, Y_AXIS);
1362 rightbeams.add_molecule (b);
1366 leftbeams.add_molecule (rightbeams);
1372 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
1374 Beam::brew_molecule (SCM smob)
1376 Grob *me =unsmob_grob (smob);
1379 if (!gh_pair_p (me->get_grob_property ("stems")))
1382 Link_array<Item>stems =
1383 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1384 if (visible_stem_count (me))
1386 // ugh -> use commonx
1387 x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
1388 dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
1392 x0 = stems[0]->relative_coordinate (0, X_AXIS);
1393 dx = stems.top ()->relative_coordinate (0, X_AXIS) - x0;
1396 SCM posns = me->get_grob_property ("positions");
1398 if (!ly_number_pair_p (posns))
1400 programming_error ("No beam posns");
1401 pos = Interval (0,0);
1404 pos= ly_scm2interval (posns);
1405 Real dy = pos.delta ();
1406 Real dydx = dy && dx ? dy/dx : 0;
1409 for (int i=0; i < stems.size (); i++)
1411 Item *item = stems[i];
1412 Item *prev = (i > 0)? stems[i-1] : 0;
1413 Item *next = (i < stems.size ()-1) ? stems[i+1] :0;
1417 Molecule sb = stem_beams (me, item, next, prev, dydx);
1418 Real x = item->relative_coordinate (0, X_AXIS) - x0;
1419 sb.translate (Offset (x, x * dydx + pos[LEFT]));
1422 mol.add_molecule (sb);
1425 mol.translate_axis (x0
1426 - dynamic_cast<Spanner*> (me)
1427 ->get_bound (LEFT)->relative_coordinate (0, X_AXIS),
1430 #if (DEBUG_QUANTING)
1433 This code prints the demerits for each beam. Perhaps this
1434 should be switchable for those who want to twiddle with the
1440 str += to_str (gh_scm2int (me->get_grob_property ("best-idx")));
1443 str += to_str (gh_scm2double (me->get_grob_property ("quant-score")),
1446 SCM properties = Font_interface::font_alist_chain (me);
1449 Molecule tm = Text_item::text2molecule (me, ly_str02scm (str.ch_C ()), properties);
1450 mol.add_at_edge (Y_AXIS, UP, tm, 5.0);
1454 return mol.smobbed_copy ();
1458 Beam::forced_stem_count (Grob *me)
1460 Link_array<Item>stems =
1461 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1463 for (int i=0; i < stems.size (); i++)
1467 if (Stem::invisible_b (s))
1470 if (((int)Stem::chord_start_y (s))
1471 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1481 Beam::visible_stem_count (Grob *me)
1483 Link_array<Item>stems =
1484 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1486 for (int i = stems.size (); i--;)
1488 if (!Stem::invisible_b (stems[i]))
1495 Beam::first_visible_stem (Grob *me)
1497 Link_array<Item>stems =
1498 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1500 for (int i = 0; i < stems.size (); i++)
1502 if (!Stem::invisible_b (stems[i]))
1509 Beam::last_visible_stem (Grob *me)
1511 Link_array<Item>stems =
1512 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1513 for (int i = stems.size (); i--;)
1515 if (!Stem::invisible_b (stems[i]))
1525 handle rest under beam (do_post: beams are calculated now)
1526 what about combination of collisions and rest under beam.
1530 rest -> stem -> beam -> interpolate_y_position ()
1532 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1534 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1536 Grob *rest = unsmob_grob (element_smob);
1537 Axis a = (Axis) gh_scm2int (axis);
1539 assert (a == Y_AXIS);
1541 Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1544 return gh_double2scm (0.0);
1545 Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1547 || !Beam::has_interface (beam)
1548 || !Beam::visible_stem_count (beam))
1549 return gh_double2scm (0.0);
1551 // make callback for rest from this.
1552 // todo: make sure this calced already.
1554 // Interval pos = ly_scm2interval (beam->get_grob_property ("positions"));
1555 Interval pos (0, 0);
1556 SCM s = beam->get_grob_property ("positions");
1557 if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1558 pos = ly_scm2interval (s);
1560 Real dy = pos.delta ();
1561 // ugh -> use commonx
1562 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1563 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1564 Real dydx = dy && dx ? dy/dx : 0;
1566 Direction d = Stem::get_direction (stem);
1567 Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + pos[LEFT];
1569 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1572 Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space; // refp??
1575 = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1577 minimum_dist + -d * (beamy - rest_dim) >? 0;
1579 int stafflines = Staff_symbol_referencer::line_count (rest);
1581 // move discretely by half spaces.
1582 int discrete_dist = int (ceil (dist));
1584 // move by whole spaces inside the staff.
1585 if (discrete_dist < stafflines+1)
1586 discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1588 return gh_double2scm (-d * discrete_dist);
1594 ADD_INTERFACE (Beam, "beam-interface",
1597 #'thickness= weight of beams, in staffspace
1600 We take the least squares line through the ideal-length stems, and
1601 then damp that using
1603 damped = tanh (slope)
1605 this gives an unquantized left and right position for the beam end.
1606 Then we take all combinations of quantings near these left and right
1607 positions, and give them a score (according to how close they are to
1608 the ideal slope, how close the result is to the ideal stems, etc.). We
1609 take the best scoring combination.
1612 "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");