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 (ly_procedure_p (func))
84 SCM s = scm_call_2 (func, me->self_scm (), scm_int2num (get_beam_count (me)));
85 return ly_scm2double (s);
93 /* Maximum beam_count. */
95 Beam::get_beam_count (Grob *me)
98 for (SCM s = me->get_property ("stems"); ly_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 = ly_scm2int (beam_count) < 4
121 ? (2*staff_space + line - thickness) / 2.0
122 : (3*staff_space + line - thickness) / 3.0;
124 return scm_make_real (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 (_ ("removing beam with less than two stems"));
158 unsmob_grob (ly_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 (ly_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 = ly_car (right_beaming); ly_pair_p (s); s = ly_cdr (s))
214 int k = - right_dir * ly_scm2int (ly_car (s)) + i;
215 if (scm_c_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 (ly_pair_p (last_beaming) && ly_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 (; ly_pair_p (s); s = ly_cdr (s))
263 start_point - this_dir * ly_scm2int (ly_car (s));
265 new_slice.add_point (new_beam_pos);
266 scm_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 scm_set_car_x ( this_beaming, SCM_EOL);
279 SCM s = ly_cdr (this_beaming);
280 for (; ly_pair_p (s); s = ly_cdr (s))
282 int np = - this_dir * ly_scm2int (ly_car (s));
283 scm_set_car_x (s, scm_int2num (np));
284 last_int.add_point (np);
288 if (i == stems.size () -1)
290 scm_set_cdr_x (this_beaming, SCM_EOL);
293 if (scm_ilength (ly_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_dimension (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) ? ly_cdr (last_beaming) : SCM_EOL;
373 SCM right = st ? ly_car (this_beaming) : SCM_EOL;
375 Array<int> full_beams;
376 Array<int> lfliebertjes;
377 Array<int> rfliebertjes;
380 ly_pair_p (s); s =ly_cdr (s))
382 int b = ly_scm2int (ly_car (s));
383 if (scm_c_memq (ly_car (s), right) != SCM_BOOL_F)
389 lfliebertjes.push (b);
393 ly_pair_p (s); s =ly_cdr (s))
395 int b = ly_scm2int (ly_car (s));
396 if (scm_c_memq (ly_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_dimension (ly_symbol2scm ("blotdiameter"));
420 Stencil whole = Lookup::beam (dydx, w, thick, blot);
424 if (ly_number_p (me->get_property ("gap-count")))
426 gap_count = ly_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 = scm_call_1 (proc, scm_int2num (t));
460 nw_f = ly_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 && ly_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 = scm_call_2 (func,
568 scm_cons (scm_int2num (count[UP]),
569 scm_int2num (count[DOWN])),
570 scm_cons (scm_int2num (total[UP]),
571 scm_int2num (total[DOWN])));
573 if (ly_number_p (s) && ly_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 (!ly_number_p (scm))
672 Real threshold = ly_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 = ly_scm2double (shorten_elt) * staff_space;
796 /* your similar cute comment here */
797 shorten_f *= forced_fraction;
800 me->set_property ("shorten", scm_make_real (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; ly_pair_p (i); i = ly_cdr (i))
834 scm_call_1 (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 scm_make_real (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", scm_make_real (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 ())
1040 warning (_ ("no viable initial configuration found: may not find good beam slope"));
1041 else if (!feasible_left_point.contains (y))
1043 if (isinf (feasible_left_point[DOWN]))
1044 y = feasible_left_point[UP] - REGION_SIZE;
1045 else if (isinf (feasible_left_point[UP]))
1046 y = feasible_left_point[DOWN]+ REGION_SIZE;
1048 y = feasible_left_point.center ();
1051 pos = Drul_array<Real> (y, (y+dy));
1052 scale_drul (&pos, 1/ Staff_symbol_referencer::staff_space (me));
1054 me->set_property ("positions", ly_interval2scm (pos));
1055 return SCM_UNSPECIFIED;
1058 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
1060 Beam::check_concave (SCM smob)
1062 Grob *me = unsmob_grob (smob);
1064 Link_array<Grob> stems =
1065 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1067 Direction beam_dir = CENTER;
1068 for (int i = 0; i < stems.size ();)
1070 if (Stem::is_invisible (stems[i]))
1074 if (Direction sd = Stem::get_direction (stems[i]))
1077 Don't do knee beams.
1079 if (beam_dir && sd && sd != beam_dir)
1080 return SCM_UNSPECIFIED;
1088 if (stems.size () < 3)
1089 return SCM_UNSPECIFIED;
1092 /* Concaveness #1: If distance of an inner notehead to line between
1093 two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
1094 beam is concave (Heinz Stolba).
1096 In the case of knees, the line connecting outer heads is often
1097 not related to the beam slope (it may even go in the other
1098 direction). Skip the check when the outer stems point in
1099 different directions. --hwn
1102 bool is_concave1 = false;
1103 SCM gap = me->get_property ("concaveness-gap");
1104 if (ly_number_p (gap))
1106 Real r1 = ly_scm2double (gap);
1107 Real dy = Stem::chord_start_y (stems.top ())
1108 - Stem::chord_start_y (stems[0]);
1111 Real slope = dy / (stems.size () - 1);
1113 Real y0 = Stem::chord_start_y (stems[0]);
1114 for (int i = 1; i < stems.size () - 1; i++)
1117 beam_dir *((Stem::chord_start_y (stems[i]) - y0) - i * slope);
1127 /* Concaveness #2: Sum distances of inner noteheads that fall
1128 outside the interval of the two outer noteheads.
1130 We only do this for beams where first and last stem have the same
1134 Note that "convex" stems compensate for "concave" stems.
1135 (is that intentional?) --hwn.
1138 Real concaveness2 = 0;
1139 SCM thresh = me->get_property ("concaveness-threshold");
1140 Real r2 = infinity_f;
1141 if (!is_concave1 && ly_number_p (thresh))
1143 r2 = ly_scm2double (thresh);
1146 iv.add_point (Stem::chord_start_y (stems[0]));
1147 iv.add_point (Stem::chord_start_y (stems.top ()));
1149 for (int i = 1; i < stems.size () - 1; i++)
1151 Real f = Stem::chord_start_y (stems[i]);
1152 concaveness2 += ( (f - iv[MAX] ) >? 0) +
1153 ( (f - iv[MIN] ) <? 0);
1156 concaveness2 *= beam_dir / (stems.size () - 2);
1159 /* TODO: some sort of damping iso -> plain horizontal */
1160 if (is_concave1 || concaveness2 > r2)
1162 Drul_array<Real> pos = ly_scm2interval (me->get_property ("positions"));
1163 Real r = linear_combination (pos, 0);
1165 r /= Staff_symbol_referencer::staff_space (me);
1166 me->set_property ("positions", ly_interval2scm (Drul_array<Real> (r, r)));
1167 me->set_property ("least-squares-dy", scm_make_real (0));
1170 return SCM_UNSPECIFIED;
1173 /* This neat trick is by Werner Lemberg,
1174 damped = tanh (slope)
1175 corresponds with some tables in [Wanske] CHECKME */
1176 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1178 Beam::slope_damping (SCM smob)
1180 Grob *me = unsmob_grob (smob);
1182 if (visible_stem_count (me) <= 1)
1183 return SCM_UNSPECIFIED;
1185 SCM s = me->get_property ("damping");
1186 int damping = ly_scm2int (s);
1190 Drul_array<Real> pos = ly_scm2interval (me->get_property ("positions"));
1191 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1193 Real dy = pos[RIGHT] - pos[LEFT];
1195 Grob *fvs = first_visible_stem (me);
1196 Grob *lvs = last_visible_stem (me);
1198 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1201 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1202 - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1203 Real dydx = dy && dx ? dy/dx : 0;
1204 dydx = 0.6 * tanh (dydx) / damping;
1206 Real damped_dy = dydx * dx;
1207 pos[LEFT] += (dy - damped_dy) / 2;
1208 pos[RIGHT] -= (dy - damped_dy) / 2;
1210 scale_drul (&pos, 1/Staff_symbol_referencer::staff_space (me));
1212 me->set_property ("positions", ly_interval2scm (pos));
1214 return SCM_UNSPECIFIED;
1218 Report slice containing the numbers that are both in (car BEAMING)
1222 where_are_the_whole_beams (SCM beaming)
1226 for ( SCM s = ly_car (beaming); ly_pair_p (s) ; s = ly_cdr (s))
1228 if (scm_c_memq (ly_car (s), ly_cdr (beaming)) != SCM_BOOL_F)
1230 l.add_point (ly_scm2int (ly_car (s)));
1236 /* Return the Y position of the stem-end, given the Y-left, Y-right
1237 in POS for stem S. This Y position is relative to S. */
1239 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1241 Drul_array<Real> pos, bool french)
1243 Real beam_translation = get_beam_translation (me);
1246 Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1247 Real dy = pos[RIGHT] - pos[LEFT];
1249 Real stem_y_beam0 = (dy && dx
1254 Direction my_dir = get_grob_direction (s);
1255 SCM beaming = s->get_property ("beaming");
1257 Real stem_y = stem_y_beam0;
1260 Slice bm = where_are_the_whole_beams (beaming);
1261 if (!bm.is_empty ())
1262 stem_y += beam_translation * bm[-my_dir];
1266 Slice bm = Stem::beam_multiplicity (s);
1267 if (!bm.is_empty ())
1268 stem_y +=bm[my_dir] * beam_translation;
1271 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1272 - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1278 Hmm. At this time, beam position and slope are determined. Maybe,
1279 stem directions and length should set to relative to the chord's
1280 position of the beam. */
1282 Beam::set_stem_lengths (Grob *me)
1284 Link_array<Grob> stems=
1285 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1291 for (int a = 2; a--;)
1292 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1294 Drul_array<Real> pos = ly_scm2realdrul (me->get_property ("positions"));
1295 Real staff_space = Staff_symbol_referencer::staff_space (me);
1296 scale_drul (&pos, staff_space);
1300 if (ly_number_p (me->get_property ("gap-count"))
1301 &&ly_scm2int (me->get_property ("gap-count")))
1304 thick = get_thickness (me);
1307 // ugh -> use commonx
1308 Grob * fvs = first_visible_stem (me);
1309 Grob *lvs = last_visible_stem (me);
1311 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1312 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1314 for (int i=0; i < stems.size (); i++)
1317 if (Stem::is_invisible (s))
1320 bool french = to_boolean (s->get_property ("french-beaming"));
1321 Real stem_y = calc_stem_y (me, s, common,
1323 pos, french && s != lvs && s!= fvs);
1326 Make the stems go up to the end of the beam. This doesn't matter
1327 for normal beams, but for tremolo beams it looks silly otherwise.
1330 stem_y += thick * 0.5 * get_grob_direction (s);
1332 Stem::set_stemend (s, 2* stem_y / staff_space);
1337 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1339 Link_array<Grob> stems=
1340 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1343 for (int i=0; i < stems.size (); i++)
1346 Don't overwrite user settings.
1351 /* Don't set beaming for outside of outer stems */
1352 if ( (d == LEFT && i == 0)
1353 || (d == RIGHT && i == stems.size () -1))
1356 Grob *st = stems[i];
1357 SCM beaming_prop = st->get_property ("beaming");
1358 if (beaming_prop == SCM_EOL ||
1359 index_get_cell (beaming_prop, d) == SCM_EOL)
1361 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1363 && i < stems.size () -1
1364 && Stem::is_invisible (st))
1365 b = b <? beaming->infos_.elem (i).beams_i_drul_[-d];
1367 Stem::set_beaming (st, b, d);
1370 while (flip (&d) != LEFT);
1375 Beam::forced_stem_count (Grob *me)
1377 Link_array<Grob>stems =
1378 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1380 for (int i=0; i < stems.size (); i++)
1384 if (Stem::is_invisible (s))
1387 /* I can imagine counting those boundaries as a half forced stem,
1388 but let's count them full for now. */
1389 if (abs (Stem::chord_start_y (s)) > 0.1
1390 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1400 Beam::visible_stem_count (Grob *me)
1402 Link_array<Grob>stems =
1403 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1405 for (int i = stems.size (); i--;)
1407 if (!Stem::is_invisible (stems[i]))
1414 Beam::first_visible_stem (Grob *me)
1416 Link_array<Grob>stems =
1417 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1419 for (int i = 0; i < stems.size (); i++)
1421 if (!Stem::is_invisible (stems[i]))
1428 Beam::last_visible_stem (Grob *me)
1430 Link_array<Grob>stems =
1431 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1432 for (int i = stems.size (); i--;)
1434 if (!Stem::is_invisible (stems[i]))
1444 handle rest under beam (do_post: beams are calculated now)
1445 what about combination of collisions and rest under beam.
1449 rest -> stem -> beam -> interpolate_y_position ()
1451 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1453 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1455 Grob *rest = unsmob_grob (element_smob);
1456 Axis a = (Axis) ly_scm2int (axis);
1458 if (ly_number_p (rest->get_property ("staff-position")))
1459 return scm_int2num (0);
1461 assert (a == Y_AXIS);
1463 Grob *st = unsmob_grob (rest->get_property ("stem"));
1466 return scm_make_real (0.0);
1467 Grob *beam = unsmob_grob (stem->get_property ("beam"));
1469 || !Beam::has_interface (beam)
1470 || !Beam::visible_stem_count (beam))
1471 return scm_make_real (0.0);
1473 Drul_array<Real> pos (0, 0);
1474 SCM s = beam->get_property ("positions");
1475 if (ly_pair_p (s) && ly_number_p (ly_car (s)))
1476 pos = ly_scm2interval (s);
1477 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1479 scale_drul (&pos, staff_space);
1482 Real dy = pos[RIGHT] - pos[LEFT];
1484 // ugh -> use commonx
1485 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1486 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1487 Real dydx = dy && dx ? dy/dx : 0;
1489 Direction d = Stem::get_direction (stem);
1490 Real stem_y = pos[LEFT] + (stem->relative_coordinate (0, X_AXIS) - x0) * dydx;
1492 Real beam_translation = get_beam_translation (beam);
1493 Real beam_thickness = Beam::get_thickness (beam);
1495 int beam_count = get_direction_beam_count (beam, d);
1496 Real height_of_my_beams = beam_thickness / 2
1497 + (beam_count - 1) * beam_translation;
1498 Real beam_y = stem_y - d * height_of_my_beams;
1500 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1502 Real rest_dim = rest->extent (common_y, Y_AXIS)[d];
1503 Real minimum_distance =
1504 staff_space * robust_scm2double (rest->get_property ("minimum-distance"), 0.0);
1506 Real shift = d * ( ((beam_y - d * minimum_distance) - rest_dim) * d <? 0.0);
1508 shift /= staff_space;
1509 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1511 /* Always move discretely by half spaces */
1512 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1514 /* Inside staff, move by whole spaces*/
1515 if ( (rest->extent (common_y, Y_AXIS)[d] + staff_space * shift) * d
1517 || (rest->extent (common_y, Y_AXIS)[-d] + staff_space * shift) * -d
1519 shift = ceil (fabs (shift)) *sign (shift);
1521 return scm_make_real (staff_space * shift);
1525 Beam::is_knee (Grob* me)
1527 SCM k = me->get_property ("knee");
1528 if (ly_boolean_p (k))
1529 return ly_scm2bool (k);
1533 for (SCM s = me->get_property ("stems"); ly_pair_p (s); s = ly_cdr (s))
1535 Direction dir = get_grob_direction (unsmob_grob (ly_car (s)));
1544 me->set_property ("knee", ly_bool2scm (knee));
1550 Beam::get_direction_beam_count (Grob *me, Direction d )
1552 Link_array<Grob>stems =
1553 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1556 for (int i = stems.size (); i--;)
1559 Should we take invisible stems into account?
1561 if (Stem::get_direction (stems[i]) == d)
1562 bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1569 ADD_INTERFACE (Beam, "beam-interface",
1571 "The @code{thickness} property is the weight of beams, and is measured "
1574 "knee positioning-done position-callbacks concaveness-gap "
1575 "concaveness-threshold dir-function quant-score auto-knee-gap gap "
1576 "gap-count chord-tremolo beamed-stem-shorten shorten least-squares-dy "
1577 "damping flag-width-function neutral-direction positions space-function "