2 beam.cc -- implement Beam
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2003 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
13 * Use Number_pair i.s.o Interval to represent (yl, yr).
15 - Determine auto knees based on positions if it's set by the user.
21 - Stems run to the Y-center of the beam.
23 - beam_translation is the offset between Y centers of the beam.
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"
46 #define DEBUG_QUANTING 0
50 #include "text-item.hh" // debug output.
51 #include "font-interface.hh" // debug output.
56 Beam::add_stem (Grob *me, Grob *s)
58 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
60 s->add_dependency (me);
62 assert (!Stem::get_beam (s));
63 s->set_grob_property ("beam", me->self_scm ());
65 add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
70 Beam::get_thickness (Grob * me)
72 SCM th = me->get_grob_property ("thickness");
74 return gh_scm2double (th)* Staff_symbol_referencer::staff_space (me);
79 /* Return the translation between 2 adjoining beams. */
81 Beam::get_beam_translation (Grob *me)
83 SCM func = me->get_grob_property ("space-function");
84 SCM s = gh_call2 (func, me->self_scm (), scm_int2num (get_beam_count (me)));
85 return gh_scm2double (s);
88 /* Maximum beam_count. */
90 Beam::get_beam_count (Grob *me)
93 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
95 Grob *stem = unsmob_grob (ly_car (s));
96 m = m >? (Stem::beam_multiplicity (stem).length () + 1);
103 Space return space between beams.
105 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
107 Beam::space_function (SCM smob, SCM beam_count)
109 Grob *me = unsmob_grob (smob);
111 Real staff_space = Staff_symbol_referencer::staff_space (me);
112 Real line = me->get_paper ()->get_var ("linethickness");
113 Real thickness = get_thickness (me);
115 Real beam_translation = gh_scm2int (beam_count) < 4
116 ? (2*staff_space + line - thickness) / 2.0
117 : (3*staff_space + line - thickness) / 3.0;
119 return gh_double2scm (beam_translation);
123 /* After pre-processing all directions should be set.
124 Several post-processing routines (stem, slur, script) need stem/beam
126 Currenly, this means that beam has set all stem's directions.
127 [Alternatively, stems could set its own directions, according to
128 their beam, during 'final-pre-processing'.] */
129 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
131 Beam::before_line_breaking (SCM smob)
133 Grob *me = unsmob_grob (smob);
135 /* Beams with less than 2 two stems don't make much sense, but could happen
140 For a beam that only has one stem, we try to do some disappearance magic:
141 we revert the flag, and move on to The Eternal Engraving Fields. */
143 int count = visible_stem_count (me);
146 me->warning (_ ("beam has less than two visible stems"));
148 SCM stems = me->get_grob_property ("stems");
149 if (scm_ilength (stems) == 1)
151 me->warning (_ ("Beam has less than two stems. Removing beam."));
153 unsmob_grob (gh_car (stems))->set_grob_property ("beam", SCM_EOL);
156 return SCM_UNSPECIFIED;
158 else if (scm_ilength (stems) == 0)
161 return SCM_UNSPECIFIED;
166 Direction d = get_default_dir (me);
168 consider_auto_knees (me);
169 set_stem_directions (me, d);
173 set_stem_shorten (me);
181 We want a maximal number of shared beams, but if there is choice, we
182 take the one that is closest to the end of the stem. This is for situations like
195 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
199 Slice lslice = int_list_to_slice (gh_cdr (left_beaming));
203 for (int i = lslice[-left_dir];
204 (i - lslice[left_dir])* left_dir <= 0 ; i+= left_dir)
207 for ( SCM s = gh_car (right_beaming); gh_pair_p (s); s = gh_cdr (s))
209 int k = - right_dir * gh_scm2int (gh_car (s)) + i;
210 if (scm_memq (scm_int2num (k), left_beaming) != SCM_BOOL_F)
214 if (count >= best_count)
225 Beam::connect_beams (Grob *me)
227 Link_array<Grob> stems=
228 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
231 last_int.set_empty();
232 SCM last_beaming = SCM_EOL;
233 Direction last_dir = CENTER;
234 for (int i = 0; i< stems.size(); i++)
236 Grob *this_stem = stems[i];
237 SCM this_beaming = this_stem->get_grob_property ("beaming");
239 Direction this_dir = Directional_element_interface::get(this_stem);
240 if (gh_pair_p (last_beaming) && gh_pair_p (this_beaming))
242 int start_point = position_with_maximal_common_beams
243 (last_beaming, this_beaming,
250 if (d == RIGHT && i == stems.size()-1)
253 new_slice.set_empty();
254 SCM s = index_get_cell (this_beaming, d);
255 for (; gh_pair_p (s); s = gh_cdr (s))
258 start_point - this_dir * gh_scm2int (gh_car (s));
260 new_slice.add_point (new_beam_pos);
261 gh_set_car_x (s, scm_int2num (new_beam_pos));
266 while (flip (&d) != LEFT);
268 if (!new_slice.empty_b())
269 last_int = new_slice;
273 gh_set_car_x ( this_beaming, SCM_EOL);
274 SCM s = gh_cdr (this_beaming);
275 for (; gh_pair_p (s); s = gh_cdr (s))
277 int np = - this_dir * gh_scm2int (gh_car(s));
278 gh_set_car_x (s, scm_int2num (np));
279 last_int.add_point (np);
283 if (i == stems.size () -1)
285 gh_set_cdr_x (this_beaming, SCM_EOL);
288 if (scm_ilength (gh_cdr (this_beaming)) > 0)
290 last_beaming = this_beaming;
296 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
298 Beam::brew_molecule (SCM grob)
300 Grob *me = unsmob_grob (grob);
301 Link_array<Grob> stems=
302 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
303 Grob* xcommon = common_refpoint_of_array (stems, me, X_AXIS);
306 if (visible_stem_count (me))
308 // ugh -> use commonx
309 x0 = first_visible_stem (me)->relative_coordinate (xcommon, X_AXIS);
310 dx = last_visible_stem (me)->relative_coordinate (xcommon, X_AXIS) - x0;
314 x0 = stems[0]->relative_coordinate (xcommon, X_AXIS);
315 dx = stems.top ()->relative_coordinate (xcommon, X_AXIS) - x0;
318 SCM posns = me->get_grob_property ("positions");
320 if (!ly_number_pair_p (posns))
322 programming_error ("No beam posns");
323 pos = Interval (0,0);
326 pos= ly_scm2interval (posns);
328 Real dy = pos.delta ();
329 Real dydx = dy && dx ? dy/dx : 0;
331 Real thick = get_thickness (me);
332 Real bdy = get_beam_translation (me);
334 SCM last_beaming = SCM_EOL;;
335 Real last_xposn = -1;
336 Real last_width = -1 ;
339 SCM gap = me->get_grob_property ("gap");
341 Real lt = me->get_paper ()->get_var ("linethickness");
343 for (int i = 0; i< stems.size(); i++)
347 SCM this_beaming = st->get_grob_property ("beaming");
348 Real xposn = st->relative_coordinate (xcommon, X_AXIS);
349 Real stem_width = gh_scm2double (st->get_grob_property ("thickness")) *lt;
352 We do the space left of ST, with lfliebertjes pointing to the
353 right from the left stem, and rfliebertjes pointing left from
358 SCM left = gh_cdr (last_beaming);
359 SCM right = gh_car (this_beaming);
361 Array<int> fullbeams;
362 Array<int> lfliebertjes;
363 Array<int> rfliebertjes;
366 gh_pair_p (s); s =gh_cdr (s))
368 int b = gh_scm2int (gh_car (s));
369 if (scm_memq (gh_car(s), right) != SCM_BOOL_F)
375 lfliebertjes.push (b);
379 gh_pair_p (s); s =gh_cdr (s))
381 int b = gh_scm2int (gh_car (s));
382 if (scm_memq (gh_car(s), left) == SCM_BOOL_F)
384 rfliebertjes.push (b);
389 Real w = xposn - last_xposn;
390 Real stem_offset = 0.0;
391 Real width_corr = 0.0;
394 stem_offset -= last_width/2;
395 width_corr += last_width/2;
398 if (i == stems.size() -1)
400 width_corr += stem_width/2;
403 if (gh_number_p (gap))
405 Real g = gh_scm2double (gap);
410 Molecule whole = Lookup::beam (dydx, w + width_corr, thick);
411 for (int j = fullbeams.size(); j--;)
414 b.translate_axis (last_xposn - x0 + stem_offset, X_AXIS);
415 b.translate_axis (dydx * (last_xposn - x0) + bdy * fullbeams[j], Y_AXIS);
416 the_beam.add_molecule (b);
419 if (lfliebertjes.size() || rfliebertjes.size())
423 int t = Stem::duration_log (st);
425 SCM proc = me->get_grob_property ("flag-width-function");
426 SCM result = gh_call1 (proc, scm_int2num (t));
427 nw_f = gh_scm2double (result);
430 /* Half beam should be one note-width,
431 but let's make sure two half-beams never touch */
433 Real w = xposn - last_xposn;
436 Molecule half = Lookup::beam (dydx, w, thick);
437 for (int j = lfliebertjes.size(); j--;)
440 b.translate_axis (last_xposn - x0, X_AXIS);
441 b.translate_axis (dydx * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
442 the_beam.add_molecule (b);
444 for (int j = rfliebertjes.size(); j--;)
447 b.translate_axis (xposn - x0 - w , X_AXIS);
448 b.translate_axis (dydx * (xposn-x0 -w) + bdy * rfliebertjes[j], Y_AXIS);
449 the_beam.add_molecule (b);
455 last_width = stem_width;
456 last_beaming = this_beaming;
459 the_beam.translate_axis (x0 - me->relative_coordinate (xcommon, X_AXIS), X_AXIS);
460 the_beam.translate_axis (pos[LEFT], Y_AXIS);
465 This code prints the demerits for each beam. Perhaps this
466 should be switchable for those who want to twiddle with the
472 str += to_string (gh_scm2int (me->get_grob_property ("best-idx")));
475 str += to_string (gh_scm2double (me->get_grob_property ("quant-score")),
478 SCM properties = Font_interface::font_alist_chain (me);
481 Molecule tm = Text_item::text2molecule (me, scm_makfrom0str (str.to_str0 ()), properties);
482 the_beam.add_at_edge (Y_AXIS, UP, tm, 5.0, 0);
488 return the_beam.smobbed_copy();
495 Beam::get_default_dir (Grob *me)
497 Drul_array<int> total;
498 total[UP] = total[DOWN] = 0;
499 Drul_array<int> count;
500 count[UP] = count[DOWN] = 0;
503 Link_array<Grob> stems=
504 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
506 for (int i=0; i <stems.size (); i++)
509 Direction sd = Directional_element_interface::get (s);
511 int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
512 int current = sd ? (1 + d * sd)/2 : center_distance;
519 } while (flip (&d) != DOWN);
521 SCM func = me->get_grob_property ("dir-function");
522 SCM s = gh_call2 (func,
523 gh_cons (scm_int2num (count[UP]),
524 scm_int2num (count[DOWN])),
525 gh_cons (scm_int2num (total[UP]),
526 scm_int2num (total[DOWN])));
528 if (gh_number_p (s) && gh_scm2int (s))
531 /* If dir is not determined: get default */
532 return to_dir (me->get_grob_property ("neutral-direction"));
536 /* Set all stems with non-forced direction to beam direction.
537 Urg: non-forced should become `without/with unforced' direction,
538 once stem gets cleaned-up. */
540 Beam::set_stem_directions (Grob *me, Direction d)
542 Link_array<Grob> stems
543 =Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
545 for (int i=0; i <stems.size (); i++)
549 SCM forcedir = s->get_grob_property ("direction");
550 if (!to_dir (forcedir))
551 Directional_element_interface::set (s, d);
556 A union of intervals in the real line.
558 Abysmal performance (quadratic) for large N, hopefully we don't have
559 that large N. In any case, this should probably be rewritten to use
564 Array<Interval> allowed_regions_;
573 allowed_regions_.clear();
576 allowed_regions_.push (s);
579 void remove_interval (Interval rm)
581 for (int i = 0; i < allowed_regions_.size(); )
585 s.intersect (allowed_regions_[i]);
589 Interval before = allowed_regions_[i];
590 Interval after = allowed_regions_[i];
592 before[RIGHT] = s[LEFT];
593 after[LEFT] = s[RIGHT];
595 if (!before.empty_b() && before.length () > 0.0)
597 allowed_regions_.insert (before, i);
600 allowed_regions_.del (i);
601 if (!after.empty_b () && after.length () > 0.0)
603 allowed_regions_.insert (after, i);
615 Only try horizontal beams for knees. No reliable detection of
616 anything else is possible here, since we don't know funky-beaming
617 settings, or X-distances (slopes!) People that want sloped
618 knee-beams, should set the directions manually.
621 Beam::consider_auto_knees (Grob* me)
623 SCM scm = me->get_grob_property ("auto-knee-gap");
624 if (!gh_number_p (scm))
627 Real threshold = gh_scm2double (scm);
633 Link_array<Grob> stems=
634 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
636 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
637 Real staff_space = Staff_symbol_referencer::staff_space (me);
639 Array<Interval> hps_array;
640 for (int i=0; i < stems.size (); i++)
642 Grob* stem = stems[i];
643 if (Stem::invisible_b (stem))
646 Interval hps = Stem::head_positions (stem);
651 hps *= staff_space * 0.5 ;
654 We could subtract beam Y position, but this routine only
655 sets stem directions, a constant shift does not have an
659 hps += stem->relative_coordinate (common, Y_AXIS);
661 if (to_dir (stem->get_grob_property ("direction")))
663 Direction stemdir = to_dir (stem->get_grob_property ("direction"));
664 hps[-stemdir] = - stemdir * infinity_f;
667 hps_array.push (hps);
669 gaps.remove_interval (hps);
673 Real max_gap_len =0.0;
675 for (int i = gaps.allowed_regions_.size() -1; i >= 0 ; i--)
677 Interval gap = gaps.allowed_regions_[i];
680 the outer gaps are not knees.
682 if (isinf (gap[LEFT]) || isinf(gap[RIGHT]))
685 if (gap.length () >= max_gap_len)
687 max_gap_len = gap.length();
692 if (max_gap_len > threshold)
695 for (int i = 0; i < stems.size(); i++)
697 Grob* stem = stems[i];
698 if (Stem::invisible_b (stem))
701 Interval hps = hps_array[j++];
704 Direction d = (hps.center () < max_gap.center()) ?
707 stem->set_grob_property ("direction", scm_int2num (d));
709 hps.intersect (max_gap);
710 assert (hps.empty_b () || hps.length () < 1e-6 );
717 /* Set stem's shorten property if unset.
720 take some y-position (chord/beam/nearest?) into account
721 scmify forced-fraction
723 This is done in beam because the shorten has to be uniform over the
728 Beam::set_stem_shorten (Grob *me)
731 shortening looks silly for x staff beams
736 Real forced_fraction = 1.0 * forced_stem_count (me)
737 / visible_stem_count (me);
739 int beam_count = get_beam_count (me);
741 SCM shorten_list = me->get_grob_property ("beamed-stem-shorten");
742 if (shorten_list == SCM_EOL)
745 Real staff_space = Staff_symbol_referencer::staff_space (me);
748 robust_list_ref (beam_count -1, shorten_list);
749 Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
751 /* your similar cute comment here */
752 shorten_f *= forced_fraction;
755 me->set_grob_property ("shorten", gh_double2scm (shorten_f));
758 /* Call list of y-dy-callbacks, that handle setting of
762 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
764 Beam::after_line_breaking (SCM smob)
766 Grob *me = unsmob_grob (smob);
768 /* Copy to mutable list. */
769 SCM s = ly_deep_copy (me->get_grob_property ("positions"));
770 me->set_grob_property ("positions", s);
772 if (ly_car (s) == SCM_BOOL_F)
775 // one wonders if such genericity is necessary --hwn.
776 SCM callbacks = me->get_grob_property ("position-callbacks");
777 for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
778 gh_call1 (ly_car (i), smob);
781 set_stem_lengths (me);
782 return SCM_UNSPECIFIED;
787 Compute a first approximation to the beam slope.
789 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
791 Beam::least_squares (SCM smob)
793 Grob *me = unsmob_grob (smob);
795 int count = visible_stem_count (me);
800 me->set_grob_property ("positions", ly_interval2scm (pos));
801 return SCM_UNSPECIFIED;
805 Array<Real> x_posns ;
806 Link_array<Grob> stems=
807 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
808 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
809 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
811 Real my_y = me->relative_coordinate (commony, Y_AXIS);
813 Grob *fvs = first_visible_stem (me);
814 Grob *lvs = last_visible_stem (me);
816 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
817 + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
818 Stem::get_stem_info (lvs).ideal_y_
819 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
821 Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
822 for (int i=0; i < stems.size (); i++)
826 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
829 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
837 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
838 Stem::chord_start_y (last_visible_stem (me)));
840 /* Simple beams (2 stems) on middle line should be allowed to be
843 However, if both stems reach middle line,
844 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
846 For that case, we apply artificial slope */
847 if (!ideal[LEFT] && chord.delta () && count == 2)
850 Direction d = (Direction) (sign (chord.delta ()) * UP);
851 pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
865 Array<Offset> ideals;
866 for (int i=0; i < stems.size (); i++)
869 if (Stem::invisible_b (s))
871 ideals.push (Offset (x_posns[i],
872 Stem::get_stem_info (s).ideal_y_
873 + s->relative_coordinate (commony, Y_AXIS)
876 minimise_least_squares (&dydx, &y, ideals);
879 me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
880 pos = Interval (y, (y+dy));
883 me->set_grob_property ("positions", ly_interval2scm (pos));
885 return SCM_UNSPECIFIED;
890 We can't combine with previous function, since check concave and
891 slope damping comes first.
893 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
895 Beam::shift_region_to_valid (SCM grob)
897 Grob *me = unsmob_grob (grob);
901 Array<Real> x_posns ;
902 Link_array<Grob> stems=
903 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
904 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
905 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
907 Grob *fvs = first_visible_stem (me);
910 return SCM_UNSPECIFIED;
912 Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
913 for (int i=0; i < stems.size (); i++)
917 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
921 Grob *lvs = last_visible_stem (me);
923 return SCM_UNSPECIFIED;
925 Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
927 Interval pos = ly_scm2interval ( me->get_grob_property ("positions"));
928 Real dy = pos.delta();
934 Shift the positions so that we have a chance of finding good
935 quants (i.e. no short stem failures.)
937 Interval feasible_left_point;
938 feasible_left_point.set_full ();
939 for (int i=0; i < stems.size (); i++)
942 if (Stem::invisible_b (s))
945 Direction d = Stem::get_direction (s);
948 Stem::get_stem_info (s).shortest_y_
949 - dydx * x_posns [i];
952 left_y is now relative to the stem S. We want relative to
953 ourselves, so translate:
956 + s->relative_coordinate (commony, Y_AXIS)
957 - me->relative_coordinate (commony, Y_AXIS);
963 feasible_left_point.intersect (flp);
966 if (feasible_left_point.empty_b())
968 warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
970 else if (!feasible_left_point.elem_b(y))
972 if (isinf (feasible_left_point[DOWN]))
973 y = feasible_left_point[UP] - REGION_SIZE;
974 else if (isinf (feasible_left_point[UP]))
975 y = feasible_left_point[DOWN]+ REGION_SIZE;
977 y = feasible_left_point.center ();
979 pos = Interval (y, (y+dy));
980 me->set_grob_property ("positions", ly_interval2scm (pos));
981 return SCM_UNSPECIFIED;
985 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
987 Beam::check_concave (SCM smob)
989 Grob *me = unsmob_grob (smob);
991 Link_array<Grob> stems =
992 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
994 for (int i = 0; i < stems.size ();)
996 if (Stem::invisible_b (stems[i]))
1002 if (stems.size () < 3)
1003 return SCM_UNSPECIFIED;
1006 /* Concaveness #1: If distance of an inner notehead to line between
1007 two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
1008 beam is concave (Heinz Stolba).
1010 In the case of knees, the line connecting outer heads is often
1011 not related to the beam slope (it may even go in the other
1012 direction). Skip the check when the outer stems point in
1013 different directions. --hwn
1016 bool concaveness1 = false;
1017 SCM gap = me->get_grob_property ("concaveness-gap");
1018 if (gh_number_p (gap)
1019 && Stem::get_direction(stems.top ())
1020 == Stem::get_direction(stems[0]))
1022 Real r1 = gh_scm2double (gap);
1023 Real dy = Stem::chord_start_y (stems.top ())
1024 - Stem::chord_start_y (stems[0]);
1027 Real slope = dy / (stems.size () - 1);
1029 Real y0 = Stem::chord_start_y (stems[0]);
1030 for (int i = 1; i < stems.size () - 1; i++)
1032 Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
1035 concaveness1 = true;
1042 /* Concaveness #2: Sum distances of inner noteheads that fall
1043 outside the interval of the two outer noteheads.
1045 We only do this for beams where first and last stem have the same
1049 Note that "convex" stems compensate for "concave" stems.
1050 (is that intentional?) --hwn.
1053 Real concaveness2 = 0;
1054 SCM thresh = me->get_grob_property ("concaveness-threshold");
1055 Real r2 = infinity_f;
1056 if (!concaveness1 && gh_number_p (thresh)
1057 && Stem::get_direction(stems.top ())
1058 == Stem::get_direction(stems[0]))
1060 r2 = gh_scm2double (thresh);
1062 Direction dir = Stem::get_direction(stems.top ());
1064 Interval iv (Stem::chord_start_y (stems[0]),
1065 Stem::chord_start_y (stems.top ()));
1067 if (iv[MAX] < iv[MIN])
1070 for (int i = 1; i < stems.size () - 1; i++)
1072 Real f = Stem::chord_start_y (stems[i]);
1073 concave += ((f - iv[MAX] ) >? 0) +
1074 ((f - iv[MIN] ) <? 0);
1077 concaveness2 = concave / (stems.size () - 2);
1081 ugh: this is the a kludge to get
1082 input/regression/beam-concave.ly to behave as
1088 huh? we're dividing twice (which is not scalable) meaning that
1089 the longer the beam, the more unlikely it will be
1090 concave. Maybe you would even expect the other way around??
1095 concaveness2 /= (stems.size () - 2);
1098 /* TODO: some sort of damping iso -> plain horizontal */
1099 if (concaveness1 || concaveness2 > r2)
1101 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1102 Real r = pos.linear_combination (0);
1103 me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
1104 me->set_grob_property ("least-squares-dy", gh_double2scm (0));
1107 return SCM_UNSPECIFIED;
1110 /* This neat trick is by Werner Lemberg,
1111 damped = tanh (slope)
1112 corresponds with some tables in [Wanske] CHECKME */
1113 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1115 Beam::slope_damping (SCM smob)
1117 Grob *me = unsmob_grob (smob);
1119 if (visible_stem_count (me) <= 1)
1120 return SCM_UNSPECIFIED;
1122 SCM s = me->get_grob_property ("damping");
1123 int damping = gh_scm2int (s);
1127 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1128 Real dy = pos.delta ();
1130 Grob *fvs = first_visible_stem (me);
1131 Grob *lvs = last_visible_stem (me);
1133 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1136 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1137 - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1138 Real dydx = dy && dx ? dy/dx : 0;
1139 dydx = 0.6 * tanh (dydx) / damping;
1141 Real damped_dy = dydx * dx;
1142 pos[LEFT] += (dy - damped_dy) / 2;
1143 pos[RIGHT] -= (dy - damped_dy) / 2;
1145 me->set_grob_property ("positions", ly_interval2scm (pos));
1147 return SCM_UNSPECIFIED;
1151 Report slice containing the numbers that are both in (car BEAMING)
1155 where_are_the_whole_beams(SCM beaming)
1159 for( SCM s = gh_car (beaming); gh_pair_p (s) ; s = gh_cdr (s))
1161 if (scm_memq (gh_car (s), gh_cdr (beaming)) != SCM_BOOL_F)
1163 l.add_point (gh_scm2int (gh_car (s)));
1169 /* Return the Y position of the stem-end, given the Y-left, Y-right
1170 in POS for stem S. This Y position is relative to S. */
1172 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1174 Interval pos, bool french)
1176 Real beam_translation = get_beam_translation (me);
1179 Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1180 Real dy = pos.delta ();
1182 Real stem_y_beam0 = (dy && dx
1187 Direction my_dir = Directional_element_interface::get (s);
1188 SCM beaming = s->get_grob_property ("beaming");
1190 Real stem_y = stem_y_beam0;
1193 Slice bm = where_are_the_whole_beams (beaming);
1195 stem_y += beam_translation * bm[-my_dir];
1199 Slice bm = Stem::beam_multiplicity(s);
1201 stem_y +=bm[my_dir] * beam_translation;
1204 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1205 - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1211 Hmm. At this time, beam position and slope are determined. Maybe,
1212 stem directions and length should set to relative to the chord's
1213 position of the beam. */
1215 Beam::set_stem_lengths (Grob *me)
1217 Link_array<Grob> stems=
1218 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1220 if (stems.size () <= 1)
1224 for (int a = 2; a--;)
1225 common[a] = common_refpoint_of_array (stems, me, Axis(a));
1227 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1228 Real staff_space = Staff_symbol_referencer::staff_space (me);
1230 bool french = to_boolean (me->get_grob_property ("french-beaming"));
1235 if (gh_number_p (me->get_grob_property ("gap"))
1236 &&gh_scm2double (me->get_grob_property ("gap")))
1239 thick = get_thickness(me);
1242 // ugh -> use commonx
1243 Grob * fvs = first_visible_stem (me);
1244 Grob *lvs = last_visible_stem (me);
1246 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1247 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1249 for (int i=0; i < stems.size (); i++)
1252 if (Stem::invisible_b (s))
1255 Real stem_y = calc_stem_y (me, s, common,
1257 pos, french && i > 0&& (i < stems.size () -1));
1260 Make the stems go up to the end of the beam. This doesn't matter
1261 for normal beams, but for tremolo beams it looks silly otherwise.
1264 stem_y += thick * 0.5 * Directional_element_interface::get(s);
1266 Stem::set_stemend (s, 2* stem_y / staff_space);
1271 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1273 Link_array<Grob> stems=
1274 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1277 for (int i=0; i < stems.size (); i++)
1280 Don't overwrite user settings.
1285 /* Don't set beaming for outside of outer stems */
1286 if ((d == LEFT && i == 0)
1287 ||(d == RIGHT && i == stems.size () -1))
1290 Grob *st = stems[i];
1291 SCM beaming_prop = st->get_grob_property ("beaming");
1292 if (beaming_prop == SCM_EOL ||
1293 index_get_cell (beaming_prop, d) == SCM_EOL)
1295 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1297 && i < stems.size() -1
1298 && Stem::invisible_b (st))
1299 b = b <? beaming->infos_.elem(i).beams_i_drul_[-d];
1301 Stem::set_beaming (st, b, d);
1304 while (flip (&d) != LEFT);
1309 Beam::forced_stem_count (Grob *me)
1311 Link_array<Grob>stems =
1312 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1314 for (int i=0; i < stems.size (); i++)
1318 if (Stem::invisible_b (s))
1321 /* I can imagine counting those boundaries as a half forced stem,
1322 but let's count them full for now. */
1323 if (abs (Stem::chord_start_y (s)) > 0.1
1324 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1334 Beam::visible_stem_count (Grob *me)
1336 Link_array<Grob>stems =
1337 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1339 for (int i = stems.size (); i--;)
1341 if (!Stem::invisible_b (stems[i]))
1348 Beam::first_visible_stem (Grob *me)
1350 Link_array<Grob>stems =
1351 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1353 for (int i = 0; i < stems.size (); i++)
1355 if (!Stem::invisible_b (stems[i]))
1362 Beam::last_visible_stem (Grob *me)
1364 Link_array<Grob>stems =
1365 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1366 for (int i = stems.size (); i--;)
1368 if (!Stem::invisible_b (stems[i]))
1378 handle rest under beam (do_post: beams are calculated now)
1379 what about combination of collisions and rest under beam.
1383 rest -> stem -> beam -> interpolate_y_position ()
1385 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1387 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1389 Grob *rest = unsmob_grob (element_smob);
1390 Axis a = (Axis) gh_scm2int (axis);
1392 assert (a == Y_AXIS);
1394 Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1397 return gh_double2scm (0.0);
1398 Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1400 || !Beam::has_interface (beam)
1401 || !Beam::visible_stem_count (beam))
1402 return gh_double2scm (0.0);
1404 Interval pos (0, 0);
1405 SCM s = beam->get_grob_property ("positions");
1406 if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1407 pos = ly_scm2interval (s);
1409 Real dy = pos.delta ();
1410 // ugh -> use commonx
1411 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1412 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1413 Real dydx = dy && dx ? dy/dx : 0;
1415 Direction d = Stem::get_direction (stem);
1416 Real stem_y = (pos[LEFT]
1417 + (stem->relative_coordinate (0, X_AXIS) - x0) * dydx)
1420 Real beam_translation = get_beam_translation (beam);
1421 Real beam_thickness = gh_scm2double (beam->get_grob_property ("thickness"));
1422 int beam_count = get_direction_beam_count (beam, d);
1423 Real height_of_my_beams = beam_thickness
1424 + (beam_count - 1) * beam_translation;
1425 Real beam_y = stem_y - height_of_my_beams + beam_thickness / 2.0;
1427 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1429 /* Better calculate relative-distance directly, rather than using
1431 Grob *common_x = rest->common_refpoint (beam, Y_AXIS);
1432 Real rest_dim = rest->extent (common_x, Y_AXIS)[d] / staff_space * d;
1434 Real minimum_distance = gh_scm2double
1435 (rest->get_grob_property ("minimum-beam-collision-distance"));
1437 Real distance = beam_y - rest_dim;
1440 shift = minimum_distance - distance;
1441 else if (minimum_distance > distance)
1442 shift = minimum_distance - distance;
1444 int stafflines = Staff_symbol_referencer::line_count (rest);
1446 /* Always move discretely by half spaces */
1447 Real discrete_shift = ceil (shift * 2.0) / 2.0;
1449 /* Inside staff, move by whole spaces*/
1450 if ((rest->extent (common_x, Y_AXIS)[d] + discrete_shift) * d
1452 ||(rest->extent (common_x, Y_AXIS)[-d] + discrete_shift) * -d
1454 discrete_shift = ceil (discrete_shift);
1456 return gh_double2scm (-d * discrete_shift);
1460 Beam::knee_b (Grob* me)
1462 SCM k = me->get_grob_property ("knee");
1463 if (gh_boolean_p (k))
1464 return gh_scm2bool (k);
1468 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
1470 Direction dir = Directional_element_interface::get
1471 (unsmob_grob (ly_car (s)));
1480 me->set_grob_property ("knee", gh_bool2scm (knee));
1486 Beam::get_direction_beam_count (Grob *me, Direction d )
1488 Link_array<Grob>stems =
1489 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1492 for (int i = stems.size (); i--;)
1495 Should we take invisible stems into account?
1497 if (Stem::get_direction (stems[i]) == d)
1498 bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1505 ADD_INTERFACE (Beam, "beam-interface",
1508 "#'thickness= weight of beams, in staffspace "
1511 "We take the least squares line through the ideal-length stems, and "
1512 "then damp that using "
1514 " damped = tanh (slope) \n"
1516 "this gives an unquantized left and right position for the beam end. "
1517 "Then we take all combinations of quantings near these left and right "
1518 "positions, and give them a score (according to how close they are to "
1519 "the ideal slope, how close the result is to the ideal stems, etc.). We "
1520 "take the best scoring combination. "
1522 "knee 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");