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_realvar (ly_symbol2scm ("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 Real gap_length =0.0;
340 SCM scm_gap = me->get_grob_property ("gap");
341 if (gh_number_p (scm_gap))
342 gap_length = gh_scm2double (scm_gap);
345 Real lt = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
347 for (int i = 0; i<= stems.size(); i++)
349 Grob * st = (i < stems.size()) ? stems[i] : 0;
351 SCM this_beaming = st ? st->get_grob_property ("beaming") : SCM_EOL;
352 Real xposn = st ? st->relative_coordinate (xcommon, X_AXIS) : 0.0;
353 Real stem_width = st ? gh_scm2double (st->get_grob_property ("thickness")) *lt : 0 ;
354 Direction stem_dir = st ? to_dir (st->get_grob_property ("direction")) : CENTER;
356 We do the space left of ST, with lfliebertjes pointing to the
357 right from the left stem, and rfliebertjes pointing left from
360 SCM left = (i>0) ? gh_cdr (last_beaming) : SCM_EOL;
361 SCM right = st ? gh_car (this_beaming) : SCM_EOL;
363 Array<int> full_beams;
364 Array<int> lfliebertjes;
365 Array<int> rfliebertjes;
368 gh_pair_p (s); s =gh_cdr (s))
370 int b = gh_scm2int (gh_car (s));
371 if (scm_memq (gh_car(s), right) != SCM_BOOL_F)
377 lfliebertjes.push (b);
381 gh_pair_p (s); s =gh_cdr (s))
383 int b = gh_scm2int (gh_car (s));
384 if (scm_memq (gh_car(s), left) == SCM_BOOL_F)
386 rfliebertjes.push (b);
391 how much to stick out for beams across linebreaks
393 Real break_overshoot = 3.0;
394 Real w = (i>0 && st)? xposn - last_xposn : break_overshoot;
395 Real stem_offset = 0.0;
396 Real width_corr = 0.0;
399 stem_offset -= last_width/2;
400 width_corr += last_width/2;
403 if (i == stems.size() -1)
405 width_corr += stem_width/2;
409 Molecule whole = Lookup::beam (dydx, w + width_corr, thick);
413 if (gh_number_p (me->get_grob_property ("gap-count")))
415 gap_count = gh_scm2int (me->get_grob_property ("gap-count"));
416 gapped = Lookup::beam (dydx, w + width_corr - 2 * gap_length, thick);
418 full_beams.sort (default_compare);
420 full_beams.reverse ();
424 for (int j = full_beams.size (); j--;)
431 b.translate_axis (gap_length, X_AXIS);
433 b.translate_axis (last_xposn - x0 + stem_offset, X_AXIS);
434 b.translate_axis (dydx * (last_xposn - x0) + bdy * full_beams[j], Y_AXIS);
436 the_beam.add_molecule (b);
441 if (lfliebertjes.size() || rfliebertjes.size())
447 int t = Stem::duration_log (st);
449 SCM proc = me->get_grob_property ("flag-width-function");
450 SCM result = gh_call1 (proc, scm_int2num (t));
451 nw_f = gh_scm2double (result);
454 nw_f = break_overshoot;
456 /* Half beam should be one note-width,
457 but let's make sure two half-beams never touch */
458 Real w = (i>0 && st) ? (xposn - last_xposn) : break_overshoot;
461 Molecule half = Lookup::beam (dydx, w, thick);
462 for (int j = lfliebertjes.size(); j--;)
465 b.translate_axis (last_xposn - x0, X_AXIS);
466 b.translate_axis (dydx * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
467 the_beam.add_molecule (b);
469 for (int j = rfliebertjes.size(); j--;)
472 b.translate_axis (xposn - x0 - w , X_AXIS);
473 b.translate_axis (dydx * (xposn-x0 -w) + bdy * rfliebertjes[j], Y_AXIS);
474 the_beam.add_molecule (b);
480 last_width = stem_width;
481 last_beaming = this_beaming;
484 the_beam.translate_axis (x0 - me->relative_coordinate (xcommon, X_AXIS), X_AXIS);
485 the_beam.translate_axis (pos[LEFT], Y_AXIS);
490 This code prints the demerits for each beam. Perhaps this
491 should be switchable for those who want to twiddle with the
497 str += to_string (gh_scm2int (me->get_grob_property ("best-idx")));
500 str += to_string (gh_scm2double (me->get_grob_property ("quant-score")),
503 SCM properties = Font_interface::font_alist_chain (me);
505 Molecule tm = Text_item::interpret_new_markup
506 (me->self_scm(), properties, scm_makfrom0str (str.to_str0 ()));
507 the_beam.add_at_edge (Y_AXIS, UP, tm, 5.0, 0);
513 return the_beam.smobbed_copy();
520 Beam::get_default_dir (Grob *me)
522 Drul_array<int> total;
523 total[UP] = total[DOWN] = 0;
524 Drul_array<int> count;
525 count[UP] = count[DOWN] = 0;
528 Link_array<Grob> stems=
529 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
531 for (int i=0; i <stems.size (); i++)
534 Direction sd = Directional_element_interface::get (s);
536 int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
537 int current = sd ? (1 + d * sd)/2 : center_distance;
544 } while (flip (&d) != DOWN);
546 SCM func = me->get_grob_property ("dir-function");
547 SCM s = gh_call2 (func,
548 gh_cons (scm_int2num (count[UP]),
549 scm_int2num (count[DOWN])),
550 gh_cons (scm_int2num (total[UP]),
551 scm_int2num (total[DOWN])));
553 if (gh_number_p (s) && gh_scm2int (s))
556 /* If dir is not determined: get default */
557 return to_dir (me->get_grob_property ("neutral-direction"));
561 /* Set all stems with non-forced direction to beam direction.
562 Urg: non-forced should become `without/with unforced' direction,
563 once stem gets cleaned-up. */
565 Beam::set_stem_directions (Grob *me, Direction d)
567 Link_array<Grob> stems
568 =Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
570 for (int i=0; i <stems.size (); i++)
574 SCM forcedir = s->get_grob_property ("direction");
575 if (!to_dir (forcedir))
576 Directional_element_interface::set (s, d);
581 A union of intervals in the real line.
583 Abysmal performance (quadratic) for large N, hopefully we don't have
584 that large N. In any case, this should probably be rewritten to use
589 Array<Interval> allowed_regions_;
598 allowed_regions_.clear();
601 allowed_regions_.push (s);
604 void remove_interval (Interval rm)
606 for (int i = 0; i < allowed_regions_.size(); )
610 s.intersect (allowed_regions_[i]);
614 Interval before = allowed_regions_[i];
615 Interval after = allowed_regions_[i];
617 before[RIGHT] = s[LEFT];
618 after[LEFT] = s[RIGHT];
620 if (!before.empty_b() && before.length () > 0.0)
622 allowed_regions_.insert (before, i);
625 allowed_regions_.del (i);
626 if (!after.empty_b () && after.length () > 0.0)
628 allowed_regions_.insert (after, i);
640 Only try horizontal beams for knees. No reliable detection of
641 anything else is possible here, since we don't know funky-beaming
642 settings, or X-distances (slopes!) People that want sloped
643 knee-beams, should set the directions manually.
646 Beam::consider_auto_knees (Grob* me)
648 SCM scm = me->get_grob_property ("auto-knee-gap");
649 if (!gh_number_p (scm))
652 Real threshold = gh_scm2double (scm);
658 Link_array<Grob> stems=
659 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
661 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
662 Real staff_space = Staff_symbol_referencer::staff_space (me);
664 Array<Interval> hps_array;
665 for (int i=0; i < stems.size (); i++)
667 Grob* stem = stems[i];
668 if (Stem::invisible_b (stem))
671 Interval hps = Stem::head_positions (stem);
676 hps *= staff_space * 0.5 ;
679 We could subtract beam Y position, but this routine only
680 sets stem directions, a constant shift does not have an
684 hps += stem->relative_coordinate (common, Y_AXIS);
686 if (to_dir (stem->get_grob_property ("direction")))
688 Direction stemdir = to_dir (stem->get_grob_property ("direction"));
689 hps[-stemdir] = - stemdir * infinity_f;
692 hps_array.push (hps);
694 gaps.remove_interval (hps);
698 Real max_gap_len =0.0;
700 for (int i = gaps.allowed_regions_.size() -1; i >= 0 ; i--)
702 Interval gap = gaps.allowed_regions_[i];
705 the outer gaps are not knees.
707 if (isinf (gap[LEFT]) || isinf(gap[RIGHT]))
710 if (gap.length () >= max_gap_len)
712 max_gap_len = gap.length();
717 if (max_gap_len > threshold)
720 for (int i = 0; i < stems.size(); i++)
722 Grob* stem = stems[i];
723 if (Stem::invisible_b (stem))
726 Interval hps = hps_array[j++];
729 Direction d = (hps.center () < max_gap.center()) ?
732 stem->set_grob_property ("direction", scm_int2num (d));
734 hps.intersect (max_gap);
735 assert (hps.empty_b () || hps.length () < 1e-6 );
742 /* Set stem's shorten property if unset.
745 take some y-position (chord/beam/nearest?) into account
746 scmify forced-fraction
748 This is done in beam because the shorten has to be uniform over the
753 Beam::set_stem_shorten (Grob *me)
756 shortening looks silly for x staff beams
761 Real forced_fraction = 1.0 * forced_stem_count (me)
762 / visible_stem_count (me);
764 int beam_count = get_beam_count (me);
766 SCM shorten_list = me->get_grob_property ("beamed-stem-shorten");
767 if (shorten_list == SCM_EOL)
770 Real staff_space = Staff_symbol_referencer::staff_space (me);
773 robust_list_ref (beam_count -1, shorten_list);
774 Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
776 /* your similar cute comment here */
777 shorten_f *= forced_fraction;
780 me->set_grob_property ("shorten", gh_double2scm (shorten_f));
783 /* Call list of y-dy-callbacks, that handle setting of
787 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
789 Beam::after_line_breaking (SCM smob)
791 Grob *me = unsmob_grob (smob);
793 /* Copy to mutable list. */
794 SCM s = ly_deep_copy (me->get_grob_property ("positions"));
795 me->set_grob_property ("positions", s);
797 if (ly_car (s) == SCM_BOOL_F)
800 // one wonders if such genericity is necessary --hwn.
801 SCM callbacks = me->get_grob_property ("position-callbacks");
802 for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
803 gh_call1 (ly_car (i), smob);
806 set_stem_lengths (me);
807 return SCM_UNSPECIFIED;
812 Compute a first approximation to the beam slope.
814 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
816 Beam::least_squares (SCM smob)
818 Grob *me = unsmob_grob (smob);
820 int count = visible_stem_count (me);
825 me->set_grob_property ("positions", ly_interval2scm (pos));
826 return SCM_UNSPECIFIED;
830 Array<Real> x_posns ;
831 Link_array<Grob> stems=
832 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
833 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
834 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
836 Real my_y = me->relative_coordinate (commony, Y_AXIS);
838 Grob *fvs = first_visible_stem (me);
839 Grob *lvs = last_visible_stem (me);
841 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
842 + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
843 Stem::get_stem_info (lvs).ideal_y_
844 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
846 Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
847 for (int i=0; i < stems.size (); i++)
851 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
854 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
863 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
864 Stem::chord_start_y (last_visible_stem (me)));
866 /* Simple beams (2 stems) on middle line should be allowed to be
869 However, if both stems reach middle line,
870 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
872 For that case, we apply artificial slope */
873 if (!ideal[LEFT] && chord.delta () && count == 2)
876 Direction d = (Direction) (sign (chord.delta ()) * UP);
877 pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
886 For broken beams this doesn't work well. In this case, the
887 slope esp. of the first part of a broken beam should predict
888 where the second part goes.
900 Array<Offset> ideals;
901 for (int i=0; i < stems.size (); i++)
904 if (Stem::invisible_b (s))
906 ideals.push (Offset (x_posns[i],
907 Stem::get_stem_info (s).ideal_y_
908 + s->relative_coordinate (commony, Y_AXIS)
912 minimise_least_squares (&dydx, &y, ideals);
915 me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
916 pos = Interval (y, (y+dy));
919 me->set_grob_property ("positions", ly_interval2scm (pos));
921 return SCM_UNSPECIFIED;
926 We can't combine with previous function, since check concave and
927 slope damping comes first.
929 TODO: we should use the concaveness to control the amount of damping
933 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
935 Beam::shift_region_to_valid (SCM grob)
937 Grob *me = unsmob_grob (grob);
941 Array<Real> x_posns ;
942 Link_array<Grob> stems=
943 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
944 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
945 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
947 Grob *fvs = first_visible_stem (me);
950 return SCM_UNSPECIFIED;
952 Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
953 for (int i=0; i < stems.size (); i++)
957 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
961 Grob *lvs = last_visible_stem (me);
963 return SCM_UNSPECIFIED;
965 Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
967 Interval pos = ly_scm2interval ( me->get_grob_property ("positions"));
968 Real dy = pos.delta();
974 Shift the positions so that we have a chance of finding good
975 quants (i.e. no short stem failures.)
977 Interval feasible_left_point;
978 feasible_left_point.set_full ();
979 for (int i=0; i < stems.size (); i++)
982 if (Stem::invisible_b (s))
985 Direction d = Stem::get_direction (s);
988 Stem::get_stem_info (s).shortest_y_
989 - dydx * x_posns [i];
992 left_y is now relative to the stem S. We want relative to
993 ourselves, so translate:
996 + s->relative_coordinate (commony, Y_AXIS)
997 - me->relative_coordinate (commony, Y_AXIS);
1003 feasible_left_point.intersect (flp);
1006 if (feasible_left_point.empty_b())
1008 warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
1010 else if (!feasible_left_point.elem_b(y))
1012 if (isinf (feasible_left_point[DOWN]))
1013 y = feasible_left_point[UP] - REGION_SIZE;
1014 else if (isinf (feasible_left_point[UP]))
1015 y = feasible_left_point[DOWN]+ REGION_SIZE;
1017 y = feasible_left_point.center ();
1019 pos = Interval (y, (y+dy));
1020 me->set_grob_property ("positions", ly_interval2scm (pos));
1021 return SCM_UNSPECIFIED;
1025 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
1027 Beam::check_concave (SCM smob)
1029 Grob *me = unsmob_grob (smob);
1031 Link_array<Grob> stems =
1032 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1034 for (int i = 0; i < stems.size ();)
1036 if (Stem::invisible_b (stems[i]))
1042 if (stems.size () < 3)
1043 return SCM_UNSPECIFIED;
1046 /* Concaveness #1: If distance of an inner notehead to line between
1047 two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
1048 beam is concave (Heinz Stolba).
1050 In the case of knees, the line connecting outer heads is often
1051 not related to the beam slope (it may even go in the other
1052 direction). Skip the check when the outer stems point in
1053 different directions. --hwn
1056 bool concaveness1 = false;
1057 SCM gap = me->get_grob_property ("concaveness-gap");
1058 if (gh_number_p (gap)
1059 && Stem::get_direction(stems.top ())
1060 == Stem::get_direction(stems[0]))
1062 Real r1 = gh_scm2double (gap);
1063 Real dy = Stem::chord_start_y (stems.top ())
1064 - Stem::chord_start_y (stems[0]);
1067 Real slope = dy / (stems.size () - 1);
1069 Real y0 = Stem::chord_start_y (stems[0]);
1070 for (int i = 1; i < stems.size () - 1; i++)
1072 Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
1075 concaveness1 = true;
1082 /* Concaveness #2: Sum distances of inner noteheads that fall
1083 outside the interval of the two outer noteheads.
1085 We only do this for beams where first and last stem have the same
1089 Note that "convex" stems compensate for "concave" stems.
1090 (is that intentional?) --hwn.
1093 Real concaveness2 = 0;
1094 SCM thresh = me->get_grob_property ("concaveness-threshold");
1095 Real r2 = infinity_f;
1096 if (!concaveness1 && gh_number_p (thresh)
1097 && Stem::get_direction(stems.top ())
1098 == Stem::get_direction(stems[0]))
1100 r2 = gh_scm2double (thresh);
1102 Direction dir = Stem::get_direction(stems.top ());
1104 Interval iv (Stem::chord_start_y (stems[0]),
1105 Stem::chord_start_y (stems.top ()));
1107 if (iv[MAX] < iv[MIN])
1110 for (int i = 1; i < stems.size () - 1; i++)
1112 Real f = Stem::chord_start_y (stems[i]);
1113 concave += ((f - iv[MAX] ) >? 0) +
1114 ((f - iv[MIN] ) <? 0);
1117 concaveness2 = concave / (stems.size () - 2);
1121 ugh: this is the a kludge to get
1122 input/regression/beam-concave.ly to behave as
1128 huh? we're dividing twice (which is not scalable) meaning that
1129 the longer the beam, the more unlikely it will be
1130 concave. Maybe you would even expect the other way around??
1135 concaveness2 /= (stems.size () - 2);
1138 /* TODO: some sort of damping iso -> plain horizontal */
1139 if (concaveness1 || concaveness2 > r2)
1141 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1142 Real r = pos.linear_combination (0);
1143 me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
1144 me->set_grob_property ("least-squares-dy", gh_double2scm (0));
1147 return SCM_UNSPECIFIED;
1150 /* This neat trick is by Werner Lemberg,
1151 damped = tanh (slope)
1152 corresponds with some tables in [Wanske] CHECKME */
1153 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1155 Beam::slope_damping (SCM smob)
1157 Grob *me = unsmob_grob (smob);
1159 if (visible_stem_count (me) <= 1)
1160 return SCM_UNSPECIFIED;
1162 SCM s = me->get_grob_property ("damping");
1163 int damping = gh_scm2int (s);
1167 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1168 Real dy = pos.delta ();
1170 Grob *fvs = first_visible_stem (me);
1171 Grob *lvs = last_visible_stem (me);
1173 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1176 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1177 - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1178 Real dydx = dy && dx ? dy/dx : 0;
1179 dydx = 0.6 * tanh (dydx) / damping;
1181 Real damped_dy = dydx * dx;
1182 pos[LEFT] += (dy - damped_dy) / 2;
1183 pos[RIGHT] -= (dy - damped_dy) / 2;
1185 me->set_grob_property ("positions", ly_interval2scm (pos));
1187 return SCM_UNSPECIFIED;
1191 Report slice containing the numbers that are both in (car BEAMING)
1195 where_are_the_whole_beams(SCM beaming)
1199 for( SCM s = gh_car (beaming); gh_pair_p (s) ; s = gh_cdr (s))
1201 if (scm_memq (gh_car (s), gh_cdr (beaming)) != SCM_BOOL_F)
1203 l.add_point (gh_scm2int (gh_car (s)));
1209 /* Return the Y position of the stem-end, given the Y-left, Y-right
1210 in POS for stem S. This Y position is relative to S. */
1212 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1214 Interval pos, bool french)
1216 Real beam_translation = get_beam_translation (me);
1219 Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1220 Real dy = pos.delta ();
1222 Real stem_y_beam0 = (dy && dx
1227 Direction my_dir = Directional_element_interface::get (s);
1228 SCM beaming = s->get_grob_property ("beaming");
1230 Real stem_y = stem_y_beam0;
1233 Slice bm = where_are_the_whole_beams (beaming);
1235 stem_y += beam_translation * bm[-my_dir];
1239 Slice bm = Stem::beam_multiplicity(s);
1241 stem_y +=bm[my_dir] * beam_translation;
1244 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1245 - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1251 Hmm. At this time, beam position and slope are determined. Maybe,
1252 stem directions and length should set to relative to the chord's
1253 position of the beam. */
1255 Beam::set_stem_lengths (Grob *me)
1257 Link_array<Grob> stems=
1258 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1264 for (int a = 2; a--;)
1265 common[a] = common_refpoint_of_array (stems, me, Axis(a));
1267 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1268 Real staff_space = Staff_symbol_referencer::staff_space (me);
1272 if (gh_number_p (me->get_grob_property ("gap"))
1273 &&gh_scm2double (me->get_grob_property ("gap")))
1276 thick = get_thickness(me);
1279 // ugh -> use commonx
1280 Grob * fvs = first_visible_stem (me);
1281 Grob *lvs = last_visible_stem (me);
1283 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1284 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1286 for (int i=0; i < stems.size (); i++)
1289 if (Stem::invisible_b (s))
1292 bool french = to_boolean (s->get_grob_property ("french-beaming"));
1293 Real stem_y = calc_stem_y (me, s, common,
1295 pos, french && s != lvs && s!= fvs);
1298 Make the stems go up to the end of the beam. This doesn't matter
1299 for normal beams, but for tremolo beams it looks silly otherwise.
1302 stem_y += thick * 0.5 * Directional_element_interface::get(s);
1304 Stem::set_stemend (s, 2* stem_y / staff_space);
1309 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1311 Link_array<Grob> stems=
1312 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1315 for (int i=0; i < stems.size (); i++)
1318 Don't overwrite user settings.
1323 /* Don't set beaming for outside of outer stems */
1324 if ((d == LEFT && i == 0)
1325 ||(d == RIGHT && i == stems.size () -1))
1328 Grob *st = stems[i];
1329 SCM beaming_prop = st->get_grob_property ("beaming");
1330 if (beaming_prop == SCM_EOL ||
1331 index_get_cell (beaming_prop, d) == SCM_EOL)
1333 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1335 && i < stems.size() -1
1336 && Stem::invisible_b (st))
1337 b = b <? beaming->infos_.elem(i).beams_i_drul_[-d];
1339 Stem::set_beaming (st, b, d);
1342 while (flip (&d) != LEFT);
1347 Beam::forced_stem_count (Grob *me)
1349 Link_array<Grob>stems =
1350 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1352 for (int i=0; i < stems.size (); i++)
1356 if (Stem::invisible_b (s))
1359 /* I can imagine counting those boundaries as a half forced stem,
1360 but let's count them full for now. */
1361 if (abs (Stem::chord_start_y (s)) > 0.1
1362 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1372 Beam::visible_stem_count (Grob *me)
1374 Link_array<Grob>stems =
1375 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1377 for (int i = stems.size (); i--;)
1379 if (!Stem::invisible_b (stems[i]))
1386 Beam::first_visible_stem (Grob *me)
1388 Link_array<Grob>stems =
1389 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1391 for (int i = 0; i < stems.size (); i++)
1393 if (!Stem::invisible_b (stems[i]))
1400 Beam::last_visible_stem (Grob *me)
1402 Link_array<Grob>stems =
1403 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1404 for (int i = stems.size (); i--;)
1406 if (!Stem::invisible_b (stems[i]))
1416 handle rest under beam (do_post: beams are calculated now)
1417 what about combination of collisions and rest under beam.
1421 rest -> stem -> beam -> interpolate_y_position ()
1423 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1425 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1427 Grob *rest = unsmob_grob (element_smob);
1428 Axis a = (Axis) gh_scm2int (axis);
1430 assert (a == Y_AXIS);
1432 Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1435 return gh_double2scm (0.0);
1436 Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1438 || !Beam::has_interface (beam)
1439 || !Beam::visible_stem_count (beam))
1440 return gh_double2scm (0.0);
1442 Interval pos (0, 0);
1443 SCM s = beam->get_grob_property ("positions");
1444 if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1445 pos = ly_scm2interval (s);
1447 Real dy = pos.delta ();
1448 // ugh -> use commonx
1449 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1450 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1451 Real dydx = dy && dx ? dy/dx : 0;
1453 Direction d = Stem::get_direction (stem);
1454 Real stem_y = (pos[LEFT]
1455 + (stem->relative_coordinate (0, X_AXIS) - x0) * dydx)
1458 Real beam_translation = get_beam_translation (beam);
1459 Real beam_thickness = gh_scm2double (beam->get_grob_property ("thickness"));
1460 int beam_count = get_direction_beam_count (beam, d);
1461 Real height_of_my_beams = beam_thickness
1462 + (beam_count - 1) * beam_translation;
1463 Real beam_y = stem_y - height_of_my_beams + beam_thickness / 2.0;
1465 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1467 /* Better calculate relative-distance directly, rather than using
1469 Grob *common_x = rest->common_refpoint (beam, Y_AXIS);
1470 Real rest_dim = rest->extent (common_x, Y_AXIS)[d] / staff_space * d;
1472 Real minimum_distance = gh_scm2double
1473 (rest->get_grob_property ("minimum-beam-collision-distance"));
1475 Real distance = beam_y - rest_dim;
1478 shift = minimum_distance - distance;
1479 else if (minimum_distance > distance)
1480 shift = minimum_distance - distance;
1482 int stafflines = Staff_symbol_referencer::line_count (rest);
1484 /* Always move discretely by half spaces */
1485 Real discrete_shift = ceil (shift * 2.0) / 2.0;
1487 /* Inside staff, move by whole spaces*/
1488 if ((rest->extent (common_x, Y_AXIS)[d] + discrete_shift) * d
1490 ||(rest->extent (common_x, Y_AXIS)[-d] + discrete_shift) * -d
1492 discrete_shift = ceil (discrete_shift);
1494 return gh_double2scm (-d * discrete_shift);
1498 Beam::knee_b (Grob* me)
1500 SCM k = me->get_grob_property ("knee");
1501 if (gh_boolean_p (k))
1502 return gh_scm2bool (k);
1506 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
1508 Direction dir = Directional_element_interface::get
1509 (unsmob_grob (ly_car (s)));
1518 me->set_grob_property ("knee", gh_bool2scm (knee));
1524 Beam::get_direction_beam_count (Grob *me, Direction d )
1526 Link_array<Grob>stems =
1527 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1530 for (int i = stems.size (); i--;)
1533 Should we take invisible stems into account?
1535 if (Stem::get_direction (stems[i]) == d)
1536 bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1543 ADD_INTERFACE (Beam, "beam-interface",
1546 "#'thickness= weight of beams, in staffspace "
1549 "We take the least squares line through the ideal-length stems, and "
1550 "then damp that using "
1552 " damped = tanh (slope) \n"
1554 "this gives an unquantized left and right position for the beam end. "
1555 "Then we take all combinations of quantings near these left and right "
1556 "positions, and give them a score (according to how close they are to "
1557 "the ideal slope, how close the result is to the ideal stems, etc.). We "
1558 "take the best scoring combination. "
1560 "knee 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");