From: Han-Wen Nienhuys Date: Thu, 23 Feb 2006 10:52:07 +0000 (+0000) Subject: patch by Joe Neeman: "I have X-Git-Tag: release/2.7.37~42 X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=ab547077f8c694c8edf882ce58fc6ed63407ccb6;p=lilypond.git patch by Joe Neeman: "I have changed it slightly so that the number of systems returned by solve () will be the same as the last value passed to resize (). I've also added more documentation, removed casts, fixed style problems and put in some recovery if constraints aren't satisfied." --- diff --git a/ChangeLog b/ChangeLog index 6f714be932..7e44af50a6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2006-02-23 Han-Wen Nienhuys + + * lily/constrained-breaking.cc: patch by Joe Neeman: "I have + changed it slightly so that the number of systems returned by + solve () will be the same as the last value passed to resize + (). I've also added more documentation, removed casts, fixed style + problems and put in some recovery if constraints aren't satisfied." + 2006-02-23 Jan Nieuwenhuizen * elisp/lilypond-mode.el (count-matches-as-number): New funtion @@ -22,6 +30,9 @@ 2006-02-22 Han-Wen Nienhuys + * scm/define-grob-interfaces.scm (parentheses-interface): add + padding to parentheses-interface. + * Documentation/user/GNUmakefile: remove $(CROSS) support from Documentation/user/GNUmakefile. Use --disable-documentation for that. diff --git a/Documentation/topdocs/NEWS.tely b/Documentation/topdocs/NEWS.tely index 3a569e8912..1ecc6c72f0 100644 --- a/Documentation/topdocs/NEWS.tely +++ b/Documentation/topdocs/NEWS.tely @@ -64,6 +64,12 @@ which scares away people. @end ignore +@item A score may now be specified to take a fixed number of +systems, by setting the @code{system-count} variable in the +@code{\layout} block. + +This feature was contributed by Joe Neeman. + @item Ties may now be attached to the left side of a note with @code{\repeatTie}, for use with volta repeats. diff --git a/Documentation/user/global.itely b/Documentation/user/global.itely index a732234816..1dc1698af1 100644 --- a/Documentation/user/global.itely +++ b/Documentation/user/global.itely @@ -660,6 +660,11 @@ If set to false, systems will be spread to fill the last page. Pieces that amply fill two pages or more should have this set to true. +@cindex @code{system-count} +@item system-count +This variable, if set, specifies into how many lines a score should be +broken. + @cindex @code{between-system-space} @item between-system-space This dimensions determines the distance between systems. It is the diff --git a/input/regression/+.ly b/input/regression/+.ly index 8e0e455789..29240472d2 100644 --- a/input/regression/+.ly +++ b/input/regression/+.ly @@ -11,7 +11,7 @@ texidoc = This document presents proofs for LilyPond " (lilypond-version) ". When the -text correspond with the shown notation, we consider LilyPond Officially +text corresponds with the shown notation, we consider LilyPond Officially BugFree (tm). This document is intended for finding bugs and for documenting bugfixes. diff --git a/lily/constrained-breaking.cc b/lily/constrained-breaking.cc index 1f3171803b..b507c0725d 100644 --- a/lily/constrained-breaking.cc +++ b/lily/constrained-breaking.cc @@ -7,36 +7,6 @@ (c) 2006 Han-Wen Nienhuys */ -/* - TODO: - - * vsize vs. int: casts should not be necessary. Use VPOS iso -1 as - magic signaling value? - - * The specification uses A j, k, n and m as variables. - - Functions use start,end,sys_count,calc_subproblem as variables. Use the same naming - for the specification as for the code. - - - FURTHER REMARKS: - - * - - int a; - int b; - - iso. - - int a, b; - - - * no spurious * in comments. - - - */ - - #include "constrained-breaking.hh" #include "international.hh" @@ -48,53 +18,71 @@ #include "system.hh" #include "warn.hh" -void -print_constrained_break_nodes (vector const &arr) -{ - for (vsize i = 0; i < arr.size (); i++) - { - printf ("node %d: ", (int)i); - arr[i].print (); - } -} - -/** - We use the following optimal substructure. Let W(A) be our weight function. - - Let A_{k,n} = (a_{k,n,1}, ... a_{k,n,k}) be the optimal set of line breaks - for k systems and n potential breakpoints. a_{k,n,k} = n (it is the end of - the piece) - - Then A_{k+1, m} is contructed from - min_ {k < j < m} ( W(A_{k,j} :: m) ) - where by A::m we denote appending m to the list A - +/* + We use the following optimal substructure. Let W(A) be our weight function. + + Let A_{k,n} = (a_{k,n,1}, ... a_{k,n,k}) be the optimal set of line breaks + for k systems and n potential breakpoints. a_{k,n,k} = n (it is the end of + the piece) + + Then A_{k+1, m} is contructed from + min_ {k < j < m} ( W(A_{k,j} :: m) ) + where by A::m we denote appending m to the list A + + Indices in the code: + + The above algorithm makes it easy to end at a point before the end of the + score (just find A_{k,m} for some m < breaks_.size () - 1). However, we must + add information for starting at a point after the beginning. One constructor + allows the specification of a list of starting columns, start_. We then have + start_.size () different solution arrays. state_[i] is the array for the + solution starting at column number start_[i]. + + The indicies "start" and "end" refer to the index in the start_ array of the + desired starting and ending columns. + + each solution array looks like + a_{1,1,1} a_{2,1,2} a_{3,1,3} . . . + X a_{2,2,2} a_{3,2,3} . . . + X X a_{3,3,3} . . . + . . . . + . . . . + where the X's mark invalid solutions (can't have more systems than + breakpoints). Note that each value is of the form a_{x,n,x}. This is because + a breakpoint of the form a_{x,n,x-1} will also be called a_{x-1,m,x-1} for + some m < n. Each cell in the array stores the value of its m (ie. the + ending breakpoint of the previous line) as "prev_". + + For finding A_{sys, brk}, let "me" be the (sys_count,brk) cell in our + solution array (state_[start][sys * rank + brk]). + + Then A_{sys, brk} = A_{sys - 1, me.prev_} :: me */ -/* start and sys here are indexed from 0. - -max_break is indexed from starting_breakpoints_[start] (for - max_break, starting_breakpoints_[start] is the beginning of the - piece; the smallest value we should ever see here is - starting_breakpoints_[start] + 1) */ +/* + start and sys here are indexed from 0. + brk is indexed from starting_breakpoints_[start] + (for brk, starting_breakpoints_[start] is the beginning + of the piece; the smallest value we should ever see here is + starting_breakpoints_[start] + 1) */ bool -Constrained_breaking::calc_subproblem (int start, int sys, int max_break) +Constrained_breaking::calc_subproblem (vsize start, vsize sys, vsize brk) { assert (sys < systems_); - assert (start < (int)start_.size ()); - assert (max_break < (int)breaks_.size ()); + assert (start < start_.size ()); + assert (brk < breaks_.size ()); bool found_something = false; - int start_col = starting_breakpoints_[start]; + vsize start_col = starting_breakpoints_[start]; vector &st = state_[start]; - int rank = breaks_.size () - start_col; - int max_index = max_break - start_col; - for (int j = sys; j < max_index; j++) + vsize rank = breaks_.size () - start_col; + vsize max_index = brk - start_col; + for (vsize j=sys; j < max_index; j++) { if (0 == sys && j > 0) break; /* the first line cannot have its first break after the beginning */ - Column_x_positions const &cur = cols_[(j + start_col)*cols_rank_ + max_break]; + Column_x_positions const &cur = cols_[(j + start_col)*cols_rank_ + brk]; Column_x_positions prev; Real prev_dem = 0; @@ -106,21 +94,18 @@ Constrained_breaking::calc_subproblem (int start, int sys, int max_break) if (isinf (prev_dem)) break; - Real dem, force, pen; + Real dem; + Real force; + Real pen; combine_demerits(prev, cur, &force, &pen, &dem); dem += prev_dem; if (isinf (dem)) continue; int k = sys*rank + max_index; - if (isinf (st[k].demerits_) - || dem < st[k].demerits_) + if (isinf (st[k].demerits_) || dem < st[k].demerits_) { found_something = true; - - /* - TODO: maybe just copy a Constrained_break_node ? - */ st[k].demerits_ = dem; st[k].force_ = force; st[k].penalty_ = pen; @@ -137,49 +122,59 @@ Constrained_breaking::solve () if (!systems_) { programming_error (_f ("no system number set in constrained-breaking")); - systems_ = start_.size () / 2; + systems_ = breaks_.size () / 4; } - resize (); - return get_solution (0, systems_, -1); + resize (systems_); + return get_solution(0, VPOS, systems_); +} + +Column_x_positions +Constrained_breaking::space_line (vsize i, vsize j) +{ + bool ragged_right = to_boolean (pscore_->layout ()->c_variable ("ragged-right")); + bool ragged_last = to_boolean (pscore_->layout ()->c_variable ("ragged-last")); + Column_x_positions col; + + vector line (all_.begin () + breaks_[i], + all_.begin() + breaks_[j] + 1); + + line[0] = dynamic_cast (line[0])->find_prebroken_piece (RIGHT); + line.back () = dynamic_cast (line.back ())->find_prebroken_piece (LEFT); + + col.cols_ = line; + + /* we have no idea what line this will be -- only whether it is the first */ + Interval line_dims = line_dimensions_int (pscore_->layout (), i); + Simple_spacer_wrapper *sp = generate_spacing_problem (line, line_dims); + + bool last = j == breaks_.size () - 1; + bool ragged = ragged_right || (last && ragged_last); + sp->solve (&col, ragged); + + delete sp; + return col; } void -Constrained_breaking::resize () +Constrained_breaking::resize (vsize systems) { - if (!breaks_.size ()) - { - bool ragged_right = to_boolean (pscore_->layout ()->c_variable ("ragged-right")); - bool ragged_last = to_boolean (pscore_->layout ()->c_variable ("ragged-last")); + systems_ = systems; + if (!breaks_.size () && pscore_) + { /* do all the rod/spring problems */ breaks_ = pscore_->find_break_indices (); cols_rank_ = breaks_.size (); all_ = pscore_->root_system ()->columns (); cols_.resize (breaks_.size () * breaks_.size ()); for (vsize i = 0; i < breaks_.size () - 1; i++) - for (vsize j = i + 1; j < breaks_.size (); j++) - { - vector line (all_.begin () + breaks_[i], - all_.begin() + breaks_[j] + 1); - - line[0] = dynamic_cast (line[0])->find_prebroken_piece (RIGHT); - line.back () = dynamic_cast (line.back ())->find_prebroken_piece (LEFT); - - cols_[i*cols_rank_ + j].cols_ = line; - - /* we have no idea what line this will be -- only whether it is the first */ - Interval line_dims = line_dimensions_int (pscore_->layout (), i); - Simple_spacer_wrapper *sp = generate_spacing_problem (line, line_dims); - - bool last = j == breaks_.size () - 1; - bool ragged = ragged_right || (last && ragged_last); - sp->solve (&cols_[i*cols_rank_ + j], ragged); - - if (!cols_[i*cols_rank_ + j].satisfies_constraints_) - break; - delete sp; - } + for (vsize j = i + 1; j < breaks_.size (); j++) + { + cols_[i*cols_rank_ + j] = space_line (i, j); + if (!cols_[i*cols_rank_ + j].satisfies_constraints_) + break; + } /* work out all the starting indices */ for (vsize i = 0; i < start_.size (); i++) @@ -193,93 +188,113 @@ Constrained_breaking::resize () state_.resize (start_.size ()); } - for (vsize i = 0; i < state_.size (); i++) - state_[i].resize((breaks_.size () - starting_breakpoints_[i]) * systems_); - - /* fill out the matrices */ - for (vsize i = 0; i < state_.size (); i++) - for (int j = valid_systems_; j < systems_; j++) - for (vsize k = starting_breakpoints_[i] + j + 1; k < breaks_.size (); k++) - if (!calc_subproblem (i, j, k)) - break; /* if we couldn't break this, it is too cramped already */ - - valid_systems_ = systems_; + if (pscore_ && systems_ > valid_systems_) + { + for (vsize i = 0; i < state_.size (); i++) + state_[i].resize((breaks_.size () - starting_breakpoints_[i]) * systems_); + + /* fill out the matrices */ + for (vsize i = 0; i < state_.size (); i++) + for (vsize j = valid_systems_; j < systems_; j++) + for (vsize k = starting_breakpoints_[i] + j + 1; k < breaks_.size (); k++) + if (!calc_subproblem (i, j, k)) + break; /* if we couldn't break this, it is too cramped already */ + valid_systems_ = systems_; + } } vector -Constrained_breaking::get_solution (int start, int end, int sys_count) +Constrained_breaking::get_solution (vsize start, vsize end, vsize sys_count) { - int rank; - int brk; - prepare_solution (start, end, sys_count, &rank, &brk); + vsize rank; + vsize end_brk; + prepare_solution (start, end, sys_count, &rank, &end_brk); vector const &st = state_[start]; vector ret; - for (int sys = sys_count-1; sys >= 0; sys--) + /* find the first solution that satisfies constraints */ + for (vsize sys = sys_count-1; sys != VPOS; sys--) { - assert (brk > 0); - ret.push_back (st[sys*rank + brk].line_config_); - brk = st[sys*rank + brk].prev_; + for (vsize brk = end_brk; brk != VPOS; brk--) + { + if (!isinf (st[sys*rank + brk].force_)) + { + if (brk != end_brk) + { + warning ( _("couldn't find line breaking that satisfies constraints" )); + ret.push_back (space_line (brk, end_brk)); + } + /* build up the good solution */ + for (vsize cur_sys = sys; cur_sys != VPOS; cur_sys--) + { + assert (brk != VPOS); + ret.push_back( st[cur_sys*rank + brk].line_config_ ); + brk = st[cur_sys*rank + brk].prev_; + } + reverse (ret); + return ret; + } + } } - assert (brk == 0); - - reverse (ret); + /* if we get to here, just put everything on one line */ + warning ( _("couldn't find line breaking that satisfies constraints" )); + ret.push_back (space_line (0, end_brk)); return ret; } Real -Constrained_breaking::get_demerits (int start, int end, int sys_count) +Constrained_breaking::get_demerits (vsize start, vsize end, vsize sys_count) { - int rank; - int brk; + vsize rank; + vsize brk; prepare_solution (start, end, sys_count, &rank, &brk); return state_[start][(sys_count-1)*rank + brk].demerits_; } Real -Constrained_breaking::get_force (int start, int end, int sys_count) +Constrained_breaking::get_force (vsize start, vsize end, vsize sys_count) { - int rank; - int brk; + vsize rank; + vsize brk; prepare_solution (start, end, sys_count, &rank, &brk); vector const &st = state_[start]; Real f = 0; - for (int sys = sys_count-1; sys >= 0 && brk >= 0; sys--) + for (int sys = sys_count-1; sys >= 0 && brk != VPOS; sys--) { f += fabs (st[sys*rank + brk].force_); brk = st[sys*rank + brk].prev_; } - if (brk < 0) + if (brk == VPOS) f = infinity_f; return f; } Real -Constrained_breaking::get_penalty (int start, int end, int sys_count) +Constrained_breaking::get_penalty (vsize start, vsize end, vsize sys_count) { - int rank; - int brk; + vsize rank; + vsize brk; prepare_solution (start, end, sys_count, &rank, &brk); return state_[start][(sys_count-1)*rank + brk].penalty_; } Real -Constrained_breaking::get_page_penalty (int start, int end, int sys_count, int sys_num) +Constrained_breaking::get_page_penalty (vsize start, vsize end, vsize sys_count, vsize sys_num) { - int rank; - int brk; + vsize rank; + vsize brk; prepare_solution (start, end, sys_count, &rank, &brk); - int sys; + vsize sys; for (sys = sys_count-1; sys > sys_num; sys--) - brk = state_[start][sys*rank + brk].prev_; + brk = state_[start][sys*rank + brk].prev_; - if (brk < 0) /* we didn't satisfy constraints */ + if (brk == VPOS) /* we didn't satisfy constraints */ return 0; vector &cols = state_[start][sys*rank + brk].line_config_.cols_; if (cols.empty ()) @@ -296,12 +311,13 @@ Constrained_breaking::get_page_penalty (int start, int end, int sys_count, int s } int -Constrained_breaking::get_min_systems (int start, int end) +Constrained_breaking::get_min_systems (vsize start, vsize end) { - int rank; - int brk; + vsize rank; + vsize brk; + vsize sys_count; + prepare_solution (start, end, 1, &rank, &brk); - int sys_count; vector const &st = state_[start]; /* sys_count < rank : rank is the # of breakpoints, we can't have more systems */ @@ -309,8 +325,7 @@ Constrained_breaking::get_min_systems (int start, int end) { if (sys_count >= valid_systems_) { - systems_ = sys_count + 3; - resize (); + resize (sys_count + 3); } if (!isinf (st[sys_count*rank + brk].force_)) return sys_count + 1; @@ -320,29 +335,24 @@ Constrained_breaking::get_min_systems (int start, int end) } int -Constrained_breaking::get_max_systems (int start, int end) +Constrained_breaking::get_max_systems (vsize start, vsize end) { - int brk = (end < 0 || end >= (int)start_.size ()) ? breaks_.size () - 1 : start_[end]; + vsize brk = (end >= start_.size ()) ? breaks_.size () - 1 : start_[end]; return brk - starting_breakpoints_[start]; } void -Constrained_breaking::prepare_solution (vsize start, int end, int sys_count, int *rank, int *brk) +Constrained_breaking::prepare_solution (vsize start, vsize end, vsize sys_count, vsize *rank, vsize *brk) { - assert (start < start_.size () && end <= int (start_.size ())); - assert (end < 0 || int (start) < end); - assert (sys_count > 0); + assert (start < start_.size () && (end == VPOS || end <= start_.size ())); + assert (start < end); - if (sys_count >= valid_systems_) - { - systems_ = sys_count; - resize (); - } - if (end == (int)start_.size ()) - end = -1; + resize (sys_count); + if (end == start_.size ()) + end = VPOS; *rank = breaks_.size () - starting_breakpoints_[start]; - *brk = end < 0 ? breaks_.size () - 1 : starting_breakpoints_[end]; + *brk = end == VPOS ? breaks_.size () - 1 : starting_breakpoints_[end]; *brk -= starting_breakpoints_[start]; } diff --git a/lily/include/constrained-breaking.hh b/lily/include/constrained-breaking.hh index 57e71984a4..f96e264a16 100644 --- a/lily/include/constrained-breaking.hh +++ b/lily/include/constrained-breaking.hh @@ -12,16 +12,16 @@ #include "break-algorithm.hh" -/** +/* Helper to trace back an optimal path */ struct Constrained_break_node { - /** the number of bars in all the systems before this one - */ + /* the number of bars in all the systems before this one + */ int prev_; - /** unlike the Gourlay breaker, this is the sum of all demerits up to, + /* unlike the Gourlay breaker, this is the sum of all demerits up to, * and including, this line */ Real demerits_; Real force_; @@ -44,7 +44,7 @@ struct Constrained_break_node } }; -/** +/* A dynamic programming solution to breaking scores into lines */ class Constrained_breaking : public Break_algorithm @@ -54,27 +54,29 @@ public: Constrained_breaking (); Constrained_breaking (std::vector const &start_col_posns); - std::vector get_solution(int start, int end, int sys_count); - Real get_demerits (int start, int end, int sys_count); - Real get_force (int start, int end, int sys_count); - Real get_penalty (int start, int end, int sys_count); - int get_max_systems (int start, int end); - int get_min_systems (int start, int end); + std::vector get_solution(vsize start, vsize end, vsize sys_count); + Real get_demerits (vsize start, vsize end, vsize sys_count); + Real get_force (vsize start, vsize end, vsize sys_count); + Real get_penalty (vsize start, vsize end, vsize sys_count); + int get_max_systems (vsize start, vsize end); + int get_min_systems (vsize start, vsize end); /* get the page penalty of system number sys with the given breaking */ - Real get_page_penalty (int start, int end, int sys_count, int sys); + Real get_page_penalty (vsize start, vsize end, vsize sys_count, vsize sys); + + void resize (vsize systems); - int systems_; private: - int valid_systems_; + vsize valid_systems_; + vsize systems_; - /* the (i,j)th entry is the column configuration for breaking - between columns i and j */ + /* the (i,j)th entry is the column configuration for breaking between + columns i and j */ std::vector cols_; - int cols_rank_; + vsize cols_rank_; /* the [i](j,k)th entry is the score for fitting the first k bars onto the - first j systems, starting at the i'th allowed starting column */ + first j systems, starting at the i'th allowed starting column */ std::vector > state_; vector start_; /* the columns at which we might be asked to start breaking */ @@ -83,12 +85,12 @@ private: vector all_; std::vector breaks_; - void prepare_solution (vsize start, int end, int sys_count, int *rank, int *brk); + Column_x_positions space_line (vsize start_col, vsize end_col); + void prepare_solution (vsize start, vsize end, vsize sys_count, vsize *rank, vsize *brk); void combine_demerits (Column_x_positions const &, Column_x_positions const &, - Real *force, Real *pen, Real *dem) const; + Real *force, Real *pen, Real *dem) const; - bool calc_subproblem(int start, int systems, int max_break_index); - void resize (); + bool calc_subproblem(vsize start, vsize systems, vsize max_break_index); }; #endif /* CONSTRAINED_BREAKING_HH */ diff --git a/lily/paper-score.cc b/lily/paper-score.cc index 2798b75938..3e41930daa 100644 --- a/lily/paper-score.cc +++ b/lily/paper-score.cc @@ -83,9 +83,9 @@ Paper_score::calc_breaking () int system_count = robust_scm2int (layout ()->c_variable ("system-count"), 0); if (system_count) { - Constrained_breaking *b = new Constrained_breaking (/* FIXME */); + Constrained_breaking *b = new Constrained_breaking; + b->resize (system_count); algorithm = b; - b->systems_ = system_count; } else algorithm = new Gourlay_breaking; diff --git a/lily/parenthesis-engraver.cc b/lily/parenthesis-engraver.cc index 1f6a196e32..01ea191cb3 100644 --- a/lily/parenthesis-engraver.cc +++ b/lily/parenthesis-engraver.cc @@ -70,7 +70,8 @@ ADD_TRANSLATOR (Parenthesis_engraver, /* doc */ "Parenthesize objects whose music cause has the @code{parenthesize} " "property.", - /* create */ "ParenthesesItem", + /* create */ + "ParenthesesItem ", /* accept */ "", /* read */ "", /* write */ ""); diff --git a/scm/define-grob-interfaces.scm b/scm/define-grob-interfaces.scm index db121eb7bd..c73ca99ab3 100644 --- a/scm/define-grob-interfaces.scm +++ b/scm/define-grob-interfaces.scm @@ -96,7 +96,7 @@ (ly:add-interface 'parentheses-interface "Parentheses for other objects" - '()) + '(padding)) (ly:add-interface 'piano-pedal-interface