2 beam.cc -- implement Beam
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
17 * Use Number_pair i.s.o Interval to represent (yl, yr).
19 - Determine auto knees based on positions if it's set by the user.
25 - Stems run to the Y-center of the beam.
27 - beam_space is the offset between Y centers of the beam.
32 #include <math.h> // tanh.
34 #include "align-interface.hh"
35 #include "molecule.hh"
36 #include "directional-element-interface.hh"
40 #include "least-squares.hh"
42 #include "paper-def.hh"
44 #include "group-interface.hh"
45 #include "staff-symbol-referencer.hh"
51 #define DEBUG_QUANTING 0
55 #include "text-item.hh" // debug output.
56 #include "font-interface.hh" // debug output.
60 const int INTER_QUANT_PENALTY = 1000;
61 const int SECONDARY_BEAM_DEMERIT = 15;
62 const int STEM_LENGTH_DEMERIT_FACTOR = 5;
63 // possibly ridiculous, but too short stems just won't do
64 const int STEM_LENGTH_LIMIT_PENALTY = 5000;
65 const int DAMPING_DIRECTIION_PENALTY = 800;
66 const int MUSICAL_DIRECTION_FACTOR = 400;
67 const int IDEAL_SLOPE_FACTOR = 10;
68 const int REGION_SIZE = 2;
72 shrink_extra_weight (Real x)
74 return fabs (x) * ((x < 0) ? 1.5 : 1.0);
78 Beam::add_stem (Grob *me, Grob *s)
80 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
82 s->add_dependency (me);
84 assert (!Stem::beam_l (s));
85 s->set_grob_property ("beam", me->self_scm ());
87 add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
91 Beam::get_beam_space (Grob *me)
93 SCM func = me->get_grob_property ("space-function");
94 SCM s = gh_call2 (func, me->self_scm (), gh_int2scm (get_beam_count (me)));
95 return gh_scm2double (s);
102 Beam::get_beam_count (Grob *me)
105 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
107 Grob *sc = unsmob_grob (ly_car (s));
109 m = m >? (Stem::beam_multiplicity (sc).length () + 1);
114 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
116 Beam::space_function (SCM smob, SCM beam_count)
118 Grob *me = unsmob_grob (smob);
120 Real staff_space = Staff_symbol_referencer::staff_space (me);
121 Real line = me->paper_l ()->get_var ("linethickness");
122 Real thickness = gh_scm2double (me->get_grob_property ("thickness"))
125 Real beam_space = gh_scm2int (beam_count) < 4
126 ? (2*staff_space + line - thickness) / 2.0
127 : (3*staff_space + line - thickness) / 3.0;
129 return gh_double2scm (beam_space);
133 /* After pre-processing all directions should be set.
134 Several post-processing routines (stem, slur, script) need stem/beam
136 Currenly, this means that beam has set all stem's directions.
137 [Alternatively, stems could set its own directions, according to
138 their beam, during 'final-pre-processing'.] */
139 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
141 Beam::before_line_breaking (SCM smob)
143 Grob *me = unsmob_grob (smob);
145 /* Beams with less than 2 two stems don't make much sense, but could happen
150 For a beam that only has one stem, we try to do some disappearance magic:
151 we revert the flag, and move on to The Eternal Engraving Fields. */
153 int count = visible_stem_count (me);
156 me->warning (_ ("beam has less than two visible stems"));
158 SCM stems = me->get_grob_property ("stems");
159 if (scm_ilength (stems) == 1)
161 me->warning (_ ("Beam has less than two stems. Removing beam."));
163 unsmob_grob (gh_car (stems))->remove_grob_property ("beam");
166 return SCM_UNSPECIFIED;
168 else if (scm_ilength (stems) == 0)
171 return SCM_UNSPECIFIED;
176 Direction d = get_default_dir (me);
178 consider_auto_knees (me, d);
179 set_stem_directions (me, d);
183 set_stem_shorten (me);
192 Beam::connect_beams (Grob *me)
194 Link_array<Grob> stems=
195 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
198 last_int.set_empty();
199 for (int i = 0; i< stems.size(); i++)
201 Grob *this_stem = stems[i];
202 SCM this_beaming = this_stem->get_grob_property ("beaming");
204 Direction this_dir = Directional_element_interface::get(this_stem);
207 int start_point = last_int [this_dir];
213 if (d == RIGHT && i == stems.size()-1)
216 new_slice.set_empty();
217 SCM s = index_get_cell (this_beaming, d);
218 for (; gh_pair_p (s); s = gh_cdr (s))
221 start_point - this_dir * gh_scm2int (gh_car (s));
223 new_slice.add_point (new_beam_pos);
224 gh_set_car_x (s, gh_int2scm (new_beam_pos));
227 while (flip (&d) != LEFT);
229 if (!new_slice.empty_b())
230 last_int = new_slice;
234 SCM s = gh_cdr (this_beaming);
235 for (; gh_pair_p (s); s = gh_cdr (s))
237 int np = - this_dir * gh_scm2int (gh_car(s));
238 gh_set_car_x (s, gh_int2scm (np));
239 last_int.add_point (np);
245 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
247 Beam::brew_molecule (SCM grob)
249 Grob *me = unsmob_grob (grob);
250 Link_array<Grob> stems=
251 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
252 Grob* xcommon = common_refpoint_of_array (stems, me, X_AXIS);
255 if (visible_stem_count (me))
257 // ugh -> use commonx
258 x0 = first_visible_stem (me)->relative_coordinate (xcommon, X_AXIS);
259 dx = last_visible_stem (me)->relative_coordinate (xcommon, X_AXIS) - x0;
263 x0 = stems[0]->relative_coordinate (xcommon, X_AXIS);
264 dx = stems.top ()->relative_coordinate (xcommon, X_AXIS) - x0;
267 SCM posns = me->get_grob_property ("positions");
269 if (!ly_number_pair_p (posns))
271 programming_error ("No beam posns");
272 pos = Interval (0,0);
275 pos= ly_scm2interval (posns);
277 Real dy = pos.delta ();
278 Real dydx = dy && dx ? dy/dx : 0;
280 Real thick = gh_scm2double (me->get_grob_property ("thickness"));
281 Real bdy = get_beam_space (me);
283 SCM last_beaming = SCM_EOL;;
284 Real last_xposn = -1;
285 Real last_width = -1 ;
288 SCM gap = me->get_grob_property ("gap");
290 Real lt = me->paper_l ()->get_var ("linethickness");
291 for (int i = 0; i< stems.size(); i++)
295 SCM this_beaming = st->get_grob_property ("beaming");
296 Real xposn = st->relative_coordinate (xcommon, X_AXIS);
297 Real stem_width = gh_scm2double (st->get_grob_property ("thickness")) *lt;
301 SCM left = gh_cdr (last_beaming);
302 SCM right = gh_car (this_beaming);
304 Array<int> fullbeams;
305 Array<int> lfliebertjes;
306 Array<int> rfliebertjes;
309 gh_pair_p (s); s =gh_cdr (s))
311 int b = gh_scm2int (gh_car (s));
312 if (scm_memq (gh_car(s), right) != SCM_BOOL_F)
318 lfliebertjes.push (b);
322 gh_pair_p (s); s =gh_cdr (s))
324 int b = gh_scm2int (gh_car (s));
325 if (scm_memq (gh_car(s), left) == SCM_BOOL_F)
327 rfliebertjes.push (b);
332 Real w = xposn - last_xposn;
333 Real stem_offset = 0.0;
334 Real width_corr = 0.0;
337 stem_offset -= last_width/2;
338 width_corr += last_width/2;
341 if (i == stems.size() -1)
343 width_corr += stem_width/2;
346 if (gh_number_p (gap))
348 Real g = gh_scm2double (gap);
353 Molecule whole = Lookup::beam (dydx, w + width_corr, thick);
354 for (int j = fullbeams.size(); j--;)
357 b.translate_axis (last_xposn - x0 + stem_offset, X_AXIS);
358 b.translate_axis (dydx * (last_xposn - x0) + bdy * fullbeams[j], Y_AXIS);
359 the_beam.add_molecule (b);
362 if (lfliebertjes.size() || rfliebertjes.size())
366 if (!Stem::first_head (st))
370 int t = Stem::duration_log (st);
372 SCM proc = me->get_grob_property ("flag-width-function");
373 SCM result = gh_call1 (proc, gh_int2scm (t));
374 nw_f = gh_scm2double (result);
377 /* Half beam should be one note-width,
378 but let's make sure two half-beams never touch */
380 Real w = xposn - last_xposn;
383 Molecule half = Lookup::beam (dydx, w, thick);
384 for (int j = lfliebertjes.size(); j--;)
387 b.translate_axis (last_xposn - x0, X_AXIS);
388 b.translate_axis (dydx * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
389 the_beam.add_molecule (b);
391 for (int j = rfliebertjes.size(); j--;)
394 b.translate_axis (xposn - x0 - w , X_AXIS);
395 b.translate_axis (dydx * (xposn-x0 -w) + bdy * rfliebertjes[j], Y_AXIS);
396 the_beam.add_molecule (b);
402 last_width = stem_width;
403 last_beaming = this_beaming;
406 the_beam.translate_axis (x0 - me->relative_coordinate (xcommon, X_AXIS), X_AXIS);
407 the_beam.translate_axis (pos[LEFT], Y_AXIS);
412 This code prints the demerits for each beam. Perhaps this
413 should be switchable for those who want to twiddle with the
419 str += to_str (gh_scm2int (me->get_grob_property ("best-idx")));
422 str += to_str (gh_scm2double (me->get_grob_property ("quant-score")),
425 SCM properties = Font_interface::font_alist_chain (me);
428 Molecule tm = Text_item::text2molecule (me, ly_str02scm (str.ch_C ()), properties);
429 the_beam.add_at_edge (Y_AXIS, UP, tm, 5.0);
435 return the_beam.smobbed_copy();
442 Beam::get_default_dir (Grob *me)
444 Drul_array<int> total;
445 total[UP] = total[DOWN] = 0;
446 Drul_array<int> count;
447 count[UP] = count[DOWN] = 0;
450 Link_array<Item> stems=
451 Pointer_group_interface__extract_grobs (me, (Item*)0, "stems");
453 for (int i=0; i <stems.size (); i++)
456 Direction sd = Directional_element_interface::get (s);
458 int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
459 int current = sd ? (1 + d * sd)/2 : center_distance;
466 } while (flip (&d) != DOWN);
468 SCM func = me->get_grob_property ("dir-function");
469 SCM s = gh_call2 (func,
470 gh_cons (gh_int2scm (count[UP]),
471 gh_int2scm (count[DOWN])),
472 gh_cons (gh_int2scm (total[UP]),
473 gh_int2scm (total[DOWN])));
475 if (gh_number_p (s) && gh_scm2int (s))
478 /* If dir is not determined: get default */
479 return to_dir (me->get_grob_property ("neutral-direction"));
483 /* Set all stems with non-forced direction to beam direction.
484 Urg: non-forced should become `without/with unforced' direction,
485 once stem gets cleaned-up. */
487 Beam::set_stem_directions (Grob *me, Direction d)
489 Link_array<Item> stems
490 =Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems");
492 for (int i=0; i <stems.size (); i++)
495 SCM force = s->remove_grob_property ("dir-forced");
496 if (!gh_boolean_p (force) || !gh_scm2bool (force))
497 Directional_element_interface::set (s, d);
501 /* Simplistic auto-knees; only consider vertical gap between two
504 `Forced' stem directions are ignored. If you don't want auto-knees,
505 don't set, or unset auto-knee-gap. */
507 Beam::consider_auto_knees (Grob *me, Direction d)
509 SCM scm = me->get_grob_property ("auto-knee-gap");
511 if (gh_number_p (scm))
515 Real staff_space = Staff_symbol_referencer::staff_space (me);
516 Real gap = gh_scm2double (scm) / staff_space;
519 Link_array<Grob> stems=
520 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
522 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
525 for (int i=1; i < stems.size (); i++)
527 if (!Stem::invisible_b (stems[i-1]))
529 if (Stem::invisible_b (stems[l]))
531 if (Stem::invisible_b (stems[i]))
534 Real left = Stem::extremal_heads (stems[l])[d]
535 ->relative_coordinate (common, Y_AXIS);
536 Real right = Stem::extremal_heads (stems[i])[-d]
537 ->relative_coordinate (common, Y_AXIS);
539 Real dy = right - left;
543 knee_y = (right + left) / 2;
551 for (int i=0; i < stems.size (); i++)
554 if (Stem::invisible_b (s) ||
555 s->get_grob_property ("dir-forced") == SCM_BOOL_T)
557 Real y = Stem::extremal_heads (stems[i])[d]
558 ->relative_coordinate (common, Y_AXIS);
560 Directional_element_interface::set (s, y < knee_y ? UP : DOWN);
561 s->set_grob_property ("dir-forced", SCM_BOOL_T);
567 /* Set stem's shorten property if unset.
570 take some y-position (chord/beam/nearest?) into account
571 scmify forced-fraction
575 why is shorten stored in beam, and not directly in stem?
579 Beam::set_stem_shorten (Grob *m)
581 Spanner*me = dynamic_cast<Spanner*> (m);
583 Real forced_fraction = forced_stem_count (me) / visible_stem_count (me);
585 int beam_count = get_beam_count (me);
587 SCM shorten = me->get_grob_property ("beamed-stem-shorten");
588 if (shorten == SCM_EOL)
591 int sz = scm_ilength (shorten);
593 Real staff_space = Staff_symbol_referencer::staff_space (me);
594 SCM shorten_elt = scm_list_ref (shorten,
595 gh_int2scm (beam_count <? (sz - 1)));
596 Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
598 /* your similar cute comment here */
599 shorten_f *= forced_fraction;
602 me->set_grob_property ("shorten", gh_double2scm (shorten_f));
605 /* Call list of y-dy-callbacks, that handle setting of
609 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
611 Beam::after_line_breaking (SCM smob)
613 Grob *me = unsmob_grob (smob);
615 /* Copy to mutable list. */
616 SCM s = ly_deep_copy (me->get_grob_property ("positions"));
617 me->set_grob_property ("positions", s);
619 if (ly_car (s) == SCM_BOOL_F)
622 // one wonders if such genericity is necessary --hwn.
623 SCM callbacks = me->get_grob_property ("position-callbacks");
624 for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
625 gh_call1 (ly_car (i), smob);
628 set_stem_lengths (me);
629 return SCM_UNSPECIFIED;
643 - Make all demerits customisable
645 - One sensible check per demerit (what's this --hwn)
647 - Add demerits for quants per se, as to forbid a specific quant
651 MAKE_SCHEME_CALLBACK (Beam, quanting, 1);
653 Beam::quanting (SCM smob)
655 Grob *me = unsmob_grob (smob);
657 SCM s = me->get_grob_property ("positions");
658 Real yl = gh_scm2double (gh_car (s));
659 Real yr = gh_scm2double (gh_cdr (s));
661 Real ss = Staff_symbol_referencer::staff_space (me);
662 Real thickness = gh_scm2double (me->get_grob_property ("thickness")) / ss;
663 Real slt = me->paper_l ()->get_var ("linethickness") / ss;
666 SCM sdy = me->get_grob_property ("least-squares-dy");
667 Real dy_mus = gh_number_p (sdy) ? gh_scm2double (sdy) : 0.0;
670 Real sit = (thickness - slt) / 2;
672 Real hang = 1.0 - (thickness - slt) / 2;
673 Real quants [] = {straddle, sit, inter, hang };
675 int num_quants = int (sizeof (quants)/sizeof (Real));
680 going to REGION_SIZE == 2, yields another 0.6 second with
684 (result indexes between 70 and 575) ? --hwn.
691 Do stem computations. These depend on YL and YR linearly, so we can
692 precompute for every stem 2 factors.
694 Link_array<Grob> stems=
695 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
696 Array<Stem_info> stem_infos;
697 Array<Real> lbase_lengths;
698 Array<Real> rbase_lengths;
700 Drul_array<bool> dirs_found(0,0);
701 Grob *common_y = common_refpoint_of_array (stems, me, Y_AXIS);
703 bool french = to_boolean (me->get_grob_property ("french-beaming"));
704 for (int i= 0; i < stems.size(); i++)
707 stem_infos.push (Stem::calc_stem_info (s));
708 dirs_found[stem_infos.top ().dir_] = true;
710 Real b = calc_stem_y (me, s, common_y , Interval (1,0), french && i > 0&& (i < stems.size () -1));
711 lbase_lengths.push (b);
713 Real a = calc_stem_y (me, s, common_y , Interval (0,1), french && i > 0&& (i < stems.size () -1));
714 rbase_lengths.push (a);
717 Direction ldir = Direction (stem_infos[0].dir_);
718 Direction rdir = Direction (stem_infos.top ().dir_);
719 bool knee_b = dirs_found[LEFT] && dirs_found[RIGHT];
722 int region_size = REGION_SIZE;
724 Knees are harder, lets try some more possibilities for knees.
729 for (int i = -region_size ; i < region_size; i++)
730 for (int j = 0; j < num_quants; j++)
732 quantsl.push (i + quants[j] + int (yl));
733 quantsr.push (i + quants[j] + int (yr));
736 Array<Quant_score> qscores;
738 for (int l =0; l < quantsl.size (); l++)
739 for (int r =0; r < quantsr.size (); r++)
751 This is a longish function, but we don't separate this out into
752 neat modular separate subfunctions, as the subfunctions would be
753 called for many values of YL, YR. By precomputing various
754 parameters outside of the loop, we can save a lot of time.
759 Grob *fvs = first_visible_stem (me);
760 Grob *lvs = last_visible_stem (me);
762 Grob *commony = fvs->common_refpoint (lvs, Y_AXIS);
763 bool xstaff= (Align_interface::has_interface (commony));
765 for (int i = qscores.size (); i--;)
766 if (qscores[i].demerits < 100)
769 += score_slopes_dy (me, qscores[i].yl, qscores[i].yr,
770 dy_mus, yr- yl, xstaff);
773 Real rad = Staff_symbol_referencer::staff_radius (me);
774 int beam_count = get_beam_count (me);
775 Real beam_space = beam_count < 4
776 ? (2*ss + slt - thickness) / 2.0
777 : (3*ss + slt - thickness) / 3.0;
779 for (int i = qscores.size (); i--;)
780 if (qscores[i].demerits < 100)
783 += score_forbidden_quants (me, qscores[i].yl, qscores[i].yr,
784 rad, slt, thickness, beam_space,
785 beam_count, ldir, rdir);
789 for (int i = qscores.size (); i--;)
790 if (qscores[i].demerits < 100)
793 += score_stem_lengths (stems, stem_infos,
794 lbase_lengths, rbase_lengths,
796 me, qscores[i].yl, qscores[i].yr);
802 for (int i = qscores.size (); i--;)
804 if (qscores[i].demerits < best)
806 best = qscores [i].demerits ;
812 me->set_grob_property ("positions",
813 gh_cons (gh_double2scm (qscores[best_idx].yl),
814 gh_double2scm (qscores[best_idx].yr))
820 me->set_grob_property ("quant-score",
821 gh_double2scm (qscores[best_idx].demerits));
822 me->set_grob_property ("best-idx", gh_int2scm (best_idx));
825 return SCM_UNSPECIFIED;
829 Beam::score_stem_lengths (Link_array<Grob>stems,
830 Array<Stem_info> stem_infos,
831 Array<Real> left_factor,
832 Array<Real> right_factor,
837 Real demerit_score = 0.0 ;
838 Real pen = STEM_LENGTH_LIMIT_PENALTY;
840 for (int i=0; i < stems.size (); i++)
843 if (Stem::invisible_b (s))
847 yl * left_factor[i] + right_factor[i]* yr;
849 Stem_info info = stem_infos[i];
850 Direction d = info.dir_;
853 * ( 0 >? (info.dir_ * (info.shortest_y_ - current_y)));
855 demerit_score += STEM_LENGTH_DEMERIT_FACTOR
856 * shrink_extra_weight (d * current_y - info.dir_ * info.ideal_y_);
859 demerit_score *= 2.0 / stems.size ();
861 return demerit_score;
865 Beam::score_slopes_dy (Grob *me,
867 Real dy_mus, Real dy_damp,
873 if (sign (dy_damp) != sign (dy))
875 dem += DAMPING_DIRECTIION_PENALTY;
878 dem += MUSICAL_DIRECTION_FACTOR * (0 >? (fabs (dy) - fabs (dy_mus)));
881 Real slope_penalty = IDEAL_SLOPE_FACTOR;
884 Xstaff beams tend to use extreme slopes to get short stems. We
885 put in a penalty here.
890 dem += shrink_extra_weight (fabs (dy_damp) - fabs (dy))* slope_penalty;
897 return x - floor (x);
901 Beam::score_forbidden_quants (Grob*me,
905 Real thickness, Real beam_space,
907 Direction ldir, Direction rdir)
912 if (fabs (yl) < rad && fabs ( my_modf (yl) - 0.5) < 1e-3)
913 dem += INTER_QUANT_PENALTY;
914 if (fabs (yr) < rad && fabs ( my_modf (yr) - 0.5) < 1e-3)
915 dem += INTER_QUANT_PENALTY;
917 // todo: use beam_count of outer stems.
922 Real sit = (thickness - slt) / 2;
924 Real hang = 1.0 - (thickness - slt) / 2;
927 if (fabs (yl - ldir * beam_space) < rad
928 && fabs (my_modf (yl) - inter) < 1e-3)
929 dem += SECONDARY_BEAM_DEMERIT;
930 if (fabs (yr - rdir * beam_space) < rad
931 && fabs (my_modf (yr) - inter) < 1e-3)
932 dem += SECONDARY_BEAM_DEMERIT;
937 Can't we simply compute the distance between the nearest
938 staffline and the secondary beam? That would get rid of the
939 silly case analysis here (which is probably not when we have
940 different beam-thicknesses.)
946 // hmm, without Interval/Drul_array, you get ~ 4x same code...
947 if (fabs (yl - ldir * beam_space) < rad + inter)
949 if (ldir == UP && dy <= eps
950 && fabs (my_modf (yl) - sit) < eps)
951 dem += SECONDARY_BEAM_DEMERIT;
953 if (ldir == DOWN && dy >= eps
954 && fabs (my_modf (yl) - hang) < eps)
955 dem += SECONDARY_BEAM_DEMERIT;
958 if (fabs (yr - rdir * beam_space) < rad + inter)
960 if (rdir == UP && dy >= eps
961 && fabs (my_modf (yr) - sit) < eps)
962 dem += SECONDARY_BEAM_DEMERIT;
964 if (rdir == DOWN && dy <= eps
965 && fabs (my_modf (yr) - hang) < eps)
966 dem += SECONDARY_BEAM_DEMERIT;
971 if (fabs (yl - 2 * ldir * beam_space) < rad + inter)
973 if (ldir == UP && dy <= eps
974 && fabs (my_modf (yl) - straddle) < eps)
975 dem += SECONDARY_BEAM_DEMERIT;
977 if (ldir == DOWN && dy >= eps
978 && fabs (my_modf (yl) - straddle) < eps)
979 dem += SECONDARY_BEAM_DEMERIT;
982 if (fabs (yr - 2 * rdir * beam_space) < rad + inter)
984 if (rdir == UP && dy >= eps
985 && fabs (my_modf (yr) - straddle) < eps)
986 dem += SECONDARY_BEAM_DEMERIT;
988 if (rdir == DOWN && dy <= eps
989 && fabs (my_modf (yr) - straddle) < eps)
990 dem += SECONDARY_BEAM_DEMERIT;
1000 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
1002 Beam::least_squares (SCM smob)
1004 Grob *me = unsmob_grob (smob);
1006 int count = visible_stem_count (me);
1007 Interval pos (0, 0);
1011 me->set_grob_property ("positions", ly_interval2scm (pos));
1012 return SCM_UNSPECIFIED;
1016 Array<Real> x_posns ;
1017 Link_array<Grob> stems=
1018 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1019 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
1020 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
1022 Real my_y = me->relative_coordinate (commony, Y_AXIS);
1024 Grob *fvs = first_visible_stem (me);
1025 Grob *lvs = last_visible_stem (me);
1027 Interval ideal (Stem::calc_stem_info (fvs).ideal_y_
1028 + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
1029 Stem::calc_stem_info (lvs).ideal_y_
1030 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
1032 Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1033 for (int i=0; i < stems.size (); i++)
1037 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
1040 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
1046 if (!ideal.delta ())
1048 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
1049 Stem::chord_start_y (last_visible_stem (me)));
1053 TODO -- use scoring for this.
1055 complicated, because we take stem-info.ideal for determining
1058 /* Make simple beam on middle line have small tilt */
1059 if (!ideal[LEFT] && chord.delta () && count == 2)
1065 Direction d = (Direction) (sign (chord.delta ()) * UP);
1066 pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
1081 Array<Offset> ideals;
1082 for (int i=0; i < stems.size (); i++)
1085 if (Stem::invisible_b (s))
1087 ideals.push (Offset (x_posns[i],
1088 Stem::calc_stem_info (s).ideal_y_
1089 + s->relative_coordinate (commony, Y_AXIS)
1092 minimise_least_squares (&dydx, &y, ideals);
1095 me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
1096 pos = Interval (y, (y+dy));
1099 me->set_grob_property ("positions", ly_interval2scm (pos));
1101 return SCM_UNSPECIFIED;
1106 We can't combine with previous function, since check concave and
1107 slope damping comes first.
1109 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
1111 Beam::shift_region_to_valid (SCM grob)
1113 Grob *me = unsmob_grob (grob);
1117 Array<Real> x_posns ;
1118 Link_array<Grob> stems=
1119 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1120 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
1121 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
1123 Grob *fvs = first_visible_stem (me);
1126 return SCM_UNSPECIFIED;
1128 Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
1129 for (int i=0; i < stems.size (); i++)
1133 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
1137 Grob *lvs = last_visible_stem (me);
1139 return SCM_UNSPECIFIED;
1141 Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
1143 Interval pos = ly_scm2interval ( me->get_grob_property ("positions"));
1144 Real dy = pos.delta();
1150 Shift the positions so that we have a chance of finding good
1151 quants (i.e. no short stem failures.)
1153 Interval feasible_left_point;
1154 feasible_left_point.set_full ();
1155 for (int i=0; i < stems.size (); i++)
1158 if (Stem::invisible_b (s))
1161 Direction d = Stem::get_direction (s);
1164 Stem::calc_stem_info (s).shortest_y_
1165 - dydx * x_posns [i];
1168 left_y is now relative to the stem S. We want relative to
1169 ourselves, so translate:
1172 + s->relative_coordinate (commony, Y_AXIS)
1173 - me->relative_coordinate (commony, Y_AXIS);
1179 feasible_left_point.intersect (flp);
1182 if (feasible_left_point.empty_b())
1184 warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
1186 else if (!feasible_left_point.elem_b(y))
1188 if (isinf (feasible_left_point[DOWN]))
1189 y = feasible_left_point[UP] - REGION_SIZE;
1190 else if (isinf (feasible_left_point[UP]))
1191 y = feasible_left_point[DOWN]+ REGION_SIZE;
1193 y = feasible_left_point.center ();
1195 pos = Interval (y, (y+dy));
1196 me->set_grob_property ("positions", ly_interval2scm (pos));
1197 return SCM_UNSPECIFIED;
1201 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
1203 Beam::check_concave (SCM smob)
1205 Grob *me = unsmob_grob (smob);
1207 Link_array<Grob> stems =
1208 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1210 for (int i = 0; i < stems.size ();)
1212 if (Stem::invisible_b (stems[i]))
1218 if (stems.size () < 3)
1219 return SCM_UNSPECIFIED;
1222 /* Concaveness #1: If distance of an inner notehead to line between
1223 two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
1224 beam is concave (Heinz Stolba).
1226 In the case of knees, the line connecting outer heads is often
1227 not related to the beam slope (it may even go in the other
1228 direction). Skip the check when the outer stems point in
1229 different directions. --hwn
1232 bool concaveness1 = false;
1233 SCM gap = me->get_grob_property ("concaveness-gap");
1234 if (gh_number_p (gap)
1235 && Stem::get_direction(stems.top ())
1236 == Stem::get_direction(stems[0]))
1238 Real r1 = gh_scm2double (gap);
1239 Real dy = Stem::chord_start_y (stems.top ())
1240 - Stem::chord_start_y (stems[0]);
1243 Real slope = dy / (stems.size () - 1);
1245 Real y0 = Stem::chord_start_y (stems[0]);
1246 for (int i = 1; i < stems.size () - 1; i++)
1248 Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
1251 concaveness1 = true;
1258 /* Concaveness #2: Sum distances of inner noteheads that fall
1259 outside the interval of the two outer noteheads.
1261 We only do this for beams where first and last stem have the same
1265 Note that "convex" stems compensate for "concave" stems.
1266 (is that intentional?) --hwn.
1269 Real concaveness2 = 0;
1270 SCM thresh = me->get_grob_property ("concaveness-threshold");
1271 Real r2 = infinity_f;
1272 if (!concaveness1 && gh_number_p (thresh)
1273 && Stem::get_direction(stems.top ())
1274 == Stem::get_direction(stems[0]))
1276 r2 = gh_scm2double (thresh);
1278 Direction dir = Stem::get_direction(stems.top ());
1280 Interval iv (Stem::chord_start_y (stems[0]),
1281 Stem::chord_start_y (stems.top ()));
1283 if (iv[MAX] < iv[MIN])
1286 for (int i = 1; i < stems.size () - 1; i++)
1288 Real f = Stem::chord_start_y (stems[i]);
1289 concave += ((f - iv[MAX] ) >? 0) +
1290 ((f - iv[MIN] ) <? 0);
1293 concaveness2 = concave / (stems.size () - 2);
1295 /* ugh: this is the a kludge to get
1296 input/regression/beam-concave.ly to behave as
1300 huh? we're dividing twice (which is not scalable) meaning that
1301 the longer the beam, the more unlikely it will be
1302 concave. Maybe you would even expect the other way around??
1307 concaveness2 /= (stems.size () - 2);
1310 /* TODO: some sort of damping iso -> plain horizontal */
1311 if (concaveness1 || concaveness2 > r2)
1313 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1314 Real r = pos.linear_combination (0);
1315 me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
1316 me->set_grob_property ("least-squares-dy", gh_double2scm (0));
1319 return SCM_UNSPECIFIED;
1322 /* This neat trick is by Werner Lemberg,
1323 damped = tanh (slope)
1324 corresponds with some tables in [Wanske] CHECKME */
1325 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1327 Beam::slope_damping (SCM smob)
1329 Grob *me = unsmob_grob (smob);
1331 if (visible_stem_count (me) <= 1)
1332 return SCM_UNSPECIFIED;
1334 SCM s = me->get_grob_property ("damping");
1335 int damping = gh_scm2int (s);
1339 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1340 Real dy = pos.delta ();
1342 Grob *fvs = first_visible_stem (me);
1343 Grob *lvs = last_visible_stem (me);
1345 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1348 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1349 - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1350 Real dydx = dy && dx ? dy/dx : 0;
1351 dydx = 0.6 * tanh (dydx) / damping;
1353 Real damped_dy = dydx * dx;
1354 pos[LEFT] += (dy - damped_dy) / 2;
1355 pos[RIGHT] -= (dy - damped_dy) / 2;
1357 me->set_grob_property ("positions", ly_interval2scm (pos));
1359 return SCM_UNSPECIFIED;
1363 where_are_the_whole_beams(SCM beaming)
1367 for( SCM s = gh_car (beaming); gh_pair_p (s) ; s = gh_cdr (s))
1369 if (scm_memq (gh_car (s), gh_cdr (beaming)) != SCM_BOOL_F)
1371 l.add_point (gh_scm2int (gh_car (s)));
1378 Calculate the Y position of the stem-end, given the Y-left, Y-right
1379 in POS, and for stem S.
1382 Beam::calc_stem_y (Grob *me, Grob* s, Grob * common_y, Interval pos, bool french)
1384 Real beam_space = get_beam_space (me);
1386 // ugh -> use commonx
1387 Grob * fvs = first_visible_stem (me);
1388 Grob *lvs = last_visible_stem (me);
1390 Real x0 = fvs ? fvs->relative_coordinate (0, X_AXIS) : 0.0;
1391 Real dx = fvs ? lvs->relative_coordinate (0, X_AXIS) - x0 : 0.0;
1392 Real r = s->relative_coordinate (0, X_AXIS) - x0;
1393 Real dy = pos.delta ();
1394 Real stem_y_beam0 = (dy && dx
1401 Direction my_dir = Directional_element_interface::get (s);
1402 SCM beaming = s->get_grob_property ("beaming");
1404 Real stem_y = stem_y_beam0;
1407 Slice bm = where_are_the_whole_beams (beaming);
1409 stem_y += beam_space * bm[-my_dir];
1413 Slice bm = Stem::beam_multiplicity(s);
1415 stem_y +=bm[my_dir] * beam_space;
1418 Real id = me->relative_coordinate (common_y, Y_AXIS)
1419 - s->relative_coordinate (common_y, Y_AXIS);
1425 Hmm. At this time, beam position and slope are determined. Maybe,
1426 stem directions and length should set to relative to the chord's
1427 position of the beam. */
1429 Beam::set_stem_lengths (Grob *me)
1431 Link_array<Grob> stems=
1432 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1434 if (stems.size () <= 1)
1437 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
1438 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1439 Real staff_space = Staff_symbol_referencer::staff_space (me);
1441 bool french = to_boolean (me->get_grob_property ("french-beaming"));
1443 for (int i=0; i < stems.size (); i++)
1446 if (Stem::invisible_b (s))
1449 Real stem_y = calc_stem_y (me, s, common , pos, french && i > 0&& (i < stems.size () -1));
1451 Stem::set_stemend (s, 2* stem_y / staff_space);
1456 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1458 Link_array<Grob> stems=
1459 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1462 for (int i=0; i < stems.size (); i++)
1465 Don't overwrite user settings.
1470 /* Don't set beaming for outside of outer stems */
1471 if ((d == LEFT && i == 0)
1472 ||(d == RIGHT && i == stems.size () -1))
1476 SCM beaming_prop = stems[i]->get_grob_property ("beaming");
1477 if (beaming_prop == SCM_EOL ||
1478 index_get_cell (beaming_prop, d) == SCM_EOL)
1480 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1481 Stem::set_beaming (stems[i], b, d);
1484 while (flip (&d) != LEFT);
1489 Beam::forced_stem_count (Grob *me)
1491 Link_array<Grob>stems =
1492 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1494 for (int i=0; i < stems.size (); i++)
1498 if (Stem::invisible_b (s))
1501 if (((int)Stem::chord_start_y (s))
1502 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1512 Beam::visible_stem_count (Grob *me)
1514 Link_array<Grob>stems =
1515 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1517 for (int i = stems.size (); i--;)
1519 if (!Stem::invisible_b (stems[i]))
1526 Beam::first_visible_stem (Grob *me)
1528 Link_array<Grob>stems =
1529 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1531 for (int i = 0; i < stems.size (); i++)
1533 if (!Stem::invisible_b (stems[i]))
1540 Beam::last_visible_stem (Grob *me)
1542 Link_array<Grob>stems =
1543 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1544 for (int i = stems.size (); i--;)
1546 if (!Stem::invisible_b (stems[i]))
1556 handle rest under beam (do_post: beams are calculated now)
1557 what about combination of collisions and rest under beam.
1561 rest -> stem -> beam -> interpolate_y_position ()
1563 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1565 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1567 Grob *rest = unsmob_grob (element_smob);
1568 Axis a = (Axis) gh_scm2int (axis);
1570 assert (a == Y_AXIS);
1572 Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1575 return gh_double2scm (0.0);
1576 Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1578 || !Beam::has_interface (beam)
1579 || !Beam::visible_stem_count (beam))
1580 return gh_double2scm (0.0);
1582 // make callback for rest from this.
1583 // todo: make sure this calced already.
1585 // Interval pos = ly_scm2interval (beam->get_grob_property ("positions"));
1586 Interval pos (0, 0);
1587 SCM s = beam->get_grob_property ("positions");
1588 if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1589 pos = ly_scm2interval (s);
1591 Real dy = pos.delta ();
1592 // ugh -> use commonx
1593 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1594 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1595 Real dydx = dy && dx ? dy/dx : 0;
1597 Direction d = Stem::get_direction (stem);
1598 Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + pos[LEFT];
1600 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1603 Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space; // refp??
1606 = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance"));
1608 minimum_dist + -d * (beamy - rest_dim) >? 0;
1610 int stafflines = Staff_symbol_referencer::line_count (rest);
1612 // move discretely by half spaces.
1613 int discrete_dist = int (ceil (dist));
1615 // move by whole spaces inside the staff.
1616 if (discrete_dist < stafflines+1)
1617 discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0);
1619 return gh_double2scm (-d * discrete_dist);
1625 ADD_INTERFACE (Beam, "beam-interface",
1628 #'thickness= weight of beams, in staffspace
1631 We take the least squares line through the ideal-length stems, and
1632 then damp that using
1634 damped = tanh (slope)
1636 this gives an unquantized left and right position for the beam end.
1637 Then we take all combinations of quantings near these left and right
1638 positions, and give them a score (according to how close they are to
1639 the ideal slope, how close the result is to the ideal stems, etc.). We
1640 take the best scoring combination.
1643 "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");