From: Han-Wen Nienhuys Date: Sun, 11 Jul 2004 02:09:07 +0000 (+0000) Subject: (class New_slur): new file. Score based slur X-Git-Tag: release/2.3.7~76 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=1493d37a97ec2e32c706d6706c476d22985a5682;p=lilypond.git (class New_slur): new file. Score based slur computations. --- diff --git a/ChangeLog b/ChangeLog index 3ddfcbd5df..cb63345045 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2004-07-11 Han-Wen Nienhuys + + * lily/new-slur.cc (class New_slur): new file. Score based slur + computations. + 2004-07-10 Jan Nieuwenhuizen * mf/SConscript: Remove Builders. diff --git a/SConstruct b/SConstruct index 1a3e000eaf..e9adef066b 100644 --- a/SConstruct +++ b/SConstruct @@ -185,7 +185,6 @@ command = r"""python -c 'import sys; sys.stdout.write ("%s/include/python%s" % ( PYTHON_INCLUDE = os.popen (command).read () env.Append (CPPPATH = PYTHON_INCLUDE) - headers = ('sys/stat.h', 'assert.h', 'kpathsea/kpathsea.h', 'Python.h') for i in headers: if conf.CheckCHeader (i): @@ -352,7 +351,15 @@ env['MAKEINFO_PATH'] = ['.', '#/Documentation/user', ## TEXINFO_PAPERSIZE_OPTION= $(if $(findstring $(PAPERSIZE),a4),,-t @afourpaper) env['TEXINFO_PAPERSIZE_OPTION'] = '-t @afourpaper' - +#FIXME: ./python isn't sconsed yet, add scrdir/python for lilylib.py ... +env.Append (PYTHONPATH = [os.path.join (outdir, 'usr/lib/python'), + os.path.join (srcdir, 'buildscripts'), + os.path.join (srcdir, 'python')]) +# huh, aha? +env.Append (ENV = { 'PYTHONPATH' : string.join (env['PYTHONPATH'], + os.pathsep) } ) + +# GS_FONTPATH, GS_LIB? SConscript ('buildscripts/builder.py') #subdirs = ['mf',] diff --git a/buildscripts/builder.py b/buildscripts/builder.py index 62cc9185ea..abd8ecd276 100644 --- a/buildscripts/builder.py +++ b/buildscripts/builder.py @@ -90,8 +90,8 @@ def add_ps_target (target, source, env): a = ('LILYPONDPREFIX=%(LILYPONDPREFIX)s '\ + '%(PYTHON)s %(LILYPOND_PY)s%(verbose)s'\ - + ' --include=$$(dirname $TARGET)'\ - + ' --output=$$(dirname $TARGET)'\ + + ' --include=${TARGET.dir}'\ + + ' --output=${TARGET.base}'\ + ' $SOURCE') % vars () lilypond = Builder (action = a, suffix = '.pdf', src_suffix = '.ly') ## emitter = add_ps_target) @@ -101,7 +101,7 @@ verbose = verbose_opt (env, ' --verbose') a = ('LILYPONDPREFIX=%(LILYPONDPREFIX)s '\ + '%(PYTHON)s %(ABC2LY_PY)s%(verbose)s'\ + ' --include=$$(dirname $TARGET)'\ - + ' --output=$$(dirname $TARGET)'\ + + ' --output=$$(dirname $TARGET)/$$(basename $TARGET .ly)'\ + ' $SOURCE') % vars () abc2ly = Builder (action = a, suffix = '.ly', src_suffix = '.abc') env.Append (BUILDERS = {'Abc2ly': abc2ly}) diff --git a/input/SConscript b/input/SConscript index 6df0a1a43b..f184f96a2c 100644 --- a/input/SConscript +++ b/input/SConscript @@ -25,15 +25,12 @@ pdfs = pdfs + map (e.LilyPond, lys) LILYPOND_BIN = env['LILYPOND_BIN'] -#e.Depends (pdfs, LILYPOND_BIN) -# e.Depends (pdfs, 'lilypond-internals.texi') - mfbuild = os.path.join (env['absbuild'], 'mf', env['out']) e.Depends ('doc', mfbuild) e.Depends ('doc', LILYPOND_BIN) -#e.Depends (doc, 'lilypond-internals.texi') e.Alias ('doc', pdfs) +#e.Alias ('doc', 'example-1.pdf') #testing all_sources = ['SConscript',] + sources + abc_sources diff --git a/input/test/new-slur.ly b/input/test/new-slur.ly new file mode 100644 index 0000000000..b408a8653e --- /dev/null +++ b/input/test/new-slur.ly @@ -0,0 +1,16 @@ + + +\relative { + + \override Slur #'after-line-breaking-callback = #New_slur::after_line_breaking + \override Slur #'print-function = #New_slur::print + \override Slur #'height = ##f + ( f ) + f + ^( f ) + f + _( f ) + _( g ) + _( \stemDown g \stemBoth ) + +} diff --git a/lily/bezier.cc b/lily/bezier.cc index b20de964a7..85208ade07 100644 --- a/lily/bezier.cc +++ b/lily/bezier.cc @@ -59,6 +59,9 @@ translate (Array* array, Offset o) Formula of the bezier 3-spline sum_{j=0}^3 (3 over j) z_j (1-t)^ (3-j) t^j + + + A is the axis of X coordinate. */ Real diff --git a/lily/include/slur.hh b/lily/include/slur.hh index 476b960b92..166c9f1631 100644 --- a/lily/include/slur.hh +++ b/lily/include/slur.hh @@ -28,7 +28,7 @@ private: static Real get_boundary_notecolumn_y (Grob *me, Direction dir); static Offset broken_trend_offset (Grob *me, Direction dir); static Offset get_attachment (Grob *me,Direction dir, Grob **common); - static void de_uglyfy (Grob *me,Slur_bezier_bow* bb, Real default_height); + static void de_uglify (Grob *me,Slur_bezier_bow* bb, Real default_height); static SCM set_extremities (Grob *me); static void set_control_points (Grob *me); static void check_slope (Grob *me); diff --git a/lily/new-slur.cc b/lily/new-slur.cc new file mode 100644 index 0000000000..37f2412433 --- /dev/null +++ b/lily/new-slur.cc @@ -0,0 +1,571 @@ +/* + slur.cc -- implement Slur + + source file of the GNU LilyPond music typesetter + + (c) 1996--2004 Han-Wen Nienhuys + Jan Nieuwenhuizen +*/ + + +#include + +#include "directional-element-interface.hh" +#include "group-interface.hh" +#include "group-interface.hh" +#include "lily-guile.hh" +#include "lily-proto.hh" +#include "lookup.hh" +#include "main.hh" +#include "note-column.hh" +#include "output-def.hh" +#include "paper-column.hh" +#include "rod.hh" +#include "slur-bezier-bow.hh" +#include "slur.hh" +#include "spanner.hh" +#include "staff-symbol-referencer.hh" +#include "stem.hh" +#include "stencil.hh" +#include "warn.hh" + +struct Encompass_info { + Real x_; + Real stem_; + Real head_; + Encompass_info () + { + x_ = 0.0; + stem_ = 0.0; + head_ = 0.0; + } +}; + +/* + TODO: put in details list. + */ +const int SLUR_REGION_SIZE = 5; +const Real HEAD_ENCOMPASS_PENALTY = 1000.0; +const Real STEM_ENCOMPASS_PENALTY = 30.0; +const Real CLOSENESS_FACTOR = 10; +const Real EDGE_ATTRACTION_FACTOR = 4; +const Real HEAD_FREE_SPACE = 0.3; +const Real SAME_SLOPE_PENALTY = 20; +const Real STEEPER_SLOPE_FACTOR = 50; +const Real NON_HORIZONTAL_PENALTY = 15; +const Real HEAD_STRICT_FREE_SPACE = 0.2; + +#define DEBUG_SLUR_QUANTING 1 + +struct Slur_score { + Drul_array attachment_; + Real score_; +#if DEBUG_SLUR_QUANTING + String score_card_; +#endif + + Slur_score() { + score_ = 0.0; + } +}; + +class New_slur +{ +public: + static void add_column (Grob *me, Grob *col); + DECLARE_SCHEME_CALLBACK (print, (SCM)); + static void score_slopes (Grob * me, Grob *common[], Drul_array base_attach, + Array * scores); + + static void score_encompass (Grob * me, Grob *common[], + Drul_array, Array * scores); + static void set_interface (Grob*); + static bool has_interface (Grob*); + static Array get_encompass_offsets (Grob *me); + static Bezier get_curve (Grob *me); + static Bezier get_bezier (Grob *me, Drul_array); + static Direction get_default_dir (Grob *me); + DECLARE_SCHEME_CALLBACK (after_line_breaking, (SCM)); + DECLARE_SCHEME_CALLBACK (height, (SCM,SCM)); +private: + static void set_end_points (Grob*); + static Real get_boundary_notecolumn_y (Grob *me, Direction dir); + static Offset broken_trend_offset (Grob *me, Direction dir); + static Offset get_attachment (Grob *me,Direction dir, Grob **common); + static void de_uglyfy (Grob *me,Slur_bezier_bow* bb, Real default_height); + static SCM set_extremities (Grob *me); + static void set_control_points (Grob *me); + static void check_slope (Grob *me); + static Encompass_info get_encompass_info (Grob *me, Grob *col, Grob **common); +}; + +void +New_slur::set_interface (Grob*me) +{ + /* Copy to mutable list. */ + me->set_property ("attachment", + ly_deep_copy (me->get_property ("attachment"))); +} + +void +New_slur::add_column (Grob*me, Grob*n) +{ + Pointer_group_interface::add_grob (me, ly_symbol2scm ("note-columns"), n); + me->add_dependency (n); + + add_bound_item (dynamic_cast (me), dynamic_cast (n)); +} + +Encompass_info +New_slur::get_encompass_info (Grob *me, + Grob *col, + Grob **common) +{ + Grob* stem = unsmob_grob (col->get_property ("stem")); + + Encompass_info ei; + + Direction dir = get_grob_direction (me); + + if (!stem) + { + programming_error ("No stem for note column?"); + ei.x_ = col->relative_coordinate (common[X_AXIS], X_AXIS); + ei.head_ = ei.stem_ = col->relative_coordinate (common[Y_AXIS], Y_AXIS); + return ei; + } + Direction stem_dir = get_grob_direction (stem); + + if (Grob *head = Note_column::first_head (col)) + ei.x_ = head->extent (common[X_AXIS], X_AXIS).center (); + else + ei.x_ = col->extent (common[X_AXIS], X_AXIS).center (); + + ei.head_ = Stem::extremal_heads (stem)[Direction (stem_dir * dir)]->extent (common[Y_AXIS], Y_AXIS)[dir]; + + if ((stem_dir == dir) + && !stem->extent (stem, Y_AXIS).is_empty ()) + { + ei.stem_ = stem->extent (common[Y_AXIS], Y_AXIS)[dir]; + } + else + ei.stem_ = ei.head_; + + return ei; +} + + +Direction +New_slur::get_default_dir (Grob*me) +{ + Link_array encompasses = + Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-columns"); + + Direction d = DOWN; + for (int i=0; i < encompasses.size (); i ++) + { + if (Note_column::dir (encompasses[i]) < 0) + { + d = UP; + break; + } + } + return d; +} + +MAKE_SCHEME_CALLBACK (New_slur, after_line_breaking,1); +SCM +New_slur::after_line_breaking (SCM smob) +{ + Spanner *me = dynamic_cast (unsmob_grob (smob)); + if (!scm_ilength (me->get_property ("note-columns"))) + { + me->suicide (); + return SCM_UNSPECIFIED; + } + + if (!get_grob_direction (me)) + set_grob_direction (me, get_default_dir (me)); + + + if (!Note_column::has_interface (me->get_bound (LEFT)) + || !Note_column::has_interface (me->get_bound (RIGHT))) + me->suicide (); // fixme. + + set_end_points (me); + + return SCM_UNSPECIFIED; +} + +Bezier +New_slur::get_bezier (Grob *me, Drul_array extremes) +{ + // SCM details = me->get_property ("details"); + SCM h_inf_scm = me->get_property ("height-limit"); + SCM r_0_scm = me->get_property ("ratio"); + Real staff_space = Staff_symbol_referencer::staff_space ((Grob*)me); + + Real r_0 = robust_scm2double (r_0_scm, 1); + Real h_inf = staff_space * ly_scm2double (h_inf_scm); + + Array encompasses; + encompasses.push (extremes[LEFT]); + encompasses.push (extremes[RIGHT]); + + Slur_bezier_bow bb (encompasses, + get_grob_direction (me), h_inf, r_0); + + return bb.get_bezier (); +} + +void +New_slur::set_end_points (Grob *me) +{ + Link_array columns = + Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-columns"); + + + Real staff_space = Staff_symbol_referencer::staff_space ((Grob*)me); + Drul_array extremes (columns[0], columns.top ()); + Direction dir = get_grob_direction (me); + + Drul_array base_attachment; + + SCM eltlist = me->get_property ("note-columns"); + Grob *common[] = {common_refpoint_of_list (eltlist, me, X_AXIS), + common_refpoint_of_list (eltlist, me, Y_AXIS)}; + + + Spanner* sp = dynamic_cast (me); + common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (RIGHT), X_AXIS); + common[X_AXIS] = common[X_AXIS]->common_refpoint (sp->get_bound (LEFT), X_AXIS); + + Direction d = LEFT; + Drul_array staves; + + do { + Grob *stem = Note_column::get_stem (extremes[d]); + Direction stemdir = get_grob_direction (stem); + Grob * h = Stem::extremal_heads (stem)[Direction (dir * stemdir)]; + staves[d] = Staff_symbol_referencer::get_staff_symbol (h); + + common[Y_AXIS] = common[Y_AXIS]->common_refpoint (staves[d], Y_AXIS); + } while (flip (&d) != LEFT); + + do + { + // c&p + Grob *stem = Note_column::get_stem (extremes[d]); + Direction stemdir = get_grob_direction (stem); + Grob * h = Stem::extremal_heads (stem)[Direction (dir * stemdir)]; + + Real y = h->extent (common[Y_AXIS], Y_AXIS)[dir]; + + y += dir * 0.5 * staff_space; + if (Staff_symbol_referencer::on_staffline (h)) + y += .5 * staff_space * dir ; + + Grob * fh = Note_column::first_head (extremes[d]); + Real x = fh->extent (common[X_AXIS], X_AXIS).linear_combination (CENTER); + + if (get_grob_direction (stem) == dir + && dir == -d) + { + x -= d * fh->extent(fh, X_AXIS).length (); + } + + base_attachment[d] = Offset (x, y); + + } while (flip (&d) != LEFT); + + Drul_array staff_offsets; + do { + staff_offsets[d] = staves[d]->relative_coordinate (common[Y_AXIS], Y_AXIS); + } while (flip (&d) != LEFT); + + Array scores; + + Drul_array os; + + /*ugh. */ + os[LEFT] = base_attachment[LEFT]; + for (int i = 0; i < SLUR_REGION_SIZE; i++) + { + os[RIGHT] = base_attachment[RIGHT]; + for (int j = 0; j < SLUR_REGION_SIZE; j++) + { + Slur_score s; + s.attachment_ = os; + + scores.push (s); + + Real y = os[RIGHT][Y_AXIS]; + + if (Staff_symbol_referencer::staff_radius (staves[RIGHT]) + > fabs ((y - staff_offsets[RIGHT]) / staff_space)) + y += dir *staff_space; + else + y += dir * staff_space / 2; + + os[RIGHT][Y_AXIS] = y; + } + + Real y = os[LEFT][Y_AXIS]; + + if (Staff_symbol_referencer::staff_radius (staves[LEFT]) + > fabs ((y - staff_offsets[LEFT]) / staff_space)) + y += dir *staff_space; + else + y += dir *staff_space / 2; + + os[LEFT][Y_AXIS] = y; + } + + + score_encompass (me, common, base_attachment, &scores); + score_slopes (me, common, base_attachment, &scores); + + Real opt = 1e6; + int opt_idx = -1; + for (int i = scores.size (); i--;) + { + if (scores[i].score_ < opt) + { + opt = scores[i].score_; + opt_idx = i; + } + } + + Bezier b (get_bezier (me, scores[opt_idx].attachment_)); + + SCM controls = SCM_EOL; + for (int i = 4; i--;) + { + Offset o = b.control_[i] - + Offset (me->relative_coordinate (common[X_AXIS], X_AXIS), + me->relative_coordinate (common[Y_AXIS], Y_AXIS)); + + controls = scm_cons (ly_offset2scm (o), controls); + } + + me->set_property ("control-points", controls); + +#if DEBUG_SLUR_QUANTING + qscores[opt_idx].score_card_ += to_string ("i%d", best_idx); + + // debug quanting + me->set_property ("quant-score", + scm_makfrom0str (scores[opt_idx].score_card_.to_str0 ())); +#endif + +} + +void +New_slur::score_encompass (Grob * me, Grob *common[], Drul_array base_attach, + Array * scores) +{ + Link_array encompasses = + Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-columns"); + Direction dir = get_grob_direction (me); + + Array infos; + + int first = 1; + int last = encompasses.size () - 2; + + for (int i = first; i <= last; i++) + infos.push (get_encompass_info (me, encompasses[i], common)); + + for (int i =0 ; i < scores->size (); i++) + { + Bezier bez (get_bezier (me, scores->elem (i).attachment_)); + Real demerit =0.; + for (int j = 0; j < infos.size(); j++) + { + Real x = infos[j].x_; + + if (!(x < scores->elem (i).attachment_[RIGHT][X_AXIS] + &&x > scores->elem (i).attachment_[LEFT][X_AXIS])) + continue; + + Real y = bez.get_other_coordinate (X_AXIS, x); + + if (dir * (y - infos[j].head_) < 0) + demerit += HEAD_ENCOMPASS_PENALTY; + + if (dir * (y - infos[j].stem_) < 0) + demerit += STEM_ENCOMPASS_PENALTY; + else + { + Interval ext; + ext.add_point (infos[j].stem_); + ext.add_point (infos[j].head_); + + demerit += - CLOSENESS_FACTOR * (dir * (y - (ext[dir] + dir * HEAD_FREE_SPACE)) elem (i).attachment_[d][Y_AXIS] - base_attach[d][Y_AXIS]); + } while (flip (&d) != LEFT); + +#if DEBUG_SLUR_QUANTING + (*qscores)[i].score_card_ += to_string ("E%.2f",d); +#endif + + (*scores)[i].score_ += demerit; + } +} + + +void +New_slur::score_slopes (Grob * me, Grob *common[], Drul_array base_attach, + Array * scores) +{ + Link_array columns = + Pointer_group_interface__extract_grobs (me, (Grob*)0, "note-columns"); + + + Drul_array extremes (columns[0], columns.top ()); + Direction dir = get_grob_direction (me); + Drul_array ys; + + Direction d = LEFT; + do { + Grob *stem = Note_column::get_stem (extremes [d]); + Direction sd = get_grob_direction (stem); + ys[d] = Stem::extremal_heads (stem)[Direction (dir * sd)] + ->relative_coordinate (common[Y_AXIS], Y_AXIS); + } while (flip (&d) != LEFT); + + + Real dy = ys[RIGHT] - ys[LEFT]; + + + + for (int i =0 ; i < scores->size (); i++) + { + Real slur_dy = (*scores)[i].attachment_[RIGHT][Y_AXIS] + - (*scores)[i].attachment_[LEFT][Y_AXIS]; + + Real demerit = 0.0; + + demerit += STEEPER_SLOPE_FACTOR * (dir * (slur_dy - dy) >? 0); + if (sign (dy) == 0 && + sign (slur_dy) != 0) + demerit += NON_HORIZONTAL_PENALTY; + if (sign (dy) + && sign (slur_dy) + && sign (slur_dy) != sign (dy)) + demerit += SAME_SLOPE_PENALTY; + +#if DEBUG_SLUR_QUANTING + (*qscores)[i].score_card_ += to_string ("E%.2f",d); +#endif + (*scores)[i].score_ += demerit; + } +} + + +Bezier +New_slur::get_curve (Grob*me) +{ + Bezier b; + int i = 0; + for (SCM s= me->get_property ("control-points"); s != SCM_EOL; s = ly_cdr (s)) + { + b.control_[i++] = ly_scm2offset (ly_car (s)); + } + + return b; +} + + + + + + +/* + ugh ? + */ +MAKE_SCHEME_CALLBACK (New_slur, height, 2); +SCM +New_slur::height (SCM smob, SCM ax) +{ + Axis a = (Axis)ly_scm2int (ax); + Grob * me = unsmob_grob (smob); + assert (a == Y_AXIS); + + SCM mol = me->get_uncached_stencil (); + Interval ext; + if (Stencil * m = unsmob_stencil (mol)) + ext = m->extent (a); + return ly_interval2scm (ext); +} + +/* + Ugh should have dash-length + dash-period + */ +MAKE_SCHEME_CALLBACK (New_slur, print,1); +SCM +New_slur::print (SCM smob) +{ + Grob * me = unsmob_grob (smob); + if (!scm_ilength (me->get_property ("note-columns"))) + { + me->suicide (); + return SCM_EOL; + } + + Real base_thick = robust_scm2double (me->get_property ("thickness"), 1); + Real thick = base_thick * Staff_symbol_referencer::line_thickness (me); + + Real ss = Staff_symbol_referencer::staff_space (me); + Bezier one = get_curve (me); + + // get_curve may suicide + if (!scm_ilength (me->get_property ("note-columns"))) + return SCM_EOL; + + Stencil a; + + /* + TODO: replace dashed with generic property. + */ + SCM d = me->get_property ("dashed"); + if (ly_c_number_p (d)) + a = Lookup::dashed_slur (one, thick, thick * robust_scm2double (d, 0)); + else + a = Lookup::slur (one, get_grob_direction (me) * base_thick * ss / 10.0, + thick); + +#if DEBUG_SLUR_QUANTING + SCM quant_score = me->get_property ("quant-score"); + if (// debug_beam_quanting_flag && + ly_c_string_p (quant_score)) + { + String str; + SCM properties = Font_interface::text_font_alist_chain (me); + + Direction stem_dir = stems.size() ? to_dir (stems[0]->get_property ("direction")) : UP; + + Stencil tm = *unsmob_stencil (Text_item::interpret_markup + (me->get_paper ()->self_scm (), properties, quant_score)); + a.add_at_edge (Y_AXIS, get_grob_direction (me), tm, 1.0, 0); + } +#endif + + return a.smobbed_copy (); +} + + + + + +ADD_INTERFACE (New_slur, "new-slur-interface", + "A slur", + "attachment attachment-offset beautiful control-points dashed details de-uglify-parameters direction extremity-function extremity-offset-alist height-limit note-columns ratio slope-limit thickness y-free"); diff --git a/lily/slur.cc b/lily/slur.cc index 0363945cc4..baaa56ec29 100644 --- a/lily/slur.cc +++ b/lily/slur.cc @@ -56,7 +56,7 @@ Slur::add_column (Grob*me, Grob*n) } void -Slur::de_uglyfy (Grob*me, Slur_bezier_bow* bb, Real default_height) +Slur::de_uglify (Grob*me, Slur_bezier_bow* bb, Real default_height) { Real length = bb->curve_.control_[3][X_AXIS] ; Real ff = bb->fit_factor (); @@ -619,7 +619,7 @@ Slur::set_control_points (Grob*me) Slurs that fit beautifully are not ugly */ if (area > beautiful) - de_uglyfy (me, &bb, default_height); + de_uglify (me, &bb, default_height); } Bezier b = bb.get_bezier (); diff --git a/lily/staff-symbol-referencer.cc b/lily/staff-symbol-referencer.cc index 4ef9fbe9be..e3070ff53d 100644 --- a/lily/staff-symbol-referencer.cc +++ b/lily/staff-symbol-referencer.cc @@ -37,6 +37,9 @@ Staff_symbol_referencer::on_staffline (Grob *me, int pos) Grob* Staff_symbol_referencer::get_staff_symbol (Grob *me) { + if (Staff_symbol::has_interface (me)) + return me; + SCM st = me->get_property ("staff-symbol"); return unsmob_grob (st); }