From: Jan Nieuwenhuizen Date: Thu, 14 Mar 2002 17:32:06 +0000 (+0100) Subject: patch::: 1.5.39.jcn3 X-Git-Tag: release/1.5.40~3 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=ab7ece48d3415644e3f9254e75baddb18f4f1fe8;p=lilypond.git patch::: 1.5.39.jcn3 * lily/include/new-beam.hh: * lily/new-beam.cc: New file. * flower/include/interval.hh: * flower/include/interval.tcc (delta): New method. (swap): Now public (previously private). --- diff --git a/ChangeLog b/ChangeLog index 536e25bbda..49d63a1fe7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,12 @@ 2002-03-14 Jan Nieuwenhuizen + * lily/include/new-beam.hh: + * lily/new-beam.cc: New file. + + * flower/include/interval.hh: + * flower/include/interval.tcc (delta): New method. + (swap): Now public (previously private). + * scm/beam.scm (default-beam-y-quants): Bugfix: lower beam-sit by 1 staff-line-thickness. Sadly, this makes dy quanting problems (dy quants allowed should depend on actual left y) more visible. diff --git a/VERSION b/VERSION index eb73436f76..b87d0a34aa 100644 --- a/VERSION +++ b/VERSION @@ -2,7 +2,7 @@ PACKAGE_NAME=LilyPond MAJOR_VERSION=1 MINOR_VERSION=5 PATCH_LEVEL=39 -MY_PATCH_LEVEL=jcn2 +MY_PATCH_LEVEL=jcn3 # use the above to send patches: MY_PATCH_LEVEL is always empty for a # released version. diff --git a/flower/include/interval.hh b/flower/include/interval.hh index 8d4d83a5dc..12d940c048 100644 --- a/flower/include/interval.hh +++ b/flower/include/interval.hh @@ -42,6 +42,7 @@ struct Interval_t : public Drul_array { void intersect (Interval_t h); T length () const; + T delta () const; void set_empty () ; bool empty_b () const { return elem (LEFT) > elem (RIGHT); } bool contains_b (Interval_t const&) const; @@ -85,8 +86,7 @@ struct Interval_t : public Drul_array { elem (LEFT) = l; elem (RIGHT) =r; } -private: - + void swap () { T t = elem (LEFT); diff --git a/flower/include/interval.tcc b/flower/include/interval.tcc index 7e773e2579..94ad71f578 100644 --- a/flower/include/interval.tcc +++ b/flower/include/interval.tcc @@ -70,6 +70,13 @@ Interval_t::length () const return elem (RIGHT)-elem (LEFT); } +template +T +Interval_t::delta () const +{ + return elem (RIGHT) - elem (LEFT); +} + /** smallest Interval which includes *this and #h# */ diff --git a/lily/grob.cc b/lily/grob.cc index c901e95cac..c6088ff5f1 100644 --- a/lily/grob.cc +++ b/lily/grob.cc @@ -239,7 +239,8 @@ Grob::calculate_dependencies (int final, int busy, SCM funcname) status_c_= busy; - for (SCM d= get_grob_property ("dependencies"); gh_pair_p (d); d = ly_cdr (d)) + for (SCM d = get_grob_property ("dependencies"); gh_pair_p (d); + d = ly_cdr (d)) { unsmob_grob (ly_car (d)) ->calculate_dependencies (final, busy, funcname); @@ -250,9 +251,11 @@ Grob::calculate_dependencies (int final, int busy, SCM funcname) SCM proc = get_grob_property (s.ch_C ()); if (gh_procedure_p (proc)) gh_call1 (proc, this->self_scm ()); + else if (gh_list_p (proc)) + for (SCM i = proc; gh_pair_p (i); i = ly_cdr (i)) + gh_call1 (ly_car (i), this->self_scm ()); status_c_= final; - } Molecule * diff --git a/lily/include/new-beam.hh b/lily/include/new-beam.hh new file mode 100644 index 0000000000..2326be7f5b --- /dev/null +++ b/lily/include/new-beam.hh @@ -0,0 +1,57 @@ +/* + beam.hh -- part of GNU LilyPond + + (c) 1996--2002 Han-Wen Nienhuys +*/ + +#ifndef NEW_BEAM_HH +#define NEW_BEAM_HH + +#include "lily-proto.hh" +#include "lily-guile.hh" + + + +class New_beam +{ +public: + static int visible_stem_count (Grob*); + static Item* first_visible_stem (Grob*); + static Item* last_visible_stem (Grob*); + static bool has_interface (Grob*); + DECLARE_SCHEME_CALLBACK (rest_collision_callback, (SCM element, SCM axis)); + New_beam (SCM); + static void add_stem (Grob*,Grob*); + static void set_beaming (Grob*,Beaming_info_list *); + static void set_stemlens (Grob*); + static int get_multiplicity (Grob*me); + DECLARE_SCHEME_CALLBACK (brew_molecule, (SCM )); + DECLARE_SCHEME_CALLBACK (before_line_breaking, (SCM )); + DECLARE_SCHEME_CALLBACK (after_line_breaking, (SCM )); + + /* + y-dy callbacks + */ + DECLARE_SCHEME_CALLBACK (least_squares, (SCM)); + DECLARE_SCHEME_CALLBACK (check_concave, (SCM)); + DECLARE_SCHEME_CALLBACK (slope_damping, (SCM)); + DECLARE_SCHEME_CALLBACK (quantise_position, (SCM)); + DECLARE_SCHEME_CALLBACK (user_override, (SCM)); + + static Molecule stem_beams (Grob*,Item *here, Item *next, Item *prev, + Real dydx); + +private: + static Direction get_default_dir (Grob*); + static void set_stem_directions (Grob*); + static void consider_auto_knees (Grob*); + static void set_stem_shorten (Grob*); + static Real calc_stem_y_f (Grob*, Item* s, Interval pos); + static Real check_stem_length_f (Grob*, Interval pos); + static void set_stem_lengths (Grob*); + static Interval quantise_interval (Grob*, Interval pos, Direction quant_dir); + static int forced_stem_count (Grob*); +}; + +#endif /* NEW_BEAM_HH */ + diff --git a/lily/new-beam.cc b/lily/new-beam.cc new file mode 100644 index 0000000000..3a27ce7983 --- /dev/null +++ b/lily/new-beam.cc @@ -0,0 +1,1081 @@ +/* + beam.cc -- implement Beam + + source file of the GNU LilyPond music typesetter + + (c) 1997--2002 Han-Wen Nienhuys + Jan Nieuwenhuizen + +*/ + +/* + [TODO] + + * Fix XXX + + * Fix TODO + + * Junk stem_info. + + * Remove #'direction from beam. A beam has no direction per se. + It may only set directions for stems. + + * Rewrite stem_beams. + + */ + + +#include // tanh. + +#include "molecule.hh" +#include "directional-element-interface.hh" +#include "beaming.hh" +#include "new-beam.hh" +#include "misc.hh" +#include "least-squares.hh" +#include "stem.hh" +#include "paper-def.hh" +#include "lookup.hh" +#include "group-interface.hh" +#include "staff-symbol-referencer.hh" +#include "item.hh" +#include "spanner.hh" +#include "warn.hh" + +void +New_beam::add_stem (Grob *me, Grob *s) +{ + Pointer_group_interface::add_grob (me, ly_symbol2scm ("stems"), s); + + s->add_dependency (me); + + assert (!Stem::beam_l (s)); + s->set_grob_property ("beam", me->self_scm ()); + + add_bound_item (dynamic_cast (me), dynamic_cast (s)); +} + +int +New_beam::get_multiplicity (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)); + + if (Stem::has_interface (sc)) + m = m >? Stem::beam_count (sc, LEFT) >? Stem::beam_count (sc, RIGHT); + } + return m; +} + +/* After pre-processing all directions should be set. + Several post-processing routines (stem, slur, script) need stem/beam + direction. + Currenly, this means that beam has set all stem's directions. + [Alternatively, stems could set its own directions, according to + their beam, during 'final-pre-processing'.] */ +MAKE_SCHEME_CALLBACK (New_beam, before_line_breaking, 1); +SCM +New_beam::before_line_breaking (SCM smob) +{ + Grob *me = unsmob_grob (smob); + + /* Beams with less than 2 two stems don't make much sense, but could happen + when you do + + [r8 c8 r8]. + + For a beam that only has one stem, we try to do some disappearance magic: + we revert the flag, and move on to The Eternal Engraving Fields. */ + + int count = visible_stem_count (me); + if (count < 2) + { + me->warning (_ ("beam has less than two visible stems")); + + SCM stems = me->get_grob_property ("stems"); + if (scm_ilength (stems) == 1) + { + me->warning (_ ("Beam has less than two stems. Removing beam.")); + + unsmob_grob (gh_car (stems))->remove_grob_property ("beam"); + me->suicide (); + + return SCM_UNSPECIFIED; + } + else if (scm_ilength (stems) == 0) + { + me->suicide (); + return SCM_UNSPECIFIED; + } + } + if (count >= 1) + { + if (!Directional_element_interface::get (me)) + Directional_element_interface::set (me, get_default_dir (me)); + + consider_auto_knees (me); + set_stem_directions (me); + set_stem_shorten (me); + } + return SCM_EOL; +} + +Direction +New_beam::get_default_dir (Grob *me) +{ + Drul_array total; + total[UP] = total[DOWN] = 0; + Drul_array count; + count[UP] = count[DOWN] = 0; + Direction d = DOWN; + + Link_array stems= + Pointer_group_interface__extract_grobs (me, (Item*)0, "stems"); + + for (int i=0; i get_grob_property ("dir-function"); + SCM s = gh_call2 (func, + gh_cons (gh_int2scm (count[UP]), + gh_int2scm (count[DOWN])), + gh_cons (gh_int2scm (total[UP]), + gh_int2scm (total[DOWN]))); + + if (gh_number_p (s) && gh_scm2int (s)) + return to_dir (s); + + /* If dir is not determined: get default */ + return to_dir (me->get_grob_property ("neutral-direction")); +} + + +/* Set all stems with non-forced direction to beam direction. + Urg: non-forced should become `without/with unforced' direction, + once stem gets cleaned-up. */ +void +New_beam::set_stem_directions (Grob *me) +{ + Link_array stems + =Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems"); + Direction d = Directional_element_interface::get (me); + + for (int i=0; i remove_grob_property ("dir-forced"); + if (!gh_boolean_p (force) || !gh_scm2bool (force)) + Directional_element_interface::set (s, d); + } +} + +/* Simplistic auto-knees; only consider vertical gap between two + adjacent chords. + + `Forced' stem directions are ignored. If you don't want auto-knees, + don't set, or unset auto-knee-gap. */ +void +New_beam::consider_auto_knees (Grob *me) +{ + SCM scm = me->get_grob_property ("auto-knee-gap"); + + if (gh_number_p (scm)) + { + bool knee_b = false; + Real knee_y = 0; + Real staff_space = Staff_symbol_referencer::staff_space (me); + Real gap = gh_scm2double (scm) / staff_space; + + Direction d = Directional_element_interface::get (me); + Link_array stems= + Pointer_group_interface__extract_grobs (me, (Item*)0, "stems"); + + Grob *common = me->common_refpoint (stems[0], Y_AXIS); + for (int i=1; i < stems.size (); i++) + if (!Stem::invisible_b (stems[i])) + common = common->common_refpoint (stems[i], Y_AXIS); + + int l = 0; + for (int i=1; i < stems.size (); i++) + { + if (!Stem::invisible_b (stems[i-1])) + l = i - 1; + if (Stem::invisible_b (stems[l])) + continue; + if (Stem::invisible_b (stems[i])) + continue; + + Real left = Stem::extremal_heads (stems[l])[d] + ->relative_coordinate (common, Y_AXIS); + Real right = Stem::extremal_heads (stems[i])[-d] + ->relative_coordinate (common, Y_AXIS); + + Real dy = right - left; + + if (abs (dy) >= gap) + { + knee_y = (right + left) / 2; + knee_b = true; + break; + } + } + + if (knee_b) + { + for (int i=0; i < stems.size (); i++) + { + if (Stem::invisible_b (stems[i])) + continue; + Item *s = stems[i]; + Real y = Stem::extremal_heads (stems[i])[d] + ->relative_coordinate (common, Y_AXIS); + + Directional_element_interface::set (s, y < knee_y ? UP : DOWN); + s->set_grob_property ("dir-forced", SCM_BOOL_T); + } + } + } +} + +/* Set stem's shorten property if unset. + TODO: + take some y-position (chord/beam/nearest?) into account + scmify forced-fraction */ +void +New_beam::set_stem_shorten (Grob *m) +{ + Spanner*me = dynamic_cast (m); + + Real forced_fraction = forced_stem_count (me) / visible_stem_count (me); + + int multiplicity = get_multiplicity (me); + + SCM shorten = me->get_grob_property ("beamed-stem-shorten"); + if (shorten == SCM_EOL) + return; + + int sz = scm_ilength (shorten); + + Real staff_space = Staff_symbol_referencer::staff_space (me); + SCM shorten_elt = scm_list_ref (shorten, + gh_int2scm (multiplicity set_grob_property ("shorten", gh_double2scm (shorten_f)); +} + +/* Call list of y-dy-callbacks, that handle setting of + grob-properties y, dy. + + User may set grob-properties: y-position-hs and height-hs + (to be fixed) that override the calculated y and dy. + + Because y and dy cannot be calculated and quanted separately, we + always calculate both, then check for user override. */ +MAKE_SCHEME_CALLBACK (New_beam, after_line_breaking, 1); +SCM +New_beam::after_line_breaking (SCM smob) +{ + Grob *me = unsmob_grob (smob); + + /* Copy to mutable list. */ + me->set_grob_property ("position", + ly_deep_copy (me->get_grob_property ("position"))); + + return SCM_UNSPECIFIED; +} + + +MAKE_SCHEME_CALLBACK (New_beam, least_squares, 1); +SCM +New_beam::least_squares (SCM smob) +{ + Grob *me = unsmob_grob (smob); + + int count = visible_stem_count (me); + if (count <= 1) + return SCM_UNSPECIFIED; + + Direction dir = Directional_element_interface::get (me); + + Interval pos (0, 0); + Interval ideal (Stem::calc_stem_info (first_visible_stem (me)).idealy_f_, + Stem::calc_stem_info (last_visible_stem (me)).idealy_f_); + if (!ideal.delta ()) + { + Interval chord (Stem::chord_start_f (first_visible_stem (me)), + Stem::chord_start_f (last_visible_stem (me))); + + /* Make simple beam on middle line have small tilt */ + if (!ideal[LEFT] && chord.delta () && count == 2) + { + Direction d = (Direction)(sign (chord.delta ()) * dir); + pos[d] = d * gh_scm2double (me->get_grob_property ("thickness")) / 2; + pos[-d] = 0; + } + else + pos = ideal; + } + else + { + Array ideals; + + // ugh -> use commonx + Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS); + Link_array stems= + Pointer_group_interface__extract_grobs (me, (Item*)0, "stems"); + + for (int i=0; i < stems.size (); i++) + { + Item* s = stems[i]; + if (Stem::invisible_b (s)) + continue; + ideals.push (Offset (s->relative_coordinate (0, X_AXIS) - x0, + Stem::calc_stem_info (s).idealy_f_)); + } + Real y; + Real dydx; + minimise_least_squares (&dydx, &y, ideals); + + Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0; + Real dy = dydx * dx; + pos = Interval (y*dir, (y+dy) * dir); + } + + me->set_grob_property ("position", ly_interval2scm (pos)); + return SCM_UNSPECIFIED; +} + +MAKE_SCHEME_CALLBACK (New_beam, check_concave, 1); +SCM +New_beam::check_concave (SCM smob) +{ + Grob *me = unsmob_grob (smob); + + Link_array stems = + Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems"); + + for (int i = 0; i < stems.size ();) + { + if (Stem::invisible_b (stems[i])) + stems.del (i); + else + i++; + } + + if (stems.size () < 3) + return SCM_UNSPECIFIED; + + /* Concaveness try #2: Sum distances of inner noteheads that + fall outside the interval of the two outer noteheads */ + Real concave = 0; + Interval iv (Stem::chord_start_f (stems[0]), + Stem::chord_start_f (stems.top ())); + + if (iv[MAX] < iv[MIN]) + iv.swap (); + + for (int i = 1; i < stems.size () - 1; i++) + { + Real c = 0; + Real f = Stem::chord_start_f (stems[i]); + if ((c = f - iv[MAX]) > 0) + concave += c; + else if ((c = f - iv[MIN]) < 0) + concave += c; + } + concave *= Directional_element_interface::get (me); + + Real concaveness = concave / (stems.size () - 2); + /* ugh: this is the a kludge to get input/regression/beam-concave.ly + to behave as baerenreiter. */ + concaveness /= (stems.size () - 2); + + Real r = gh_scm2double (me->get_grob_property ("concaveness-threshold")); + + /* TODO: some sort of damping iso -> plain horizontal */ + if (concaveness > r) + { + Interval pos = ly_scm2interval (me->get_grob_property ("position")); + Real r = pos.linear_combination (0); + me->set_grob_property ("position", ly_interval2scm (Interval (r, r))); + } + + return SCM_UNSPECIFIED; +} + +/* This neat trick is by Werner Lemberg, + damped = tanh (slope) + corresponds with some tables in [Wanske] CHECKME */ +MAKE_SCHEME_CALLBACK (New_beam, slope_damping, 1); +SCM +New_beam::slope_damping (SCM smob) +{ + Grob *me = unsmob_grob (smob); + + if (visible_stem_count (me) <= 1) + return SCM_UNSPECIFIED; + + SCM s = me->get_grob_property ("damping"); + int damping = gh_scm2int (s); + + if (damping) + { + Interval pos = ly_scm2interval (me->get_grob_property ("position")); + Real dy = pos.delta (); + + // ugh -> use commonx + Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) + - first_visible_stem (me)->relative_coordinate (0, X_AXIS); + Real dydx = dy && dx ? dy/dx : 0; + dydx = 0.6 * tanh (dydx) / damping; + + Real damped_dy = dydx * dx; + pos[LEFT] += (dy - damped_dy) / 2; + pos[RIGHT] -= (dy - damped_dy) / 2; + + me->set_grob_property ("position", ly_interval2scm (pos)); + } + return SCM_UNSPECIFIED; +} + + +/* Prevent interference from stafflines. */ +Interval +New_beam::quantise_interval (Grob *me, Interval pos, Direction quant_dir) +{ + int multiplicity = get_multiplicity (me); + + Real staff_space = Staff_symbol_referencer::staff_space (me); + Real thick = me->paper_l ()->get_var ("stafflinethickness"); + + SCM proc = me->get_grob_property ("vertical-position-quant-function"); + SCM quants = scm_apply (proc, + me->self_scm (), + scm_list_n (gh_int2scm (multiplicity), + gh_double2scm (1), /* junkme */ + gh_double2scm (thick / staff_space), + /* HUH? */ + SCM_EOL, + SCM_UNDEFINED)); + + Array a; + for (; gh_pair_p (quants); quants = ly_cdr (quants)) + a.push (gh_scm2double (ly_car (quants))); + + if (a.size () <= 1) + return pos; + + Direction dir = Directional_element_interface::get (me); + Interval left = quantise_iv (a, pos[LEFT]*dir/staff_space) * staff_space; + Interval right = quantise_iv (a, pos[RIGHT]*dir/staff_space) * staff_space; + + if (quant_dir) + return Interval (left[quant_dir]*dir, right[quant_dir]*dir); + + /* XXX TODO: some intelligent choice of quants */ + return Interval (left[MAX]*dir, right[MAX]*dir); +} + + +/* Quantise vertial position (left and right) of beam. + Generalisation of [Ross]. */ +MAKE_SCHEME_CALLBACK (New_beam, quantise_position, 1); +SCM +New_beam::quantise_position (SCM smob) +{ + Grob *me = unsmob_grob (smob); + + Interval pos = ly_scm2interval (me->get_grob_property ("position")); + Real y_shift = check_stem_length_f (me, pos); + pos += y_shift; + pos = quantise_interval (me, pos, CENTER); + + me->set_grob_property ("position", ly_interval2scm (pos)); + set_stem_lengths (me); + pos = ly_scm2interval (me->get_grob_property ("position")); + + y_shift = check_stem_length_f (me, pos); + + Real half_space = Staff_symbol_referencer::staff_space (me) / 2; + /* HMMM */ + if (y_shift > half_space / 4) + { + pos += y_shift; + int quant_dir = 0; + /* for significantly lengthened or shortened stems, + request quanting the other way. + HMMM */ + if (abs (y_shift) > half_space / 2) + quant_dir = sign (y_shift) * Directional_element_interface::get (me); + pos = quantise_interval (me, pos, (Direction)quant_dir); + } + + me->set_grob_property ("position", ly_interval2scm (pos)); + return SCM_UNSPECIFIED; +} + +/* It's tricky to have the user override y, dy directly, so we use this + translation func. Also, if our staff_space != 1 (smaller staff, eg), + user will expect staff-position to be discrete values. */ +MAKE_SCHEME_CALLBACK (New_beam, user_override, 1); +SCM +New_beam::user_override (SCM smob) +{ + Grob *me = unsmob_grob (smob); + + // UGR, MOVE ME + set_stem_lengths (me); +#if 0 + Real staff_space = Staff_symbol_referencer::staff_space (me); + SCM s = me->get_grob_property ("staff-position"); + if (gh_number_p (s)) + { + Real y = gh_scm2double (s) * staff_space * 0.5; + me->set_grob_property ("y", gh_double2scm (y)); + } + + /* Name suggestions? Tilt, slope, vertical-* ? */ + s = me->get_grob_property ("height"); + if (gh_number_p (s)) + { + Real dy = gh_scm2double (s) * staff_space * 0.5; + me->set_grob_property ("dy", gh_double2scm (dy)); + } +#endif + return SCM_UNSPECIFIED; +} + +Real +New_beam::calc_stem_y_f (Grob *me, Item* s, Interval pos) +{ + int beam_multiplicity = get_multiplicity (me); + int stem_multiplicity = (Stem::flag_i (s) - 2) >? 0; + + SCM space_proc = me->get_grob_property ("space-function"); + SCM space = gh_call1 (space_proc, gh_int2scm (beam_multiplicity)); + + Real thick = gh_scm2double (me->get_grob_property ("thickness")); + Real interbeam_f = gh_scm2double (space); + + // ugh -> use commonx + Real x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS); + Real dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0; + Real dy = pos.delta (); + Real stem_y = (dy && dx + ? (s->relative_coordinate (0, X_AXIS) - x0) / dx + * dy + : 0) + pos[LEFT]; + + /* knee */ + Direction dir = Directional_element_interface::get (me); + Direction sdir = Directional_element_interface::get (s); + + /* knee */ + if (dir!= sdir) + { + stem_y -= dir * (thick / 2 + (beam_multiplicity - 1) * interbeam_f); + + // huh, why not for first visible? + if (Staff_symbol_referencer::staff_symbol_l (s) + != Staff_symbol_referencer::staff_symbol_l (last_visible_stem (me))) + stem_y += Directional_element_interface::get (me) + * (beam_multiplicity - stem_multiplicity) * interbeam_f; + } + + return stem_y; +} + +/* Make very sure that we don't have stems that are too short. + Try our best not to have stems that are too long (think: knees). + + Optionally (testing): try to lengthen more, to reach more ideal + stem lengths */ +Real +New_beam::check_stem_length_f (Grob *me, Interval pos) +{ + Real shorten = 0; + Real lengthen = 0; + Direction dir = Directional_element_interface::get (me); + + Link_array stems= + Pointer_group_interface__extract_grobs (me, (Item*)0, "stems"); + + bool knee = false; + int ideal_lengthen_count = 0; + Real ideal_lengthen = 0; + int ideal_shorten_count = 0; + Real ideal_shorten = 0; + + for (int i=0; i < stems.size (); i++) + { + Item* s = stems[i]; + if (Stem::invisible_b (s)) + continue; + + knee |= dir != Directional_element_interface::get (s); + + Real stem_y = calc_stem_y_f (me, s, pos); + + stem_y *= dir; + Stem_info info = Stem::calc_stem_info (s); + + shorten = shorten ? info.miny_f_ - stem_y; + + if (info.idealy_f_ - stem_y > 0) + { + ideal_lengthen += info.idealy_f_ - stem_y; + ideal_lengthen_count++; + } + else if (info.idealy_f_ - stem_y < 0) + { + ideal_shorten += info.idealy_f_ - stem_y; + ideal_shorten_count++; + } + } + + if (lengthen && shorten) + me->warning (_ ("weird beam vertical offset")); + + if (ideal_lengthen_count) + lengthen = (ideal_lengthen / ideal_lengthen_count) >? lengthen; + if (knee && ideal_shorten_count) + shorten = (ideal_shorten / ideal_shorten_count) stems= + Pointer_group_interface__extract_grobs (me, (Item*)0, "stems"); + + if (stems.size () <= 1) + return; + + Grob *common = me->common_refpoint (stems[0], Y_AXIS); + for (int i=1; i < stems.size (); i++) + if (!Stem::invisible_b (stems[i])) + common = common->common_refpoint (stems[i], Y_AXIS); + + Direction dir = Directional_element_interface::get (me); + Interval pos = ly_scm2interval (me->get_grob_property ("position")); + Real staff_space = Staff_symbol_referencer::staff_space (me); + Real thick = gh_scm2double (me->get_grob_property ("thickness")); + bool ps_testing = to_boolean (ly_symbol2scm ("ps-testing")); + for (int i=0; i < stems.size (); i++) + { + Item* s = stems[i]; + if (Stem::invisible_b (s)) + continue; + + Real stem_y = calc_stem_y_f (me, s, pos); + + // doesn't play well with dvips + if (ps_testing) + if (Stem::get_direction (s) == dir) + stem_y += Stem::get_direction (s) * thick / 2; + + /* caution: stem measures in staff-positions */ + Real id = me->relative_coordinate (common, Y_AXIS) + - stems[i]->relative_coordinate (common, Y_AXIS); + Stem::set_stemend (s, (stem_y + id) / staff_space * 2); + } +} + +void +New_beam::set_beaming (Grob *me, Beaming_info_list *beaming) +{ + Link_array stems= + Pointer_group_interface__extract_grobs (me, (Grob *)0, "stems"); + + Direction d = LEFT; + for (int i=0; i < stems.size (); i++) + { + do + { + /* Don't overwrite user override (?) */ + if (Stem::beam_count (stems[i], d) == -1 + /* Don't set beaming for outside of outer stems */ + && ! (d == LEFT && i == 0) + && ! (d == RIGHT && i == stems.size () -1)) + { + int b = beaming->infos_.elem (i).beams_i_drul_[d]; + Stem::set_beaming (stems[i], b, d); + } + } + while (flip (&d) != LEFT); + } +} + + + +/* + beams to go with one stem. + + FIXME: clean me up. + */ +Molecule +New_beam::stem_beams (Grob *me, Item *here, Item *next, Item *prev, Real dydx) +{ + // ugh -> use commonx + if ((next + && !(next->relative_coordinate (0, X_AXIS) + > here->relative_coordinate (0, X_AXIS))) + || (prev + && !(prev->relative_coordinate (0, X_AXIS) + < here->relative_coordinate (0, X_AXIS)))) + programming_error ("Beams are not left-to-right"); + + int multiplicity = get_multiplicity (me); + + SCM space_proc = me->get_grob_property ("space-function"); + SCM space = gh_call1 (space_proc, gh_int2scm (multiplicity)); + + Real thick = gh_scm2double (me->get_grob_property ("thickness")); + Real interbeam_f = gh_scm2double (space); + + Real bdy = interbeam_f; + + Molecule leftbeams; + Molecule rightbeams; + + Real nw_f; + if (!Stem::first_head (here)) + nw_f = 0; + else { + int t = Stem::type_i (here); + + SCM proc = me->get_grob_property ("flag-width-function"); + SCM result = gh_call1 (proc, gh_int2scm (t)); + nw_f = gh_scm2double (result); + } + + + Direction dir = Directional_element_interface::get (me); + + /* [Tremolo] beams on whole notes may not have direction set? */ + if (dir == CENTER) + dir = Directional_element_interface::get (here); + + + /* half beams extending to the left. */ + if (prev) + { + int lhalfs= lhalfs = Stem::beam_count (here, LEFT) + - Stem::beam_count (prev, RIGHT); + int lwholebeams= Stem::beam_count (here, LEFT) + relative_coordinate (0, X_AXIS) + - prev->relative_coordinate (0, X_AXIS); + Real stem_w = gh_scm2double (prev->get_grob_property ("thickness")) + // URG + * me->paper_l ()->get_var ("stafflinethickness"); + + w = w/2 relative_coordinate (0, X_AXIS) + - here->relative_coordinate (0, X_AXIS); + + Real stem_w = gh_scm2double (next->get_grob_property ("thickness")) + // URG + * me->paper_l ()->get_var ("stafflinethickness"); + + Molecule a = Lookup::beam (dydx, w + stem_w, thick); + a.translate_axis (- stem_w/2, X_AXIS); + int j = 0; + Real gap_f = 0; + + SCM gap = me->get_grob_property ("gap"); + if (gh_number_p (gap)) + { + int gap_i = gh_scm2int ((gap)); + int nogap = rwholebeams - gap_i; + + for (; j < nogap; j++) + { + Molecule b (a); + b.translate_axis (-dir * bdy * j, Y_AXIS); + rightbeams.add_molecule (b); + } + if (Stem::invisible_b (here)) + gap_f = nw_f; + else + gap_f = nw_f / 2; + w -= 2 * gap_f; + a = Lookup::beam (dydx, w + stem_w, thick); + } + + for (; j < rwholebeams; j++) + { + Molecule b (a); + Real tx = 0; + if (Stem::invisible_b (here)) + // ugh, see chord-tremolo.ly + tx = (-dir + 1) / 2 * nw_f * 1.5 + gap_f/4; + else + tx = gap_f; + b.translate (Offset (tx, -dir * bdy * j)); + rightbeams.add_molecule (b); + } + + w = w/2 get_grob_property ("stems"))) + return SCM_EOL; + Real x0, dx; + Link_arraystems = + Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems"); + if (visible_stem_count (me)) + { + // ugh -> use commonx + x0 = first_visible_stem (me)->relative_coordinate (0, X_AXIS); + dx = last_visible_stem (me)->relative_coordinate (0, X_AXIS) - x0; + } + else + { + x0 = stems[0]->relative_coordinate (0, X_AXIS); + dx = stems.top ()->relative_coordinate (0, X_AXIS) - x0; + } + + Interval pos = ly_scm2interval (me->get_grob_property ("position")); + Real dy = pos.delta (); + Real dydx = dy && dx ? dy/dx : 0; + + for (int i=0; i < stems.size (); i++) + { + Item *item = stems[i]; + Item *prev = (i > 0)? stems[i-1] : 0; + Item *next = (i < stems.size ()-1) ? stems[i+1] :0; + + Molecule sb = stem_beams (me, item, next, prev, dydx); + Real x = item->relative_coordinate (0, X_AXIS) - x0; + sb.translate (Offset (x, x * dydx + pos[LEFT])); + mol.add_molecule (sb); + } + + mol.translate_axis (x0 + - dynamic_cast (me) + ->get_bound (LEFT)->relative_coordinate (0, X_AXIS), + X_AXIS); + + return mol.smobbed_copy (); +} + +int +New_beam::forced_stem_count (Grob *me) +{ + Link_arraystems = + Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems"); + int f = 0; + for (int i=0; i < stems.size (); i++) + { + Item *s = stems[i]; + + if (Stem::invisible_b (s)) + continue; + + if (((int)Stem::chord_start_f (s)) + && (Stem::get_direction (s) != Stem::get_default_dir (s))) + f++; + } + return f; +} + + + + +/* TODO: + use filter and standard list functions. + */ +int +New_beam::visible_stem_count (Grob *me) +{ + Link_arraystems = + Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems"); + int c = 0; + for (int i = stems.size (); i--;) + { + if (!Stem::invisible_b (stems[i])) + c++; + } + return c; +} + +Item* +New_beam::first_visible_stem (Grob *me) +{ + Link_arraystems = + Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems"); + + for (int i = 0; i < stems.size (); i++) + { + if (!Stem::invisible_b (stems[i])) + return stems[i]; + } + return 0; +} + +Item* +New_beam::last_visible_stem (Grob *me) +{ + Link_arraystems = + Pointer_group_interface__extract_grobs (me, (Item*) 0, "stems"); + for (int i = stems.size (); i--;) + { + if (!Stem::invisible_b (stems[i])) + return stems[i]; + } + return 0; +} + + +/* + [TODO] + handle rest under beam (do_post: beams are calculated now) + what about combination of collisions and rest under beam. + + Should lookup + + rest -> stem -> beam -> interpolate_y_position () +*/ +MAKE_SCHEME_CALLBACK (New_beam, rest_collision_callback, 2); +SCM +New_beam::rest_collision_callback (SCM element_smob, SCM axis) +{ + Grob *rest = unsmob_grob (element_smob); + Axis a = (Axis) gh_scm2int (axis); + + assert (a == Y_AXIS); + + Grob *st = unsmob_grob (rest->get_grob_property ("stem")); + Grob *stem = st; + if (!stem) + return gh_double2scm (0.0); + Grob *beam = unsmob_grob (stem->get_grob_property ("beam")); + if (!beam + || !New_beam::has_interface (beam) + || !New_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 ("position")); + Interval pos (0, 0); + SCM s = beam->get_grob_property ("position"); + if (gh_pair_p (s)) + pos = ly_scm2interval (s); + + Real dy = pos.delta (); + // ugh -> use commonx + Real x0 = first_visible_stem (beam)->relative_coordinate (0, X_AXIS); + Real dx = last_visible_stem (beam)->relative_coordinate (0, X_AXIS) - x0; + 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 staff_space = Staff_symbol_referencer::staff_space (rest); + + + Real rest_dim = rest->extent (rest, Y_AXIS)[d]*2.0 / staff_space; // refp?? + + Real minimum_dist + = gh_scm2double (rest->get_grob_property ("minimum-beam-collision-distance")); + Real dist = + minimum_dist + -d * (beamy - rest_dim) >? 0; + + int stafflines = Staff_symbol_referencer::line_count (rest); + + // move discretely by half spaces. + int discrete_dist = int (ceil (dist)); + + // move by whole spaces inside the staff. + if (discrete_dist < stafflines+1) + discrete_dist = int (ceil (discrete_dist / 2.0)* 2.0); + + return gh_double2scm (-d * discrete_dist); +} + + +bool +New_beam::has_interface (Grob *me) +{ + return me->has_interface (ly_symbol2scm ("beam-interface")); +} + diff --git a/scm/grob-description.scm b/scm/grob-description.scm index e2190da9dc..ccd0c0afa7 100644 --- a/scm/grob-description.scm +++ b/scm/grob-description.scm @@ -97,8 +97,10 @@ . ( ;; todo: clean this up a bit: the list is getting ;; rather long. - (molecule-callback . ,Beam::brew_molecule) + ;(molecule-callback . ,Beam::brew_molecule) + (molecule-callback . ,New_beam::brew_molecule) (concaveness-threshold . 0.08) + (position . (#f . #f)) (y-dy-callbacks . (,Beam::least_squares ,Beam::check_concave ,Beam::slope_damping @@ -106,9 +108,21 @@ ,Beam::user_override ,Beam::do_quantise_y)) + (position-callbacks . (,New_beam::least_squares + ,New_beam::check_concave + ,New_beam::slope_damping + ,New_beam::quantise_position + ,New_beam::user_override)) + (thickness . 0.48) ; in staff-space (before-line-breaking-callback . ,Beam::before_line_breaking) - (after-line-breaking-callback . ,Beam::after_line_breaking) + ;;(after-line-breaking-callback . ,Beam::after_line_breaking) + (after-line-breaking-callback . (,New_beam::after_line_breaking + ,New_beam::least_squares + ,New_beam::check_concave + ,New_beam::slope_damping + ,New_beam::quantise_position + ,New_beam::user_override)) (neutral-direction . -1) (dir-function . ,beam-dir-majority) (height-quant-function . ,default-beam-dy-quants)