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_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_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_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_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_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_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_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_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_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_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_property ("thickness"), 1.0) *lt : 0 ;
366 Direction stem_dir = st ? to_dir (st->get_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_property ("gap-count")))
426 gap_count = gh_scm2int (me->get_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_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_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::text_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_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_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_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_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);
692 if (!hps.is_empty ())
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_property ("direction")))
708 Direction stemdir = to_dir (stem->get_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_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_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_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_property ("positioning-done")))
823 me->set_property ("positioning-done", SCM_BOOL_T);
825 /* Copy to mutable list. */
826 SCM s = ly_deep_copy (me->get_property ("positions"));
827 me->set_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_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_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_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_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_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_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_property ("positions", ly_interval2scm (pos));
1057 return SCM_UNSPECIFIED;
1060 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
1062 Beam::check_concave (SCM smob)
1064 Grob *me = unsmob_grob (smob);
1066 Link_array<Grob> stems =
1067 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1069 Direction beam_dir = CENTER;
1070 for (int i = 0; i < stems.size ();)
1072 if (Stem::is_invisible (stems[i]))
1076 if (Direction sd = Stem::get_direction (stems[i]))
1079 Don't do knee beams.
1081 if (beam_dir && sd && sd != beam_dir)
1082 return SCM_UNSPECIFIED;
1090 if (stems.size () < 3)
1091 return SCM_UNSPECIFIED;
1094 /* Concaveness #1: If distance of an inner notehead to line between
1095 two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
1096 beam is concave (Heinz Stolba).
1098 In the case of knees, the line connecting outer heads is often
1099 not related to the beam slope (it may even go in the other
1100 direction). Skip the check when the outer stems point in
1101 different directions. --hwn
1104 bool is_concave1 = false;
1105 SCM gap = me->get_property ("concaveness-gap");
1106 if (gh_number_p (gap))
1108 Real r1 = gh_scm2double (gap);
1109 Real dy = Stem::chord_start_y (stems.top ())
1110 - Stem::chord_start_y (stems[0]);
1113 Real slope = dy / (stems.size () - 1);
1115 Real y0 = Stem::chord_start_y (stems[0]);
1116 for (int i = 1; i < stems.size () - 1; i++)
1119 beam_dir *((Stem::chord_start_y (stems[i]) - y0) - i * slope);
1129 /* Concaveness #2: Sum distances of inner noteheads that fall
1130 outside the interval of the two outer noteheads.
1132 We only do this for beams where first and last stem have the same
1136 Note that "convex" stems compensate for "concave" stems.
1137 (is that intentional?) --hwn.
1140 Real concaveness2 = 0;
1141 SCM thresh = me->get_property ("concaveness-threshold");
1142 Real r2 = infinity_f;
1143 if (!is_concave1 && gh_number_p (thresh))
1145 r2 = gh_scm2double (thresh);
1148 iv.add_point (Stem::chord_start_y (stems[0]));
1149 iv.add_point (Stem::chord_start_y (stems.top ()));
1151 for (int i = 1; i < stems.size () - 1; i++)
1153 Real f = Stem::chord_start_y (stems[i]);
1154 concaveness2 += ( (f - iv[MAX] ) >? 0) +
1155 ( (f - iv[MIN] ) <? 0);
1158 concaveness2 *= beam_dir / (stems.size () - 2);
1161 /* TODO: some sort of damping iso -> plain horizontal */
1162 if (is_concave1 || concaveness2 > r2)
1164 Drul_array<Real> pos = ly_scm2interval (me->get_property ("positions"));
1165 Real r = linear_combination (pos, 0);
1167 r /= Staff_symbol_referencer::staff_space (me);
1168 me->set_property ("positions", ly_interval2scm (Drul_array<Real> (r, r)));
1169 me->set_property ("least-squares-dy", gh_double2scm (0));
1172 return SCM_UNSPECIFIED;
1175 /* This neat trick is by Werner Lemberg,
1176 damped = tanh (slope)
1177 corresponds with some tables in [Wanske] CHECKME */
1178 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1180 Beam::slope_damping (SCM smob)
1182 Grob *me = unsmob_grob (smob);
1184 if (visible_stem_count (me) <= 1)
1185 return SCM_UNSPECIFIED;
1187 SCM s = me->get_property ("damping");
1188 int damping = gh_scm2int (s);
1192 Drul_array<Real> pos = ly_scm2interval (me->get_property ("positions"));
1193 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1195 Real dy = pos[RIGHT] - pos[LEFT];
1197 Grob *fvs = first_visible_stem (me);
1198 Grob *lvs = last_visible_stem (me);
1200 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1203 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1204 - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1205 Real dydx = dy && dx ? dy/dx : 0;
1206 dydx = 0.6 * tanh (dydx) / damping;
1208 Real damped_dy = dydx * dx;
1209 pos[LEFT] += (dy - damped_dy) / 2;
1210 pos[RIGHT] -= (dy - damped_dy) / 2;
1212 scale_drul (&pos, 1/Staff_symbol_referencer::staff_space (me));
1214 me->set_property ("positions", ly_interval2scm (pos));
1216 return SCM_UNSPECIFIED;
1220 Report slice containing the numbers that are both in (car BEAMING)
1224 where_are_the_whole_beams (SCM beaming)
1228 for ( SCM s = gh_car (beaming); gh_pair_p (s) ; s = gh_cdr (s))
1230 if (scm_memq (gh_car (s), gh_cdr (beaming)) != SCM_BOOL_F)
1232 l.add_point (gh_scm2int (gh_car (s)));
1238 /* Return the Y position of the stem-end, given the Y-left, Y-right
1239 in POS for stem S. This Y position is relative to S. */
1241 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1243 Drul_array<Real> pos, bool french)
1245 Real beam_translation = get_beam_translation (me);
1248 Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1249 Real dy = pos[RIGHT] - pos[LEFT];
1251 Real stem_y_beam0 = (dy && dx
1256 Direction my_dir = get_grob_direction (s);
1257 SCM beaming = s->get_property ("beaming");
1259 Real stem_y = stem_y_beam0;
1262 Slice bm = where_are_the_whole_beams (beaming);
1263 if (!bm.is_empty ())
1264 stem_y += beam_translation * bm[-my_dir];
1268 Slice bm = Stem::beam_multiplicity (s);
1269 if (!bm.is_empty ())
1270 stem_y +=bm[my_dir] * beam_translation;
1273 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1274 - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1280 Hmm. At this time, beam position and slope are determined. Maybe,
1281 stem directions and length should set to relative to the chord's
1282 position of the beam. */
1284 Beam::set_stem_lengths (Grob *me)
1286 Link_array<Grob> stems=
1287 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1293 for (int a = 2; a--;)
1294 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1296 Drul_array<Real> pos = ly_scm2realdrul (me->get_property ("positions"));
1297 Real staff_space = Staff_symbol_referencer::staff_space (me);
1298 scale_drul (&pos, staff_space);
1302 if (gh_number_p (me->get_property ("gap-count"))
1303 &&gh_scm2int (me->get_property ("gap-count")))
1306 thick = get_thickness (me);
1309 // ugh -> use commonx
1310 Grob * fvs = first_visible_stem (me);
1311 Grob *lvs = last_visible_stem (me);
1313 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1314 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1316 for (int i=0; i < stems.size (); i++)
1319 if (Stem::is_invisible (s))
1322 bool french = to_boolean (s->get_property ("french-beaming"));
1323 Real stem_y = calc_stem_y (me, s, common,
1325 pos, french && s != lvs && s!= fvs);
1328 Make the stems go up to the end of the beam. This doesn't matter
1329 for normal beams, but for tremolo beams it looks silly otherwise.
1332 stem_y += thick * 0.5 * get_grob_direction (s);
1334 Stem::set_stemend (s, 2* stem_y / staff_space);
1339 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1341 Link_array<Grob> stems=
1342 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1345 for (int i=0; i < stems.size (); i++)
1348 Don't overwrite user settings.
1353 /* Don't set beaming for outside of outer stems */
1354 if ( (d == LEFT && i == 0)
1355 || (d == RIGHT && i == stems.size () -1))
1358 Grob *st = stems[i];
1359 SCM beaming_prop = st->get_property ("beaming");
1360 if (beaming_prop == SCM_EOL ||
1361 index_get_cell (beaming_prop, d) == SCM_EOL)
1363 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1365 && i < stems.size () -1
1366 && Stem::is_invisible (st))
1367 b = b <? beaming->infos_.elem (i).beams_i_drul_[-d];
1369 Stem::set_beaming (st, b, d);
1372 while (flip (&d) != LEFT);
1377 Beam::forced_stem_count (Grob *me)
1379 Link_array<Grob>stems =
1380 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1382 for (int i=0; i < stems.size (); i++)
1386 if (Stem::is_invisible (s))
1389 /* I can imagine counting those boundaries as a half forced stem,
1390 but let's count them full for now. */
1391 if (abs (Stem::chord_start_y (s)) > 0.1
1392 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1402 Beam::visible_stem_count (Grob *me)
1404 Link_array<Grob>stems =
1405 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1407 for (int i = stems.size (); i--;)
1409 if (!Stem::is_invisible (stems[i]))
1416 Beam::first_visible_stem (Grob *me)
1418 Link_array<Grob>stems =
1419 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1421 for (int i = 0; i < stems.size (); i++)
1423 if (!Stem::is_invisible (stems[i]))
1430 Beam::last_visible_stem (Grob *me)
1432 Link_array<Grob>stems =
1433 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1434 for (int i = stems.size (); i--;)
1436 if (!Stem::is_invisible (stems[i]))
1446 handle rest under beam (do_post: beams are calculated now)
1447 what about combination of collisions and rest under beam.
1451 rest -> stem -> beam -> interpolate_y_position ()
1453 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1455 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1457 Grob *rest = unsmob_grob (element_smob);
1458 Axis a = (Axis) gh_scm2int (axis);
1460 if (gh_number_p (rest->get_property ("staff-position")))
1461 return gh_int2scm (0);
1463 assert (a == Y_AXIS);
1465 Grob *st = unsmob_grob (rest->get_property ("stem"));
1468 return gh_double2scm (0.0);
1469 Grob *beam = unsmob_grob (stem->get_property ("beam"));
1471 || !Beam::has_interface (beam)
1472 || !Beam::visible_stem_count (beam))
1473 return gh_double2scm (0.0);
1475 Drul_array<Real> pos (0, 0);
1476 SCM s = beam->get_property ("positions");
1477 if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1478 pos = ly_scm2interval (s);
1479 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1481 scale_drul (&pos, staff_space);
1484 Real dy = pos[RIGHT] - pos[LEFT];
1486 // ugh -> use commonx
1487 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1488 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1489 Real dydx = dy && dx ? dy/dx : 0;
1491 Direction d = Stem::get_direction (stem);
1492 Real stem_y = pos[LEFT] + (stem->relative_coordinate (0, X_AXIS) - x0) * dydx;
1494 Real beam_translation = get_beam_translation (beam);
1495 Real beam_thickness = Beam::get_thickness (beam);
1497 int beam_count = get_direction_beam_count (beam, d);
1498 Real height_of_my_beams = beam_thickness / 2
1499 + (beam_count - 1) * beam_translation;
1500 Real beam_y = stem_y - d * height_of_my_beams;
1502 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1504 Real rest_dim = rest->extent (common_y, Y_AXIS)[d];
1505 Real minimum_distance =
1506 staff_space * robust_scm2double (rest->get_property ("minimum-distance"), 0.0);
1508 Real shift = d * ( ((beam_y - d * minimum_distance) - rest_dim) * d <? 0.0);
1510 shift /= staff_space;
1511 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1513 /* Always move discretely by half spaces */
1514 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1516 /* Inside staff, move by whole spaces*/
1517 if ( (rest->extent (common_y, Y_AXIS)[d] + staff_space * shift) * d
1519 || (rest->extent (common_y, Y_AXIS)[-d] + staff_space * shift) * -d
1521 shift = ceil (fabs (shift)) *sign (shift);
1523 return gh_double2scm (staff_space * shift);
1527 Beam::is_knee (Grob* me)
1529 SCM k = me->get_property ("knee");
1530 if (gh_boolean_p (k))
1531 return gh_scm2bool (k);
1535 for (SCM s = me->get_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
1537 Direction dir = get_grob_direction (unsmob_grob (ly_car (s)));
1546 me->set_property ("knee", gh_bool2scm (knee));
1552 Beam::get_direction_beam_count (Grob *me, Direction d )
1554 Link_array<Grob>stems =
1555 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1558 for (int i = stems.size (); i--;)
1561 Should we take invisible stems into account?
1563 if (Stem::get_direction (stems[i]) == d)
1564 bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1571 ADD_INTERFACE (Beam, "beam-interface",
1573 "The @code{thickness} property is the weight of beams, and is measured "
1576 "knee positioning-done position-callbacks concaveness-gap "
1577 "concaveness-threshold dir-function quant-score auto-knee-gap gap "
1578 "gap-count chord-tremolo beamed-stem-shorten shorten least-squares-dy "
1579 "damping flag-width-function neutral-direction positions space-function "