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 #include <math.h> // tanh.
30 #include "molecule.hh"
31 #include "directional-element-interface.hh"
35 #include "least-squares.hh"
37 #include "paper-def.hh"
39 #include "group-interface.hh"
40 #include "staff-symbol-referencer.hh"
44 #include "text-item.hh" // debug output.
45 #include "font-interface.hh" // debug output.
48 #define DEBUG_QUANTING 0
52 shrink_extra_weight (Real x)
54 return fabs (x) * ((x < 0) ? 1.5 : 1.0);
58 Beam::add_stem (Grob *me, Grob *s)
60 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
62 s->add_dependency (me);
64 assert (!Stem::beam_l (s));
65 s->set_grob_property ("beam", me->self_scm ());
67 add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
72 TODO: fix this for grace notes.
75 Beam::get_interbeam (Grob *me)
77 Real slt = me->paper_l ()->get_var ("stafflinethickness");
78 Real ss = Staff_symbol_referencer::staff_space (me);
79 Real thickness = gh_scm2double (me->get_grob_property ("thickness"))
82 int multiplicity = get_multiplicity (me);
83 Real interbeam = multiplicity < 4
84 ? (2*ss + slt - thickness) / 2.0
85 : (3*ss + slt - thickness) / 3.0;
91 Beam::get_multiplicity (Grob *me)
94 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
96 Grob *sc = unsmob_grob (ly_car (s));
98 if (Stem::has_interface (sc))
99 m = m >? Stem::beam_count (sc, LEFT) >? Stem::beam_count (sc, RIGHT);
104 /* After pre-processing all directions should be set.
105 Several post-processing routines (stem, slur, script) need stem/beam
107 Currenly, this means that beam has set all stem's directions.
108 [Alternatively, stems could set its own directions, according to
109 their beam, during 'final-pre-processing'.] */
110 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
112 Beam::before_line_breaking (SCM smob)
114 Grob *me = unsmob_grob (smob);
116 /* Beams with less than 2 two stems don't make much sense, but could happen
121 For a beam that only has one stem, we try to do some disappearance magic:
122 we revert the flag, and move on to The Eternal Engraving Fields. */
124 int count = visible_stem_count (me);
127 me->warning (_ ("beam has less than two visible stems"));
129 SCM stems = me->get_grob_property ("stems");
130 if (scm_ilength (stems) == 1)
132 me->warning (_ ("Beam has less than two stems. Removing beam."));
134 unsmob_grob (gh_car (stems))->remove_grob_property ("beam");
137 return SCM_UNSPECIFIED;
139 else if (scm_ilength (stems) == 0)
142 return SCM_UNSPECIFIED;
147 if (!Directional_element_interface::get (me))
148 Directional_element_interface::set (me, get_default_dir (me));
150 consider_auto_knees (me);
151 set_stem_directions (me);
152 set_stem_shorten (me);
158 Beam::get_default_dir (Grob *me)
160 Drul_array<int> total;
161 total[UP] = total[DOWN] = 0;
162 Drul_array<int> count;
163 count[UP] = count[DOWN] = 0;
166 Link_array<Item> stems=
167 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
169 for (int i=0; i <stems.size (); i++)
172 Direction sd = Directional_element_interface::get (s);
173 int current = sd ? (1 + d * sd)/2
174 : Stem::get_center_distance (s, (Direction)-d);
181 } while (flip (&d) != DOWN);
183 SCM func = me->get_grob_property ("dir-function");
184 SCM s = gh_call2 (func,
185 gh_cons (gh_int2scm (count[UP]),
186 gh_int2scm (count[DOWN])),
187 gh_cons (gh_int2scm (total[UP]),
188 gh_int2scm (total[DOWN])));
190 if (gh_number_p (s) && gh_scm2int (s))
193 /* If dir is not determined: get default */
194 return to_dir (me->get_grob_property ("neutral-direction"));
198 /* Set all stems with non-forced direction to beam direction.
199 Urg: non-forced should become `without/with unforced' direction,
200 once stem gets cleaned-up. */
202 Beam::set_stem_directions (Grob *me)
204 Link_array<Item> stems
205 =Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
206 Direction d = Directional_element_interface::get (me);
208 for (int i=0; i <stems.size (); i++)
211 SCM force = s->remove_grob_property ("dir-forced");
212 if (!gh_boolean_p (force) || !gh_scm2bool (force))
213 Directional_element_interface::set (s, d);
217 /* Simplistic auto-knees; only consider vertical gap between two
220 `Forced' stem directions are ignored. If you don't want auto-knees,
221 don't set, or unset auto-knee-gap. */
223 Beam::consider_auto_knees (Grob *me)
225 SCM scm = me->get_grob_property ("auto-knee-gap");
227 if (gh_number_p (scm))
231 Real staff_space = Staff_symbol_referencer::staff_space (me);
232 Real gap = gh_scm2double (scm) / staff_space;
234 Direction d = Directional_element_interface::get (me);
235 Link_array<Item> stems=
236 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
238 Grob *common = me->common_refpoint (stems[0], Y_AXIS);
239 for (int i=1; i < stems.size (); i++)
240 if (!Stem::invisible_b (stems[i]))
241 common = common->common_refpoint (stems[i], Y_AXIS);
244 for (int i=1; i < stems.size (); i++)
246 if (!Stem::invisible_b (stems[i-1]))
248 if (Stem::invisible_b (stems[l]))
250 if (Stem::invisible_b (stems[i]))
253 Real left = Stem::extremal_heads (stems[l])[d]
254 ->relative_coordinate (common, Y_AXIS);
255 Real right = Stem::extremal_heads (stems[i])[-d]
256 ->relative_coordinate (common, Y_AXIS);
258 Real dy = right - left;
262 knee_y = (right + left) / 2;
270 for (int i=0; i < stems.size (); i++)
272 if (Stem::invisible_b (stems[i]))
275 Real y = Stem::extremal_heads (stems[i])[d]
276 ->relative_coordinate (common, Y_AXIS);
278 Directional_element_interface::set (s, y < knee_y ? UP : DOWN);
279 s->set_grob_property ("dir-forced", SCM_BOOL_T);
285 /* Set stem's shorten property if unset.
288 take some y-position (chord/beam/nearest?) into account
289 scmify forced-fraction */
291 Beam::set_stem_shorten (Grob *m)
293 Spanner*me = dynamic_cast<Spanner*> (m);
295 Real forced_fraction = forced_stem_count (me) / visible_stem_count (me);
297 int multiplicity = get_multiplicity (me);
299 SCM shorten = me->get_grob_property ("beamed-stem-shorten");
300 if (shorten == SCM_EOL)
303 int sz = scm_ilength (shorten);
305 Real staff_space = Staff_symbol_referencer::staff_space (me);
306 SCM shorten_elt = scm_list_ref (shorten,
307 gh_int2scm (multiplicity <? (sz - 1)));
308 Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
310 /* your similar cute comment here */
311 shorten_f *= forced_fraction;
313 me->set_grob_property ("shorten", gh_double2scm (shorten_f));
316 /* Call list of y-dy-callbacks, that handle setting of
317 grob-properties y, dy.
319 User may set grob-properties: y-position-hs and height-hs
320 (to be fixed) that override the calculated y and dy.
322 Because y and dy cannot be calculated and quanted separately, we
323 always calculate both, then check for user override. */
324 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
326 Beam::after_line_breaking (SCM smob)
328 Grob *me = unsmob_grob (smob);
330 /* Copy to mutable list. */
331 SCM s = ly_deep_copy (me->get_grob_property ("positions"));
332 me->set_grob_property ("positions", s);
334 if (ly_car (s) != SCM_BOOL_F)
335 return SCM_UNSPECIFIED;
337 SCM callbacks = me->get_grob_property ("position-callbacks");
338 for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
339 gh_call1 (ly_car (i), smob);
341 return SCM_UNSPECIFIED;
354 - Make all demerits customisable
355 - One sensible check per demerit
356 - Add demerits for quants per se, as to forbid a specific quant
359 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
361 Beam::quanting (SCM smob)
363 Grob *me = unsmob_grob (smob);
365 SCM s = me->get_grob_property ("positions");
366 Real yl = gh_scm2double (gh_car (s));
367 Real yr = gh_scm2double (gh_cdr (s));
369 Real ss = Staff_symbol_referencer::staff_space (me);
370 Real thickness = gh_scm2double (me->get_grob_property ("thickness")) / ss;
371 Real slt = me->paper_l ()->get_var ("stafflinethickness") / ss;
374 SCM sdy = me->get_grob_property ("least-squares-dy");
375 Real dy_mus = gh_number_p (sdy) ? gh_scm2double (sdy) : 0.0;
378 Real sit = (thickness - slt) / 2;
380 Real hang = 1.0 - (thickness - slt) / 2;
381 Real quants [] = {straddle, sit, inter, hang };
383 int num_quants = int (sizeof (quants)/sizeof (Real));
388 going to REGION_SIZE == 2, yields another 0.6 second with
392 (result indexes between 70 and 575) ? --hwn.
396 const int REGION_SIZE = 3;
397 for (int i = -REGION_SIZE ; i < REGION_SIZE; i++)
398 for (int j = 0; j < num_quants; j++)
400 quantsl.push (i + quants[j] + int (yl));
401 quantsr.push (i + quants[j] + int (yr));
404 Array<Quant_score> qscores;
406 for (int l =0; l < quantsl.size (); l++)
407 for (int r =0; r < quantsr.size (); r++)
419 This is a longish function, but we don't separate this out into
420 neat modular separate subfunctions, as the subfunctions would be
421 called for many values of YL, YR. By precomputing various
422 parameters outside of the loop, we can save a lot of time.
425 for (int i = qscores.size (); i--;)
426 if (qscores[i].demerits < 100)
429 += score_slopes_dy (me, qscores[i].yl, qscores[i].yr,
433 Real rad = Staff_symbol_referencer::staff_radius (me);
434 int multiplicity = get_multiplicity (me);
435 Real interbeam = multiplicity < 4
436 ? (2*ss + slt - thickness) / 2.0
437 : (3*ss + slt - thickness) / 3.0;
439 for (int i = qscores.size (); i--;)
440 if (qscores[i].demerits < 100)
443 += score_forbidden_quants (me, qscores[i].yl, qscores[i].yr,
444 rad, slt, thickness, interbeam,
450 Do stem lengths. These depend on YL and YR linearly, so we can
451 precompute for every stem 2 factors.
453 Link_array<Grob> stems=
454 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
455 Array<Stem_info> stem_infos;
456 Array<Real> lbase_lengths;
457 Array<Real> rbase_lengths;
459 Array<int> directions;
460 for (int i= 0; i < stems.size(); i++)
463 stem_infos.push( Stem::calc_stem_info (s));
465 Real b = calc_stem_y (me, s, Interval (1,0));
466 lbase_lengths.push (b);
468 b = calc_stem_y (me, s, Interval (0,1));
469 rbase_lengths.push (b);
470 directions.push( Directional_element_interface::get( s));
473 for (int i = qscores.size (); i--;)
474 if (qscores[i].demerits < 100)
477 += score_stem_lengths (stems, stem_infos,
478 lbase_lengths, rbase_lengths,
480 me, qscores[i].yl, qscores[i].yr);
486 for (int i = qscores.size (); i--;)
488 if (qscores[i].demerits < best)
490 best = qscores [i].demerits ;
496 me->set_grob_property ("positions",
497 gh_cons (gh_double2scm (qscores[best_idx].yl),
498 gh_double2scm (qscores[best_idx].yr))
504 me->set_grob_property ("quant-score",
505 gh_double2scm (qscores[best_idx].demerits));
506 me->set_grob_property ("best-idx", gh_int2scm (best_idx));
509 return SCM_UNSPECIFIED;
513 Beam::score_stem_lengths (Link_array<Grob>stems,
514 Array<Stem_info> stem_infos,
515 Array<Real> left_factor,
516 Array<Real> right_factor,
517 Array<int> directions,
518 Grob*me, Real yl, Real yr)
520 Real demerit_score = 0.0 ;
522 for (int i=0; i < stems.size (); i++)
525 if (Stem::invisible_b (s))
529 yl * left_factor[i] + right_factor[i]* yr;
531 Stem_info info = stem_infos[i];
532 Direction d = Direction (directions[i]);
534 demerit_score += 500 * ( 0 >? (info.min_y - d * current_y));
535 demerit_score += 500 * ( 0 >? (d * current_y - info.max_y));
537 demerit_score += 5 * shrink_extra_weight (d * current_y - info.ideal_y);
540 demerit_score *= 2.0 /stems.size ();
542 return demerit_score;
546 Beam::score_slopes_dy (Grob *me, Real yl, Real yr,
547 Real dy_mus, Real dy_damp)
552 if (sign (dy_damp) != sign (dy))
557 dem += 400* (0 >? (fabs (dy) - fabs (dy_mus)));
560 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy))* 10;
567 return x - floor (x);
571 Beam::score_forbidden_quants (Grob*me,
575 Real thickness, Real interbeam,
581 if (fabs (yl) < rad && fabs ( my_modf (yl) - 0.5) < 1e-3)
583 if (fabs (yr) < rad && fabs ( my_modf (yr) - 0.5) < 1e-3)
586 // todo: use multiplicity of outer stems.
587 if (multiplicity >= 2)
591 Real sit = (thickness - slt) / 2;
593 Real hang = 1.0 - (thickness - slt) / 2;
595 Direction dir = Directional_element_interface::get (me);
596 if (fabs (yl - dir * interbeam) < rad
597 && fabs (my_modf (yl) - inter) < 1e-3)
599 if (fabs (yr - dir * interbeam) < rad
600 && fabs (my_modf (yr) - inter) < 1e-3)
606 Can't we simply compute the distance between the nearest
607 staffline and the secondary beam? That would get rid of the
608 silly case analysis here (which is probably not when we have
609 different beam-thicknesses.)
614 // hmm, without Interval/Drul_array, you get ~ 4x same code...
615 if (fabs (yl - dir * interbeam) < rad + inter)
617 if (dir == UP && dy <= eps
618 && fabs (my_modf (yl) - sit) < eps)
621 if (dir == DOWN && dy >= eps
622 && fabs (my_modf (yl) - hang) < eps)
626 if (fabs (yr - dir * interbeam) < rad + inter)
628 if (dir == UP && dy >= eps
629 && fabs (my_modf (yr) - sit) < eps)
632 if (dir == DOWN && dy <= eps
633 && fabs (my_modf (yr) - hang) < eps)
637 if (multiplicity >= 3)
639 if (fabs (yl - 2 * dir * interbeam) < rad + inter)
641 if (dir == UP && dy <= eps
642 && fabs (my_modf (yl) - straddle) < eps)
645 if (dir == DOWN && dy >= eps
646 && fabs (my_modf (yl) - straddle) < eps)
650 if (fabs (yr - 2 * dir * interbeam) < rad + inter)
652 if (dir == UP && dy >= eps
653 && fabs (my_modf (yr) - straddle) < eps)
656 if (dir == DOWN && dy <= eps
657 && fabs (my_modf (yr) - straddle) < eps)
668 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
670 Beam::least_squares (SCM smob)
672 Grob *me = unsmob_grob (smob);
674 int count = visible_stem_count (me);
679 me->set_grob_property ("positions", ly_interval2scm (pos));
680 return SCM_UNSPECIFIED;
683 Direction dir = Directional_element_interface::get (me);
685 Interval ideal (Stem::calc_stem_info (first_visible_stem (me)).ideal_y,
686 Stem::calc_stem_info (last_visible_stem (me)).ideal_y);
690 Interval chord (Stem::chord_start_f (first_visible_stem (me)),
691 Stem::chord_start_f (last_visible_stem (me)));
695 TODO : use scoring for this.
697 complicated, because we take stem-info.ideal for determining
701 /* Make simple beam on middle line have small tilt */
702 if (!ideal[LEFT] && chord.delta () && count == 2)
704 Direction d = (Direction) (sign (chord.delta ()) * dir);
705 pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2
718 Array<Offset> ideals;
720 // ugh -> use commonx
721 Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
722 Link_array<Item> stems=
723 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
725 for (int i=0; i < stems.size (); i++)
728 if (Stem::invisible_b (s))
730 ideals.push (Offset (s->relative_coordinate (0, X_AXIS) - x0,
731 Stem::calc_stem_info (s).ideal_y));
735 minimise_least_squares (&dydx, &y, ideals);
737 Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
739 me->set_grob_property ("least-squares-dy", gh_double2scm (dy * dir));
741 pos = Interval (y*dir, (y+dy) * dir);
744 me->set_grob_property ("positions", ly_interval2scm (pos));
745 return SCM_UNSPECIFIED;
748 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
750 Beam::check_concave (SCM smob)
752 Grob *me = unsmob_grob (smob);
754 Link_array<Item> stems =
755 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
757 for (int i = 0; i < stems.size ();)
759 if (Stem::invisible_b (stems[i]))
765 if (stems.size () < 3)
766 return SCM_UNSPECIFIED;
768 /* Concaveness try #2: Sum distances of inner noteheads that
769 fall outside the interval of the two outer noteheads */
771 Interval iv (Stem::chord_start_f (stems[0]),
772 Stem::chord_start_f (stems.top ()));
774 if (iv[MAX] < iv[MIN])
777 for (int i = 1; i < stems.size () - 1; i++)
780 Real f = Stem::chord_start_f (stems[i]);
781 if ((c = f - iv[MAX]) > 0)
783 else if ((c = f - iv[MIN]) < 0)
787 Direction dir = Directional_element_interface::get (me);
790 Real concaveness = concave / (stems.size () - 2);
791 /* ugh: this is the a kludge to get input/regression/beam-concave.ly
792 to behave as baerenreiter. */
793 concaveness /= (stems.size () - 2);
795 Real r = gh_scm2double (me->get_grob_property ("concaveness-threshold"));
797 /* TODO: some sort of damping iso -> plain horizontal */
800 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
801 Real r = pos.linear_combination (0);
802 me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
803 me->set_grob_property ("least-squares-dy", gh_double2scm (0));
806 return SCM_UNSPECIFIED;
809 /* This neat trick is by Werner Lemberg,
810 damped = tanh (slope)
811 corresponds with some tables in [Wanske] CHECKME */
812 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
814 Beam::slope_damping (SCM smob)
816 Grob *me = unsmob_grob (smob);
818 if (visible_stem_count (me) <= 1)
819 return SCM_UNSPECIFIED;
821 SCM s = me->get_grob_property ("damping");
822 int damping = gh_scm2int (s);
826 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
827 Real dy = pos.delta ();
829 // ugh -> use commonx
830 Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS)
831 - first_visible_stem (me)->relative_coordinate (0, X_AXIS);
832 Real dydx = dy && dx ? dy/dx : 0;
833 dydx = 0.6 * tanh (dydx) / damping;
835 Real damped_dy = dydx * dx;
836 pos[LEFT] += (dy - damped_dy) / 2;
837 pos[RIGHT] -= (dy - damped_dy) / 2;
839 me->set_grob_property ("positions", ly_interval2scm (pos));
841 return SCM_UNSPECIFIED;
844 MAKE_SCHEME_CALLBACK (Beam, end_after_line_breaking, 1);
846 Beam::end_after_line_breaking (SCM smob)
848 Grob *me = unsmob_grob (smob);
849 set_stem_lengths (me);
851 return SCM_UNSPECIFIED;
855 Calculate the Y position of the stem-end, given the Y-left, Y-right
856 in POS, and for stem S.
859 Beam::calc_stem_y (Grob *me, Grob* s, Interval pos)
861 int beam_multiplicity = get_multiplicity (me);
862 int stem_multiplicity = (Stem::flag_i (s) - 2) >? 0;
864 Real thick = gh_scm2double (me->get_grob_property ("thickness"));
865 Real interbeam = get_interbeam (me);
867 // ugh -> use commonx
868 Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
869 Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
870 Real dy = pos.delta ();
871 Real stem_y = (dy && dx
872 ? (s->relative_coordinate (0, X_AXIS) - x0) / dx
877 Direction dir = Directional_element_interface::get (me);
878 Direction sdir = Directional_element_interface::get (s);
883 stem_y -= dir * (thick / 2 + (beam_multiplicity - 1) * interbeam);
885 // huh, why not for first visible?
887 Grob *last_visible = last_visible_stem (me);
890 if ( Staff_symbol_referencer::staff_symbol_l (s)
891 != Staff_symbol_referencer::staff_symbol_l (last_visible))
892 stem_y += Directional_element_interface::get (me)
893 * (beam_multiplicity - stem_multiplicity) * interbeam;
896 programming_error ("No last visible stem");
903 Hmm. At this time, beam position and slope are determined. Maybe,
904 stem directions and length should set to relative to the chord's
905 position of the beam. */
907 Beam::set_stem_lengths (Grob *me)
909 Link_array<Item> stems=
910 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
912 if (stems.size () <= 1)
915 Grob *common = me->common_refpoint (stems[0], Y_AXIS);
916 for (int i=1; i < stems.size (); i++)
917 if (!Stem::invisible_b (stems[i]))
918 common = common->common_refpoint (stems[i], Y_AXIS);
920 Direction dir = Directional_element_interface::get (me);
921 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
922 Real staff_space = Staff_symbol_referencer::staff_space (me);
923 Real thick = gh_scm2double (me->get_grob_property ("thickness"));
924 bool ps_testing = to_boolean (ly_symbol2scm ("ps-testing"));
925 for (int i=0; i < stems.size (); i++)
928 if (Stem::invisible_b (s))
931 Real stem_y = calc_stem_y (me, s, pos);
933 // doesn't play well with dvips
935 if (Stem::get_direction (s) == dir)
936 stem_y += Stem::get_direction (s) * thick / 2;
938 /* caution: stem measures in staff-positions */
939 Real id = me->relative_coordinate (common, Y_AXIS)
940 - stems[i]->relative_coordinate (common, Y_AXIS);
941 Stem::set_stemend (s, (stem_y + id) / staff_space * 2);
946 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
948 Link_array<Grob> stems=
949 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
952 for (int i=0; i < stems.size (); i++)
956 /* Don't overwrite user override (?) */
957 if (Stem::beam_count (stems[i], d) == -1
958 /* Don't set beaming for outside of outer stems */
959 && ! (d == LEFT && i == 0)
960 && ! (d == RIGHT && i == stems.size () -1))
962 int b = beaming->infos_.elem (i).beams_i_drul_[d];
963 Stem::set_beaming (stems[i], b, d);
966 while (flip (&d) != LEFT);
973 beams to go with one stem.
978 Beam::stem_beams (Grob *me, Item *here, Item *next, Item *prev, Real dydx)
980 // ugh -> use commonx
982 && ! (next->relative_coordinate (0, X_AXIS)
983 > here->relative_coordinate (0, X_AXIS)))
985 && ! (prev->relative_coordinate (0, X_AXIS)
986 < here->relative_coordinate (0, X_AXIS))))
987 programming_error ("Beams are not left-to-right");
989 Real thick = gh_scm2double (me->get_grob_property ("thickness"));
990 Real bdy = get_interbeam (me);
996 if (!Stem::first_head (here))
999 int t = Stem::type_i (here);
1001 SCM proc = me->get_grob_property ("flag-width-function");
1002 SCM result = gh_call1 (proc, gh_int2scm (t));
1003 nw_f = gh_scm2double (result);
1007 Direction dir = Directional_element_interface::get (me);
1009 /* [Tremolo] beams on whole notes may not have direction set? */
1011 dir = Directional_element_interface::get (here);
1014 /* half beams extending to the left. */
1017 int lhalfs= lhalfs = Stem::beam_count (here, LEFT)
1018 - Stem::beam_count (prev, RIGHT);
1019 int lwholebeams= Stem::beam_count (here, LEFT)
1020 <? Stem::beam_count (prev, RIGHT);
1022 /* Half beam should be one note-width,
1023 but let's make sure two half-beams never touch */
1025 // FIXME: TODO (check) stem width / sloped beams
1026 Real w = here->relative_coordinate (0, X_AXIS)
1027 - prev->relative_coordinate (0, X_AXIS);
1028 Real stem_w = gh_scm2double (prev->get_grob_property ("thickness"))
1030 * me->paper_l ()->get_var ("stafflinethickness");
1034 if (lhalfs) // generates warnings if not
1035 a = Lookup::beam (dydx, w + stem_w, thick);
1036 a.translate (Offset (-w, -w * dydx));
1037 a.translate_axis (-stem_w/2, X_AXIS);
1038 for (int j = 0; j < lhalfs; j++)
1041 b.translate_axis (-dir * bdy * (lwholebeams+j), Y_AXIS);
1042 leftbeams.add_molecule (b);
1048 int rhalfs = Stem::beam_count (here, RIGHT)
1049 - Stem::beam_count (next, LEFT);
1050 int rwholebeams= Stem::beam_count (here, RIGHT)
1051 <? Stem::beam_count (next, LEFT);
1053 Real w = next->relative_coordinate (0, X_AXIS)
1054 - here->relative_coordinate (0, X_AXIS);
1056 Real stem_w = gh_scm2double (next->get_grob_property ("thickness"))
1058 * me->paper_l ()->get_var ("stafflinethickness");
1060 Molecule a = Lookup::beam (dydx, w + stem_w, thick);
1061 a.translate_axis (- stem_w/2, X_AXIS);
1065 SCM gap = me->get_grob_property ("gap");
1066 if (gh_number_p (gap))
1068 int gap_i = gh_scm2int ((gap));
1069 int nogap = rwholebeams - gap_i;
1071 for (; j < nogap; j++)
1074 b.translate_axis (-dir * bdy * j, Y_AXIS);
1075 rightbeams.add_molecule (b);
1077 if (Stem::invisible_b (here))
1082 a = Lookup::beam (dydx, w + stem_w, thick);
1085 for (; j < rwholebeams; j++)
1089 if (Stem::invisible_b (here))
1090 // ugh, see chord-tremolo.ly
1091 tx = (-dir + 1) / 2 * nw_f * 1.5 + gap_f/4;
1094 b.translate (Offset (tx, -dir * bdy * j));
1095 rightbeams.add_molecule (b);
1100 a = Lookup::beam (dydx, w, thick);
1102 for (; j < rwholebeams + rhalfs; j++)
1105 b.translate_axis (- dir * bdy * j, Y_AXIS);
1106 rightbeams.add_molecule (b);
1110 leftbeams.add_molecule (rightbeams);
1112 /* Does beam quanting think of the asymetry of beams?
1113 Refpoint is on bottom of symbol. (FIXTHAT) --hwn. */
1118 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
1120 Beam::brew_molecule (SCM smob)
1122 Grob *me =unsmob_grob (smob);
1125 if (!gh_pair_p (me->get_grob_property ("stems")))
1128 Link_array<Item>stems =
1129 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1130 if (visible_stem_count (me))
1132 // ugh -> use commonx
1133 x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS);
1134 dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0;
1138 x0 = stems[0]->relative_coordinate (0, X_AXIS);
1139 dx = stems.top ()->relative_coordinate (0, X_AXIS) - x0;
1142 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1143 Real dy = pos.delta ();
1144 Real dydx = dy && dx ? dy/dx : 0;
1146 for (int i=0; i < stems.size (); i++)
1148 Item *item = stems[i];
1149 Item *prev = (i > 0)? stems[i-1] : 0;
1150 Item *next = (i < stems.size ()-1) ? stems[i+1] :0;
1152 Molecule sb = stem_beams (me, item, next, prev, dydx);
1153 Real x = item->relative_coordinate (0, X_AXIS) - x0;
1154 sb.translate (Offset (x, x * dydx + pos[LEFT]));
1155 mol.add_molecule (sb);
1158 mol.translate_axis (x0
1159 - dynamic_cast<Spanner*> (me)
1160 ->get_bound (LEFT)->relative_coordinate (0, X_AXIS),
1166 This code prints the demerits for each beam. Perhaps this
1167 should be switchable for those who want to twiddle with the
1173 str += to_str (gh_scm2int (me->get_grob_property ("best-idx")));
1176 str += to_str (gh_scm2double (me->get_grob_property ("quant-score")),
1179 SCM properties = Font_interface::font_alist_chain (me);
1181 Molecule tm = Text_item::text2molecule (me, gh_str02scm (str.ch_C ()), properties);
1182 mol.add_at_edge (Y_AXIS, UP, tm, 5.0);
1185 return mol.smobbed_copy ();
1189 Beam::forced_stem_count (Grob *me)
1191 Link_array<Item>stems =
1192 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1194 for (int i=0; i < stems.size (); i++)
1198 if (Stem::invisible_b (s))
1201 if (((int)Stem::chord_start_f (s))
1202 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1214 use filter and standard list functions.
1218 Beam::visible_stem_count (Grob *me)
1220 Link_array<Item>stems =
1221 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1223 for (int i = stems.size (); i--;)
1225 if (!Stem::invisible_b (stems[i]))
1232 Beam::first_visible_stem (Grob *me)
1234 Link_array<Item>stems =
1235 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1237 for (int i = 0; i < stems.size (); i++)
1239 if (!Stem::invisible_b (stems[i]))
1246 Beam::last_visible_stem (Grob *me)
1248 Link_array<Item>stems =
1249 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1250 for (int i = stems.size (); i--;)
1252 if (!Stem::invisible_b (stems[i]))
1262 handle rest under beam (do_post: beams are calculated now)
1263 what about combination of collisions and rest under beam.
1267 rest -> stem -> beam -> interpolate_y_position ()
1269 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1271 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1273 Grob *rest = unsmob_grob (element_smob);
1274 Axis a = (Axis) gh_scm2int (axis);
1276 assert (a == Y_AXIS);
1278 Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1281 return gh_double2scm (0.0);
1282 Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1284 || !Beam::has_interface (beam)
1285 || !Beam::visible_stem_count (beam))
1286 return gh_double2scm (0.0);
1288 // make callback for rest from this.
1289 // todo: make sure this calced already.
1291 // Interval pos = ly_scm2interval (beam->get_grob_property ("positions"));
1292 Interval pos (0, 0);
1293 SCM s = beam->get_grob_property ("positions");
1294 if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1295 pos = ly_scm2interval (s);
1297 Real dy = pos.delta ();
1298 // ugh -> use commonx
1299 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1300 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1301 Real dydx = dy && dx ? dy/dx : 0;
1303 Direction d = Stem::get_direction (stem);
1304 Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + pos[LEFT];
1306 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1309 Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space; // refp??
1312 = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1314 minimum_dist + -d * (beamy - rest_dim) >? 0;
1316 int stafflines = Staff_symbol_referencer::line_count (rest);
1318 // move discretely by half spaces.
1319 int discrete_dist = int (ceil (dist));
1321 // move by whole spaces inside the staff.
1322 if (discrete_dist < stafflines+1)
1323 discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1325 return gh_double2scm (-d * discrete_dist);
1330 Beam::has_interface (Grob *me)
1332 return me->has_interface (ly_symbol2scm ("beam-interface"));