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>
15 * Use Number_pair i.s.o Interval to represent (yl, yr).
19 - Stems run to the Y-center of the beam.
21 - beam_space is the offset between Y centers of the beam.
26 #include <math.h> // tanh.
28 #include "molecule.hh"
29 #include "directional-element-interface.hh"
33 #include "least-squares.hh"
35 #include "paper-def.hh"
37 #include "group-interface.hh"
38 #include "staff-symbol-referencer.hh"
44 #define DEBUG_QUANTING 0
48 #include "text-item.hh" // debug output.
49 #include "font-interface.hh" // debug output.
53 const int INTER_QUANT_PENALTY = 1000;
54 const int SECONDARY_BEAM_DEMERIT = 15;
55 const int STEM_LENGTH_DEMERIT_FACTOR = 5;
56 // possibly ridiculous, but too short stems just won't do
57 const int STEM_LENGTH_LIMIT_PENALTY = 5000;
58 const int DAMPING_DIRECTIION_PENALTY = 800;
59 const int MUSICAL_DIRECTION_FACTOR = 400;
60 const int IDEAL_SLOPE_FACTOR = 10;
61 const int REGION_SIZE = 2;
65 shrink_extra_weight (Real x)
67 return fabs (x) * ((x < 0) ? 1.5 : 1.0);
72 int_list_to_slice (SCM l)
76 for (; gh_pair_p (l); l = gh_cdr (l))
78 if (gh_number_p (gh_car (l)))
79 s.add_point (gh_scm2int (gh_car (l)));
87 stem_beam_multiplicity (Grob *stem)
89 SCM beaming= stem->get_grob_property ("beaming");
90 Slice l = int_list_to_slice (gh_car (beaming));
91 Slice r = int_list_to_slice (gh_cdr (beaming));
98 Beam::add_stem (Grob *me, Grob *s)
100 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
102 s->add_dependency (me);
104 assert (!Stem::beam_l (s));
105 s->set_grob_property ("beam", me->self_scm ());
107 add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
111 Beam::get_beam_space (Grob *me)
113 SCM func = me->get_grob_property ("space-function");
114 SCM s = gh_call2 (func, me->self_scm (), gh_int2scm (get_beam_count (me)));
115 return gh_scm2double (s);
122 Beam::get_beam_count (Grob *me)
125 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
127 Grob *sc = unsmob_grob (ly_car (s));
129 m = m >? (stem_beam_multiplicity (sc).length () + 1);
134 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
136 Beam::space_function (SCM smob, SCM beam_count)
138 Grob *me = unsmob_grob (smob);
140 Real staff_space = Staff_symbol_referencer::staff_space (me);
141 Real line = me->paper_l ()->get_var ("linethickness");
142 Real thickness = gh_scm2double (me->get_grob_property ("thickness"))
145 Real beam_space = gh_scm2int (beam_count) < 4
146 ? (2*staff_space + line - thickness) / 2.0
147 : (3*staff_space + line - thickness) / 3.0;
149 return gh_double2scm (beam_space);
153 /* After pre-processing all directions should be set.
154 Several post-processing routines (stem, slur, script) need stem/beam
156 Currenly, this means that beam has set all stem's directions.
157 [Alternatively, stems could set its own directions, according to
158 their beam, during 'final-pre-processing'.] */
159 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
161 Beam::before_line_breaking (SCM smob)
163 Grob *me = unsmob_grob (smob);
165 /* Beams with less than 2 two stems don't make much sense, but could happen
170 For a beam that only has one stem, we try to do some disappearance magic:
171 we revert the flag, and move on to The Eternal Engraving Fields. */
173 int count = visible_stem_count (me);
176 me->warning (_ ("beam has less than two visible stems"));
178 SCM stems = me->get_grob_property ("stems");
179 if (scm_ilength (stems) == 1)
181 me->warning (_ ("Beam has less than two stems. Removing beam."));
183 unsmob_grob (gh_car (stems))->remove_grob_property ("beam");
186 return SCM_UNSPECIFIED;
188 else if (scm_ilength (stems) == 0)
191 return SCM_UNSPECIFIED;
196 Direction d = get_default_dir (me);
198 consider_auto_knees (me, d);
199 set_stem_directions (me, d);
203 set_stem_shorten (me);
212 Beam::connect_beams (Grob *me)
214 Link_array<Grob> stems=
215 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
218 last_int.set_empty();
219 for (int i = 0; i< stems.size(); i++)
221 Grob *this_stem = stems[i];
222 SCM this_beaming = this_stem->get_grob_property ("beaming");
224 Direction this_dir = Directional_element_interface::get(this_stem);
227 int start_point = last_int [this_dir];
233 if (d == RIGHT && i == stems.size()-1)
236 new_slice.set_empty();
237 SCM s = index_get_cell (this_beaming, d);
238 for (; gh_pair_p (s); s = gh_cdr (s))
241 start_point - this_dir * gh_scm2int (gh_car (s));
243 new_slice.add_point (new_beam_pos);
244 gh_set_car_x (s, gh_int2scm (new_beam_pos));
247 while (flip (&d) != LEFT);
249 last_int = new_slice;
253 SCM s = gh_cdr (this_beaming);
254 for (; gh_pair_p (s); s = gh_cdr (s))
256 int np = - this_dir * gh_scm2int (gh_car(s));
257 gh_set_car_x (s, gh_int2scm (np));
258 last_int.add_point (np);
264 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
266 Beam::brew_molecule (SCM grob)
268 Grob *me = unsmob_grob (grob);
269 Link_array<Grob> stems=
270 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
271 Grob* xcommon = common_refpoint_of_array (stems, me, X_AXIS);
274 if (visible_stem_count (me))
276 // ugh -> use commonx
277 x0 = first_visible_stem (me)->relative_coordinate (xcommon, X_AXIS);
278 dx = last_visible_stem (me)->relative_coordinate (xcommon, X_AXIS) - x0;
282 x0 = stems[0]->relative_coordinate (xcommon, X_AXIS);
283 dx = stems.top ()->relative_coordinate (xcommon, X_AXIS) - x0;
286 SCM posns = me->get_grob_property ("positions");
288 if (!ly_number_pair_p (posns))
290 programming_error ("No beam posns");
291 pos = Interval (0,0);
294 pos= ly_scm2interval (posns);
296 Real dy = pos.delta ();
297 Real dydx = dy && dx ? dy/dx : 0;
299 Real thick = gh_scm2double (me->get_grob_property ("thickness"));
300 Real bdy = get_beam_space (me);
302 SCM last_beaming = SCM_EOL;;
303 Real last_xposn = -1;
304 Real last_width = -1 ;
309 Real lt = me->paper_l ()->get_var ("linethickness");
310 for (int i = 0; i< stems.size(); i++)
314 SCM this_beaming = st->get_grob_property ("beaming");
315 Real xposn = st->relative_coordinate (xcommon, X_AXIS);
316 Real stem_width = gh_scm2double (st->get_grob_property ("thickness")) *lt;
320 SCM left = gh_cdr (last_beaming);
321 SCM right = gh_car (this_beaming);
323 Array<int> fullbeams;
324 Array<int> lfliebertjes;
325 Array<int> rfliebertjes;
328 gh_pair_p (s); s =gh_cdr (s))
330 int b = gh_scm2int (gh_car (s));
331 if (scm_memq (gh_car(s), right) != SCM_BOOL_F)
337 lfliebertjes.push (b);
341 gh_pair_p (s); s =gh_cdr (s))
343 int b = gh_scm2int (gh_car (s));
344 if (scm_memq (gh_car(s), left) == SCM_BOOL_F)
346 rfliebertjes.push (b);
351 Real w = xposn - last_xposn;
352 Real stem_offset = 0.0;
353 Real width_corr = 0.0;
356 stem_offset -= last_width/2;
357 width_corr += last_width/2;
360 if (i == stems.size() -1)
362 width_corr += stem_width/2;
365 Molecule whole = Lookup::beam (dydx, w + width_corr, thick);
366 for (int j = fullbeams.size(); j--;)
369 b.translate_axis (last_xposn - x0 + stem_offset, X_AXIS);
370 b.translate_axis (dydx * (last_xposn - x0) + bdy * fullbeams[j], Y_AXIS);
371 the_beam.add_molecule (b);
374 if (lfliebertjes.size() || rfliebertjes.size())
378 if (!Stem::first_head (st))
382 int t = Stem::duration_log (st);
384 SCM proc = me->get_grob_property ("flag-width-function");
385 SCM result = gh_call1 (proc, gh_int2scm (t));
386 nw_f = gh_scm2double (result);
389 /* Half beam should be one note-width,
390 but let's make sure two half-beams never touch */
392 Real w = xposn - last_xposn;
395 Molecule half = Lookup::beam (dydx, w, thick);
396 for (int j = lfliebertjes.size(); j--;)
399 b.translate_axis (last_xposn - x0, X_AXIS);
400 b.translate_axis (dydx * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
401 the_beam.add_molecule (b);
403 for (int j = rfliebertjes.size(); j--;)
406 b.translate_axis (xposn - x0 - w , X_AXIS);
407 b.translate_axis (dydx * (xposn-x0 -w) + bdy * rfliebertjes[j], Y_AXIS);
408 the_beam.add_molecule (b);
414 last_width = stem_width;
415 last_beaming = this_beaming;
418 the_beam.translate_axis (x0 - me->relative_coordinate (xcommon, X_AXIS), X_AXIS);
419 the_beam.translate_axis (pos[LEFT], Y_AXIS);
424 This code prints the demerits for each beam. Perhaps this
425 should be switchable for those who want to twiddle with the
431 str += to_str (gh_scm2int (me->get_grob_property ("best-idx")));
434 str += to_str (gh_scm2double (me->get_grob_property ("quant-score")),
437 SCM properties = Font_interface::font_alist_chain (me);
440 Molecule tm = Text_item::text2molecule (me, ly_str02scm (str.ch_C ()), properties);
441 the_beam.add_at_edge (Y_AXIS, UP, tm, 5.0);
447 return the_beam.smobbed_copy();
454 Beam::get_default_dir (Grob *me)
456 Drul_array<int> total;
457 total[UP] = total[DOWN] = 0;
458 Drul_array<int> count;
459 count[UP] = count[DOWN] = 0;
462 Link_array<Item> stems=
463 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
465 for (int i=0; i <stems.size (); i++)
468 Direction sd = Directional_element_interface::get (s);
470 int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
471 int current = sd ? (1 + d * sd)/2 : center_distance;
478 } while (flip (&d) != DOWN);
480 SCM func = me->get_grob_property ("dir-function");
481 SCM s = gh_call2 (func,
482 gh_cons (gh_int2scm (count[UP]),
483 gh_int2scm (count[DOWN])),
484 gh_cons (gh_int2scm (total[UP]),
485 gh_int2scm (total[DOWN])));
487 if (gh_number_p (s) && gh_scm2int (s))
490 /* If dir is not determined: get default */
491 return to_dir (me->get_grob_property ("neutral-direction"));
495 /* Set all stems with non-forced direction to beam direction.
496 Urg: non-forced should become `without/with unforced' direction,
497 once stem gets cleaned-up. */
499 Beam::set_stem_directions (Grob *me, Direction d)
501 Link_array<Item> stems
502 =Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
504 for (int i=0; i <stems.size (); i++)
507 SCM force = s->remove_grob_property ("dir-forced");
508 if (!gh_boolean_p (force) || !gh_scm2bool (force))
509 Directional_element_interface::set (s, d);
513 /* Simplistic auto-knees; only consider vertical gap between two
516 `Forced' stem directions are ignored. If you don't want auto-knees,
517 don't set, or unset auto-knee-gap. */
519 Beam::consider_auto_knees (Grob *me, Direction d)
521 SCM scm = me->get_grob_property ("auto-knee-gap");
523 if (gh_number_p (scm))
527 Real staff_space = Staff_symbol_referencer::staff_space (me);
528 Real gap = gh_scm2double (scm) / staff_space;
531 Link_array<Item> stems=
532 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
534 Grob *common = me->common_refpoint (stems[0], Y_AXIS);
535 for (int i=1; i < stems.size (); i++)
536 if (!Stem::invisible_b (stems[i]))
537 common = common->common_refpoint (stems[i], Y_AXIS);
540 for (int i=1; i < stems.size (); i++)
542 if (!Stem::invisible_b (stems[i-1]))
544 if (Stem::invisible_b (stems[l]))
546 if (Stem::invisible_b (stems[i]))
549 Real left = Stem::extremal_heads (stems[l])[d]
550 ->relative_coordinate (common, Y_AXIS);
551 Real right = Stem::extremal_heads (stems[i])[-d]
552 ->relative_coordinate (common, Y_AXIS);
554 Real dy = right - left;
558 knee_y = (right + left) / 2;
566 for (int i=0; i < stems.size (); i++)
568 if (Stem::invisible_b (stems[i]))
571 Real y = Stem::extremal_heads (stems[i])[d]
572 ->relative_coordinate (common, Y_AXIS);
574 Directional_element_interface::set (s, y < knee_y ? UP : DOWN);
575 s->set_grob_property ("dir-forced", SCM_BOOL_T);
581 /* Set stem's shorten property if unset.
584 take some y-position (chord/beam/nearest?) into account
585 scmify forced-fraction
589 why is shorten stored in beam, and not directly in stem?
593 Beam::set_stem_shorten (Grob *m)
595 Spanner*me = dynamic_cast<Spanner*> (m);
597 Real forced_fraction = forced_stem_count (me) / visible_stem_count (me);
599 int beam_count = get_beam_count (me);
601 SCM shorten = me->get_grob_property ("beamed-stem-shorten");
602 if (shorten == SCM_EOL)
605 int sz = scm_ilength (shorten);
607 Real staff_space = Staff_symbol_referencer::staff_space (me);
608 SCM shorten_elt = scm_list_ref (shorten,
609 gh_int2scm (beam_count <? (sz - 1)));
610 Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
612 /* your similar cute comment here */
613 shorten_f *= forced_fraction;
616 me->set_grob_property ("shorten", gh_double2scm (shorten_f));
619 /* Call list of y-dy-callbacks, that handle setting of
623 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
625 Beam::after_line_breaking (SCM smob)
627 Grob *me = unsmob_grob (smob);
629 /* Copy to mutable list. */
630 SCM s = ly_deep_copy (me->get_grob_property ("positions"));
631 me->set_grob_property ("positions", s);
633 if (ly_car (s) != SCM_BOOL_F)
634 return SCM_UNSPECIFIED;
636 // one wonders if such genericity is necessary --hwn.
637 SCM callbacks = me->get_grob_property ("position-callbacks");
638 for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
639 gh_call1 (ly_car (i), smob);
641 set_stem_lengths (me);
642 return SCM_UNSPECIFIED;
656 - Make all demerits customisable
658 - One sensible check per demerit (what's this --hwn)
660 - Add demerits for quants per se, as to forbid a specific quant
664 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
666 Beam::quanting (SCM smob)
668 Grob *me = unsmob_grob (smob);
670 SCM s = me->get_grob_property ("positions");
671 Real yl = gh_scm2double (gh_car (s));
672 Real yr = gh_scm2double (gh_cdr (s));
674 Real ss = Staff_symbol_referencer::staff_space (me);
675 Real thickness = gh_scm2double (me->get_grob_property ("thickness")) / ss;
676 Real slt = me->paper_l ()->get_var ("linethickness") / ss;
679 SCM sdy = me->get_grob_property ("least-squares-dy");
680 Real dy_mus = gh_number_p (sdy) ? gh_scm2double (sdy) : 0.0;
683 Real sit = (thickness - slt) / 2;
685 Real hang = 1.0 - (thickness - slt) / 2;
686 Real quants [] = {straddle, sit, inter, hang };
688 int num_quants = int (sizeof (quants)/sizeof (Real));
693 going to REGION_SIZE == 2, yields another 0.6 second with
697 (result indexes between 70 and 575) ? --hwn.
704 Do stem computations. These depend on YL and YR linearly, so we can
705 precompute for every stem 2 factors.
707 Link_array<Grob> stems=
708 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
709 Array<Stem_info> stem_infos;
710 Array<Real> lbase_lengths;
711 Array<Real> rbase_lengths;
713 Drul_array<bool> dirs_found(0,0);
715 bool french = to_boolean (me->get_grob_property ("french-beaming"));
716 for (int i= 0; i < stems.size(); i++)
719 stem_infos.push (Stem::calc_stem_info (s));
720 dirs_found[stem_infos.top ().dir_] = true;
722 Real b = calc_stem_y (me, s, Interval (1,0), french && i > 0&& (i < stems.size () -1));
723 lbase_lengths.push (b);
725 Real a = calc_stem_y (me, s, Interval (0,1), french && i > 0&& (i < stems.size () -1));
726 rbase_lengths.push (a);
729 Direction ldir = Direction (stem_infos[0].dir_);
730 Direction rdir = Direction (stem_infos.top ().dir_);
731 bool knee_b = dirs_found[LEFT] && dirs_found[RIGHT];
734 int region_size = REGION_SIZE;
736 Knees are harder, lets try some more possibilities for knees.
741 for (int i = -region_size ; i < region_size; i++)
742 for (int j = 0; j < num_quants; j++)
744 quantsl.push (i + quants[j] + int (yl));
745 quantsr.push (i + quants[j] + int (yr));
748 Array<Quant_score> qscores;
750 for (int l =0; l < quantsl.size (); l++)
751 for (int r =0; r < quantsr.size (); r++)
763 This is a longish function, but we don't separate this out into
764 neat modular separate subfunctions, as the subfunctions would be
765 called for many values of YL, YR. By precomputing various
766 parameters outside of the loop, we can save a lot of time.
769 for (int i = qscores.size (); i--;)
770 if (qscores[i].demerits < 100)
773 += score_slopes_dy (me, qscores[i].yl, qscores[i].yr,
777 Real rad = Staff_symbol_referencer::staff_radius (me);
778 int beam_count = get_beam_count (me);
779 Real beam_space = beam_count < 4
780 ? (2*ss + slt - thickness) / 2.0
781 : (3*ss + slt - thickness) / 3.0;
783 for (int i = qscores.size (); i--;)
784 if (qscores[i].demerits < 100)
787 += score_forbidden_quants (me, qscores[i].yl, qscores[i].yr,
788 rad, slt, thickness, beam_space,
789 beam_count, ldir, rdir);
793 for (int i = qscores.size (); i--;)
794 if (qscores[i].demerits < 100)
797 += score_stem_lengths (stems, stem_infos,
798 lbase_lengths, rbase_lengths,
800 me, qscores[i].yl, qscores[i].yr);
806 for (int i = qscores.size (); i--;)
808 if (qscores[i].demerits < best)
810 best = qscores [i].demerits ;
816 me->set_grob_property ("positions",
817 gh_cons (gh_double2scm (qscores[best_idx].yl),
818 gh_double2scm (qscores[best_idx].yr))
824 me->set_grob_property ("quant-score",
825 gh_double2scm (qscores[best_idx].demerits));
826 me->set_grob_property ("best-idx", gh_int2scm (best_idx));
829 return SCM_UNSPECIFIED;
833 Beam::score_stem_lengths (Link_array<Grob>stems,
834 Array<Stem_info> stem_infos,
835 Array<Real> left_factor,
836 Array<Real> right_factor,
841 Real demerit_score = 0.0 ;
842 Real pen = STEM_LENGTH_LIMIT_PENALTY;
844 for (int i=0; i < stems.size (); i++)
847 if (Stem::invisible_b (s))
851 yl * left_factor[i] + right_factor[i]* yr;
853 Stem_info info = stem_infos[i];
854 Direction d = info.dir_;
857 * ( 0 >? (info.dir_ * (info.shortest_y_ - current_y)));
859 demerit_score += STEM_LENGTH_DEMERIT_FACTOR
860 * shrink_extra_weight (d * current_y - info.dir_ * info.ideal_y_);
863 demerit_score *= 2.0 / stems.size ();
865 return demerit_score;
869 Beam::score_slopes_dy (Grob *me,
871 Real dy_mus, Real dy_damp)
876 if (sign (dy_damp) != sign (dy))
878 dem += DAMPING_DIRECTIION_PENALTY;
881 dem += MUSICAL_DIRECTION_FACTOR * (0 >? (fabs (dy) - fabs (dy_mus)));
882 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy))* IDEAL_SLOPE_FACTOR;
890 return x - floor (x);
894 Beam::score_forbidden_quants (Grob*me,
898 Real thickness, Real beam_space,
900 Direction ldir, Direction rdir)
905 if (fabs (yl) < rad && fabs ( my_modf (yl) - 0.5) < 1e-3)
906 dem += INTER_QUANT_PENALTY;
907 if (fabs (yr) < rad && fabs ( my_modf (yr) - 0.5) < 1e-3)
908 dem += INTER_QUANT_PENALTY;
910 // todo: use beam_count of outer stems.
915 Real sit = (thickness - slt) / 2;
917 Real hang = 1.0 - (thickness - slt) / 2;
920 if (fabs (yl - ldir * beam_space) < rad
921 && fabs (my_modf (yl) - inter) < 1e-3)
922 dem += SECONDARY_BEAM_DEMERIT;
923 if (fabs (yr - rdir * beam_space) < rad
924 && fabs (my_modf (yr) - inter) < 1e-3)
925 dem += SECONDARY_BEAM_DEMERIT;
930 Can't we simply compute the distance between the nearest
931 staffline and the secondary beam? That would get rid of the
932 silly case analysis here (which is probably not when we have
933 different beam-thicknesses.)
939 // hmm, without Interval/Drul_array, you get ~ 4x same code...
940 if (fabs (yl - ldir * beam_space) < rad + inter)
942 if (ldir == UP && dy <= eps
943 && fabs (my_modf (yl) - sit) < eps)
944 dem += SECONDARY_BEAM_DEMERIT;
946 if (ldir == DOWN && dy >= eps
947 && fabs (my_modf (yl) - hang) < eps)
948 dem += SECONDARY_BEAM_DEMERIT;
951 if (fabs (yr - rdir * beam_space) < rad + inter)
953 if (rdir == UP && dy >= eps
954 && fabs (my_modf (yr) - sit) < eps)
955 dem += SECONDARY_BEAM_DEMERIT;
957 if (rdir == DOWN && dy <= eps
958 && fabs (my_modf (yr) - hang) < eps)
959 dem += SECONDARY_BEAM_DEMERIT;
964 if (fabs (yl - 2 * ldir * beam_space) < rad + inter)
966 if (ldir == UP && dy <= eps
967 && fabs (my_modf (yl) - straddle) < eps)
968 dem += SECONDARY_BEAM_DEMERIT;
970 if (ldir == DOWN && dy >= eps
971 && fabs (my_modf (yl) - straddle) < eps)
972 dem += SECONDARY_BEAM_DEMERIT;
975 if (fabs (yr - 2 * rdir * beam_space) < rad + inter)
977 if (rdir == UP && dy >= eps
978 && fabs (my_modf (yr) - straddle) < eps)
979 dem += SECONDARY_BEAM_DEMERIT;
981 if (rdir == DOWN && dy <= eps
982 && fabs (my_modf (yr) - straddle) < eps)
983 dem += SECONDARY_BEAM_DEMERIT;
993 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
995 Beam::least_squares (SCM smob)
997 Grob *me = unsmob_grob (smob);
999 int count = visible_stem_count (me);
1000 Interval pos (0, 0);
1004 me->set_grob_property ("positions", ly_interval2scm (pos));
1005 return SCM_UNSPECIFIED;
1008 Interval ideal (Stem::calc_stem_info (first_visible_stem (me)).ideal_y_,
1009 Stem::calc_stem_info (last_visible_stem (me)).ideal_y_);
1013 Array<Real> x_posns ;
1014 Link_array<Item> stems=
1015 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
1016 Grob *common = stems[0];
1017 for (int i=1; i < stems.size (); i++)
1018 common = stems[i]->common_refpoint (common, X_AXIS);
1020 Real x0 = first_visible_stem (me)->relative_coordinate (common, X_AXIS);
1021 for (int i=0; i < stems.size (); i++)
1025 Real x = s->relative_coordinate (common, X_AXIS) - x0;
1028 Real dx = last_visible_stem (me)->relative_coordinate (common, X_AXIS) - x0;
1034 if (!ideal.delta ())
1036 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
1037 Stem::chord_start_y (last_visible_stem (me)));
1041 TODO -- use scoring for this.
1043 complicated, because we take stem-info.ideal for determining
1046 /* Make simple beam on middle line have small tilt */
1047 if (!ideal[LEFT] && chord.delta () && count == 2)
1053 Direction d = (Direction) (sign (chord.delta ()) * UP);
1054 pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
1069 Array<Offset> ideals;
1070 for (int i=0; i < stems.size (); i++)
1073 if (Stem::invisible_b (s))
1075 ideals.push (Offset (x_posns[i],
1076 Stem::calc_stem_info (s).ideal_y_));
1078 minimise_least_squares (&dydx, &y, ideals);
1081 me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
1082 pos = Interval (y, (y+dy));
1085 me->set_grob_property ("positions", ly_interval2scm (pos));
1087 return SCM_UNSPECIFIED;
1092 We can't combine with previous function, since check concave and
1093 slope damping comes first.
1095 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
1097 Beam::shift_region_to_valid (SCM grob)
1099 Grob *me = unsmob_grob (grob);
1103 Array<Real> x_posns ;
1104 Link_array<Item> stems=
1105 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
1106 Grob *common = stems[0];
1107 for (int i=1; i < stems.size (); i++)
1108 common = stems[i]->common_refpoint (common, X_AXIS);
1110 Grob *fvs = first_visible_stem (me);
1113 return SCM_UNSPECIFIED;
1115 Real x0 =fvs->relative_coordinate (common, X_AXIS);
1116 for (int i=0; i < stems.size (); i++)
1120 Real x = s->relative_coordinate (common, X_AXIS) - x0;
1124 Grob *lvs = last_visible_stem (me);
1126 return SCM_UNSPECIFIED;
1128 Real dx = lvs->relative_coordinate (common, X_AXIS) - x0;
1130 Interval pos = ly_scm2interval ( me->get_grob_property ("positions"));
1131 Real dy = pos.delta();
1137 Shift the positions so that we have a chance of finding good
1138 quants (i.e. no short stem failures.)
1140 Interval feasible_left_point;
1141 feasible_left_point.set_full ();
1142 for (int i=0; i < stems.size (); i++)
1145 if (Stem::invisible_b (s))
1149 Direction d = Stem::get_direction (s);
1152 Real left_y = Stem::calc_stem_info (s).shortest_y_
1153 - dydx * x_posns [i];
1159 feasible_left_point.intersect (flp);
1162 if (feasible_left_point.empty_b())
1164 warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
1166 else if (!feasible_left_point.elem_b(y))
1168 if (isinf (feasible_left_point[DOWN]))
1169 y = feasible_left_point[UP] - REGION_SIZE;
1170 else if (isinf (feasible_left_point[UP]))
1171 y = feasible_left_point[DOWN]+ REGION_SIZE;
1173 y = feasible_left_point.center ();
1175 pos = Interval (y, (y+dy));
1176 me->set_grob_property ("positions", ly_interval2scm (pos));
1177 return SCM_UNSPECIFIED;
1181 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
1183 Beam::check_concave (SCM smob)
1185 Grob *me = unsmob_grob (smob);
1187 Link_array<Item> stems =
1188 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1190 for (int i = 0; i < stems.size ();)
1192 if (Stem::invisible_b (stems[i]))
1198 if (stems.size () < 3)
1199 return SCM_UNSPECIFIED;
1202 /* Concaveness #1: If distance of an inner notehead to line between
1203 two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
1204 beam is concave (Heinz Stolba).
1206 In the case of knees, the line connecting outer heads is often
1207 not related to the beam slope (it may even go in the other
1208 direction). Skip the check when the outer stems point in
1209 different directions. --hwn
1212 bool concaveness1 = false;
1213 SCM gap = me->get_grob_property ("concaveness-gap");
1214 if (gh_number_p (gap)
1215 && Stem::get_direction(stems.top ())
1216 == Stem::get_direction(stems[0]))
1218 Real r1 = gh_scm2double (gap);
1219 Real dy = Stem::chord_start_y (stems.top ())
1220 - Stem::chord_start_y (stems[0]);
1223 Real slope = dy / (stems.size () - 1);
1225 Real y0 = Stem::chord_start_y (stems[0]);
1226 for (int i = 1; i < stems.size () - 1; i++)
1228 Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
1231 concaveness1 = true;
1238 /* Concaveness #2: Sum distances of inner noteheads that fall
1239 outside the interval of the two outer noteheads.
1241 We only do this for beams where first and last stem have the same
1245 Note that "convex" stems compensate for "concave" stems.
1246 (is that intentional?) --hwn.
1249 Real concaveness2 = 0;
1250 SCM thresh = me->get_grob_property ("concaveness-threshold");
1251 Real r2 = infinity_f;
1252 if (!concaveness1 && gh_number_p (thresh)
1253 && Stem::get_direction(stems.top ())
1254 == Stem::get_direction(stems[0]))
1256 r2 = gh_scm2double (thresh);
1258 Direction dir = Stem::get_direction(stems.top ());
1260 Interval iv (Stem::chord_start_y (stems[0]),
1261 Stem::chord_start_y (stems.top ()));
1263 if (iv[MAX] < iv[MIN])
1266 for (int i = 1; i < stems.size () - 1; i++)
1268 Real f = Stem::chord_start_y (stems[i]);
1269 concave += ((f - iv[MAX] ) >? 0) +
1270 ((f - iv[MIN] ) <? 0);
1273 concaveness2 = concave / (stems.size () - 2);
1275 /* ugh: this is the a kludge to get
1276 input/regression/beam-concave.ly to behave as
1280 huh? we're dividing twice (which is not scalable) meaning that
1281 the longer the beam, the more unlikely it will be
1282 concave. Maybe you would even expect the other way around??
1287 concaveness2 /= (stems.size () - 2);
1290 /* TODO: some sort of damping iso -> plain horizontal */
1291 if (concaveness1 || concaveness2 > r2)
1293 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1294 Real r = pos.linear_combination (0);
1295 me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
1296 me->set_grob_property ("least-squares-dy", gh_double2scm (0));
1299 return SCM_UNSPECIFIED;
1302 /* This neat trick is by Werner Lemberg,
1303 damped = tanh (slope)
1304 corresponds with some tables in [Wanske] CHECKME */
1305 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1307 Beam::slope_damping (SCM smob)
1309 Grob *me = unsmob_grob (smob);
1311 if (visible_stem_count (me) <= 1)
1312 return SCM_UNSPECIFIED;
1314 SCM s = me->get_grob_property ("damping");
1315 int damping = gh_scm2int (s);
1319 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1320 Real dy = pos.delta ();
1322 // ugh -> use commonx
1323 Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS)
1324 - first_visible_stem (me)->relative_coordinate (0, X_AXIS);
1325 Real dydx = dy && dx ? dy/dx : 0;
1326 dydx = 0.6 * tanh (dydx) / damping;
1328 Real damped_dy = dydx * dx;
1329 pos[LEFT] += (dy - damped_dy) / 2;
1330 pos[RIGHT] -= (dy - damped_dy) / 2;
1332 me->set_grob_property ("positions", ly_interval2scm (pos));
1334 return SCM_UNSPECIFIED;
1338 where_are_the_whole_beams(SCM beaming)
1342 for( SCM s = gh_car (beaming); gh_pair_p (s) ; s = gh_cdr (s))
1344 if (scm_memq (gh_car (s), gh_cdr (beaming)) != SCM_BOOL_F)
1346 l.add_point (gh_scm2int (gh_car (s)));
1353 Calculate the Y position of the stem-end, given the Y-left, Y-right
1354 in POS, and for stem S.
1356 If CORRECT, correct for beam_count of beam in case of knees.
1359 TODO: junk CORRECT from this.
1362 Beam::calc_stem_y (Grob *me, Grob* s, Interval pos, bool french)
1364 Real thick = gh_scm2double (me->get_grob_property ("thickness"));
1365 Real beam_space = get_beam_space (me);
1367 // ugh -> use commonx
1368 Grob * fvs = first_visible_stem (me);
1369 Grob *lvs = last_visible_stem (me);
1371 Real x0 = fvs ? fvs->relative_coordinate (0, X_AXIS) : 0.0;
1372 Real dx = fvs ? lvs->relative_coordinate (0, X_AXIS) - x0 : 0.0;
1373 Real r = s->relative_coordinate (0, X_AXIS) - x0;
1374 Real dy = pos.delta ();
1375 Real stem_y_beam0 = (dy && dx
1382 Direction my_dir = Directional_element_interface::get (s);
1383 SCM beaming = s->get_grob_property ("beaming");
1385 Real stem_y = stem_y_beam0;
1388 stem_y += beam_space * where_are_the_whole_beams (beaming)[-my_dir];
1392 stem_y += (stem_beam_multiplicity(s)[my_dir]) * beam_space;
1399 Hmm. At this time, beam position and slope are determined. Maybe,
1400 stem directions and length should set to relative to the chord's
1401 position of the beam. */
1403 Beam::set_stem_lengths (Grob *me)
1405 Link_array<Item> stems=
1406 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
1408 if (stems.size () <= 1)
1411 Grob *common = me->common_refpoint (stems[0], Y_AXIS);
1412 for (int i=1; i < stems.size (); i++)
1413 if (!Stem::invisible_b (stems[i]))
1414 common = common->common_refpoint (stems[i], Y_AXIS);
1416 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1417 Real staff_space = Staff_symbol_referencer::staff_space (me);
1419 bool french = to_boolean (me->get_grob_property ("french-beaming"));
1421 for (int i=0; i < stems.size (); i++)
1424 if (Stem::invisible_b (s))
1427 Real stem_y = calc_stem_y (me, s, pos, french && i > 0&& (i < stems.size () -1));
1429 /* caution: stem measures in staff-positions */
1430 Real id = me->relative_coordinate (common, Y_AXIS)
1431 - stems[i]->relative_coordinate (common, Y_AXIS);
1432 Stem::set_stemend (s, (stem_y + id) / staff_space * 2);
1437 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1439 Link_array<Grob> stems=
1440 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1443 for (int i=0; i < stems.size (); i++)
1446 Don't overwrite user settings.
1451 /* Don't set beaming for outside of outer stems */
1452 if ((d == LEFT && i == 0)
1453 ||(d == RIGHT && i == stems.size () -1))
1457 SCM beaming_prop = stems[i]->get_grob_property ("beaming");
1458 if (beaming_prop == SCM_EOL ||
1459 index_get_cell (beaming_prop, d) == SCM_EOL)
1461 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1462 Stem::set_beaming (stems[i], b, d);
1465 while (flip (&d) != LEFT);
1470 Beam::forced_stem_count (Grob *me)
1472 Link_array<Item>stems =
1473 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1475 for (int i=0; i < stems.size (); i++)
1479 if (Stem::invisible_b (s))
1482 if (((int)Stem::chord_start_y (s))
1483 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1493 Beam::visible_stem_count (Grob *me)
1495 Link_array<Item>stems =
1496 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1498 for (int i = stems.size (); i--;)
1500 if (!Stem::invisible_b (stems[i]))
1507 Beam::first_visible_stem (Grob *me)
1509 Link_array<Item>stems =
1510 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1512 for (int i = 0; i < stems.size (); i++)
1514 if (!Stem::invisible_b (stems[i]))
1521 Beam::last_visible_stem (Grob *me)
1523 Link_array<Item>stems =
1524 Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
1525 for (int i = stems.size (); i--;)
1527 if (!Stem::invisible_b (stems[i]))
1537 handle rest under beam (do_post: beams are calculated now)
1538 what about combination of collisions and rest under beam.
1542 rest -> stem -> beam -> interpolate_y_position ()
1544 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1546 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1548 Grob *rest = unsmob_grob (element_smob);
1549 Axis a = (Axis) gh_scm2int (axis);
1551 assert (a == Y_AXIS);
1553 Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1556 return gh_double2scm (0.0);
1557 Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1559 || !Beam::has_interface (beam)
1560 || !Beam::visible_stem_count (beam))
1561 return gh_double2scm (0.0);
1563 // make callback for rest from this.
1564 // todo: make sure this calced already.
1566 // Interval pos = ly_scm2interval (beam->get_grob_property ("positions"));
1567 Interval pos (0, 0);
1568 SCM s = beam->get_grob_property ("positions");
1569 if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1570 pos = ly_scm2interval (s);
1572 Real dy = pos.delta ();
1573 // ugh -> use commonx
1574 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1575 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1576 Real dydx = dy && dx ? dy/dx : 0;
1578 Direction d = Stem::get_direction (stem);
1579 Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + pos[LEFT];
1581 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1584 Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space; // refp??
1587 = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1589 minimum_dist + -d * (beamy - rest_dim) >? 0;
1591 int stafflines = Staff_symbol_referencer::line_count (rest);
1593 // move discretely by half spaces.
1594 int discrete_dist = int (ceil (dist));
1596 // move by whole spaces inside the staff.
1597 if (discrete_dist < stafflines+1)
1598 discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1600 return gh_double2scm (-d * discrete_dist);
1606 ADD_INTERFACE (Beam, "beam-interface",
1609 #'thickness= weight of beams, in staffspace
1612 We take the least squares line through the ideal-length stems, and
1613 then damp that using
1615 damped = tanh (slope)
1617 this gives an unquantized left and right position for the beam end.
1618 Then we take all combinations of quantings near these left and right
1619 positions, and give them a score (according to how close they are to
1620 the ideal slope, how close the result is to the ideal stems, etc.). We
1621 take the best scoring combination.
1624 "french-beaming position-callbacks concaveness-gap concaveness-threshold dir-function quant-score auto-knee-gap gap chord-tremolo beamed-stem-shorten shorten least-squares-dy damping flag-width-function neutral-direction positions space-function thickness");