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 "output-def.hh"
40 #include "group-interface.hh"
41 #include "staff-symbol-referencer.hh"
48 #include "text-item.hh" // debug output.
49 #include "font-interface.hh" // debug output.
54 Beam::add_stem (Grob *me, Grob *s)
56 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
58 s->add_dependency (me);
60 assert (!Stem::get_beam (s));
61 s->set_property ("beam", me->self_scm ());
63 add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
68 Beam::get_thickness (Grob * me)
70 return robust_scm2double (me->get_property ("thickness"), 0)
71 * Staff_symbol_referencer::staff_space (me);
74 /* Return the translation between 2 adjoining beams. */
76 Beam::get_beam_translation (Grob *me)
78 SCM func = me->get_property ("space-function");
80 if (ly_c_procedure_p (func))
82 SCM s = scm_call_2 (func, me->self_scm (), scm_int2num (get_beam_count (me)));
83 return scm_to_double (s);
91 /* Maximum beam_count. */
93 Beam::get_beam_count (Grob *me)
96 for (SCM s = me->get_property ("stems"); ly_c_pair_p (s); s = ly_cdr (s))
98 Grob *stem = unsmob_grob (ly_car (s));
99 m = m >? (Stem::beam_multiplicity (stem).length () + 1);
106 Space return space between beams.
108 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
110 Beam::space_function (SCM smob, SCM beam_count)
112 Grob *me = unsmob_grob (smob);
114 Real staff_space = Staff_symbol_referencer::staff_space (me);
115 Real line = Staff_symbol_referencer::line_thickness (me);
116 Real thickness = get_thickness (me);
118 Real beam_translation = scm_to_int (beam_count) < 4
119 ? (2*staff_space + line - thickness) / 2.0
120 : (3*staff_space + line - thickness) / 3.0;
122 return scm_make_real (beam_translation);
126 /* After pre-processing all directions should be set.
127 Several post-processing routines (stem, slur, script) need stem/beam
129 Currenly, this means that beam has set all stem's directions.
130 [Alternatively, stems could set its own directions, according to
131 their beam, during 'final-pre-processing'.] */
132 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
134 Beam::before_line_breaking (SCM smob)
136 Grob *me = unsmob_grob (smob);
138 /* Beams with less than 2 two stems don't make much sense, but could happen
143 For a beam that only has one stem, we try to do some disappearance magic:
144 we revert the flag, and move on to The Eternal Engraving Fields. */
146 int count = visible_stem_count (me);
149 me->warning (_ ("beam has less than two visible stems"));
151 SCM stems = me->get_property ("stems");
152 if (scm_ilength (stems) == 1)
154 me->warning (_ ("removing beam with less than two stems"));
156 unsmob_grob (ly_car (stems))->set_property ("beam", SCM_EOL);
159 return SCM_UNSPECIFIED;
161 else if (scm_ilength (stems) == 0)
164 return SCM_UNSPECIFIED;
169 Direction d = get_default_dir (me);
171 consider_auto_knees (me);
172 set_stem_directions (me, d);
176 set_stem_shorten (me);
184 We want a maximal number of shared beams, but if there is choice, we
185 take the one that is closest to the end of the stem. This is for situations like
198 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
202 Slice lslice = int_list_to_slice (ly_cdr (left_beaming));
206 for (int i = lslice[-left_dir];
207 (i - lslice[left_dir])* left_dir <= 0 ; i+= left_dir)
210 for ( SCM s = ly_car (right_beaming); ly_c_pair_p (s); s = ly_cdr (s))
212 int k = - right_dir * scm_to_int (ly_car (s)) + i;
213 if (scm_c_memq (scm_int2num (k), left_beaming) != SCM_BOOL_F)
217 if (count >= best_count)
228 Beam::connect_beams (Grob *me)
230 Link_array<Grob> stems=
231 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
234 last_int.set_empty ();
235 SCM last_beaming = SCM_EOL;
236 Direction last_dir = CENTER;
237 for (int i = 0; i< stems.size (); i++)
239 Grob *this_stem = stems[i];
240 SCM this_beaming = this_stem->get_property ("beaming");
242 Direction this_dir = get_grob_direction (this_stem);
243 if (ly_c_pair_p (last_beaming) && ly_c_pair_p (this_beaming))
245 int start_point = position_with_maximal_common_beams
246 (last_beaming, this_beaming,
253 if (d == RIGHT && i == stems.size ()-1)
256 new_slice.set_empty ();
257 SCM s = index_get_cell (this_beaming, d);
258 for (; ly_c_pair_p (s); s = ly_cdr (s))
261 start_point - this_dir * scm_to_int (ly_car (s));
263 new_slice.add_point (new_beam_pos);
264 scm_set_car_x (s, scm_int2num (new_beam_pos));
269 while (flip (&d) != LEFT);
271 if (!new_slice.is_empty ())
272 last_int = new_slice;
276 scm_set_car_x ( this_beaming, SCM_EOL);
277 SCM s = ly_cdr (this_beaming);
278 for (; ly_c_pair_p (s); s = ly_cdr (s))
280 int np = - this_dir * scm_to_int (ly_car (s));
281 scm_set_car_x (s, scm_int2num (np));
282 last_int.add_point (np);
286 if (i == stems.size () -1)
288 scm_set_cdr_x (this_beaming, SCM_EOL);
291 if (scm_ilength (ly_cdr (this_beaming)) > 0)
293 last_beaming = this_beaming;
301 TODO: should not make beams per stem, but per Y-level.
303 MAKE_SCHEME_CALLBACK (Beam, print, 1);
305 Beam::print (SCM grob)
307 Spanner *me = unsmob_spanner (grob);
310 Link_array<Grob> stems=
311 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
312 Grob* xcommon = common_refpoint_of_array (stems, me, X_AXIS);
314 xcommon = me->get_bound (LEFT)->common_refpoint (xcommon, X_AXIS);
315 xcommon = me->get_bound (RIGHT)->common_refpoint (xcommon, X_AXIS);
318 if (visible_stem_count (me))
320 // ugh -> use commonx
321 x0 = first_visible_stem (me)->relative_coordinate (xcommon, X_AXIS);
322 dx = last_visible_stem (me)->relative_coordinate (xcommon, X_AXIS) - x0;
326 x0 = stems[0]->relative_coordinate (xcommon, X_AXIS);
327 dx = stems.top ()->relative_coordinate (xcommon, X_AXIS) - x0;
330 SCM posns = me->get_property ("positions");
331 Drul_array<Real> pos;
332 if (!is_number_pair (posns))
334 programming_error ("No beam posns");
335 pos = Interval (0,0);
338 pos= ly_scm2realdrul (posns);
340 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
342 Real dy = pos[RIGHT] - pos[LEFT];
343 Real slope = (dy && dx) ? dy/dx : 0;
345 Real thick = get_thickness (me);
346 Real bdy = get_beam_translation (me);
348 SCM last_beaming = SCM_EOL;
349 Real last_xposn = -1;
350 Real last_stem_width = -1 ;
352 Real gap_length = robust_scm2double (me->get_property ("gap"), 0.0);
355 Real lt = me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
357 for (int i = 0; i<= stems.size (); i++)
359 Grob * st = (i < stems.size ()) ? stems[i] : 0;
361 SCM this_beaming = st ? st->get_property ("beaming") : SCM_EOL;
362 Real xposn = st ? st->relative_coordinate (xcommon, X_AXIS) : 0.0;
363 Real stem_width = st ? robust_scm2double (st->get_property ("thickness"), 1.0) *lt : 0 ;
364 Direction stem_dir = st ? to_dir (st->get_property ("direction")) : CENTER;
366 We do the space left of ST, with lfliebertjes pointing to the
367 right from the left stem, and rfliebertjes pointing left from
370 SCM left = (i > 0) ? ly_cdr (last_beaming) : SCM_EOL;
371 SCM right = st ? ly_car (this_beaming) : SCM_EOL;
373 Array<int> full_beams;
374 Array<int> lfliebertjes;
375 Array<int> rfliebertjes;
378 ly_c_pair_p (s); s =ly_cdr (s))
380 int b = scm_to_int (ly_car (s));
381 if (scm_c_memq (ly_car (s), right) != SCM_BOOL_F)
387 lfliebertjes.push (b);
391 ly_c_pair_p (s); s =ly_cdr (s))
393 int b = scm_to_int (ly_car (s));
394 if (scm_c_memq (ly_car (s), left) == SCM_BOOL_F)
396 rfliebertjes.push (b);
401 how much to stick out for beams across linebreaks
403 Real break_overshoot = 3.0;
404 Real w = (i > 0 && st) ? (xposn - last_xposn) : break_overshoot;
406 Real stem_offset =0.0;
409 w += last_stem_width / 2;
410 stem_offset = -last_stem_width / 2;
417 Real blot = me->get_paper ()->get_dimension (ly_symbol2scm ("blotdiameter"));
418 Stencil whole = Lookup::beam (slope, w, thick, blot);
422 if (scm_is_number (me->get_property ("gap-count")))
424 gap_count = scm_to_int (me->get_property ("gap-count"));
425 gapped = Lookup::beam (slope, w - 2 * gap_length, thick, blot);
427 full_beams.sort (default_compare);
429 full_beams.reverse ();
433 for (int j = full_beams.size (); j--;)
440 b.translate_axis (gap_length, X_AXIS);
442 b.translate_axis (last_xposn - x0 + stem_offset, X_AXIS);
443 b.translate_axis (slope * (last_xposn - x0) + bdy * full_beams[j], Y_AXIS);
445 the_beam.add_stencil (b);
448 if (lfliebertjes.size () || rfliebertjes.size ())
454 int t = Stem::duration_log (st);
456 SCM proc = me->get_property ("flag-width-function");
457 SCM result = scm_call_1 (proc, scm_int2num (t));
458 nw_f = scm_to_double (result);
461 nw_f = break_overshoot / 2;
463 /* Half beam should be one note-width,
464 but let's make sure two half-beams never touch */
468 rw = nw_f <? ( (xposn - last_xposn) / 2);
471 TODO: 0.5 is a guess.
473 rw = xposn - me->get_bound (LEFT)->extent (xcommon, X_AXIS)[RIGHT]
477 lw = nw_f <? ( (xposn - last_xposn) / 2);
479 lw = me->get_bound (RIGHT)->relative_coordinate (xcommon, X_AXIS)
482 Stencil rhalf = Lookup::beam (slope, rw, thick, blot);
483 Stencil lhalf = Lookup::beam (slope, lw, thick, blot);
484 for (int j = lfliebertjes.size (); j--;)
487 b.translate_axis (last_xposn - x0, X_AXIS);
488 b.translate_axis (slope * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
489 the_beam.add_stencil (b);
491 for (int j = rfliebertjes.size (); j--;)
494 b.translate_axis (xposn - x0 - rw , X_AXIS);
495 b.translate_axis (slope * (xposn-x0 -rw) + bdy * rfliebertjes[j], Y_AXIS);
496 the_beam.add_stencil (b);
502 last_stem_width = stem_width;
503 last_beaming = this_beaming;
506 the_beam.translate_axis (x0 - me->relative_coordinate (xcommon, X_AXIS), X_AXIS);
507 the_beam.translate_axis (pos[LEFT], Y_AXIS);
510 SCM quant_score = me->get_property ("quant-score");
511 if (to_boolean (me->get_paper ()->lookup_variable (ly_symbol2scm ("debug-beam-quanting")))
512 && scm_is_string (quant_score))
516 This code prints the demerits for each beam. Perhaps this
517 should be switchable for those who want to twiddle with the
521 SCM properties = Font_interface::text_font_alist_chain (me);
523 Direction stem_dir = stems.size() ? to_dir (stems[0]->get_property ("direction")) : UP;
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, stem_dir, tm, 1.0, 0);
531 return the_beam.smobbed_copy ();
538 Beam::get_default_dir (Grob *me)
540 Drul_array<int> total;
541 total[UP] = total[DOWN] = 0;
542 Drul_array<int> count;
543 count[UP] = count[DOWN] = 0;
546 Link_array<Grob> stems=
547 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
549 for (int i=0; i < stems.size (); i++)
552 Direction sd = get_grob_direction (s);
554 int center_distance = int (- d * Stem::head_positions (s) [-d]) >? 0;
555 int current = sd ? (1 + d * sd)/2 : center_distance;
562 } while (flip (&d) != DOWN);
564 SCM func = me->get_property ("dir-function");
565 SCM s = scm_call_2 (func,
566 scm_cons (scm_int2num (count[UP]),
567 scm_int2num (count[DOWN])),
568 scm_cons (scm_int2num (total[UP]),
569 scm_int2num (total[DOWN])));
571 if (scm_is_number (s) && scm_to_int (s))
574 /* If dir is not determined: get default */
575 return to_dir (me->get_property ("neutral-direction"));
579 /* Set all stems with non-forced direction to beam direction.
580 Urg: non-forced should become `without/with unforced' direction,
581 once stem gets cleaned-up. */
583 Beam::set_stem_directions (Grob *me, Direction d)
585 Link_array<Grob> stems
586 =Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
588 for (int i=0; i <stems.size (); i++)
592 SCM forcedir = s->get_property ("direction");
593 if (!to_dir (forcedir))
594 set_grob_direction (s, d);
599 A union of intervals in the real line.
601 Abysmal performance (quadratic) for large N, hopefully we don't have
602 that large N. In any case, this should probably be rewritten to use
607 Array<Interval> allowed_regions_;
616 allowed_regions_.clear ();
619 allowed_regions_.push (s);
622 void remove_interval (Interval rm)
624 for (int i = 0; i < allowed_regions_.size (); )
628 s.intersect (allowed_regions_[i]);
632 Interval before = allowed_regions_[i];
633 Interval after = allowed_regions_[i];
635 before[RIGHT] = s[LEFT];
636 after[LEFT] = s[RIGHT];
638 if (!before.is_empty () && before.length () > 0.0)
640 allowed_regions_.insert (before, i);
643 allowed_regions_.del (i);
644 if (!after.is_empty () && after.length () > 0.0)
646 allowed_regions_.insert (after, i);
658 Only try horizontal beams for knees. No reliable detection of
659 anything else is possible here, since we don't know funky-beaming
660 settings, or X-distances (slopes!) People that want sloped
661 knee-beams, should set the directions manually.
664 Beam::consider_auto_knees (Grob* me)
666 SCM scm = me->get_property ("auto-knee-gap");
667 if (!scm_is_number (scm))
670 Real threshold = scm_to_double (scm);
676 Link_array<Grob> stems=
677 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
679 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
680 Real staff_space = Staff_symbol_referencer::staff_space (me);
682 Array<Interval> hps_array;
683 for (int i=0; i < stems.size (); i++)
685 Grob* stem = stems[i];
686 if (Stem::is_invisible (stem))
689 Interval hps = Stem::head_positions (stem);
690 if (!hps.is_empty ())
694 hps *= staff_space * 0.5 ;
697 We could subtract beam Y position, but this routine only
698 sets stem directions, a constant shift does not have an
702 hps += stem->relative_coordinate (common, Y_AXIS);
704 if (to_dir (stem->get_property ("direction")))
706 Direction stemdir = to_dir (stem->get_property ("direction"));
707 hps[-stemdir] = - stemdir * infinity_f;
710 hps_array.push (hps);
712 gaps.remove_interval (hps);
716 Real max_gap_len =0.0;
718 for (int i = gaps.allowed_regions_.size () -1; i >= 0 ; i--)
720 Interval gap = gaps.allowed_regions_[i];
723 the outer gaps are not knees.
725 if (isinf (gap[LEFT]) || isinf (gap[RIGHT]))
728 if (gap.length () >= max_gap_len)
730 max_gap_len = gap.length ();
735 if (max_gap_len > threshold)
738 for (int i = 0; i < stems.size (); i++)
740 Grob* stem = stems[i];
741 if (Stem::is_invisible (stem))
744 Interval hps = hps_array[j++];
747 Direction d = (hps.center () < max_gap.center ()) ?
750 stem->set_property ("direction", scm_int2num (d));
752 hps.intersect (max_gap);
753 assert (hps.is_empty () || hps.length () < 1e-6 );
760 /* Set stem's shorten property if unset.
763 take some y-position (chord/beam/nearest?) into account
764 scmify forced-fraction
766 This is done in beam because the shorten has to be uniform over the
771 Beam::set_stem_shorten (Grob *me)
774 shortening looks silly for x staff beams
779 Real forced_fraction = 1.0 * forced_stem_count (me)
780 / visible_stem_count (me);
782 int beam_count = get_beam_count (me);
784 SCM shorten_list = me->get_property ("beamed-stem-shorten");
785 if (shorten_list == SCM_EOL)
788 Real staff_space = Staff_symbol_referencer::staff_space (me);
791 robust_list_ref (beam_count -1, shorten_list);
792 Real shorten_f = scm_to_double (shorten_elt) * staff_space;
794 /* your similar cute comment here */
795 shorten_f *= forced_fraction;
798 me->set_property ("shorten", scm_make_real (shorten_f));
801 /* Call list of y-dy-callbacks, that handle setting of
805 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
807 Beam::after_line_breaking (SCM smob)
809 Grob *me = unsmob_grob (smob);
812 return SCM_UNSPECIFIED;
816 Beam::position_beam (Grob *me)
818 if (to_boolean (me->get_property ("positioning-done")))
821 me->set_property ("positioning-done", SCM_BOOL_T);
823 /* Copy to mutable list. */
824 SCM s = ly_deep_copy (me->get_property ("positions"));
825 me->set_property ("positions", s);
827 if (ly_car (s) == SCM_BOOL_F)
829 // one wonders if such genericity is necessary --hwn.
830 SCM callbacks = me->get_property ("position-callbacks");
831 for (SCM i = callbacks; ly_c_pair_p (i); i = ly_cdr (i))
832 scm_call_1 (ly_car (i), me->self_scm ());
835 set_stem_lengths (me);
840 Compute a first approximation to the beam slope.
842 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
844 Beam::least_squares (SCM smob)
846 Grob *me = unsmob_grob (smob);
848 int count = visible_stem_count (me);
853 me->set_property ("positions", ly_interval2scm (pos));
854 return SCM_UNSPECIFIED;
858 Array<Real> x_posns ;
859 Link_array<Grob> stems=
860 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
861 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
862 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
864 Real my_y = me->relative_coordinate (commony, Y_AXIS);
866 Grob *fvs = first_visible_stem (me);
867 Grob *lvs = last_visible_stem (me);
869 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
870 + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
871 Stem::get_stem_info (lvs).ideal_y_
872 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
874 Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
875 for (int i=0; i < stems.size (); i++)
879 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
882 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
891 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
892 Stem::chord_start_y (last_visible_stem (me)));
894 /* Simple beams (2 stems) on middle line should be allowed to be
897 However, if both stems reach middle line,
898 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
900 For that case, we apply artificial slope */
901 if (!ideal[LEFT] && chord.delta () && count == 2)
904 Direction d = (Direction) (sign (chord.delta ()) * UP);
905 pos[d] = get_thickness (me) / 2;
914 For broken beams this doesn't work well. In this case, the
915 slope esp. of the first part of a broken beam should predict
916 where the second part goes.
918 me->set_property ("least-squares-dy",
919 scm_make_real (pos[RIGHT] - pos[LEFT]));
923 Array<Offset> ideals;
924 for (int i=0; i < stems.size (); i++)
927 if (Stem::is_invisible (s))
929 ideals.push (Offset (x_posns[i],
930 Stem::get_stem_info (s).ideal_y_
931 + s->relative_coordinate (commony, Y_AXIS)
935 minimise_least_squares (&slope, &y, ideals);
938 me->set_property ("least-squares-dy", scm_make_real (dy));
939 pos = Interval (y, (y+dy));
943 "position" is relative to the staff.
945 scale_drul (&pos, 1/ Staff_symbol_referencer::staff_space (me));
947 me->set_property ("positions", ly_interval2scm (pos));
949 return SCM_UNSPECIFIED;
954 We can't combine with previous function, since check concave and
955 slope damping comes first.
957 TODO: we should use the concaveness to control the amount of damping
961 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
963 Beam::shift_region_to_valid (SCM grob)
965 Grob *me = unsmob_grob (grob);
969 Array<Real> x_posns ;
970 Link_array<Grob> stems=
971 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
972 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
973 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
975 Grob *fvs = first_visible_stem (me);
978 return SCM_UNSPECIFIED;
980 Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
981 for (int i=0; i < stems.size (); i++)
985 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
989 Grob *lvs = last_visible_stem (me);
991 return SCM_UNSPECIFIED;
993 Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
995 Drul_array<Real> pos = ly_scm2interval ( me->get_property ("positions"));
997 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
999 Real dy = pos[RIGHT] - pos[LEFT];
1005 Shift the positions so that we have a chance of finding good
1006 quants (i.e. no short stem failures.)
1008 Interval feasible_left_point;
1009 feasible_left_point.set_full ();
1010 for (int i=0; i < stems.size (); i++)
1013 if (Stem::is_invisible (s))
1016 Direction d = Stem::get_direction (s);
1019 Stem::get_stem_info (s).shortest_y_
1020 - slope * x_posns [i];
1023 left_y is now relative to the stem S. We want relative to
1024 ourselves, so translate:
1027 + s->relative_coordinate (commony, Y_AXIS)
1028 - me->relative_coordinate (commony, Y_AXIS);
1034 feasible_left_point.intersect (flp);
1037 if (feasible_left_point.is_empty ())
1038 warning (_ ("no viable initial configuration found: may not find good beam slope"));
1039 else if (!feasible_left_point.contains (y))
1041 if (isinf (feasible_left_point[DOWN]))
1042 y = feasible_left_point[UP] - REGION_SIZE;
1043 else if (isinf (feasible_left_point[UP]))
1044 y = feasible_left_point[DOWN]+ REGION_SIZE;
1046 y = feasible_left_point.center ();
1049 pos = Drul_array<Real> (y, (y+dy));
1050 scale_drul (&pos, 1/ Staff_symbol_referencer::staff_space (me));
1052 me->set_property ("positions", ly_interval2scm (pos));
1053 return SCM_UNSPECIFIED;
1056 /* This neat trick is by Werner Lemberg,
1057 damped = tanh (slope)
1058 corresponds with some tables in [Wanske] CHECKME */
1059 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1061 Beam::slope_damping (SCM smob)
1063 Grob *me = unsmob_grob (smob);
1065 if (visible_stem_count (me) <= 1)
1066 return SCM_UNSPECIFIED;
1068 SCM s = me->get_property ("damping");
1069 Real damping = scm_to_double (s);
1073 Drul_array<Real> pos = ly_scm2interval (me->get_property ("positions"));
1074 scale_drul (&pos, Staff_symbol_referencer::staff_space (me));
1076 Real dy = pos[RIGHT] - pos[LEFT];
1078 Grob *fvs = first_visible_stem (me);
1079 Grob *lvs = last_visible_stem (me);
1081 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1084 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1085 - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1087 Real slope = dy && dx ? dy/dx : 0;
1089 Real concaveness = robust_scm2double (me->get_property ("concaveness"), 0.0);
1091 slope = 0.6 * tanh (slope) / (damping + concaveness);
1093 Real damped_dy = slope * dx;
1094 pos[LEFT] += (dy - damped_dy) / 2;
1095 pos[RIGHT] -= (dy - damped_dy) / 2;
1097 scale_drul (&pos, 1/Staff_symbol_referencer::staff_space (me));
1099 me->set_property ("positions", ly_interval2scm (pos));
1101 return SCM_UNSPECIFIED;
1105 Report slice containing the numbers that are both in (car BEAMING)
1109 where_are_the_whole_beams (SCM beaming)
1113 for ( SCM s = ly_car (beaming); ly_c_pair_p (s) ; s = ly_cdr (s))
1115 if (scm_c_memq (ly_car (s), ly_cdr (beaming)) != SCM_BOOL_F)
1117 l.add_point (scm_to_int (ly_car (s)));
1123 /* Return the Y position of the stem-end, given the Y-left, Y-right
1124 in POS for stem S. This Y position is relative to S. */
1126 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1128 Drul_array<Real> pos, bool french)
1130 Real beam_translation = get_beam_translation (me);
1133 Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1134 Real dy = pos[RIGHT] - pos[LEFT];
1136 Real stem_y_beam0 = (dy && dx
1141 Direction my_dir = get_grob_direction (s);
1142 SCM beaming = s->get_property ("beaming");
1144 Real stem_y = stem_y_beam0;
1147 Slice bm = where_are_the_whole_beams (beaming);
1148 if (!bm.is_empty ())
1149 stem_y += beam_translation * bm[-my_dir];
1153 Slice bm = Stem::beam_multiplicity (s);
1154 if (!bm.is_empty ())
1155 stem_y +=bm[my_dir] * beam_translation;
1158 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1159 - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1165 Hmm. At this time, beam position and slope are determined. Maybe,
1166 stem directions and length should set to relative to the chord's
1167 position of the beam. */
1169 Beam::set_stem_lengths (Grob *me)
1171 Link_array<Grob> stems=
1172 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1178 for (int a = 2; a--;)
1179 common[a] = common_refpoint_of_array (stems, me, Axis (a));
1181 Drul_array<Real> pos = ly_scm2realdrul (me->get_property ("positions"));
1182 Real staff_space = Staff_symbol_referencer::staff_space (me);
1183 scale_drul (&pos, staff_space);
1187 if (scm_is_number (me->get_property ("gap-count"))
1188 &&scm_to_int (me->get_property ("gap-count")))
1191 thick = get_thickness (me);
1194 // ugh -> use commonx
1195 Grob * fvs = first_visible_stem (me);
1196 Grob *lvs = last_visible_stem (me);
1198 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1199 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1201 for (int i=0; i < stems.size (); i++)
1204 if (Stem::is_invisible (s))
1207 bool french = to_boolean (s->get_property ("french-beaming"));
1208 Real stem_y = calc_stem_y (me, s, common,
1210 pos, french && s != lvs && s!= fvs);
1213 Make the stems go up to the end of the beam. This doesn't matter
1214 for normal beams, but for tremolo beams it looks silly otherwise.
1217 stem_y += thick * 0.5 * get_grob_direction (s);
1219 Stem::set_stemend (s, 2* stem_y / staff_space);
1224 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1226 Link_array<Grob> stems=
1227 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1230 for (int i=0; i < stems.size (); i++)
1233 Don't overwrite user settings.
1238 /* Don't set beaming for outside of outer stems */
1239 if ( (d == LEFT && i == 0)
1240 || (d == RIGHT && i == stems.size () -1))
1243 Grob *st = stems[i];
1244 SCM beaming_prop = st->get_property ("beaming");
1245 if (beaming_prop == SCM_EOL ||
1246 index_get_cell (beaming_prop, d) == SCM_EOL)
1248 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1250 && i < stems.size () -1
1251 && Stem::is_invisible (st))
1252 b = b <? beaming->infos_.elem (i).beams_i_drul_[-d];
1254 Stem::set_beaming (st, b, d);
1257 while (flip (&d) != LEFT);
1262 Beam::forced_stem_count (Grob *me)
1264 Link_array<Grob>stems =
1265 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1267 for (int i=0; i < stems.size (); i++)
1271 if (Stem::is_invisible (s))
1274 /* I can imagine counting those boundaries as a half forced stem,
1275 but let's count them full for now. */
1276 if (abs (Stem::chord_start_y (s)) > 0.1
1277 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1287 Beam::visible_stem_count (Grob *me)
1289 Link_array<Grob>stems =
1290 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1292 for (int i = stems.size (); i--;)
1294 if (!Stem::is_invisible (stems[i]))
1301 Beam::first_visible_stem (Grob *me)
1303 Link_array<Grob>stems =
1304 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1306 for (int i = 0; i < stems.size (); i++)
1308 if (!Stem::is_invisible (stems[i]))
1315 Beam::last_visible_stem (Grob *me)
1317 Link_array<Grob>stems =
1318 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1319 for (int i = stems.size (); i--;)
1321 if (!Stem::is_invisible (stems[i]))
1331 handle rest under beam (do_post: beams are calculated now)
1332 what about combination of collisions and rest under beam.
1336 rest -> stem -> beam -> interpolate_y_position ()
1338 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1340 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1342 Grob *rest = unsmob_grob (element_smob);
1343 Axis a = (Axis) scm_to_int (axis);
1345 if (scm_is_number (rest->get_property ("staff-position")))
1346 return scm_int2num (0);
1348 assert (a == Y_AXIS);
1350 Grob *st = unsmob_grob (rest->get_property ("stem"));
1353 return scm_make_real (0.0);
1354 Grob *beam = unsmob_grob (stem->get_property ("beam"));
1356 || !Beam::has_interface (beam)
1357 || !Beam::visible_stem_count (beam))
1358 return scm_make_real (0.0);
1360 Drul_array<Real> pos (0, 0);
1361 SCM s = beam->get_property ("positions");
1362 if (ly_c_pair_p (s) && scm_is_number (ly_car (s)))
1363 pos = ly_scm2interval (s);
1364 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1366 scale_drul (&pos, staff_space);
1369 Real dy = pos[RIGHT] - pos[LEFT];
1371 // ugh -> use commonx
1372 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1373 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1374 Real slope = dy && dx ? dy/dx : 0;
1376 Direction d = Stem::get_direction (stem);
1377 Real stem_y = pos[LEFT] + (stem->relative_coordinate (0, X_AXIS) - x0) * slope;
1379 Real beam_translation = get_beam_translation (beam);
1380 Real beam_thickness = Beam::get_thickness (beam);
1382 int beam_count = get_direction_beam_count (beam, d);
1383 Real height_of_my_beams = beam_thickness / 2
1384 + (beam_count - 1) * beam_translation;
1385 Real beam_y = stem_y - d * height_of_my_beams;
1387 Grob *common_y = rest->common_refpoint (beam, Y_AXIS);
1389 Real rest_dim = rest->extent (common_y, Y_AXIS)[d];
1390 Real minimum_distance =
1391 staff_space * robust_scm2double (rest->get_property ("minimum-distance"), 0.0);
1393 Real shift = d * ( ((beam_y - d * minimum_distance) - rest_dim) * d <? 0.0);
1395 shift /= staff_space;
1396 Real rad = Staff_symbol_referencer::line_count (rest) * staff_space / 2;
1398 /* Always move discretely by half spaces */
1399 shift = ceil (fabs (shift * 2.0)) / 2.0 * sign (shift);
1401 /* Inside staff, move by whole spaces*/
1402 if ( (rest->extent (common_y, Y_AXIS)[d] + staff_space * shift) * d
1404 || (rest->extent (common_y, Y_AXIS)[-d] + staff_space * shift) * -d
1406 shift = ceil (fabs (shift)) *sign (shift);
1408 return scm_make_real (staff_space * shift);
1412 Beam::is_knee (Grob* me)
1414 SCM k = me->get_property ("knee");
1415 if (scm_is_bool (k))
1416 return ly_scm2bool (k);
1420 for (SCM s = me->get_property ("stems"); ly_c_pair_p (s); s = ly_cdr (s))
1422 Direction dir = get_grob_direction (unsmob_grob (ly_car (s)));
1431 me->set_property ("knee", ly_bool2scm (knee));
1437 Beam::get_direction_beam_count (Grob *me, Direction d )
1439 Link_array<Grob>stems =
1440 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1443 for (int i = stems.size (); i--;)
1446 Should we take invisible stems into account?
1448 if (Stem::get_direction (stems[i]) == d)
1449 bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1456 ADD_INTERFACE (Beam, "beam-interface",
1458 "The @code{thickness} property is the weight of beams, and is measured "
1461 "knee positioning-done position-callbacks "
1462 "concaveness dir-function quant-score auto-knee-gap gap "
1463 "gap-count chord-tremolo beamed-stem-shorten shorten least-squares-dy "
1464 "damping inspect-quants flag-width-function neutral-direction positions space-function "