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).
28 /* snapnie now also works */
31 #include <math.h> // tanh.
33 #include "molecule.hh"
34 #include "directional-element-interface.hh"
38 #include "least-squares.hh"
40 #include "paper-def.hh"
42 #include "group-interface.hh"
43 #include "staff-symbol-referencer.hh"
49 #define DEBUG_QUANTING 0
53 #include "text-item.hh" // debug output.
54 #include "font-interface.hh" // debug output.
58 const int INTER_QUANT_PENALTY = 1000;
59 const int SECONDARY_BEAM_DEMERIT = 15;
60 const int STEM_LENGTH_DEMERIT_FACTOR = 5;
61 // possibly ridiculous, but too short stems just won't do
62 const int STEM_LENGTH_LIMIT_PENALTY = 5000;
63 const int DAMPING_DIRECTIION_PENALTY = 800;
64 const int MUSICAL_DIRECTION_FACTOR = 400;
65 const int IDEAL_SLOPE_FACTOR = 10;
69 shrink_extra_weight (Real x)
71 return fabs (x) * ((x < 0) ? 1.5 : 1.0);
75 Beam::add_stem (Grob *me, Grob *s)
77 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
79 s->add_dependency (me);
81 assert (!Stem::beam_l (s));
82 s->set_grob_property ("beam", me->self_scm ());
84 add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
88 Beam::get_interbeam (Grob *me)
90 SCM func = me->get_grob_property ("space-function");
91 SCM s = gh_call2 (func, me->self_scm (), gh_int2scm (get_multiplicity (me)));
92 return gh_scm2double (s);
99 Beam::get_multiplicity (Grob *me)
102 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
104 Grob *sc = unsmob_grob (ly_car (s));
106 if (Stem::has_interface (sc))
107 m = m >? Stem::beam_count (sc, LEFT) >? Stem::beam_count (sc, RIGHT);
112 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
114 Beam::space_function (SCM smob, SCM multiplicity)
116 Grob *me = unsmob_grob (smob);
118 Real staff_space = Staff_symbol_referencer::staff_space (me);
119 Real line = me->paper_l ()->get_var ("linethickness");
120 Real thickness = gh_scm2double (me->get_grob_property ("thickness"))
123 Real interbeam = gh_scm2int (multiplicity) < 4
124 ? (2*staff_space + line - thickness) / 2.0
125 : (3*staff_space + line - thickness) / 3.0;
127 return gh_double2scm (interbeam);
131 /* After pre-processing all directions should be set.
132 Several post-processing routines (stem, slur, script) need stem/beam
134 Currenly, this means that beam has set all stem's directions.
135 [Alternatively, stems could set its own directions, according to
136 their beam, during 'final-pre-processing'.] */
137 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
139 Beam::before_line_breaking (SCM smob)
141 Grob *me = unsmob_grob (smob);
143 /* Beams with less than 2 two stems don't make much sense, but could happen
148 For a beam that only has one stem, we try to do some disappearance magic:
149 we revert the flag, and move on to The Eternal Engraving Fields. */
151 int count = visible_stem_count (me);
154 me->warning (_ ("beam has less than two visible stems"));
156 SCM stems = me->get_grob_property ("stems");
157 if (scm_ilength (stems) == 1)
159 me->warning (_ ("Beam has less than two stems. Removing beam."));
161 unsmob_grob (gh_car (stems))->remove_grob_property ("beam");
164 return SCM_UNSPECIFIED;
166 else if (scm_ilength (stems) == 0)
169 return SCM_UNSPECIFIED;
174 Direction d = get_default_dir (me);
176 consider_auto_knees (me, d);
177 set_stem_directions (me, d);
178 set_stem_shorten (me);
185 Beam::get_default_dir (Grob *me)
187 Drul_array<int> total;
188 total[UP] = total[DOWN] = 0;
189 Drul_array<int> count;
190 count[UP] = count[DOWN] = 0;
193 Link_array<Item> stems=
194 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
196 for (int i=0; i <stems.size (); i++)
199 Direction sd = Directional_element_interface::get (s);
201 int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
202 int current = sd ? (1 + d * sd)/2 : center_distance;
209 } while (flip (&d) != DOWN);
211 SCM func = me->get_grob_property ("dir-function");
212 SCM s = gh_call2 (func,
213 gh_cons (gh_int2scm (count[UP]),
214 gh_int2scm (count[DOWN])),
215 gh_cons (gh_int2scm (total[UP]),
216 gh_int2scm (total[DOWN])));
218 if (gh_number_p (s) && gh_scm2int (s))
221 /* If dir is not determined: get default */
222 return to_dir (me->get_grob_property ("neutral-direction"));
226 /* Set all stems with non-forced direction to beam direction.
227 Urg: non-forced should become `without/with unforced' direction,
228 once stem gets cleaned-up. */
230 Beam::set_stem_directions (Grob *me, Direction d)
232 Link_array<Item> stems
233 =Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
235 for (int i=0; i <stems.size (); i++)
238 SCM force = s->remove_grob_property ("dir-forced");
239 if (!gh_boolean_p (force) || !gh_scm2bool (force))
240 Directional_element_interface::set (s, d);
244 /* Simplistic auto-knees; only consider vertical gap between two
247 `Forced' stem directions are ignored. If you don't want auto-knees,
248 don't set, or unset auto-knee-gap. */
250 Beam::consider_auto_knees (Grob *me, Direction d)
252 SCM scm = me->get_grob_property ("auto-knee-gap");
254 if (gh_number_p (scm))
258 Real staff_space = Staff_symbol_referencer::staff_space (me);
259 Real gap = gh_scm2double (scm) / staff_space;
262 Link_array<Item> stems=
263 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
265 Grob *common = me->common_refpoint (stems[0], Y_AXIS);
266 for (int i=1; i < stems.size (); i++)
267 if (!Stem::invisible_b (stems[i]))
268 common = common->common_refpoint (stems[i], Y_AXIS);
271 for (int i=1; i < stems.size (); i++)
273 if (!Stem::invisible_b (stems[i-1]))
275 if (Stem::invisible_b (stems[l]))
277 if (Stem::invisible_b (stems[i]))
280 Real left = Stem::extremal_heads (stems[l])[d]
281 ->relative_coordinate (common, Y_AXIS);
282 Real right = Stem::extremal_heads (stems[i])[-d]
283 ->relative_coordinate (common, Y_AXIS);
285 Real dy = right - left;
289 knee_y = (right + left) / 2;
297 for (int i=0; i < stems.size (); i++)
299 if (Stem::invisible_b (stems[i]))
302 Real y = Stem::extremal_heads (stems[i])[d]
303 ->relative_coordinate (common, Y_AXIS);
305 Directional_element_interface::set (s, y < knee_y ? UP : DOWN);
306 s->set_grob_property ("dir-forced", SCM_BOOL_T);
312 /* Set stem's shorten property if unset.
315 take some y-position (chord/beam/nearest?) into account
316 scmify forced-fraction
320 why is shorten stored in beam, and not directly in stem?
324 Beam::set_stem_shorten (Grob *m)
326 Spanner*me = dynamic_cast<Spanner*> (m);
328 Real forced_fraction = forced_stem_count (me) / visible_stem_count (me);
330 int multiplicity = get_multiplicity (me);
332 SCM shorten = me->get_grob_property ("beamed-stem-shorten");
333 if (shorten == SCM_EOL)
336 int sz = scm_ilength (shorten);
338 Real staff_space = Staff_symbol_referencer::staff_space (me);
339 SCM shorten_elt = scm_list_ref (shorten,
340 gh_int2scm (multiplicity <? (sz - 1)));
341 Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
343 /* your similar cute comment here */
344 shorten_f *= forced_fraction;
347 me->set_grob_property ("shorten", gh_double2scm (shorten_f));
350 /* Call list of y-dy-callbacks, that handle setting of
351 grob-properties y, dy.
353 User may set grob-properties: y-position-hs and height-hs
354 (to be fixed) that override the calculated y and dy.
356 Because y and dy cannot be calculated and quanted separately, we
357 always calculate both, then check for user override. */
358 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
360 Beam::after_line_breaking (SCM smob)
362 Grob *me = unsmob_grob (smob);
364 /* Copy to mutable list. */
365 SCM s = ly_deep_copy (me->get_grob_property ("positions"));
366 me->set_grob_property ("positions", s);
368 if (ly_car (s) != SCM_BOOL_F)
369 return SCM_UNSPECIFIED;
371 // one wonders if such genericity is necessary --hwn.
372 SCM callbacks = me->get_grob_property ("position-callbacks");
373 for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
374 gh_call1 (ly_car (i), smob);
376 set_stem_lengths (me);
377 return SCM_UNSPECIFIED;
391 - Make all demerits customisable
393 - One sensible check per demerit (what's this --hwn)
395 - Add demerits for quants per se, as to forbid a specific quant
399 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
401 Beam::quanting (SCM smob)
403 Grob *me = unsmob_grob (smob);
405 SCM s = me->get_grob_property ("positions");
406 Real yl = gh_scm2double (gh_car (s));
407 Real yr = gh_scm2double (gh_cdr (s));
409 Real ss = Staff_symbol_referencer::staff_space (me);
410 Real thickness = gh_scm2double (me->get_grob_property ("thickness")) / ss;
411 Real slt = me->paper_l ()->get_var ("linethickness") / ss;
414 SCM sdy = me->get_grob_property ("least-squares-dy");
415 Real dy_mus = gh_number_p (sdy) ? gh_scm2double (sdy) : 0.0;
418 Real sit = (thickness - slt) / 2;
420 Real hang = 1.0 - (thickness - slt) / 2;
421 Real quants [] = {straddle, sit, inter, hang };
423 int num_quants = int (sizeof (quants)/sizeof (Real));
428 going to REGION_SIZE == 2, yields another 0.6 second with
432 (result indexes between 70 and 575) ? --hwn.
439 Do stem computations. These depend on YL and YR linearly, so we can
440 precompute for every stem 2 factors.
442 Link_array<Grob> stems=
443 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
444 Array<Stem_info> stem_infos;
445 Array<Real> lbase_lengths;
446 Array<Real> rbase_lengths;
448 Drul_array<bool> dirs_found(0,0);
449 for (int i= 0; i < stems.size(); i++)
452 stem_infos.push (Stem::calc_stem_info (s));
453 dirs_found[stem_infos.top ().dir_] = true;
456 Real b = calc_stem_y (me, s, Interval (1,0), false);
457 lbase_lengths.push (b);
459 Real a = calc_stem_y (me, s, Interval (0,1), false);
460 rbase_lengths.push (a);
464 Direction ldir = Direction (stem_infos[0].dir_);
465 Direction rdir = Direction (stem_infos.top ().dir_);
466 bool knee_b = dirs_found[LEFT] && dirs_found[RIGHT];
472 \context Staff \notes {
477 breaks with REGION_SIZE < 4
482 Knees are harder, lets try some more possibilities for knees.
487 for (int i = -REGION_SIZE ; i < REGION_SIZE; i++)
488 for (int j = 0; j < num_quants; j++)
490 quantsl.push (i + quants[j] + int (yl));
491 quantsr.push (i + quants[j] + int (yr));
494 Array<Quant_score> qscores;
496 for (int l =0; l < quantsl.size (); l++)
497 for (int r =0; r < quantsr.size (); r++)
509 This is a longish function, but we don't separate this out into
510 neat modular separate subfunctions, as the subfunctions would be
511 called for many values of YL, YR. By precomputing various
512 parameters outside of the loop, we can save a lot of time.
515 for (int i = qscores.size (); i--;)
516 if (qscores[i].demerits < 100)
519 += score_slopes_dy (me, qscores[i].yl, qscores[i].yr,
523 Real rad = Staff_symbol_referencer::staff_radius (me);
524 int multiplicity = get_multiplicity (me);
525 Real interbeam = multiplicity < 4
526 ? (2*ss + slt - thickness) / 2.0
527 : (3*ss + slt - thickness) / 3.0;
529 for (int i = qscores.size (); i--;)
530 if (qscores[i].demerits < 100)
533 += score_forbidden_quants (me, qscores[i].yl, qscores[i].yr,
534 rad, slt, thickness, interbeam,
535 multiplicity, ldir, rdir);
539 for (int i = qscores.size (); i--;)
540 if (qscores[i].demerits < 100)
543 += score_stem_lengths (stems, stem_infos,
544 lbase_lengths, rbase_lengths,
546 me, qscores[i].yl, qscores[i].yr);
552 for (int i = qscores.size (); i--;)
554 if (qscores[i].demerits < best)
556 best = qscores [i].demerits ;
562 me->set_grob_property ("positions",
563 gh_cons (gh_double2scm (qscores[best_idx].yl),
564 gh_double2scm (qscores[best_idx].yr))
570 me->set_grob_property ("quant-score",
571 gh_double2scm (qscores[best_idx].demerits));
572 me->set_grob_property ("best-idx", gh_int2scm (best_idx));
575 return SCM_UNSPECIFIED;
579 Beam::score_stem_lengths (Link_array<Grob>stems,
580 Array<Stem_info> stem_infos,
581 Array<Real> left_factor,
582 Array<Real> right_factor,
587 Real demerit_score = 0.0 ;
588 Real pen = STEM_LENGTH_LIMIT_PENALTY;
595 Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
596 Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
598 for (int i=0; i < stems.size (); i++)
601 if (Stem::invisible_b (s))
605 /* for a two-stemmed, interstaff beam knee up/down:
608 \context PianoStaff \notes\relative c' <
609 \context Staff = lh {
610 \stemDown [c8 \translator Staff = rh \stemUp a'' ]
612 \context Staff = rh \relative c' s4
616 with yl = -5.8 (about ideal)
617 and yr = -1 (ridiculous pos)
618 this yields current_y = -8.1 (about ideal) */
621 yl * left_factor[i] + right_factor[i]* yr;
623 Real f = (s->relative_coordinate (0, X_AXIS) - x0) / dx;
624 Real current_y = yl + f * (yr - yl);
627 Stem_info info = stem_infos[i];
628 Direction d = info.dir_;
631 * ( 0 >? (info.dir_ * (info.shortest_y_ - current_y)));
633 demerit_score += STEM_LENGTH_DEMERIT_FACTOR
634 * shrink_extra_weight (d * current_y - info.dir_ * info.ideal_y_);
637 demerit_score *= 2.0 / stems.size ();
639 return demerit_score;
643 Beam::score_slopes_dy (Grob *me,
645 Real dy_mus, Real dy_damp)
650 if (sign (dy_damp) != sign (dy))
652 dem += DAMPING_DIRECTIION_PENALTY;
655 dem += MUSICAL_DIRECTION_FACTOR * (0 >? (fabs (dy) - fabs (dy_mus)));
656 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy))* IDEAL_SLOPE_FACTOR;
664 return x - floor (x);
668 Beam::score_forbidden_quants (Grob*me,
672 Real thickness, Real interbeam,
674 Direction ldir, Direction rdir)
679 if (fabs (yl) < rad && fabs ( my_modf (yl) - 0.5) < 1e-3)
680 dem += INTER_QUANT_PENALTY;
681 if (fabs (yr) < rad && fabs ( my_modf (yr) - 0.5) < 1e-3)
682 dem += INTER_QUANT_PENALTY;
684 // todo: use multiplicity of outer stems.
685 if (multiplicity >= 2)
689 Real sit = (thickness - slt) / 2;
691 Real hang = 1.0 - (thickness - slt) / 2;
694 if (fabs (yl - ldir * interbeam) < rad
695 && fabs (my_modf (yl) - inter) < 1e-3)
696 dem += SECONDARY_BEAM_DEMERIT;
697 if (fabs (yr - rdir * interbeam) < rad
698 && fabs (my_modf (yr) - inter) < 1e-3)
699 dem += SECONDARY_BEAM_DEMERIT;
704 Can't we simply compute the distance between the nearest
705 staffline and the secondary beam? That would get rid of the
706 silly case analysis here (which is probably not when we have
707 different beam-thicknesses.)
713 // hmm, without Interval/Drul_array, you get ~ 4x same code...
714 if (fabs (yl - ldir * interbeam) < rad + inter)
716 if (ldir == UP && dy <= eps
717 && fabs (my_modf (yl) - sit) < eps)
718 dem += SECONDARY_BEAM_DEMERIT;
720 if (ldir == DOWN && dy >= eps
721 && fabs (my_modf (yl) - hang) < eps)
722 dem += SECONDARY_BEAM_DEMERIT;
725 if (fabs (yr - rdir * interbeam) < rad + inter)
727 if (rdir == UP && dy >= eps
728 && fabs (my_modf (yr) - sit) < eps)
729 dem += SECONDARY_BEAM_DEMERIT;
731 if (rdir == DOWN && dy <= eps
732 && fabs (my_modf (yr) - hang) < eps)
733 dem += SECONDARY_BEAM_DEMERIT;
736 if (multiplicity >= 3)
738 if (fabs (yl - 2 * ldir * interbeam) < rad + inter)
740 if (ldir == UP && dy <= eps
741 && fabs (my_modf (yl) - straddle) < eps)
742 dem += SECONDARY_BEAM_DEMERIT;
744 if (ldir == DOWN && dy >= eps
745 && fabs (my_modf (yl) - straddle) < eps)
746 dem += SECONDARY_BEAM_DEMERIT;
749 if (fabs (yr - 2 * rdir * interbeam) < rad + inter)
751 if (rdir == UP && dy >= eps
752 && fabs (my_modf (yr) - straddle) < eps)
753 dem += SECONDARY_BEAM_DEMERIT;
755 if (rdir == DOWN && dy <= eps
756 && fabs (my_modf (yr) - straddle) < eps)
757 dem += SECONDARY_BEAM_DEMERIT;
767 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
769 Beam::least_squares (SCM smob)
771 Grob *me = unsmob_grob (smob);
773 int count = visible_stem_count (me);
778 me->set_grob_property ("positions", ly_interval2scm (pos));
779 return SCM_UNSPECIFIED;
782 Interval ideal (Stem::calc_stem_info (first_visible_stem (me)).ideal_y_,
783 Stem::calc_stem_info (last_visible_stem (me)).ideal_y_);
787 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
788 Stem::chord_start_y (last_visible_stem (me)));
792 TODO : use scoring for this.
794 complicated, because we take stem-info.ideal for determining
798 /* Make simple beam on middle line have small tilt */
799 if (!ideal[LEFT] && chord.delta () && count == 2)
805 Direction d = (Direction) (sign (chord.delta ()) * UP);
806 pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
817 Array<Offset> ideals;
819 // ugh -> use commonx
820 Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
821 Link_array<Item> stems=
822 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
824 for (int i=0; i < stems.size (); i++)
827 if (Stem::invisible_b (s))
829 ideals.push (Offset (s->relative_coordinate (0, X_AXIS) - x0,
830 Stem::calc_stem_info (s).ideal_y_));
834 minimise_least_squares (&dydx, &y, ideals);
836 Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
838 me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
840 pos = Interval (y, (y+dy));
843 me->set_grob_property ("positions", ly_interval2scm (pos));
844 return SCM_UNSPECIFIED;
847 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
849 Beam::check_concave (SCM smob)
851 Grob *me = unsmob_grob (smob);
853 Link_array<Item> stems =
854 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
856 for (int i = 0; i < stems.size ();)
858 if (Stem::invisible_b (stems[i]))
864 if (stems.size () < 3)
865 return SCM_UNSPECIFIED;
868 /* Concaveness #1: If distance of an inner notehead to line between
869 two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
870 beam is concave (Heinz Stolba).
872 In the case of knees, the line connecting outer heads is often
873 not related to the beam slope (it may even go in the other
874 direction). Skip the check when the outer stems point in
875 different directions. --hwn
878 bool concaveness1 = false;
879 SCM gap = me->get_grob_property ("concaveness-gap");
880 if (gh_number_p (gap)
881 && Stem::get_direction(stems.top ())
882 == Stem::get_direction(stems[0]))
884 Real r1 = gh_scm2double (gap);
885 Real dy = Stem::chord_start_y (stems.top ())
886 - Stem::chord_start_y (stems[0]);
889 Real slope = dy / (stems.size () - 1);
891 Real y0 = Stem::chord_start_y (stems[0]);
892 for (int i = 1; i < stems.size () - 1; i++)
894 Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
904 /* Concaveness #2: Sum distances of inner noteheads that fall
905 outside the interval of the two outer noteheads.
907 We only do this for beams where first and last stem have the same
911 Note that "convex" stems compensate for "concave" stems.
912 (is that intentional?) --hwn.
915 Real concaveness2 = 0;
916 SCM thresh = me->get_grob_property ("concaveness-threshold");
917 Real r2 = infinity_f;
918 if (!concaveness1 && gh_number_p (thresh)
919 && Stem::get_direction(stems.top ())
920 == Stem::get_direction(stems[0]))
922 r2 = gh_scm2double (thresh);
924 Direction dir = Stem::get_direction(stems.top ());
926 Interval iv (Stem::chord_start_y (stems[0]),
927 Stem::chord_start_y (stems.top ()));
929 if (iv[MAX] < iv[MIN])
932 for (int i = 1; i < stems.size () - 1; i++)
934 Real f = Stem::chord_start_y (stems[i]);
935 concave += ((f - iv[MAX] ) >? 0) +
936 ((f - iv[MIN] ) <? 0);
939 concaveness2 = concave / (stems.size () - 2);
941 /* ugh: this is the a kludge to get
942 input/regression/beam-concave.ly to behave as
946 huh? we're dividing twice (which is not scalable) meaning that
947 the longer the beam, the more unlikely it will be
948 concave. Maybe you would even expect the other way around??
953 concaveness2 /= (stems.size () - 2);
956 /* TODO: some sort of damping iso -> plain horizontal */
957 if (concaveness1 || concaveness2 > r2)
959 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
960 Real r = pos.linear_combination (0);
961 me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
962 me->set_grob_property ("least-squares-dy", gh_double2scm (0));
965 return SCM_UNSPECIFIED;
968 /* This neat trick is by Werner Lemberg,
969 damped = tanh (slope)
970 corresponds with some tables in [Wanske] CHECKME */
971 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
973 Beam::slope_damping (SCM smob)
975 Grob *me = unsmob_grob (smob);
977 if (visible_stem_count (me) <= 1)
978 return SCM_UNSPECIFIED;
980 SCM s = me->get_grob_property ("damping");
981 int damping = gh_scm2int (s);
985 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
986 Real dy = pos.delta ();
988 // ugh -> use commonx
989 Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS)
990 - first_visible_stem (me)->relative_coordinate (0, X_AXIS);
991 Real dydx = dy && dx ? dy/dx : 0;
992 dydx = 0.6 * tanh (dydx) / damping;
994 Real damped_dy = dydx * dx;
995 pos[LEFT] += (dy - damped_dy) / 2;
996 pos[RIGHT] -= (dy - damped_dy) / 2;
998 me->set_grob_property ("positions", ly_interval2scm (pos));
1000 return SCM_UNSPECIFIED;
1004 Calculate the Y position of the stem-end, given the Y-left, Y-right
1005 in POS, and for stem S.
1007 If CORRECT, correct for multiplicity of beam in case of knees.
1010 Beam::calc_stem_y (Grob *me, Grob* s, Interval pos, bool correct)
1012 int beam_multiplicity = get_multiplicity (me);
1013 int stem_multiplicity = (Stem::duration_log (s) - 2) >? 0;
1015 int first_multiplicity = (Stem::duration_log (first_visible_stem (me))
1017 int last_multiplicity = (Stem::duration_log (last_visible_stem (me))
1020 Real thick = gh_scm2double (me->get_grob_property ("thickness"));
1021 Real interbeam = get_interbeam (me);
1023 // ugh -> use commonx
1024 Grob * fvs = first_visible_stem (me);
1025 Grob *lvs = last_visible_stem (me);
1027 Real x0 = fvs ? fvs->relative_coordinate (0, X_AXIS) : 0.0;
1028 Real dx = fvs ? lvs->relative_coordinate (0, X_AXIS) - x0 : 0.0;
1029 Real r = s->relative_coordinate (0, X_AXIS) - x0;
1030 Real dy = pos.delta ();
1031 Real stem_y = (dy && dx
1036 Direction my_dir = Directional_element_interface::get (s);
1037 Direction first_dir = fvs? Directional_element_interface::get (fvs) : my_dir;
1039 if (correct && my_dir != first_dir)
1042 WTF is happening here ?
1044 It looks as if this is some kind of fixup for multiple kneed
1045 beams to get a piece of stem at the #.
1056 Rules for this kind of stuff are hairy. In any event, the
1057 current stem should look at the multiplicity of its
1064 // FIXME, hairy stuff
1065 stem_y += my_dir * (thick / 2 + (beam_multiplicity - 1) * interbeam);
1067 // huh, why not for first visible?
1070 What the heck is happening here??
1072 Grob *last_visible = last_visible_stem (me);
1075 if ( Staff_symbol_referencer::staff_symbol_l (s)
1076 != Staff_symbol_referencer::staff_symbol_l (last_visible))
1077 stem_y += Directional_element_interface::get (me)
1078 * (beam_multiplicity - stem_multiplicity) * interbeam;
1081 programming_error ("No last visible stem");
1087 Hmm. At this time, beam position and slope are determined. Maybe,
1088 stem directions and length should set to relative to the chord's
1089 position of the beam. */
1091 Beam::set_stem_lengths (Grob *me)
1093 Link_array<Item> stems=
1094 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
1096 if (stems.size () <= 1)
1099 Grob *common = me->common_refpoint (stems[0], Y_AXIS);
1100 for (int i=1; i < stems.size (); i++)
1101 if (!Stem::invisible_b (stems[i]))
1102 common = common->common_refpoint (stems[i], Y_AXIS);
1104 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1105 Real staff_space = Staff_symbol_referencer::staff_space (me);
1111 Real thick = gh_scm2double (me->get_grob_property ("thickness"));
1112 Direction dir = Directional_element_interface::get (me);
1113 bool ps_testing = to_boolean (ly_symbol2scm ("ps-testing"));
1116 for (int i=0; i < stems.size (); i++)
1119 if (Stem::invisible_b (s))
1122 Real stem_y = calc_stem_y (me, s, pos, true);
1125 // doesn't play well with dvips
1127 if (Stem::get_direction (s) == dir)
1128 stem_y += Stem::get_direction (s) * thick / 2;
1131 /* caution: stem measures in staff-positions */
1132 Real id = me->relative_coordinate (common, Y_AXIS)
1133 - stems[i]->relative_coordinate (common, Y_AXIS);
1134 Stem::set_stemend (s, (stem_y + id) / staff_space * 2);
1139 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1141 Link_array<Grob> stems=
1142 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1145 for (int i=0; i < stems.size (); i++)
1149 /* Don't overwrite user override (?) */
1150 if (Stem::beam_count (stems[i], d) == -1
1151 /* Don't set beaming for outside of outer stems */
1152 && ! (d == LEFT && i == 0)
1153 && ! (d == RIGHT && i == stems.size () -1))
1155 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1156 Stem::set_beaming (stems[i], b, d);
1159 while (flip (&d) != LEFT);
1166 beams to go with one stem.
1170 The beam should be constructed by one function that knows where the
1171 X and Y points are, and only inspects the stems to obtain
1172 multiplicity and stem directions.
1176 Beam::stem_beams (Grob *me, Item *here, Item *next, Item *prev, Real dydx)
1178 // ugh -> use commonx
1180 && ! (next->relative_coordinate (0, X_AXIS)
1181 > here->relative_coordinate (0, X_AXIS)))
1183 && ! (prev->relative_coordinate (0, X_AXIS)
1184 < here->relative_coordinate (0, X_AXIS))))
1185 programming_error ("Beams are not left-to-right");
1187 Real thick = gh_scm2double (me->get_grob_property ("thickness"));
1188 Real bdy = get_interbeam (me);
1191 Molecule rightbeams;
1194 if (!Stem::first_head (here))
1198 int t = Stem::duration_log (here);
1200 SCM proc = me->get_grob_property ("flag-width-function");
1201 SCM result = gh_call1 (proc, gh_int2scm (t));
1202 nw_f = gh_scm2double (result);
1206 /* [Tremolo] beams on whole notes may not have direction set? */
1207 Direction dir = Directional_element_interface::get (here);
1209 /* half beams extending to the left. */
1212 int lhalfs= lhalfs = Stem::beam_count (here, LEFT)
1213 - Stem::beam_count (prev, RIGHT);
1214 int lwholebeams= Stem::beam_count (here, LEFT)
1215 <? Stem::beam_count (prev, RIGHT);
1217 /* Half beam should be one note-width,
1218 but let's make sure two half-beams never touch */
1220 // FIXME: TODO (check) stem width / sloped beams
1221 Real w = here->relative_coordinate (0, X_AXIS)
1222 - prev->relative_coordinate (0, X_AXIS);
1223 Real stem_w = gh_scm2double (prev->get_grob_property ("thickness"))
1225 * me->paper_l ()->get_var ("linethickness");
1229 if (lhalfs) // generates warnings if not
1230 a = Lookup::beam (dydx, w + stem_w, thick);
1231 a.translate (Offset (-w, -w * dydx));
1232 a.translate_axis (-stem_w/2, X_AXIS);
1233 for (int j = 0; j < lhalfs; j++)
1236 b.translate_axis (-dir * bdy * (lwholebeams+j), Y_AXIS);
1237 leftbeams.add_molecule (b);
1243 int rhalfs = Stem::beam_count (here, RIGHT)
1244 - Stem::beam_count (next, LEFT);
1245 int rwholebeams= Stem::beam_count (here, RIGHT)
1246 <? Stem::beam_count (next, LEFT);
1248 Real w = next->relative_coordinate (0, X_AXIS)
1249 - here->relative_coordinate (0, X_AXIS);
1251 Real stem_w = gh_scm2double (next->get_grob_property ("thickness"))
1253 * me->paper_l ()->get_var ("linethickness");
1255 Molecule a = Lookup::beam (dydx, w + stem_w, thick);
1256 a.translate_axis (- stem_w/2, X_AXIS);
1260 SCM gap = me->get_grob_property ("gap");
1261 if (gh_number_p (gap))
1263 int gap_i = gh_scm2int ((gap));
1264 int nogap = rwholebeams - gap_i;
1266 for (; j < nogap; j++)
1269 b.translate_axis (-dir * bdy * j, Y_AXIS);
1270 rightbeams.add_molecule (b);
1272 if (Stem::invisible_b (here))
1277 a = Lookup::beam (dydx, w + stem_w, thick);
1280 for (; j < rwholebeams; j++)
1284 if (Stem::invisible_b (here))
1285 // ugh, see chord-tremolo.ly
1286 tx = (-dir + 1) / 2 * nw_f * 1.5 + gap_f/4;
1289 b.translate (Offset (tx, -dir * bdy * j));
1290 rightbeams.add_molecule (b);
1295 a = Lookup::beam (dydx, w, thick);
1297 for (; j < rwholebeams + rhalfs; j++)
1300 b.translate_axis (- dir * bdy * j, Y_AXIS);
1301 rightbeams.add_molecule (b);
1305 leftbeams.add_molecule (rightbeams);
1311 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
1313 Beam::brew_molecule (SCM smob)
1315 Grob *me =unsmob_grob (smob);
1318 if (!gh_pair_p (me->get_grob_property ("stems")))
1321 Link_array<Item>stems =
1322 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1323 if (visible_stem_count (me))
1325 // ugh -> use commonx
1326 x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
1327 dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
1331 x0 = stems[0]->relative_coordinate (0, X_AXIS);
1332 dx = stems.top ()->relative_coordinate (0, X_AXIS) - x0;
1335 SCM posns = me->get_grob_property ("positions");
1337 if (!ly_number_pair_p (posns))
1339 programming_error ("No beam posns");
1340 pos = Interval (0,0);
1343 pos= ly_scm2interval (posns);
1344 Real dy = pos.delta ();
1345 Real dydx = dy && dx ? dy/dx : 0;
1348 for (int i=0; i < stems.size (); i++)
1350 Item *item = stems[i];
1351 Item *prev = (i > 0)? stems[i-1] : 0;
1352 Item *next = (i < stems.size ()-1) ? stems[i+1] :0;
1356 Molecule sb = stem_beams (me, item, next, prev, dydx);
1357 Real x = item->relative_coordinate (0, X_AXIS) - x0;
1358 sb.translate (Offset (x, x * dydx + pos[LEFT]));
1361 mol.add_molecule (sb);
1364 mol.translate_axis (x0
1365 - dynamic_cast<Spanner*> (me)
1366 ->get_bound (LEFT)->relative_coordinate (0, X_AXIS),
1369 #if (DEBUG_QUANTING)
1372 This code prints the demerits for each beam. Perhaps this
1373 should be switchable for those who want to twiddle with the
1379 str += to_str (gh_scm2int (me->get_grob_property ("best-idx")));
1382 str += to_str (gh_scm2double (me->get_grob_property ("quant-score")),
1385 SCM properties = Font_interface::font_alist_chain (me);
1388 Molecule tm = Text_item::text2molecule (me, ly_str02scm (str.ch_C ()), properties);
1389 mol.add_at_edge (Y_AXIS, UP, tm, 5.0);
1393 return mol.smobbed_copy ();
1397 Beam::forced_stem_count (Grob *me)
1399 Link_array<Item>stems =
1400 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1402 for (int i=0; i < stems.size (); i++)
1406 if (Stem::invisible_b (s))
1409 if (((int)Stem::chord_start_y (s))
1410 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1420 Beam::visible_stem_count (Grob *me)
1422 Link_array<Item>stems =
1423 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1425 for (int i = stems.size (); i--;)
1427 if (!Stem::invisible_b (stems[i]))
1434 Beam::first_visible_stem (Grob *me)
1436 Link_array<Item>stems =
1437 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1439 for (int i = 0; i < stems.size (); i++)
1441 if (!Stem::invisible_b (stems[i]))
1448 Beam::last_visible_stem (Grob *me)
1450 Link_array<Item>stems =
1451 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1452 for (int i = stems.size (); i--;)
1454 if (!Stem::invisible_b (stems[i]))
1464 handle rest under beam (do_post: beams are calculated now)
1465 what about combination of collisions and rest under beam.
1469 rest -> stem -> beam -> interpolate_y_position ()
1471 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1473 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1475 Grob *rest = unsmob_grob (element_smob);
1476 Axis a = (Axis) gh_scm2int (axis);
1478 assert (a == Y_AXIS);
1480 Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1483 return gh_double2scm (0.0);
1484 Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1486 || !Beam::has_interface (beam)
1487 || !Beam::visible_stem_count (beam))
1488 return gh_double2scm (0.0);
1490 // make callback for rest from this.
1491 // todo: make sure this calced already.
1493 // Interval pos = ly_scm2interval (beam->get_grob_property ("positions"));
1494 Interval pos (0, 0);
1495 SCM s = beam->get_grob_property ("positions");
1496 if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1497 pos = ly_scm2interval (s);
1499 Real dy = pos.delta ();
1500 // ugh -> use commonx
1501 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1502 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1503 Real dydx = dy && dx ? dy/dx : 0;
1505 Direction d = Stem::get_direction (stem);
1506 Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + pos[LEFT];
1508 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1511 Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space; // refp??
1514 = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1516 minimum_dist + -d * (beamy - rest_dim) >? 0;
1518 int stafflines = Staff_symbol_referencer::line_count (rest);
1520 // move discretely by half spaces.
1521 int discrete_dist = int (ceil (dist));
1523 // move by whole spaces inside the staff.
1524 if (discrete_dist < stafflines+1)
1525 discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1527 return gh_double2scm (-d * discrete_dist);
1533 ADD_INTERFACE (Beam, "beam-interface",
1536 #'thickness= weight of beams, in staffspace
1539 We take the least squares line through the ideal-length stems, and
1540 then damp that using
1542 damped = tanh (slope)
1544 this gives an unquantized left and right position for the beam end.
1545 Then we take all combinations of quantings near these left and right
1546 positions, and give them a score (according to how close they are to
1547 the ideal slope, how close the result is to the ideal stems, etc.). We
1548 take the best scoring combination.
1551 "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");