2 beam.cc -- implement Beam
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2003 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 Jan Nieuwenhuizen <janneke@gnu.org>
13 * Use Number_pair i.s.o Interval to represent (yl, yr).
15 - Determine auto knees based on positions if it's set by the user.
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.
31 #include "molecule.hh"
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"
48 bool debug_beam_quanting_flag;
52 #include "text-item.hh" // debug output.
53 #include "font-interface.hh" // debug output.
58 Beam::add_stem (Grob *me, Grob *s)
60 Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s);
62 s->add_dependency (me);
64 assert (!Stem::get_beam (s));
65 s->set_grob_property ("beam", me->self_scm ());
67 add_bound_item (dynamic_cast<Spanner*> (me), dynamic_cast<Item*> (s));
72 Beam::get_thickness (Grob * me)
74 SCM th = me->get_grob_property ("thickness");
76 return gh_scm2double (th)* Staff_symbol_referencer::staff_space (me);
81 /* Return the translation between 2 adjoining beams. */
83 Beam::get_beam_translation (Grob *me)
85 SCM func = me->get_grob_property ("space-function");
86 SCM s = gh_call2 (func, me->self_scm (), scm_int2num (get_beam_count (me)));
87 return gh_scm2double (s);
90 /* Maximum beam_count. */
92 Beam::get_beam_count (Grob *me)
95 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
97 Grob *stem = unsmob_grob (ly_car (s));
98 m = m >? (Stem::beam_multiplicity (stem).length () + 1);
105 Space return space between beams.
107 MAKE_SCHEME_CALLBACK (Beam, space_function, 2);
109 Beam::space_function (SCM smob, SCM beam_count)
111 Grob *me = unsmob_grob (smob);
113 Real staff_space = Staff_symbol_referencer::staff_space (me);
114 Real line = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
115 Real thickness = get_thickness (me);
117 Real beam_translation = gh_scm2int (beam_count) < 4
118 ? (2*staff_space + line - thickness) / 2.0
119 : (3*staff_space + line - thickness) / 3.0;
121 return gh_double2scm (beam_translation);
125 /* After pre-processing all directions should be set.
126 Several post-processing routines (stem, slur, script) need stem/beam
128 Currenly, this means that beam has set all stem's directions.
129 [Alternatively, stems could set its own directions, according to
130 their beam, during 'final-pre-processing'.] */
131 MAKE_SCHEME_CALLBACK (Beam, before_line_breaking, 1);
133 Beam::before_line_breaking (SCM smob)
135 Grob *me = unsmob_grob (smob);
137 /* Beams with less than 2 two stems don't make much sense, but could happen
142 For a beam that only has one stem, we try to do some disappearance magic:
143 we revert the flag, and move on to The Eternal Engraving Fields. */
145 int count = visible_stem_count (me);
148 me->warning (_ ("beam has less than two visible stems"));
150 SCM stems = me->get_grob_property ("stems");
151 if (scm_ilength (stems) == 1)
153 me->warning (_ ("Beam has less than two stems. Removing beam."));
155 unsmob_grob (gh_car (stems))->set_grob_property ("beam", SCM_EOL);
158 return SCM_UNSPECIFIED;
160 else if (scm_ilength (stems) == 0)
163 return SCM_UNSPECIFIED;
168 Direction d = get_default_dir (me);
170 consider_auto_knees (me);
171 set_stem_directions (me, d);
175 set_stem_shorten (me);
183 We want a maximal number of shared beams, but if there is choice, we
184 take the one that is closest to the end of the stem. This is for situations like
197 position_with_maximal_common_beams (SCM left_beaming, SCM right_beaming,
201 Slice lslice = int_list_to_slice (gh_cdr (left_beaming));
205 for (int i = lslice[-left_dir];
206 (i - lslice[left_dir])* left_dir <= 0 ; i+= left_dir)
209 for ( SCM s = gh_car (right_beaming); gh_pair_p (s); s = gh_cdr (s))
211 int k = - right_dir * gh_scm2int (gh_car (s)) + i;
212 if (scm_memq (scm_int2num (k), left_beaming) != SCM_BOOL_F)
216 if (count >= best_count)
227 Beam::connect_beams (Grob *me)
229 Link_array<Grob> stems=
230 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
233 last_int.set_empty();
234 SCM last_beaming = SCM_EOL;
235 Direction last_dir = CENTER;
236 for (int i = 0; i< stems.size(); i++)
238 Grob *this_stem = stems[i];
239 SCM this_beaming = this_stem->get_grob_property ("beaming");
241 Direction this_dir = get_grob_direction (this_stem);
242 if (gh_pair_p (last_beaming) && gh_pair_p (this_beaming))
244 int start_point = position_with_maximal_common_beams
245 (last_beaming, this_beaming,
252 if (d == RIGHT && i == stems.size()-1)
255 new_slice.set_empty();
256 SCM s = index_get_cell (this_beaming, d);
257 for (; gh_pair_p (s); s = gh_cdr (s))
260 start_point - this_dir * gh_scm2int (gh_car (s));
262 new_slice.add_point (new_beam_pos);
263 gh_set_car_x (s, scm_int2num (new_beam_pos));
268 while (flip (&d) != LEFT);
270 if (!new_slice.is_empty ())
271 last_int = new_slice;
275 gh_set_car_x ( this_beaming, SCM_EOL);
276 SCM s = gh_cdr (this_beaming);
277 for (; gh_pair_p (s); s = gh_cdr (s))
279 int np = - this_dir * gh_scm2int (gh_car(s));
280 gh_set_car_x (s, scm_int2num (np));
281 last_int.add_point (np);
285 if (i == stems.size () -1)
287 gh_set_cdr_x (this_beaming, SCM_EOL);
290 if (scm_ilength (gh_cdr (this_beaming)) > 0)
292 last_beaming = this_beaming;
298 MAKE_SCHEME_CALLBACK (Beam, brew_molecule, 1);
300 Beam::brew_molecule (SCM grob)
302 Grob *me = unsmob_grob (grob);
303 Link_array<Grob> stems=
304 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
305 Grob* xcommon = common_refpoint_of_array (stems, me, X_AXIS);
308 if (visible_stem_count (me))
310 // ugh -> use commonx
311 x0 = first_visible_stem (me)->relative_coordinate (xcommon, X_AXIS);
312 dx = last_visible_stem (me)->relative_coordinate (xcommon, X_AXIS) - x0;
316 x0 = stems[0]->relative_coordinate (xcommon, X_AXIS);
317 dx = stems.top ()->relative_coordinate (xcommon, X_AXIS) - x0;
320 SCM posns = me->get_grob_property ("positions");
322 if (!is_number_pair (posns))
324 programming_error ("No beam posns");
325 pos = Interval (0,0);
328 pos= ly_scm2interval (posns);
330 Real dy = pos.delta ();
331 Real dydx = (dy && dx) ? dy/dx : 0;
333 Real thick = get_thickness (me);
334 Real bdy = get_beam_translation (me);
336 SCM last_beaming = SCM_EOL;;
337 Real last_xposn = -1;
338 Real last_width = -1 ;
340 Real gap_length =0.0;
341 SCM scm_gap = me->get_grob_property ("gap");
342 if (gh_number_p (scm_gap))
343 gap_length = gh_scm2double (scm_gap);
346 Real lt = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
348 for (int i = 0; i<= stems.size(); i++)
350 Grob * st = (i < stems.size()) ? stems[i] : 0;
352 SCM this_beaming = st ? st->get_grob_property ("beaming") : SCM_EOL;
353 Real xposn = st ? st->relative_coordinate (xcommon, X_AXIS) : 0.0;
354 Real stem_width = st ? gh_scm2double (st->get_grob_property ("thickness")) *lt : 0 ;
355 Direction stem_dir = st ? to_dir (st->get_grob_property ("direction")) : CENTER;
357 We do the space left of ST, with lfliebertjes pointing to the
358 right from the left stem, and rfliebertjes pointing left from
361 SCM left = (i>0) ? gh_cdr (last_beaming) : SCM_EOL;
362 SCM right = st ? gh_car (this_beaming) : SCM_EOL;
364 Array<int> full_beams;
365 Array<int> lfliebertjes;
366 Array<int> rfliebertjes;
369 gh_pair_p (s); s =gh_cdr (s))
371 int b = gh_scm2int (gh_car (s));
372 if (scm_memq (gh_car(s), right) != SCM_BOOL_F)
378 lfliebertjes.push (b);
382 gh_pair_p (s); s =gh_cdr (s))
384 int b = gh_scm2int (gh_car (s));
385 if (scm_memq (gh_car(s), left) == SCM_BOOL_F)
387 rfliebertjes.push (b);
392 how much to stick out for beams across linebreaks
394 Real break_overshoot = 3.0;
395 Real w = (i>0 && st)? xposn - last_xposn : break_overshoot;
396 Real stem_offset = 0.0;
397 Real width_corr = 0.0;
400 stem_offset -= last_width/2;
401 width_corr += last_width/2;
404 if (i == stems.size() -1)
406 width_corr += stem_width/2;
410 Molecule whole = Lookup::beam (dydx, w + width_corr, thick);
414 if (gh_number_p (me->get_grob_property ("gap-count")))
416 gap_count = gh_scm2int (me->get_grob_property ("gap-count"));
417 gapped = Lookup::beam (dydx, w + width_corr - 2 * gap_length, thick);
419 full_beams.sort (default_compare);
421 full_beams.reverse ();
425 for (int j = full_beams.size (); j--;)
432 b.translate_axis (gap_length, X_AXIS);
434 b.translate_axis (last_xposn - x0 + stem_offset, X_AXIS);
435 b.translate_axis (dydx * (last_xposn - x0) + bdy * full_beams[j], Y_AXIS);
437 the_beam.add_molecule (b);
442 if (lfliebertjes.size() || rfliebertjes.size())
448 int t = Stem::duration_log (st);
450 SCM proc = me->get_grob_property ("flag-width-function");
451 SCM result = gh_call1 (proc, scm_int2num (t));
452 nw_f = gh_scm2double (result);
455 nw_f = break_overshoot;
457 /* Half beam should be one note-width,
458 but let's make sure two half-beams never touch */
459 Real w = (i>0 && st) ? (xposn - last_xposn) : break_overshoot;
462 Molecule half = Lookup::beam (dydx, w, thick);
463 for (int j = lfliebertjes.size(); j--;)
466 b.translate_axis (last_xposn - x0, X_AXIS);
467 b.translate_axis (dydx * (last_xposn-x0) + bdy * lfliebertjes[j], Y_AXIS);
468 the_beam.add_molecule (b);
470 for (int j = rfliebertjes.size(); j--;)
473 b.translate_axis (xposn - x0 - w , X_AXIS);
474 b.translate_axis (dydx * (xposn-x0 -w) + bdy * rfliebertjes[j], Y_AXIS);
475 the_beam.add_molecule (b);
481 last_width = stem_width;
482 last_beaming = this_beaming;
485 the_beam.translate_axis (x0 - me->relative_coordinate (xcommon, X_AXIS), X_AXIS);
486 the_beam.translate_axis (pos[LEFT], Y_AXIS);
489 SCM quant_score = me->get_grob_property ("quant-score");
490 if (debug_beam_quanting_flag
491 && gh_string_p (quant_score))
495 This code prints the demerits for each beam. Perhaps this
496 should be switchable for those who want to twiddle with the
500 SCM properties = Font_interface::font_alist_chain (me);
502 Molecule tm = *unsmob_molecule (Text_item::interpret_markup
503 (me->get_paper ()->self_scm (), properties, quant_score));
504 the_beam.add_at_edge (Y_AXIS, UP, tm, 5.0, 0);
510 return the_beam.smobbed_copy();
517 Beam::get_default_dir (Grob *me)
519 Drul_array<int> total;
520 total[UP] = total[DOWN] = 0;
521 Drul_array<int> count;
522 count[UP] = count[DOWN] = 0;
525 Link_array<Grob> stems=
526 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
528 for (int i=0; i <stems.size (); i++)
531 Direction sd = get_grob_direction (s);
533 int center_distance = int(- d * Stem::head_positions (s) [-d]) >? 0;
534 int current = sd ? (1 + d * sd)/2 : center_distance;
541 } while (flip (&d) != DOWN);
543 SCM func = me->get_grob_property ("dir-function");
544 SCM s = gh_call2 (func,
545 gh_cons (scm_int2num (count[UP]),
546 scm_int2num (count[DOWN])),
547 gh_cons (scm_int2num (total[UP]),
548 scm_int2num (total[DOWN])));
550 if (gh_number_p (s) && gh_scm2int (s))
553 /* If dir is not determined: get default */
554 return to_dir (me->get_grob_property ("neutral-direction"));
558 /* Set all stems with non-forced direction to beam direction.
559 Urg: non-forced should become `without/with unforced' direction,
560 once stem gets cleaned-up. */
562 Beam::set_stem_directions (Grob *me, Direction d)
564 Link_array<Grob> stems
565 =Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
567 for (int i=0; i <stems.size (); i++)
571 SCM forcedir = s->get_grob_property ("direction");
572 if (!to_dir (forcedir))
573 set_grob_direction (s, d);
578 A union of intervals in the real line.
580 Abysmal performance (quadratic) for large N, hopefully we don't have
581 that large N. In any case, this should probably be rewritten to use
586 Array<Interval> allowed_regions_;
595 allowed_regions_.clear();
598 allowed_regions_.push (s);
601 void remove_interval (Interval rm)
603 for (int i = 0; i < allowed_regions_.size(); )
607 s.intersect (allowed_regions_[i]);
611 Interval before = allowed_regions_[i];
612 Interval after = allowed_regions_[i];
614 before[RIGHT] = s[LEFT];
615 after[LEFT] = s[RIGHT];
617 if (!before.is_empty () && before.length () > 0.0)
619 allowed_regions_.insert (before, i);
622 allowed_regions_.del (i);
623 if (!after.is_empty () && after.length () > 0.0)
625 allowed_regions_.insert (after, i);
637 Only try horizontal beams for knees. No reliable detection of
638 anything else is possible here, since we don't know funky-beaming
639 settings, or X-distances (slopes!) People that want sloped
640 knee-beams, should set the directions manually.
643 Beam::consider_auto_knees (Grob* me)
645 SCM scm = me->get_grob_property ("auto-knee-gap");
646 if (!gh_number_p (scm))
649 Real threshold = gh_scm2double (scm);
655 Link_array<Grob> stems=
656 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
658 Grob *common = common_refpoint_of_array (stems, me, Y_AXIS);
659 Real staff_space = Staff_symbol_referencer::staff_space (me);
661 Array<Interval> hps_array;
662 for (int i=0; i < stems.size (); i++)
664 Grob* stem = stems[i];
665 if (Stem::invisible_b (stem))
668 Interval hps = Stem::head_positions (stem);
673 hps *= staff_space * 0.5 ;
676 We could subtract beam Y position, but this routine only
677 sets stem directions, a constant shift does not have an
681 hps += stem->relative_coordinate (common, Y_AXIS);
683 if (to_dir (stem->get_grob_property ("direction")))
685 Direction stemdir = to_dir (stem->get_grob_property ("direction"));
686 hps[-stemdir] = - stemdir * infinity_f;
689 hps_array.push (hps);
691 gaps.remove_interval (hps);
695 Real max_gap_len =0.0;
697 for (int i = gaps.allowed_regions_.size() -1; i >= 0 ; i--)
699 Interval gap = gaps.allowed_regions_[i];
702 the outer gaps are not knees.
704 if (isinf (gap[LEFT]) || isinf(gap[RIGHT]))
707 if (gap.length () >= max_gap_len)
709 max_gap_len = gap.length();
714 if (max_gap_len > threshold)
717 for (int i = 0; i < stems.size(); i++)
719 Grob* stem = stems[i];
720 if (Stem::invisible_b (stem))
723 Interval hps = hps_array[j++];
726 Direction d = (hps.center () < max_gap.center()) ?
729 stem->set_grob_property ("direction", scm_int2num (d));
731 hps.intersect (max_gap);
732 assert (hps.is_empty () || hps.length () < 1e-6 );
739 /* Set stem's shorten property if unset.
742 take some y-position (chord/beam/nearest?) into account
743 scmify forced-fraction
745 This is done in beam because the shorten has to be uniform over the
750 Beam::set_stem_shorten (Grob *me)
753 shortening looks silly for x staff beams
758 Real forced_fraction = 1.0 * forced_stem_count (me)
759 / visible_stem_count (me);
761 int beam_count = get_beam_count (me);
763 SCM shorten_list = me->get_grob_property ("beamed-stem-shorten");
764 if (shorten_list == SCM_EOL)
767 Real staff_space = Staff_symbol_referencer::staff_space (me);
770 robust_list_ref (beam_count -1, shorten_list);
771 Real shorten_f = gh_scm2double (shorten_elt) * staff_space;
773 /* your similar cute comment here */
774 shorten_f *= forced_fraction;
777 me->set_grob_property ("shorten", gh_double2scm (shorten_f));
780 /* Call list of y-dy-callbacks, that handle setting of
784 MAKE_SCHEME_CALLBACK (Beam, after_line_breaking, 1);
786 Beam::after_line_breaking (SCM smob)
788 Grob *me = unsmob_grob (smob);
790 /* Copy to mutable list. */
791 SCM s = ly_deep_copy (me->get_grob_property ("positions"));
792 me->set_grob_property ("positions", s);
794 if (ly_car (s) == SCM_BOOL_F)
797 // one wonders if such genericity is necessary --hwn.
798 SCM callbacks = me->get_grob_property ("position-callbacks");
799 for (SCM i = callbacks; gh_pair_p (i); i = ly_cdr (i))
800 gh_call1 (ly_car (i), smob);
803 set_stem_lengths (me);
804 return SCM_UNSPECIFIED;
809 Compute a first approximation to the beam slope.
811 MAKE_SCHEME_CALLBACK (Beam, least_squares, 1);
813 Beam::least_squares (SCM smob)
815 Grob *me = unsmob_grob (smob);
817 int count = visible_stem_count (me);
822 me->set_grob_property ("positions", ly_interval2scm (pos));
823 return SCM_UNSPECIFIED;
827 Array<Real> x_posns ;
828 Link_array<Grob> stems=
829 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
830 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
831 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
833 Real my_y = me->relative_coordinate (commony, Y_AXIS);
835 Grob *fvs = first_visible_stem (me);
836 Grob *lvs = last_visible_stem (me);
838 Interval ideal (Stem::get_stem_info (fvs).ideal_y_
839 + fvs->relative_coordinate (commony, Y_AXIS) -my_y,
840 Stem::get_stem_info (lvs).ideal_y_
841 + lvs->relative_coordinate (commony, Y_AXIS) - my_y);
843 Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
844 for (int i=0; i < stems.size (); i++)
848 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
851 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS) - x0;
860 Interval chord (Stem::chord_start_y (first_visible_stem (me)),
861 Stem::chord_start_y (last_visible_stem (me)));
863 /* Simple beams (2 stems) on middle line should be allowed to be
866 However, if both stems reach middle line,
867 ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0.
869 For that case, we apply artificial slope */
870 if (!ideal[LEFT] && chord.delta () && count == 2)
873 Direction d = (Direction) (sign (chord.delta ()) * UP);
874 pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2;
883 For broken beams this doesn't work well. In this case, the
884 slope esp. of the first part of a broken beam should predict
885 where the second part goes.
897 Array<Offset> ideals;
898 for (int i=0; i < stems.size (); i++)
901 if (Stem::invisible_b (s))
903 ideals.push (Offset (x_posns[i],
904 Stem::get_stem_info (s).ideal_y_
905 + s->relative_coordinate (commony, Y_AXIS)
909 minimise_least_squares (&dydx, &y, ideals);
912 me->set_grob_property ("least-squares-dy", gh_double2scm (dy));
913 pos = Interval (y, (y+dy));
916 me->set_grob_property ("positions", ly_interval2scm (pos));
918 return SCM_UNSPECIFIED;
923 We can't combine with previous function, since check concave and
924 slope damping comes first.
926 TODO: we should use the concaveness to control the amount of damping
930 MAKE_SCHEME_CALLBACK (Beam, shift_region_to_valid, 1);
932 Beam::shift_region_to_valid (SCM grob)
934 Grob *me = unsmob_grob (grob);
938 Array<Real> x_posns ;
939 Link_array<Grob> stems=
940 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
941 Grob *commonx = common_refpoint_of_array (stems, me, X_AXIS);
942 Grob *commony = common_refpoint_of_array (stems, me, Y_AXIS);
944 Grob *fvs = first_visible_stem (me);
947 return SCM_UNSPECIFIED;
949 Real x0 =fvs->relative_coordinate (commonx, X_AXIS);
950 for (int i=0; i < stems.size (); i++)
954 Real x = s->relative_coordinate (commonx, X_AXIS) - x0;
958 Grob *lvs = last_visible_stem (me);
960 return SCM_UNSPECIFIED;
962 Real dx = lvs->relative_coordinate (commonx, X_AXIS) - x0;
964 Interval pos = ly_scm2interval ( me->get_grob_property ("positions"));
965 Real dy = pos.delta();
971 Shift the positions so that we have a chance of finding good
972 quants (i.e. no short stem failures.)
974 Interval feasible_left_point;
975 feasible_left_point.set_full ();
976 for (int i=0; i < stems.size (); i++)
979 if (Stem::invisible_b (s))
982 Direction d = Stem::get_direction (s);
985 Stem::get_stem_info (s).shortest_y_
986 - dydx * x_posns [i];
989 left_y is now relative to the stem S. We want relative to
990 ourselves, so translate:
993 + s->relative_coordinate (commony, Y_AXIS)
994 - me->relative_coordinate (commony, Y_AXIS);
1000 feasible_left_point.intersect (flp);
1003 if (feasible_left_point.is_empty ())
1005 warning (_("Not sure that we can find a nice beam slope (no viable initial configuration found)."));
1007 else if (!feasible_left_point.elem_b(y))
1009 if (isinf (feasible_left_point[DOWN]))
1010 y = feasible_left_point[UP] - REGION_SIZE;
1011 else if (isinf (feasible_left_point[UP]))
1012 y = feasible_left_point[DOWN]+ REGION_SIZE;
1014 y = feasible_left_point.center ();
1016 pos = Interval (y, (y+dy));
1017 me->set_grob_property ("positions", ly_interval2scm (pos));
1018 return SCM_UNSPECIFIED;
1022 MAKE_SCHEME_CALLBACK (Beam, check_concave, 1);
1024 Beam::check_concave (SCM smob)
1026 Grob *me = unsmob_grob (smob);
1028 Link_array<Grob> stems =
1029 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1031 for (int i = 0; i < stems.size ();)
1033 if (Stem::invisible_b (stems[i]))
1039 if (stems.size () < 3)
1040 return SCM_UNSPECIFIED;
1043 /* Concaveness #1: If distance of an inner notehead to line between
1044 two outer noteheads is bigger than CONCAVENESS-GAP (2.0ss),
1045 beam is concave (Heinz Stolba).
1047 In the case of knees, the line connecting outer heads is often
1048 not related to the beam slope (it may even go in the other
1049 direction). Skip the check when the outer stems point in
1050 different directions. --hwn
1053 bool concaveness1 = false;
1054 SCM gap = me->get_grob_property ("concaveness-gap");
1055 if (gh_number_p (gap)
1056 && Stem::get_direction(stems.top ())
1057 == Stem::get_direction(stems[0]))
1059 Real r1 = gh_scm2double (gap);
1060 Real dy = Stem::chord_start_y (stems.top ())
1061 - Stem::chord_start_y (stems[0]);
1064 Real slope = dy / (stems.size () - 1);
1066 Real y0 = Stem::chord_start_y (stems[0]);
1067 for (int i = 1; i < stems.size () - 1; i++)
1069 Real c = (Stem::chord_start_y (stems[i]) - y0) - i * slope;
1072 concaveness1 = true;
1079 /* Concaveness #2: Sum distances of inner noteheads that fall
1080 outside the interval of the two outer noteheads.
1082 We only do this for beams where first and last stem have the same
1086 Note that "convex" stems compensate for "concave" stems.
1087 (is that intentional?) --hwn.
1090 Real concaveness2 = 0;
1091 SCM thresh = me->get_grob_property ("concaveness-threshold");
1092 Real r2 = infinity_f;
1093 if (!concaveness1 && gh_number_p (thresh)
1094 && Stem::get_direction(stems.top ())
1095 == Stem::get_direction(stems[0]))
1097 r2 = gh_scm2double (thresh);
1099 Direction dir = Stem::get_direction(stems.top ());
1101 Interval iv (Stem::chord_start_y (stems[0]),
1102 Stem::chord_start_y (stems.top ()));
1104 if (iv[MAX] < iv[MIN])
1107 for (int i = 1; i < stems.size () - 1; i++)
1109 Real f = Stem::chord_start_y (stems[i]);
1110 concave += ((f - iv[MAX] ) >? 0) +
1111 ((f - iv[MIN] ) <? 0);
1114 concaveness2 = concave / (stems.size () - 2);
1118 ugh: this is the a kludge to get
1119 input/regression/beam-concave.ly to behave as
1125 huh? we're dividing twice (which is not scalable) meaning that
1126 the longer the beam, the more unlikely it will be
1127 concave. Maybe you would even expect the other way around??
1132 concaveness2 /= (stems.size () - 2);
1135 /* TODO: some sort of damping iso -> plain horizontal */
1136 if (concaveness1 || concaveness2 > r2)
1138 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1139 Real r = pos.linear_combination (0);
1140 me->set_grob_property ("positions", ly_interval2scm (Interval (r, r)));
1141 me->set_grob_property ("least-squares-dy", gh_double2scm (0));
1144 return SCM_UNSPECIFIED;
1147 /* This neat trick is by Werner Lemberg,
1148 damped = tanh (slope)
1149 corresponds with some tables in [Wanske] CHECKME */
1150 MAKE_SCHEME_CALLBACK (Beam, slope_damping, 1);
1152 Beam::slope_damping (SCM smob)
1154 Grob *me = unsmob_grob (smob);
1156 if (visible_stem_count (me) <= 1)
1157 return SCM_UNSPECIFIED;
1159 SCM s = me->get_grob_property ("damping");
1160 int damping = gh_scm2int (s);
1164 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1165 Real dy = pos.delta ();
1167 Grob *fvs = first_visible_stem (me);
1168 Grob *lvs = last_visible_stem (me);
1170 Grob *commonx = fvs->common_refpoint (lvs, X_AXIS);
1173 Real dx = last_visible_stem (me)->relative_coordinate (commonx, X_AXIS)
1174 - first_visible_stem (me)->relative_coordinate (commonx, X_AXIS);
1175 Real dydx = dy && dx ? dy/dx : 0;
1176 dydx = 0.6 * tanh (dydx) / damping;
1178 Real damped_dy = dydx * dx;
1179 pos[LEFT] += (dy - damped_dy) / 2;
1180 pos[RIGHT] -= (dy - damped_dy) / 2;
1182 me->set_grob_property ("positions", ly_interval2scm (pos));
1184 return SCM_UNSPECIFIED;
1188 Report slice containing the numbers that are both in (car BEAMING)
1192 where_are_the_whole_beams(SCM beaming)
1196 for( SCM s = gh_car (beaming); gh_pair_p (s) ; s = gh_cdr (s))
1198 if (scm_memq (gh_car (s), gh_cdr (beaming)) != SCM_BOOL_F)
1200 l.add_point (gh_scm2int (gh_car (s)));
1206 /* Return the Y position of the stem-end, given the Y-left, Y-right
1207 in POS for stem S. This Y position is relative to S. */
1209 Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common,
1211 Interval pos, bool french)
1213 Real beam_translation = get_beam_translation (me);
1216 Real r = s->relative_coordinate (common[X_AXIS], X_AXIS) - xl;
1217 Real dy = pos.delta ();
1219 Real stem_y_beam0 = (dy && dx
1224 Direction my_dir = get_grob_direction (s);
1225 SCM beaming = s->get_grob_property ("beaming");
1227 Real stem_y = stem_y_beam0;
1230 Slice bm = where_are_the_whole_beams (beaming);
1231 if (!bm.is_empty ())
1232 stem_y += beam_translation * bm[-my_dir];
1236 Slice bm = Stem::beam_multiplicity(s);
1237 if (!bm.is_empty ())
1238 stem_y +=bm[my_dir] * beam_translation;
1241 Real id = me->relative_coordinate (common[Y_AXIS], Y_AXIS)
1242 - s->relative_coordinate (common[Y_AXIS], Y_AXIS);
1248 Hmm. At this time, beam position and slope are determined. Maybe,
1249 stem directions and length should set to relative to the chord's
1250 position of the beam. */
1252 Beam::set_stem_lengths (Grob *me)
1254 Link_array<Grob> stems=
1255 Pointer_group_interface__extract_grobs (me, (Grob*)0, "stems");
1261 for (int a = 2; a--;)
1262 common[a] = common_refpoint_of_array (stems, me, Axis(a));
1264 Interval pos = ly_scm2interval (me->get_grob_property ("positions"));
1265 Real staff_space = Staff_symbol_referencer::staff_space (me);
1269 if (gh_number_p (me->get_grob_property ("gap-count"))
1270 &&gh_scm2int (me->get_grob_property ("gap-count")))
1273 thick = get_thickness(me);
1276 // ugh -> use commonx
1277 Grob * fvs = first_visible_stem (me);
1278 Grob *lvs = last_visible_stem (me);
1280 Real xl = fvs ? fvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1281 Real xr = lvs ? lvs->relative_coordinate (common[X_AXIS], X_AXIS) : 0.0;
1283 for (int i=0; i < stems.size (); i++)
1286 if (Stem::invisible_b (s))
1289 bool french = to_boolean (s->get_grob_property ("french-beaming"));
1290 Real stem_y = calc_stem_y (me, s, common,
1292 pos, french && s != lvs && s!= fvs);
1295 Make the stems go up to the end of the beam. This doesn't matter
1296 for normal beams, but for tremolo beams it looks silly otherwise.
1299 stem_y += thick * 0.5 * get_grob_direction (s);
1301 Stem::set_stemend (s, 2* stem_y / staff_space);
1306 Beam::set_beaming (Grob *me, Beaming_info_list *beaming)
1308 Link_array<Grob> stems=
1309 Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems");
1312 for (int i=0; i < stems.size (); i++)
1315 Don't overwrite user settings.
1320 /* Don't set beaming for outside of outer stems */
1321 if ((d == LEFT && i == 0)
1322 ||(d == RIGHT && i == stems.size () -1))
1325 Grob *st = stems[i];
1326 SCM beaming_prop = st->get_grob_property ("beaming");
1327 if (beaming_prop == SCM_EOL ||
1328 index_get_cell (beaming_prop, d) == SCM_EOL)
1330 int b = beaming->infos_.elem (i).beams_i_drul_[d];
1332 && i < stems.size() -1
1333 && Stem::invisible_b (st))
1334 b = b <? beaming->infos_.elem(i).beams_i_drul_[-d];
1336 Stem::set_beaming (st, b, d);
1339 while (flip (&d) != LEFT);
1344 Beam::forced_stem_count (Grob *me)
1346 Link_array<Grob>stems =
1347 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1349 for (int i=0; i < stems.size (); i++)
1353 if (Stem::invisible_b (s))
1356 /* I can imagine counting those boundaries as a half forced stem,
1357 but let's count them full for now. */
1358 if (abs (Stem::chord_start_y (s)) > 0.1
1359 && (Stem::get_direction (s) != Stem::get_default_dir (s)))
1369 Beam::visible_stem_count (Grob *me)
1371 Link_array<Grob>stems =
1372 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1374 for (int i = stems.size (); i--;)
1376 if (!Stem::invisible_b (stems[i]))
1383 Beam::first_visible_stem (Grob *me)
1385 Link_array<Grob>stems =
1386 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1388 for (int i = 0; i < stems.size (); i++)
1390 if (!Stem::invisible_b (stems[i]))
1397 Beam::last_visible_stem (Grob *me)
1399 Link_array<Grob>stems =
1400 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1401 for (int i = stems.size (); i--;)
1403 if (!Stem::invisible_b (stems[i]))
1413 handle rest under beam (do_post: beams are calculated now)
1414 what about combination of collisions and rest under beam.
1418 rest -> stem -> beam -> interpolate_y_position ()
1420 MAKE_SCHEME_CALLBACK (Beam, rest_collision_callback, 2);
1422 Beam::rest_collision_callback (SCM element_smob, SCM axis)
1424 Grob *rest = unsmob_grob (element_smob);
1425 Axis a = (Axis) gh_scm2int (axis);
1427 assert (a == Y_AXIS);
1429 Grob *st = unsmob_grob (rest->get_grob_property ("stem"));
1432 return gh_double2scm (0.0);
1433 Grob *beam = unsmob_grob (stem->get_grob_property ("beam"));
1435 || !Beam::has_interface (beam)
1436 || !Beam::visible_stem_count (beam))
1437 return gh_double2scm (0.0);
1439 Interval pos (0, 0);
1440 SCM s = beam->get_grob_property ("positions");
1441 if (gh_pair_p (s) && gh_number_p (ly_car (s)))
1442 pos = ly_scm2interval (s);
1444 Real dy = pos.delta ();
1445 // ugh -> use commonx
1446 Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS);
1447 Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0;
1448 Real dydx = dy && dx ? dy/dx : 0;
1450 Direction d = Stem::get_direction (stem);
1451 Real stem_y = (pos[LEFT]
1452 + (stem->relative_coordinate (0, X_AXIS) - x0) * dydx)
1455 Real beam_translation = get_beam_translation (beam);
1456 Real beam_thickness = gh_scm2double (beam->get_grob_property ("thickness"));
1457 int beam_count = get_direction_beam_count (beam, d);
1458 Real height_of_my_beams = beam_thickness
1459 + (beam_count - 1) * beam_translation;
1460 Real beam_y = stem_y - height_of_my_beams + beam_thickness / 2.0;
1462 Real staff_space = Staff_symbol_referencer::staff_space (rest);
1464 /* Better calculate relative-distance directly, rather than using
1466 Grob *common_x = rest->common_refpoint (beam, Y_AXIS);
1467 Real rest_dim = rest->extent (common_x, Y_AXIS)[d] / staff_space * d;
1469 Real minimum_distance = gh_scm2double
1470 (rest->get_grob_property ("minimum-beam-collision-distance"));
1472 Real distance = beam_y - rest_dim;
1475 shift = minimum_distance - distance;
1476 else if (minimum_distance > distance)
1477 shift = minimum_distance - distance;
1479 int stafflines = Staff_symbol_referencer::line_count (rest);
1481 /* Always move discretely by half spaces */
1482 Real discrete_shift = ceil (shift * 2.0) / 2.0;
1484 /* Inside staff, move by whole spaces*/
1485 if ((rest->extent (common_x, Y_AXIS)[d] + discrete_shift) * d
1487 ||(rest->extent (common_x, Y_AXIS)[-d] + discrete_shift) * -d
1489 discrete_shift = ceil (discrete_shift);
1491 return gh_double2scm (-d * discrete_shift);
1495 Beam::knee_b (Grob* me)
1497 SCM k = me->get_grob_property ("knee");
1498 if (gh_boolean_p (k))
1499 return gh_scm2bool (k);
1503 for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s))
1505 Direction dir = get_grob_direction (unsmob_grob (ly_car (s)));
1514 me->set_grob_property ("knee", gh_bool2scm (knee));
1520 Beam::get_direction_beam_count (Grob *me, Direction d )
1522 Link_array<Grob>stems =
1523 Pointer_group_interface__extract_grobs (me, (Grob*) 0, "stems");
1526 for (int i = stems.size (); i--;)
1529 Should we take invisible stems into account?
1531 if (Stem::get_direction (stems[i]) == d)
1532 bc = bc >? (Stem::beam_multiplicity (stems[i]).length () + 1);
1539 ADD_INTERFACE (Beam, "beam-interface",
1542 "#'thickness= weight of beams, in staffspace "
1545 "We take the least squares line through the ideal-length stems, and "
1546 "then damp that using "
1548 " damped = tanh (slope) \n"
1550 "this gives an unquantized left and right position for the beam end. "
1551 "Then we take all combinations of quantings near these left and right "
1552 "positions, and give them a score (according to how close they are to "
1553 "the ideal slope, how close the result is to the ideal stems, etc.). We "
1554 "take the best scoring combination. "
1556 "knee position-callbacks concaveness-gap concaveness-threshold dir-function quant-score auto-knee-gap gap gap-count chord-tremolo beamed-stem-shorten shorten least-squares-dy damping flag-width-function neutral-direction positions space-function thickness");