X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=lily%2Fbeam.cc;h=34601e1b99613e29ea81c1db57a8219ff4f81fa6;hb=b9955d706ca6f136b160f0611db85c6bdf0fea9b;hp=0ecb3bfa22c14d0f6b05262f2d0c94f4a171c847;hpb=be9d398d705ecf2308e10864e37c6f1315c78ec9;p=lilypond.git diff --git a/lily/beam.cc b/lily/beam.cc index 0ecb3bfa22..34601e1b99 100644 --- a/lily/beam.cc +++ b/lily/beam.cc @@ -3,17 +3,13 @@ source file of the GNU LilyPond music typesetter - (c) 1997--2002 Han-Wen Nienhuys + (c) 1997--2003 Han-Wen Nienhuys Jan Nieuwenhuizen - */ /* TODO: - - * Junk stem_info. - * Use Number_pair i.s.o Interval to represent (yl, yr). - Determine auto knees based on positions if it's set by the user. @@ -70,9 +66,17 @@ Beam::add_stem (Grob *me, Grob *s) } -/* - this returns the translation between 2 adjoining beams. - */ +Real +Beam::get_thickness (Grob * me) +{ + SCM th = me->get_grob_property ("thickness"); + if (gh_number_p (th)) + return gh_scm2double (th)* Staff_symbol_referencer::staff_space (me); + else + return 0.0; +} + +/* Return the translation between 2 adjoining beams. */ Real Beam::get_beam_translation (Grob *me) { @@ -81,22 +85,23 @@ Beam::get_beam_translation (Grob *me) return gh_scm2double (s); } -/* - Maximum beam_count. - */ +/* Maximum beam_count. */ int Beam::get_beam_count (Grob *me) { int m = 0; for (SCM s = me->get_grob_property ("stems"); gh_pair_p (s); s = ly_cdr (s)) { - Grob *sc = unsmob_grob (ly_car (s)); - - m = m >? (Stem::beam_multiplicity (sc).length () + 1); + Grob *stem = unsmob_grob (ly_car (s)); + m = m >? (Stem::beam_multiplicity (stem).length () + 1); } return m; } + +/* + Space return space between beams. + */ MAKE_SCHEME_CALLBACK (Beam, space_function, 2); SCM Beam::space_function (SCM smob, SCM beam_count) @@ -105,8 +110,7 @@ Beam::space_function (SCM smob, SCM beam_count) Real staff_space = Staff_symbol_referencer::staff_space (me); Real line = me->get_paper ()->get_var ("linethickness"); - Real thickness = gh_scm2double (me->get_grob_property ("thickness")) - * staff_space; + Real thickness = get_thickness (me); Real beam_translation = gh_scm2int (beam_count) < 4 ? (2*staff_space + line - thickness) / 2.0 @@ -146,7 +150,7 @@ Beam::before_line_breaking (SCM smob) { me->warning (_ ("Beam has less than two stems. Removing beam.")); - unsmob_grob (gh_car (stems))->remove_grob_property ("beam"); + unsmob_grob (gh_car (stems))->set_grob_property ("beam", SCM_EOL); me->suicide (); return SCM_UNSPECIFIED; @@ -233,7 +237,7 @@ Beam::connect_beams (Grob *me) SCM this_beaming = this_stem->get_grob_property ("beaming"); Direction this_dir = Directional_element_interface::get(this_stem); - if (i > 0) + if (gh_pair_p (last_beaming) && gh_pair_p (this_beaming)) { int start_point = position_with_maximal_common_beams (last_beaming, this_beaming, @@ -324,7 +328,7 @@ Beam::brew_molecule (SCM grob) Real dy = pos.delta (); Real dydx = dy && dx ? dy/dx : 0; - Real thick = gh_scm2double (me->get_grob_property ("thickness")); + Real thick = get_thickness (me); Real bdy = get_beam_translation (me); SCM last_beaming = SCM_EOL;; @@ -335,115 +339,121 @@ Beam::brew_molecule (SCM grob) SCM gap = me->get_grob_property ("gap"); Molecule the_beam; Real lt = me->get_paper ()->get_var ("linethickness"); - for (int i = 0; i< stems.size(); i++) + + for (int i = 0; i<= stems.size(); i++) { - Grob * st =stems[i]; + Grob * st = (i < stems.size()) ? stems[i] : 0; - SCM this_beaming = st->get_grob_property ("beaming"); - Real xposn = st->relative_coordinate (xcommon, X_AXIS); - Real stem_width = gh_scm2double (st->get_grob_property ("thickness")) *lt; + SCM this_beaming = st ? st->get_grob_property ("beaming") : SCM_EOL; + Real xposn = st ? st->relative_coordinate (xcommon, X_AXIS) : 0.0; + Real stem_width = st ? gh_scm2double (st->get_grob_property ("thickness")) *lt : 0 ; - if (i > 0) - { - SCM left = gh_cdr (last_beaming); - SCM right = gh_car (this_beaming); + /* + We do the space left of ST, with lfliebertjes pointing to the + right from the left stem, and rfliebertjes pointing left from + right stem. + */ + SCM left = (i>0) ? gh_cdr (last_beaming) : SCM_EOL; + SCM right = st ? gh_car (this_beaming) : SCM_EOL; - Array fullbeams; - Array lfliebertjes; - Array rfliebertjes; + Array fullbeams; + Array lfliebertjes; + Array rfliebertjes; - for (SCM s = left; - gh_pair_p (s); s =gh_cdr (s)) + for (SCM s = left; + gh_pair_p (s); s =gh_cdr (s)) + { + int b = gh_scm2int (gh_car (s)); + if (scm_memq (gh_car(s), right) != SCM_BOOL_F) { - int b = gh_scm2int (gh_car (s)); - if (scm_memq (gh_car(s), right) != SCM_BOOL_F) - { - fullbeams.push (b); - } - else - { - lfliebertjes.push (b); - } + fullbeams.push (b); } - for (SCM s = right; - gh_pair_p (s); s =gh_cdr (s)) + else { - int b = gh_scm2int (gh_car (s)); - if (scm_memq (gh_car(s), left) == SCM_BOOL_F) - { - rfliebertjes.push (b); - } + lfliebertjes.push (b); } - - - Real w = xposn - last_xposn; - Real stem_offset = 0.0; - Real width_corr = 0.0; - if (i == 1) + } + for (SCM s = right; + gh_pair_p (s); s =gh_cdr (s)) + { + int b = gh_scm2int (gh_car (s)); + if (scm_memq (gh_car(s), left) == SCM_BOOL_F) { - stem_offset -= last_width/2; - width_corr += last_width/2; + rfliebertjes.push (b); } + } + + /* + how much to stick out for beams across linebreaks + */ + Real break_overshoot = 3.0; + Real w = (i>0 && st)? xposn - last_xposn : break_overshoot; + Real stem_offset = 0.0; + Real width_corr = 0.0; + if (i == 1) + { + stem_offset -= last_width/2; + width_corr += last_width/2; + } + + if (i == stems.size() -1) + { + width_corr += stem_width/2; + } + + if (gh_number_p (gap)) + { + Real g = gh_scm2double (gap); + stem_offset += g; + width_corr -= 2*g; + } - if (i == stems.size() -1) + Molecule whole = Lookup::beam (dydx, w + width_corr, thick); + for (int j = fullbeams.size(); j--;) + { + Molecule b (whole); + b.translate_axis (last_xposn - x0 + stem_offset, X_AXIS); + b.translate_axis (dydx * (last_xposn - x0) + bdy * fullbeams[j], Y_AXIS); + the_beam.add_molecule (b); + } + + if (lfliebertjes.size() || rfliebertjes.size()) + { + Real nw_f; + + if (st) { - width_corr += stem_width/2; + int t = Stem::duration_log (st); + + SCM proc = me->get_grob_property ("flag-width-function"); + SCM result = gh_call1 (proc, scm_int2num (t)); + nw_f = gh_scm2double (result); } + else + nw_f = break_overshoot; + + /* Half beam should be one note-width, + but let's make sure two half-beams never touch */ + Real w = (i>0 && st) ? (xposn - last_xposn) : break_overshoot; + w = w/2 get_grob_property ("flag-width-function"); - SCM result = gh_call1 (proc, scm_int2num (t)); - nw_f = gh_scm2double (result); - } - - /* Half beam should be one note-width, - but let's make sure two half-beams never touch */ - - Real w = xposn - last_xposn; - w = w/2 remove_grob_property ("dir-forced"); - if (!gh_boolean_p (force) || !gh_scm2bool (force)) - Directional_element_interface::set (s, d); - } + + SCM forcedir = s->get_grob_property ("direction"); + if (!to_dir (forcedir)) + Directional_element_interface::set (s, d); } -} +} /* A union of intervals in the real line. @@ -649,20 +646,25 @@ Beam::consider_auto_knees (Grob* me) Grob* stem = stems[i]; if (Stem::invisible_b (stem)) continue; - Interval hps = Stem::head_positions (stem); - if(!hps.empty_b()) { hps[LEFT] += -1; hps[RIGHT] += 1; hps *= staff_space * 0.5 ; + + /* + We could subtract beam Y position, but this routine only + sets stem directions, a constant shift does not have an + influence. + + */ hps += stem->relative_coordinate (common, Y_AXIS); - - if (to_boolean (stem->get_grob_property ("dir-forced"))) + + if (to_dir (stem->get_grob_property ("direction"))) { - Direction stemdir =Directional_element_interface::get (stem); + Direction stemdir = to_dir (stem->get_grob_property ("direction")); hps[-stemdir] = - stemdir * infinity_f; } } @@ -707,12 +709,6 @@ Beam::consider_auto_knees (Grob* me) UP : DOWN ; stem->set_grob_property ("direction", scm_int2num (d)); - - /* - UGH. Check why we still need dir-forced; I think we can - junk it. - */ - stem->set_grob_property ("dir-forced", SCM_BOOL_T); hps.intersect (max_gap); assert (hps.empty_b () || hps.length () < 1e-6 ); @@ -741,7 +737,8 @@ Beam::set_stem_shorten (Grob *me) if (knee_b(me)) return ; - Real forced_fraction = forced_stem_count (me) / visible_stem_count (me); + Real forced_fraction = 1.0 * forced_stem_count (me) + / visible_stem_count (me); int beam_count = get_beam_count (me); @@ -789,6 +786,10 @@ Beam::after_line_breaking (SCM smob) return SCM_UNSPECIFIED; } + +/* + Compute a first approximation to the beam slope. + */ MAKE_SCHEME_CALLBACK (Beam, least_squares, 1); SCM Beam::least_squares (SCM smob) @@ -816,9 +817,9 @@ Beam::least_squares (SCM smob) Grob *fvs = first_visible_stem (me); Grob *lvs = last_visible_stem (me); - Interval ideal (Stem::calc_stem_info (fvs).ideal_y_ + Interval ideal (Stem::get_stem_info (fvs).ideal_y_ + fvs->relative_coordinate (commony, Y_AXIS) -my_y, - Stem::calc_stem_info (lvs).ideal_y_ + Stem::get_stem_info (lvs).ideal_y_ + lvs->relative_coordinate (commony, Y_AXIS) - my_y); Real x0 = first_visible_stem (me)->relative_coordinate (commonx, X_AXIS); @@ -840,23 +841,18 @@ Beam::least_squares (SCM smob) Interval chord (Stem::chord_start_y (first_visible_stem (me)), Stem::chord_start_y (last_visible_stem (me))); + /* Simple beams (2 stems) on middle line should be allowed to be + slightly sloped. + + However, if both stems reach middle line, + ideal[LEFT] == ideal[RIGHT] and ideal.delta () == 0. - /* - TODO -- use scoring for this. - - complicated, because we take stem-info.ideal for determining - beam slopes. - */ - /* Make simple beam on middle line have small tilt */ + For that case, we apply artificial slope */ if (!ideal[LEFT] && chord.delta () && count == 2) { - - /* - FIXME. -> UP - */ + /* FIXME. -> UP */ Direction d = (Direction) (sign (chord.delta ()) * UP); pos[d] = gh_scm2double (me->get_grob_property ("thickness")) / 2; - // * dir; pos[-d] = - pos[d]; } else @@ -877,7 +873,7 @@ Beam::least_squares (SCM smob) if (Stem::invisible_b (s)) continue; ideals.push (Offset (x_posns[i], - Stem::calc_stem_info (s).ideal_y_ + Stem::get_stem_info (s).ideal_y_ + s->relative_coordinate (commony, Y_AXIS) - my_y)); } @@ -953,7 +949,7 @@ Beam::shift_region_to_valid (SCM grob) Direction d = Stem::get_direction (s); Real left_y = - Stem::calc_stem_info (s).shortest_y_ + Stem::get_stem_info (s).shortest_y_ - dydx * x_posns [i]; /* @@ -1084,9 +1080,13 @@ Beam::check_concave (SCM smob) concave *= dir; concaveness2 = concave / (stems.size () - 2); - /* ugh: this is the a kludge to get - input/regression/beam-concave.ly to behave as - baerenreiter. */ + /* + + ugh: this is the a kludge to get + input/regression/beam-concave.ly to behave as + baerenreiter. + + */ /* huh? we're dividing twice (which is not scalable) meaning that @@ -1170,10 +1170,8 @@ where_are_the_whole_beams(SCM beaming) return l; } -/* - Calculate the Y position of the stem-end, given the Y-left, Y-right - in POS for stem S. This Y position is relative to S. - */ +/* Return the Y position of the stem-end, given the Y-left, Y-right + in POS for stem S. This Y position is relative to S. */ Real Beam::calc_stem_y (Grob *me, Grob* s, Grob ** common, Real xl, Real xr, @@ -1233,17 +1231,13 @@ Beam::set_stem_lengths (Grob *me) Interval pos = ly_scm2interval (me->get_grob_property ("positions")); Real staff_space = Staff_symbol_referencer::staff_space (me); - bool french = to_boolean (me->get_grob_property ("french-beaming")); - - bool gap = false; Real thick =0.0; if (gh_number_p (me->get_grob_property ("gap")) &&gh_scm2double (me->get_grob_property ("gap"))) { gap = true; - thick = gh_scm2double (me->get_grob_property ("thickness")) - * Staff_symbol_referencer::staff_space(me); + thick = get_thickness(me); } // ugh -> use commonx @@ -1259,6 +1253,8 @@ Beam::set_stem_lengths (Grob *me) if (Stem::invisible_b (s)) continue; + + bool french = to_boolean (s->get_grob_property ("french-beaming")); Real stem_y = calc_stem_y (me, s, common, xl, xr, pos, french && i > 0&& (i < stems.size () -1)); @@ -1294,13 +1290,18 @@ Beam::set_beaming (Grob *me, Beaming_info_list *beaming) ||(d == RIGHT && i == stems.size () -1)) continue; - - SCM beaming_prop = stems[i]->get_grob_property ("beaming"); + Grob *st = stems[i]; + SCM beaming_prop = st->get_grob_property ("beaming"); if (beaming_prop == SCM_EOL || index_get_cell (beaming_prop, d) == SCM_EOL) { int b = beaming->infos_.elem (i).beams_i_drul_[d]; - Stem::set_beaming (stems[i], b, d); + if (i>0 + && i < stems.size() -1 + && Stem::invisible_b (st)) + b = b infos_.elem(i).beams_i_drul_[-d]; + + Stem::set_beaming (st, b, d); } } while (flip (&d) != LEFT); @@ -1320,7 +1321,9 @@ Beam::forced_stem_count (Grob *me) if (Stem::invisible_b (s)) continue; - if (((int)Stem::chord_start_y (s)) + /* I can imagine counting those boundaries as a half forced stem, + but let's count them full for now. */ + if (abs (Stem::chord_start_y (s)) > 0.1 && (Stem::get_direction (s) != Stem::get_default_dir (s))) f++; } @@ -1401,10 +1404,6 @@ Beam::rest_collision_callback (SCM element_smob, SCM axis) || !Beam::visible_stem_count (beam)) return gh_double2scm (0.0); - // make callback for rest from this. - // todo: make sure this calced already. - - // Interval pos = ly_scm2interval (beam->get_grob_property ("positions")); Interval pos (0, 0); SCM s = beam->get_grob_property ("positions"); if (gh_pair_p (s) && gh_number_p (ly_car (s))) @@ -1417,28 +1416,47 @@ Beam::rest_collision_callback (SCM element_smob, SCM axis) Real dydx = dy && dx ? dy/dx : 0; Direction d = Stem::get_direction (stem); - Real beamy = (stem->relative_coordinate (0, X_AXIS) - x0) * dydx + pos[LEFT]; + Real stem_y = (pos[LEFT] + + (stem->relative_coordinate (0, X_AXIS) - x0) * dydx) + * d; + + Real beam_translation = get_beam_translation (beam); + Real beam_thickness = gh_scm2double (beam->get_grob_property ("thickness")); + int beam_count = get_direction_beam_count (beam, d); + Real height_of_my_beams = beam_thickness + + (beam_count - 1) * beam_translation; + Real beam_y = stem_y - height_of_my_beams + beam_thickness / 2.0; Real staff_space = Staff_symbol_referencer::staff_space (rest); - - Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space; // refp?? + /* Better calculate relative-distance directly, rather than using + rest_dim? */ + Grob *common_x = rest->common_refpoint (beam, Y_AXIS); + Real rest_dim = rest->extent (common_x, Y_AXIS)[d] / staff_space * d; - Real minimum_dist - = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance")); - Real dist = - minimum_dist + -d * (beamy - rest_dim) >? 0; + Real minimum_distance = gh_scm2double + (rest->get_grob_property ("minimum-beam-collision-distance")); + Real distance = beam_y - rest_dim; + Real shift = 0; + if (distance < 0) + shift = minimum_distance - distance; + else if (minimum_distance > distance) + shift = minimum_distance - distance; + int stafflines = Staff_symbol_referencer::line_count (rest); - // move discretely by half spaces. - int discrete_dist = int (ceil (dist)); + /* Always move discretely by half spaces */ + Real discrete_shift = ceil (shift * 2.0) / 2.0; - // move by whole spaces inside the staff. - if (discrete_dist < stafflines+1) - discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0); + /* Inside staff, move by whole spaces*/ + if ((rest->extent (common_x, Y_AXIS)[d] + discrete_shift) * d + < stafflines / 2.0 + ||(rest->extent (common_x, Y_AXIS)[-d] + discrete_shift) * -d + < stafflines / 2.0) + discrete_shift = ceil (discrete_shift); - return gh_double2scm (-d * discrete_dist); + return gh_double2scm (-d * discrete_shift); } bool @@ -1488,23 +1506,22 @@ Beam::get_direction_beam_count (Grob *me, Direction d ) ADD_INTERFACE (Beam, "beam-interface", - "A beam. - -#'thickness= weight of beams, in staffspace - - -We take the least squares line through the ideal-length stems, and -then damp that using - - damped = tanh (slope) - -this gives an unquantized left and right position for the beam end. -Then we take all combinations of quantings near these left and right -positions, and give them a score (according to how close they are to -the ideal slope, how close the result is to the ideal stems, etc.). We -take the best scoring combination. - -", - "french-beaming position-callbacks concaveness-gap concaveness-threshold dir-function quant-score auto-knee-gap gap chord-tremolo beamed-stem-shorten shorten least-squares-dy damping flag-width-function neutral-direction positions space-function thickness"); + "A beam. \n\n" +" " +"#'thickness= weight of beams, in staffspace " +" " +" " +"We take the least squares line through the ideal-length stems, and " +"then damp that using " +" \n" +" damped = tanh (slope) \n" +" \n" +"this gives an unquantized left and right position for the beam end. " +"Then we take all combinations of quantings near these left and right " +"positions, and give them a score (according to how close they are to " +"the ideal slope, how close the result is to the ideal stems, etc.). We " +"take the best scoring combination. " +, + "knee position-callbacks concaveness-gap concaveness-threshold dir-function quant-score auto-knee-gap gap chord-tremolo beamed-stem-shorten shorten least-squares-dy damping flag-width-function neutral-direction positions space-function thickness");