2 beam.cc -- implement Beam
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
13 - Determine auto knees based on positions if it's set by the user.
15 - the code is littered with * and / staff_space calls for
16 #'positions. Consider moving to real-world coordinates?
18 Problematic issue is user tweaks (user tweaks are in staff-coordinates.)
22 - Stems run to the Y-center of the beam.
24 - beam_translation is the offset between Y centers of the beam.
29 #include <math.h> // tanh.
32 #include "directional-element-interface.hh"
36 #include "least-squares.hh"
38 #include "paper-def.hh"
40 #include "group-interface.hh"
41 #include "staff-symbol-referencer.hh"
46 bool debug_beam_quanting_flag;
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 return robust_scm2double (me->get_grob_property ("thickness"), 0)
73 * Staff_symbol_referencer::staff_space (me);
76 /* Return the translation between 2 adjoining beams. */
78 Beam::get_beam_translation (Grob *me)
80 SCM func = me->get_grob_property ("space-function");
82 if (gh_procedure_p (func))
84 SCM s = gh_call2 (func, me->self_scm (), scm_int2num (get_beam_count (me)));
85 return gh_scm2double (s);
93 /* Maximum beam_count. */
95 Beam::get_beam_count (Grob *me)
98 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
100 Grob *stem = unsmob_grob (ly_car (s));
101 m = m >? (Stem::beam_multiplicity (stem).length () + 1);
108 Space return space between beams.
110 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
112 Beam::space_function (SCM smob, SCM beam_count)
114 Grob *me = unsmob_grob (smob);
116 Real staff_space = Staff_symbol_referencer::staff_space (me);
117 Real line = Staff_symbol_referencer::line_thickness (me);
118 Real thickness = get_thickness (me);
120 Real beam_translation = gh_scm2int (beam_count) < 4
121 ? (2*staff_space + line - thickness) / 2.0
122 : (3*staff_space + line - thickness) / 3.0;
124 return gh_double2scm (beam_translation);
128 /* After pre-processing all directions should be set.
129 Several post-processing routines (stem, slur, script) need stem/beam
131 Currenly, this means that beam has set all stem's directions.
132 [Alternatively, stems could set its own directions, according to
133 their beam, during 'final-pre-processing'.] */
134 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
136 Beam::before_line_breaking (SCM smob)
138 Grob *me = unsmob_grob (smob);
140 /* Beams with less than 2 two stems don't make much sense, but could happen
145 For a beam that only has one stem, we try to do some disappearance magic:
146 we revert the flag, and move on to The Eternal Engraving Fields. */
148 int count = visible_stem_count (me);
151 me->warning (_ ("beam has less than two visible stems"));
153 SCM stems = me->get_grob_property ("stems");
154 if (scm_ilength (stems) == 1)
156 me->warning (_ ("Beam has less than two stems. Removing beam."));
158 unsmob_grob (gh_car (stems))->set_grob_property ("beam", SCM_EOL);
161 return SCM_UNSPECIFIED;
163 else if (scm_ilength (stems) == 0)
166 return SCM_UNSPECIFIED;
171 Direction d = get_default_dir (me);
173 consider_auto_knees (me);
174 set_stem_directions (me, d);
178 set_stem_shorten (me);
186 We want a maximal number of shared beams, but if there is choice, we
187 take the one that is closest to the end of the stem. This is for situations like
200 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
204 Slice lslice = int_list_to_slice (gh_cdr (left_beaming));
208 for (int i = lslice[-left_dir];
209 (i - lslice[left_dir])* left_dir <= 0 ; i+= left_dir)
212 for ( SCM s = gh_car (right_beaming); gh_pair_p (s); s = gh_cdr (s))
214 int k = - right_dir * gh_scm2int (gh_car (s)) + i;
215 if (scm_memq (scm_int2num (k), left_beaming) != SCM_BOOL_F)
219 if (count >= best_count)
230 Beam::connect_beams (Grob *me)
232 Link_array<Grob> stems=
233 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
236 last_int.set_empty();
237 SCM last_beaming = SCM_EOL;
238 Direction last_dir = CENTER;
239 for (int i = 0; i< stems.size(); i++)
241 Grob *this_stem = stems[i];
242 SCM this_beaming = this_stem->get_grob_property ("beaming");
244 Direction this_dir = get_grob_direction (this_stem);
245 if (gh_pair_p (last_beaming) && gh_pair_p (this_beaming))
247 int start_point = position_with_maximal_common_beams
248 (last_beaming, this_beaming,
255 if (d == RIGHT && i == stems.size()-1)
258 new_slice.set_empty();
259 SCM s = index_get_cell (this_beaming, d);
260 for (; gh_pair_p (s); s = gh_cdr (s))
263 start_point - this_dir * gh_scm2int (gh_car (s));
265 new_slice.add_point (new_beam_pos);
266 gh_set_car_x (s, scm_int2num (new_beam_pos));
271 while (flip (&d) != LEFT);
273 if (!new_slice.is_empty ())
274 last_int = new_slice;
278 gh_set_car_x ( this_beaming, SCM_EOL);
279 SCM s = gh_cdr (this_beaming);
280 for (; gh_pair_p (s); s = gh_cdr (s))
282 int np = - this_dir * gh_scm2int (gh_car(s));
283 gh_set_car_x (s, scm_int2num (np));
284 last_int.add_point (np);
288 if (i == stems.size () -1)
290 gh_set_cdr_x (this_beaming, SCM_EOL);
293 if (scm_ilength (gh_cdr (this_beaming)) > 0)
295 last_beaming = this_beaming;
303 TODO: should not make beams per stem, but per Y-level.
305 MAKE_SCHEME_CALLBACK (Beam, print, 1);
307 Beam::print (SCM grob)
309 Spanner *me = unsmob_spanner (grob);
312 Link_array<Grob> stems=
313 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
314 Grob* xcommon = common_refpoint_of_array (stems, me, X_AXIS);
316 xcommon = me->get_bound (LEFT)->common_refpoint (xcommon, X_AXIS);
317 xcommon = me->get_bound (RIGHT)->common_refpoint (xcommon, X_AXIS);
320 if (visible_stem_count (me))
322 // ugh -> use commonx
323 x0 = first_visible_stem (me)->relative_coordinate (xcommon, X_AXIS);
324 dx = last_visible_stem (me)->relative_coordinate (xcommon, X_AXIS) - x0;
328 x0 = stems[0]->relative_coordinate (xcommon, X_AXIS);
329 dx = stems.top ()->relative_coordinate (xcommon, X_AXIS) - x0;
332 SCM posns = me->get_grob_property ("positions");
333 Drul_array<Real> pos;
334 if (!is_number_pair (posns))
336 programming_error ("No beam posns");
337 pos = Interval (0,0);
340 pos= ly_scm2realdrul (posns);
342 scale_drul ( &pos, Staff_symbol_referencer::staff_space (me));
344 Real dy = pos[RIGHT] - pos[LEFT];
345 Real dydx = (dy && dx) ? dy/dx : 0;
347 Real thick = get_thickness (me);
348 Real bdy = get_beam_translation (me);
350 SCM last_beaming = SCM_EOL;
351 Real last_xposn = -1;
352 Real last_stem_width = -1 ;
354 Real gap_length =robust_scm2double ( me->get_grob_property ("gap"), 0.0);
357 Real lt = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
359 for (int i = 0; i<= stems.size(); i++)
361 Grob * st = (i < stems.size()) ? stems[i] : 0;
363 SCM this_beaming = st ? st->get_grob_property ("beaming") : SCM_EOL;
364 Real xposn = st ? st->relative_coordinate (xcommon, X_AXIS) : 0.0;
365 Real stem_width = st ? robust_scm2double (st->get_grob_property ("thickness"), 1.0) *lt : 0 ;
366 Direction stem_dir = st ? to_dir (st->get_grob_property ("direction")) : CENTER;
368 We do the space left of ST, with lfliebertjes pointing to the
369 right from the left stem, and rfliebertjes pointing left from
372 SCM left = (i>0) ? gh_cdr (last_beaming) : SCM_EOL;
373 SCM right = st ? gh_car (this_beaming) : SCM_EOL;
375 Array<int> full_beams;
376 Array<int> lfliebertjes;
377 Array<int> rfliebertjes;
380 gh_pair_p (s); s =gh_cdr (s))
382 int b = gh_scm2int (gh_car (s));
383 if (scm_memq (gh_car(s), right) != SCM_BOOL_F)
389 lfliebertjes.push (b);
393 gh_pair_p (s); s =gh_cdr (s))
395 int b = gh_scm2int (gh_car (s));
396 if (scm_memq (gh_car(s), left) == SCM_BOOL_F)
398 rfliebertjes.push (b);
403 how much to stick out for beams across linebreaks
405 Real break_overshoot = 3.0;
406 Real w = (i > 0 && st) ? (xposn - last_xposn) : break_overshoot;
408 Real stem_offset =0.0;
411 w += last_stem_width / 2;
412 stem_offset = -last_stem_width / 2;
419 Real blot = me->get_paper ()->get_realvar (ly_symbol2scm ("blotdiameter"));
420 Stencil whole = Lookup::beam (dydx, w, thick, blot);
424 if (gh_number_p (me->get_grob_property ("gap-count")))
426 gap_count = gh_scm2int (me->get_grob_property ("gap-count"));
427 gapped = Lookup::beam (dydx, w - 2 * gap_length, thick, blot);
429 full_beams.sort (default_compare);
431 full_beams.reverse ();
435 for (int j = full_beams.size (); j--;)
442 b.translate_axis (gap_length, X_AXIS);
444 b.translate_axis (last_xposn - x0 + stem_offset, X_AXIS);
445 b.translate_axis (dydx * (last_xposn - x0) + bdy * full_beams[j], Y_AXIS);
447 the_beam.add_stencil (b);
450 if (lfliebertjes.size() || rfliebertjes.size())
456 int t = Stem::duration_log (st);
458 SCM proc = me->get_grob_property ("flag-width-function");
459 SCM result = gh_call1 (proc, scm_int2num (t));
460 nw_f = gh_scm2double (result);
463 nw_f = break_overshoot / 2;
465 /* Half beam should be one note-width,
466 but let's make sure two half-beams never touch */
470 rw = nw_f <? ((xposn - last_xposn) / 2);
473 TODO: 0.5 is a guess.
475 rw = xposn - me->get_bound (LEFT)->extent (xcommon, X_AXIS)[RIGHT]
479 lw = nw_f <? ((xposn - last_xposn) / 2);
481 lw = me->get_bound(RIGHT)->relative_coordinate (xcommon, X_AXIS)
484 Stencil rhalf = Lookup::beam (dydx, rw, thick, blot);
485 Stencil lhalf = Lookup::beam (dydx, lw, thick, blot);
486 for (int j = lfliebertjes.size(); j--;)
489 b.translate_axis (last_xposn - x0, X_AXIS);
490 b.translate_axis (dydx * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
491 the_beam.add_stencil (b);
493 for (int j = rfliebertjes.size(); j--;)
496 b.translate_axis (xposn - x0 - rw , X_AXIS);
497 b.translate_axis (dydx * (xposn-x0 -rw) + bdy * rfliebertjes[j], Y_AXIS);
498 the_beam.add_stencil (b);
504 last_stem_width = stem_width;
505 last_beaming = this_beaming;
508 the_beam.translate_axis (x0 - me->relative_coordinate (xcommon, X_AXIS), X_AXIS);
509 the_beam.translate_axis (pos[LEFT], Y_AXIS);
512 SCM quant_score = me->get_grob_property ("quant-score");
513 if (debug_beam_quanting_flag
514 && gh_string_p (quant_score))
518 This code prints the demerits for each beam. Perhaps this
519 should be switchable for those who want to twiddle with the
523 SCM properties = Font_interface::font_alist_chain (me);
525 Stencil tm = *unsmob_stencil (Text_item::interpret_markup
526 (me->get_paper ()->self_scm (), properties, quant_score));
527 the_beam.add_at_edge (Y_AXIS, UP, tm, 5.0, 0);
533 return the_beam.smobbed_copy();
540 Beam::get_default_dir (Grob *me)
542 Drul_array<int> total;
543 total[UP] = total[DOWN] = 0;
544 Drul_array<int> count;
545 count[UP] = count[DOWN] = 0;
548 Link_array<Grob> stems=
549 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
551 for (int i=0; i <stems.size (); i++)
554 Direction sd = get_grob_direction (s);
556 int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
557 int current = sd ? (1 + d * sd)/2 : center_distance;
564 } while (flip (&d) != DOWN);
566 SCM func = me->get_grob_property ("dir-function");
567 SCM s = gh_call2 (func,
568 gh_cons (scm_int2num (count[UP]),
569 scm_int2num (count[DOWN])),
570 gh_cons (scm_int2num (total[UP]),
571 scm_int2num (total[DOWN])));
573 if (gh_number_p (s) && gh_scm2int (s))
576 /* If dir is not determined: get default */
577 return to_dir (me->get_grob_property ("neutral-direction"));
581 /* Set all stems with non-forced direction to beam direction.
582 Urg: non-forced should become `without/with unforced' direction,
583 once stem gets cleaned-up. */
585 Beam::set_stem_directions (Grob *me, Direction d)
587 Link_array<Grob> stems
588 =Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
590 for (int i=0; i <stems.size (); i++)
594 SCM forcedir = s->get_grob_property ("direction");
595 if (!to_dir (forcedir))
596 set_grob_direction (s, d);
601 A union of intervals in the real line.
603 Abysmal performance (quadratic) for large N, hopefully we don't have
604 that large N. In any case, this should probably be rewritten to use
609 Array<Interval> allowed_regions_;
618 allowed_regions_.clear();
621 allowed_regions_.push (s);
624 void remove_interval (Interval rm)
626 for (int i = 0; i < allowed_regions_.size(); )
630 s.intersect (allowed_regions_[i]);
634 Interval before = allowed_regions_[i];
635 Interval after = allowed_regions_[i];
637 before[RIGHT] = s[LEFT];
638 after[LEFT] = s[RIGHT];
640 if (!before.is_empty () && before.length () > 0.0)
642 allowed_regions_.insert (before, i);
645 allowed_regions_.del (i);
646 if (!after.is_empty () && after.length () > 0.0)
648 allowed_regions_.insert (after, i);
660 Only try horizontal beams for knees. No reliable detection of
661 anything else is possible here, since we don't know funky-beaming
662 settings, or X-distances (slopes!) People that want sloped
663 knee-beams, should set the directions manually.
666 Beam::consider_auto_knees (Grob* me)
668 SCM scm = me->get_grob_property ("auto-knee-gap");
669 if (!gh_number_p (scm))
672 Real threshold = gh_scm2double (scm);
678 Link_array<Grob> stems=
679 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
681 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
682 Real staff_space = Staff_symbol_referencer::staff_space (me);
684 Array<Interval> hps_array;
685 for (int i=0; i < stems.size (); i++)
687 Grob* stem = stems[i];
688 if (Stem::is_invisible (stem))
691 Interval hps = Stem::head_positions (stem);
696 hps *= staff_space * 0.5 ;
699 We could subtract beam Y position, but this routine only
700 sets stem directions, a constant shift does not have an
704 hps += stem->relative_coordinate (common, Y_AXIS);
706 if (to_dir (stem->get_grob_property ("direction")))
708 Direction stemdir = to_dir (stem->get_grob_property ("direction"));
709 hps[-stemdir] = - stemdir * infinity_f;
712 hps_array.push (hps);
714 gaps.remove_interval (hps);
718 Real max_gap_len =0.0;
720 for (int i = gaps.allowed_regions_.size() -1; i >= 0 ; i--)
722 Interval gap = gaps.allowed_regions_[i];
725 the outer gaps are not knees.
727 if (isinf (gap[LEFT]) || isinf(gap[RIGHT]))
730 if (gap.length () >= max_gap_len)
732 max_gap_len = gap.length();
737 if (max_gap_len > threshold)
740 for (int i = 0; i < stems.size(); i++)
742 Grob* stem = stems[i];
743 if (Stem::is_invisible (stem))
746 Interval hps = hps_array[j++];
749 Direction d = (hps.center () < max_gap.center()) ?
752 stem->set_grob_property ("direction", scm_int2num (d));
754 hps.intersect (max_gap);
755 assert (hps.is_empty () || hps.length () < 1e-6 );
762 /* Set stem's shorten property if unset.
765 take some y-position (chord/beam/nearest?) into account
766 scmify forced-fraction
768 This is done in beam because the shorten has to be uniform over the
773 Beam::set_stem_shorten (Grob *me)
776 shortening looks silly for x staff beams
781 Real forced_fraction = 1.0 * forced_stem_count (me)
782 / visible_stem_count (me);
784 int beam_count = get_beam_count (me);
786 SCM shorten_list = me->get_grob_property ("beamed-stem-shorten");
787 if (shorten_list == SCM_EOL)
790 Real staff_space = Staff_symbol_referencer::staff_space (me);
793 robust_list_ref (beam_count -1, shorten_list);
794 Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
796 /* your similar cute comment here */
797 shorten_f *= forced_fraction;
800 me->set_grob_property ("shorten", gh_double2scm (shorten_f));
803 /* Call list of y-dy-callbacks, that handle setting of
807 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
809 Beam::after_line_breaking (SCM smob)
811 Grob *me = unsmob_grob (smob);
814 return SCM_UNSPECIFIED;
818 Beam::position_beam (Grob *me)
820 if (to_boolean (me->get_grob_property ("positioning-done")))
823 me->set_grob_property ("positioning-done", SCM_BOOL_T);
825 /* Copy to mutable list. */
826 SCM s = ly_deep_copy (me->get_grob_property ("positions"));
827 me->set_grob_property ("positions", s);
829 if (ly_car (s) == SCM_BOOL_F)
831 // one wonders if such genericity is necessary --hwn.
832 SCM callbacks = me->get_grob_property ("position-callbacks");
833 for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
834 gh_call1 (ly_car (i), me->self_scm ());
837 set_stem_lengths (me);
842 Compute a first approximation to the beam slope.
844 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
846 Beam::least_squares (SCM smob)
848 Grob *me = unsmob_grob (smob);
850 int count = visible_stem_count (me);
855 me->set_grob_property ("positions", ly_interval2scm (pos));
856 return SCM_UNSPECIFIED;
860 Array<Real> x_posns ;
861 Link_array<Grob> stems=
862 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
863 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
864 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
866 Real my_y = me->relative_coordinate (commony, Y_AXIS);
868 Grob *fvs = first_visible_stem (me);
869 Grob *lvs = last_visible_stem (me);
871 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
872 + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
873 Stem::get_stem_info (lvs).ideal_y_
874 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
876 Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
877 for (int i=0; i < stems.size (); i++)
881 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
884 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
893 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
894 Stem::chord_start_y (last_visible_stem (me)));
896 /* Simple beams (2 stems) on middle line should be allowed to be
899 However, if both stems reach middle line,
900 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
902 For that case, we apply artificial slope */
903 if (!ideal[LEFT] && chord.delta () && count == 2)
906 Direction d = (Direction) (sign (chord.delta ()) * UP);
907 pos[d] = get_thickness (me) / 2;
916 For broken beams this doesn't work well. In this case, the
917 slope esp. of the first part of a broken beam should predict
918 where the second part goes.
920 me->set_grob_property ("least-squares-dy",
921 gh_double2scm (pos[RIGHT] - pos[LEFT]));
925 Array<Offset> ideals;
926 for (int i=0; i < stems.size (); i++)
929 if (Stem::is_invisible (s))
931 ideals.push (Offset (x_posns[i],
932 Stem::get_stem_info (s).ideal_y_
933 + s->relative_coordinate (commony, Y_AXIS)
937 minimise_least_squares (&dydx, &y, ideals);
940 me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
941 pos = Interval (y, (y+dy));
945 "position" is relative to the staff.
947 scale_drul (&pos, 1/ Staff_symbol_referencer::staff_space (me));
949 me->set_grob_property ("positions", ly_interval2scm (pos));
951 return SCM_UNSPECIFIED;
956 We can't combine with previous function, since check concave and
957 slope damping comes first.
959 TODO: we should use the concaveness to control the amount of damping
963 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
965 Beam::shift_region_to_valid (SCM grob)
967 Grob *me = unsmob_grob (grob);
971 Array<Real> x_posns ;
972 Link_array<Grob> stems=
973 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
974 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
975 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
977 Grob *fvs = first_visible_stem (me);
980 return SCM_UNSPECIFIED;
982 Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
983 for (int i=0; i < stems.size (); i++)
987 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
991 Grob *lvs = last_visible_stem (me);
993 return SCM_UNSPECIFIED;
995 Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
997 Drul_array<Real> pos = ly_scm2interval ( me->get_grob_property ("positions"));
999 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1001 Real dy = pos[RIGHT] - pos[LEFT];
1007 Shift the positions so that we have a chance of finding good
1008 quants (i.e. no short stem failures.)
1010 Interval feasible_left_point;
1011 feasible_left_point.set_full ();
1012 for (int i=0; i < stems.size (); i++)
1015 if (Stem::is_invisible (s))
1018 Direction d = Stem::get_direction (s);
1021 Stem::get_stem_info (s).shortest_y_
1022 - dydx * x_posns [i];
1025 left_y is now relative to the stem S. We want relative to
1026 ourselves, so translate:
1029 + s->relative_coordinate (commony, Y_AXIS)
1030 - me->relative_coordinate (commony, Y_AXIS);
1036 feasible_left_point.intersect (flp);
1039 if (feasible_left_point.is_empty ())
1041 warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
1043 else if (!feasible_left_point.contains (y))
1045 if (isinf (feasible_left_point[DOWN]))
1046 y = feasible_left_point[UP] - REGION_SIZE;
1047 else if (isinf (feasible_left_point[UP]))
1048 y = feasible_left_point[DOWN]+ REGION_SIZE;
1050 y = feasible_left_point.center ();
1053 pos = Drul_array<Real> (y, (y+dy));
1054 scale_drul (&pos, 1/ Staff_symbol_referencer::staff_space (me));
1056 me->set_grob_property ("positions", ly_interval2scm (pos));
1057 return SCM_UNSPECIFIED;
1061 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
1063 Beam::check_concave (SCM smob)
1065 Grob *me = unsmob_grob (smob);
1067 Link_array<Grob> stems =
1068 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1070 for (int i = 0; i < stems.size ();)
1072 if (Stem::is_invisible (stems[i]))
1078 if (stems.size () < 3)
1079 return SCM_UNSPECIFIED;
1082 /* Concaveness #1: If distance of an inner notehead to line between
1083 two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
1084 beam is concave (Heinz Stolba).
1086 In the case of knees, the line connecting outer heads is often
1087 not related to the beam slope (it may even go in the other
1088 direction). Skip the check when the outer stems point in
1089 different directions. --hwn
1092 bool concaveness1 = false;
1093 SCM gap = me->get_grob_property ("concaveness-gap");
1094 if (gh_number_p (gap)
1095 && Stem::get_direction(stems.top ())
1096 == Stem::get_direction(stems[0]))
1098 Real r1 = gh_scm2double (gap);
1099 Real dy = Stem::chord_start_y (stems.top ())
1100 - Stem::chord_start_y (stems[0]);
1103 Real slope = dy / (stems.size () - 1);
1105 Real y0 = Stem::chord_start_y (stems[0]);
1106 for (int i = 1; i < stems.size () - 1; i++)
1108 Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
1111 concaveness1 = true;
1118 /* Concaveness #2: Sum distances of inner noteheads that fall
1119 outside the interval of the two outer noteheads.
1121 We only do this for beams where first and last stem have the same
1125 Note that "convex" stems compensate for "concave" stems.
1126 (is that intentional?) --hwn.
1129 Real concaveness2 = 0;
1130 SCM thresh = me->get_grob_property ("concaveness-threshold");
1131 Real r2 = infinity_f;
1132 if (!concaveness1 && gh_number_p (thresh)
1133 && Stem::get_direction(stems.top ())
1134 == Stem::get_direction(stems[0]))
1136 r2 = gh_scm2double (thresh);
1138 Direction dir = Stem::get_direction(stems.top ());
1140 Interval iv (Stem::chord_start_y (stems[0]),
1141 Stem::chord_start_y (stems.top ()));
1143 if (iv[MAX] < iv[MIN])
1146 for (int i = 1; i < stems.size () - 1; i++)
1148 Real f = Stem::chord_start_y (stems[i]);
1149 concave += ((f - iv[MAX] ) >? 0) +
1150 ((f - iv[MIN] ) <? 0);
1153 concaveness2 = concave / (stems.size () - 2);
1157 ugh: this is the a kludge to get
1158 input/regression/beam-concave.ly to behave as
1164 huh? we're dividing twice (which is not scalable) meaning that
1165 the longer the beam, the more unlikely it will be
1166 concave. Maybe you would even expect the other way around??
1171 concaveness2 /= (stems.size () - 2);
1174 /* TODO: some sort of damping iso -> plain horizontal */
1175 if (concaveness1 || concaveness2 > r2)
1177 Drul_array<Real> pos = ly_scm2interval (me->get_grob_property ("positions"));
1178 Real r = linear_combination (pos, 0);
1180 r /= Staff_symbol_referencer::staff_space (me);
1181 me->set_grob_property ("positions", ly_interval2scm (Drul_array<Real> (r, r)));
1182 me->set_grob_property ("least-squares-dy", gh_double2scm (0));
1185 return SCM_UNSPECIFIED;
1188 /* This neat trick is by Werner Lemberg,
1189 damped = tanh (slope)
1190 corresponds with some tables in [Wanske] CHECKME */
1191 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1193 Beam::slope_damping (SCM smob)
1195 Grob *me = unsmob_grob (smob);
1197 if (visible_stem_count (me) <= 1)
1198 return SCM_UNSPECIFIED;
1200 SCM s = me->get_grob_property ("damping");
1201 int damping = gh_scm2int (s);
1205 Drul_array<Real> pos = ly_scm2interval (me->get_grob_property ("positions"));
1206 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1208 Real dy = pos[RIGHT] - pos[LEFT];
1210 Grob *fvs = first_visible_stem (me);
1211 Grob *lvs = last_visible_stem (me);
1213 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1216 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1217 - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1218 Real dydx = dy && dx ? dy/dx : 0;
1219 dydx = 0.6 * tanh (dydx) / damping;
1221 Real damped_dy = dydx * dx;
1222 pos[LEFT] += (dy - damped_dy) / 2;
1223 pos[RIGHT] -= (dy - damped_dy) / 2;
1225 scale_drul (&pos, 1/Staff_symbol_referencer::staff_space (me));
1227 me->set_grob_property ("positions", ly_interval2scm (pos));
1229 return SCM_UNSPECIFIED;
1233 Report slice containing the numbers that are both in (car BEAMING)
1237 where_are_the_whole_beams(SCM beaming)
1241 for( SCM s = gh_car (beaming); gh_pair_p (s) ; s = gh_cdr (s))
1243 if (scm_memq (gh_car (s), gh_cdr (beaming)) != SCM_BOOL_F)
1245 l.add_point (gh_scm2int (gh_car (s)));
1251 /* Return the Y position of the stem-end, given the Y-left, Y-right
1252 in POS for stem S. This Y position is relative to S. */
1254 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1256 Drul_array<Real> pos, bool french)
1258 Real beam_translation = get_beam_translation (me);
1261 Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1262 Real dy = pos[RIGHT] - pos[LEFT];
1264 Real stem_y_beam0 = (dy && dx
1269 Direction my_dir = get_grob_direction (s);
1270 SCM beaming = s->get_grob_property ("beaming");
1272 Real stem_y = stem_y_beam0;
1275 Slice bm = where_are_the_whole_beams (beaming);
1276 if (!bm.is_empty ())
1277 stem_y += beam_translation * bm[-my_dir];
1281 Slice bm = Stem::beam_multiplicity(s);
1282 if (!bm.is_empty ())
1283 stem_y +=bm[my_dir] * beam_translation;
1286 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1287 - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1293 Hmm. At this time, beam position and slope are determined. Maybe,
1294 stem directions and length should set to relative to the chord's
1295 position of the beam. */
1297 Beam::set_stem_lengths (Grob *me)
1299 Link_array<Grob> stems=
1300 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1306 for (int a = 2; a--;)
1307 common[a] = common_refpoint_of_array (stems, me, Axis(a));
1309 Drul_array<Real> pos = ly_scm2realdrul (me->get_grob_property ("positions"));
1310 Real staff_space = Staff_symbol_referencer::staff_space (me);
1311 scale_drul (&pos, staff_space);
1315 if (gh_number_p (me->get_grob_property ("gap-count"))
1316 &&gh_scm2int (me->get_grob_property ("gap-count")))
1319 thick = get_thickness(me);
1322 // ugh -> use commonx
1323 Grob * fvs = first_visible_stem (me);
1324 Grob *lvs = last_visible_stem (me);
1326 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1327 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1329 for (int i=0; i < stems.size (); i++)
1332 if (Stem::is_invisible (s))
1335 bool french = to_boolean (s->get_grob_property ("french-beaming"));
1336 Real stem_y = calc_stem_y (me, s, common,
1338 pos, french && s != lvs && s!= fvs);
1341 Make the stems go up to the end of the beam. This doesn't matter
1342 for normal beams, but for tremolo beams it looks silly otherwise.
1345 stem_y += thick * 0.5 * get_grob_direction (s);
1347 Stem::set_stemend (s, 2* stem_y / staff_space);
1352 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1354 Link_array<Grob> stems=
1355 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1358 for (int i=0; i < stems.size (); i++)
1361 Don't overwrite user settings.
1366 /* Don't set beaming for outside of outer stems */
1367 if ((d == LEFT && i == 0)
1368 ||(d == RIGHT && i == stems.size () -1))
1371 Grob *st = stems[i];
1372 SCM beaming_prop = st->get_grob_property ("beaming");
1373 if (beaming_prop == SCM_EOL ||
1374 index_get_cell (beaming_prop, d) == SCM_EOL)
1376 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1378 && i < stems.size() -1
1379 && Stem::is_invisible (st))
1380 b = b <? beaming->infos_.elem(i).beams_i_drul_[-d];
1382 Stem::set_beaming (st, b, d);
1385 while (flip (&d) != LEFT);
1390 Beam::forced_stem_count (Grob *me)
1392 Link_array<Grob>stems =
1393 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1395 for (int i=0; i < stems.size (); i++)
1399 if (Stem::is_invisible (s))
1402 /* I can imagine counting those boundaries as a half forced stem,
1403 but let's count them full for now. */
1404 if (abs (Stem::chord_start_y (s)) > 0.1
1405 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1415 Beam::visible_stem_count (Grob *me)
1417 Link_array<Grob>stems =
1418 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1420 for (int i = stems.size (); i--;)
1422 if (!Stem::is_invisible (stems[i]))
1429 Beam::first_visible_stem (Grob *me)
1431 Link_array<Grob>stems =
1432 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1434 for (int i = 0; i < stems.size (); i++)
1436 if (!Stem::is_invisible (stems[i]))
1443 Beam::last_visible_stem (Grob *me)
1445 Link_array<Grob>stems =
1446 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1447 for (int i = stems.size (); i--;)
1449 if (!Stem::is_invisible (stems[i]))
1459 handle rest under beam (do_post: beams are calculated now)
1460 what about combination of collisions and rest under beam.
1464 rest -> stem -> beam -> interpolate_y_position ()
1466 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1468 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1470 Grob *rest = unsmob_grob (element_smob);
1471 Axis a = (Axis) gh_scm2int (axis);
1473 if (gh_number_p (rest->get_grob_property ("staff-position")))
1474 return gh_int2scm (0);
1476 assert (a == Y_AXIS);
1478 Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1481 return gh_double2scm (0.0);
1482 Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1484 || !Beam::has_interface (beam)
1485 || !Beam::visible_stem_count (beam))
1486 return gh_double2scm (0.0);
1488 Drul_array<Real> pos (0, 0);
1489 SCM s = beam->get_grob_property ("positions");
1490 if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1491 pos = ly_scm2interval (s);
1492 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1494 scale_drul (&pos, staff_space);
1497 Real dy = pos[RIGHT] - pos[LEFT];
1499 // ugh -> use commonx
1500 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1501 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1502 Real dydx = dy && dx ? dy/dx : 0;
1504 Direction d = Stem::get_direction (stem);
1505 Real stem_y = pos[LEFT] + (stem->relative_coordinate (0, X_AXIS) - x0) * dydx;
1507 Real beam_translation = get_beam_translation (beam);
1508 Real beam_thickness = Beam::get_thickness (beam);
1510 int beam_count = get_direction_beam_count (beam, d);
1511 Real height_of_my_beams = beam_thickness / 2
1512 + (beam_count - 1) * beam_translation;
1513 Real beam_y = stem_y - d * height_of_my_beams;
1515 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1517 Real rest_dim = rest->extent (common_y, Y_AXIS)[d];
1518 Real minimum_distance =
1519 staff_space * robust_scm2double (rest->get_grob_property ("minimum-distance"), 0.0);
1521 Real shift = d * (((beam_y - d * minimum_distance) - rest_dim) * d <? 0.0);
1523 shift /= staff_space;
1524 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1526 /* Always move discretely by half spaces */
1527 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1529 /* Inside staff, move by whole spaces*/
1530 if ((rest->extent (common_y, Y_AXIS)[d] + staff_space * shift) * d
1532 || (rest->extent (common_y, Y_AXIS)[-d] + staff_space * shift) * -d
1534 shift = ceil (fabs (shift)) *sign (shift);
1536 return gh_double2scm (staff_space * shift);
1540 Beam::is_knee (Grob* me)
1542 SCM k = me->get_grob_property ("knee");
1543 if (gh_boolean_p (k))
1544 return gh_scm2bool (k);
1548 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
1550 Direction dir = get_grob_direction (unsmob_grob (ly_car (s)));
1559 me->set_grob_property ("knee", gh_bool2scm (knee));
1565 Beam::get_direction_beam_count (Grob *me, Direction d )
1567 Link_array<Grob>stems =
1568 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1571 for (int i = stems.size (); i--;)
1574 Should we take invisible stems into account?
1576 if (Stem::get_direction (stems[i]) == d)
1577 bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1584 ADD_INTERFACE (Beam, "beam-interface",
1586 "The @code{thickness} property is the weight of beams, and is measured "
1589 "knee positioning-done position-callbacks concaveness-gap "
1590 "concaveness-threshold dir-function quant-score auto-knee-gap gap "
1591 "gap-count chord-tremolo beamed-stem-shorten shorten least-squares-dy "
1592 "damping flag-width-function neutral-direction positions space-function "