lilypond-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: setting the number of pages for a score


From: Joe Neeman
Subject: Re: setting the number of pages for a score
Date: Mon, 20 Feb 2006 08:33:07 +1100
User-agent: Mozilla Thunderbird 1.0.7 (X11/20051121)

I was going to wait until my previous replies turned up on the list, but it's taking a while...

Joe Neeman wrote:

              /* TODO: support raggedness */

Done.

if (!systems_) systems_ = 4; /* just to make this compatible with Gourlay */

I've changed this to print a programming_error now.

I've also cleaned up some more variable names, comments, declarations and includes.
/*
  constrained-breaking.cc -- implement a line breaker that
  support limits on the number of systems

  source file of the GNU LilyPond music typesetter

  (c) 2006 Han-Wen Nienhuys <address@hidden>
*/

#include "constrained-breaking.hh"

#include "warn.hh"
#include "main.hh"
#include "paper-column.hh"
#include "paper-score.hh"
#include "output-def.hh"
#include "simple-spacer.hh"
#include "system.hh"

#include "international.hh"

void
print_constrained_break_nodes (vector<Constrained_break_node> 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
*/

/* 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) */
bool
Constrained_breaking::calc_subproblem (int start, int sys, int max_break)
{
  assert (sys < systems_);
  assert (start < (int)start_.size ());
  assert (max_break < (int)breaks_.size ());

  bool found_something = false;
  int start_col = starting_breakpoints_[start];
  vector<Constrained_break_node> &st = state_[start];
  int rank = breaks_.size () - start_col;
  int max_index = max_break - start_col;
  for (int 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 prev;
      Real prev_dem = 0;

      if (sys > 0)
        {
          prev = st[(sys-1) * rank + j].line_config_;
          prev_dem = st[(sys-1) * rank + j].demerits_;
        }
      if (isinf (prev_dem))
        break;

      Real dem, force, pen;
      combine_demerits(prev, cur, &force, &pen, &dem);
      dem += prev_dem;
      if (isinf (dem))
        continue;

      if (isinf (st[sys*rank + max_index].demerits_)
          || dem < st[sys*rank + max_index].demerits_)
        {
          found_something = true;
          st[sys*rank + max_index].demerits_ = dem;
          st[sys*rank + max_index].force_ = force;
          st[sys*rank + max_index].penalty_ = pen;
          st[sys*rank + max_index].prev_ = j;
          st[sys*rank + max_index].line_config_ = cur;
        }
    }
  return found_something;
}

vector<Column_x_positions>
Constrained_breaking::do_solve ()
{
  if (!systems_)
    {
      programming_error (_f ("no system number set in constrained-breaking"));
      systems_ = 4;
    }

  resize ();
  return get_solution(0, systems_, -1);
}

void
Constrained_breaking::resize ()
{
  if (!breaks_.size ())
    {
      bool ragged_right = to_boolean (pscore_->layout ()->c_variable 
("ragged-right"));
      bool ragged_last = to_boolean (pscore_->layout ()->c_variable 
("raggedlast"));

      /* do all the rod/spring problems */
      breaks_ = 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<Grob*> line (all_.begin () + breaks_[i],
                                  all_.begin() + breaks_[j] + 1);

              line[0] = dynamic_cast<Item *> (line[0])->find_prebroken_piece 
(RIGHT);
              line.back () = dynamic_cast<Item *> (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);

              /* TODO: support raggedness */
              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;
            }

      /* work out all the starting indices */
      for (vsize i = 0; i < start_.size (); i++)
        {
          vsize j;
          for (j = 0; j < breaks_.size () - 1 && breaks_[j] < start_[i]; j++)
            ;
          starting_breakpoints_.push_back (j);
          start_[i] = breaks_[j];
        }
      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_;
}

vector<Column_x_positions>
Constrained_breaking::get_solution (int start, int end, int sys_count)
{
  int rank, brk;
  prepare_solution (start, end, sys_count, &rank, &brk);

  vector<Constrained_break_node> const &st = state_[start];
  vector<Column_x_positions> ret;

  for (int sys = sys_count-1; sys >= 0; sys--)
    {
      assert (brk > 0);
      ret.push_back( st[sys*rank + brk].line_config_ );
      brk = st[sys*rank + brk].prev_;
    }
  assert (brk == 0);
  reverse (ret);
  return ret;
}

Real
Constrained_breaking::get_demerits (int start, int end, int sys_count)
{
  int rank, 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)
{
  int rank, brk;
  prepare_solution (start, end, sys_count, &rank, &brk);
  vector<Constrained_break_node> const &st = state_[start];
  Real f = 0;

  for (int sys = sys_count-1; sys >= 0 && brk >= 0; sys--)
    {
      f += fabs (st[sys*rank + brk].force_);
      brk = st[sys*rank + brk].prev_;
    }
  if (brk < 0)
    f = infinity_f;

  return f;
}

Real
Constrained_breaking::get_penalty (int start, int end, int sys_count)
{
  int rank, 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)
{
  int rank, brk;
  prepare_solution (start, end, sys_count, &rank, &brk);

  int sys;
  for (sys = sys_count-1; sys > sys_num; sys--)
      brk = state_[start][sys*rank + brk].prev_;

  if (brk < 0) /* we didn't satisfy constraints */
    return 0;
  vector<Grob*> &cols = state_[start][sys*rank + brk].line_config_.cols_;
  if (cols.empty ())
    return 0;

  Grob *pc = cols.back ();
  if (pc->original ())
    {
      SCM pen = pc->get_property ("page-penalty");
      if (scm_is_number (pen) && fabs (scm_to_double (pen)) < 10000)
        return scm_to_double (pen);
    }
  return 0;
}

int
Constrained_breaking::get_min_systems (int start, int end)
{
  int rank, brk;
  prepare_solution (start, end, 1, &rank, &brk);
  int sys_count;
  vector<Constrained_break_node> const &st = state_[start];

  /* sys_count < rank : rank is the # of breakpoints, we can't have more 
systems */
  for (sys_count = 0; sys_count < rank; sys_count++)
    {
      if (sys_count >= valid_systems_)
        {
          systems_ = sys_count + 3;
          resize ();
        }
      if (!isinf (st[sys_count*rank + brk].force_))
        return sys_count + 1;
    }
  /* no possible breaks satisfy constraints */
  return 0;
}

int
Constrained_breaking::get_max_systems (int start, int end)
{
  int brk = (end < 0 || end >= (int)start_.size ()) ? breaks_.size () - 1 : 
start_[end];
  return brk - starting_breakpoints_[start];
}

void
Constrained_breaking::prepare_solution (int start, int end, int sys_count, int 
*rank, int *brk)
{
  assert (start < (int)start_.size () && end <= (int)start_.size ());
  assert (end < 0 || start < end);
  assert (sys_count > 0);

  if (sys_count >= valid_systems_)
    {
      systems_ = sys_count;
      resize ();
    }
  if (end == (int)start_.size ())
    end = -1;

  *rank = breaks_.size () - starting_breakpoints_[start];
  *brk = end < 0 ? breaks_.size () - 1 : starting_breakpoints_[end];
  *brk -= starting_breakpoints_[start];
}

Constrained_breaking::Constrained_breaking ()
{
  valid_systems_ = systems_ = 0;
  start_.push_back (0);
}

Constrained_breaking::Constrained_breaking (vector<int> const &start):
  start_ (start)
{
  valid_systems_ = systems_ = 0;
}

void
Constrained_breaking::combine_demerits (Column_x_positions const &prev,
                                        Column_x_positions const &col,
                                        Real *force,
                                        Real *penalty,
                                        Real *demerits) const
{
  *penalty = 0;
  if (col.cols_.empty () || !col.satisfies_constraints_)
    *force = infinity_f;
  else
    {
      *force = col.force_;

      Grob *pc = col.cols_.back ();
      if (pc->original ())
        {
          SCM pen = pc->get_property ("penalty");
          if (scm_is_number (pen) && fabs (scm_to_double (pen)) < 10000)
            *penalty += scm_to_double (pen);
        }
    }

  *demerits = (*force) * (*force) + abs (prev.force_ - *force) + *penalty;
}

/*
  optimal-breaking-scheme.cc -- implement Optimal_breaking bindings

  source file of the GNU LilyPond music typesetter

  (c) 2006 Han-Wen Nienhuys <address@hidden>
*/

#include "paper-book.hh"
#include "optimal-breaking.hh"

LY_DEFINE (ly_optimal_breaking, "ly:optimal-breaking",
           1, 0, 0, (SCM pb),
           "Optimally break (pages and lines) the Paper_book PB, returning its 
pages.")
{
  Optimal_breaking b (unsmob_paper_book (pb));
  return b.solve();
}

/*
  optimal-breaking.cc -- implement Optimal_breakinr

  source file of the GNU LilyPond music typesetter

  (c) 2006 Han-Wen Nienhuys <address@hidden>
*/

#include "optimal-breaking.hh"

#include "item.hh"
#include "paper-book.hh"
#include "output-def.hh"
#include "system.hh"
#include "paper-score.hh"
#include "paper-system.hh"
#include "warn.hh"

#include "international.hh"

System_spec::System_spec (Paper_score *ps, int break_count)
{
  pscore_ = ps;
  prob_ = 0;
  Output_def *l = ps->layout ();
  size_ = scm_to_double (l->c_variable ("system-height"));
  penalty_ = 0;
  between_size_ = scm_to_double (l->c_variable ("between-system-padding"));
  between_space_ = scm_to_double (l->c_variable ("between-system-space"));

  score_break_count_ = break_count;
}

System_spec::System_spec (Prob *pb)
{
  pscore_ = 0;
  prob_ = pb;
  Stencil *s = unsmob_stencil (pb->get_property ("stencil"));
  size_ = s->extent (Y_AXIS).length ();
  /* these get determined later */
  penalty_ = 0;
  between_size_ = 0;
  between_space_ = 0;

  score_break_count_ = 0;
}

System_spec::System_spec ()
{
  size_ = between_size_ = between_space_ = 0;
  pscore_ = 0;
  prob_ = 0;
  score_break_count_ = 0;
}

void
Page_spacing::calc_force ()
{
  if (rod_height_ >= page_height_ || !inverse_spring_k_)
    force_ = infinity_f;
  else
    force_ = (spring_space_ - spring_len_) / inverse_spring_k_;
}

/* move sys from me to the given page,
 * updating all the forces. prev will now be the last system on this page */
void
Page_spacing::shift_system (const System_spec &sys,
                            const System_spec &prev,
                            Page_spacing *to)
{
  rod_height_ -= sys.size_ + prev.between_size_;
  spring_len_ -= sys.between_space_;
  spring_space_ += prev.between_size_;
  inverse_spring_k_--;
  if (to->rod_height_)
    {
      to->rod_height_ += sys.between_size_;
      to->spring_space_ -= sys.between_size_;
    }
  to->rod_height_ += sys.size_;
  to->spring_len_ += sys.between_space_;
  to->inverse_spring_k_++;

  calc_force ();
  to->calc_force ();
}

void
Page_spacing::fill_page (vector<System_spec> const &sys)
{
  rod_height_ = 0;
  spring_space_ = page_height_;
  spring_len_ = 0;
  inverse_spring_k_ = 0;

  for (vsize i = 0; i < sys.size (); i++)
    {
      rod_height_ += sys[i].size_ + sys[i].between_size_;
      spring_len_ += sys[i].between_space_;
      spring_space_ -= sys[i].between_size_;
      inverse_spring_k_++;
    }
  rod_height_ -= sys.back ().between_size_;
  spring_space_ += sys.back ().between_size_;

  calc_force ();
}

/* for Optimal_breaking, the start index (when we are dealing with the stuff
 * between a pair of breakpoints) refers to the break_ index of the end of
 * the previous page. So the system index of the start of the current page
 * could either be the same as the end of the previous page or one more than
 * it. */

/* Turn a break index into the sys index that starts the next page */
int Optimal_breaking::next_system (int start) const
{
  int sys = breaks_[start].sys_;

  if (sys < 0) /* beginning of the piece */
    return 0;
  if (all_[sys].pscore_ && all_[sys].score_break_count_ > 
breaks_[start].score_break_)
    return sys; /* the score overflows the previous page */
  return sys + 1; /* this page starts with a new sys */
}

Optimal_breaking::Optimal_breaking (Paper_book *pb)
{
  book_ = pb;
  create_system_list ();
}

void
Optimal_breaking::calc_demerits (Optimal_break_node *me)
{
  Real prev_f = 0;
  Real prev_dem = 0;
  Real break_pen = 0;
  int prev_break = 0;
  if (me->prev_ >= 0)
    {
      prev_f = state_[me->prev_].force_;
      prev_dem = state_[me->prev_].demerits_;
      prev_break = state_[me->prev_].break_pos_;
    }

  int cur_system = 1; /* for tracking down which system the page breaks */
  int start = prev_break;
  int end = me->break_pos_;
  Real line_f = 0, line_pen = 0;
  for (vsize i = next_system (prev_break); (int)i <= 
breaks_[me->break_pos_].sys_; i++)
    {
      if (all_[i].pscore_)
        {
          int sys_count = me->div_[i - next_system (prev_break)];
          if (cur_system <= me->left_page_sys_
              && cur_system + sys_count > me->left_page_sys_)
            break_pen += get_page_penalty (i, sys_count, start, end, 
me->left_page_sys_ - cur_system);
          if (cur_system + sys_count > me->left_page_sys_ + me->right_page_sys_)
            break_pen += get_page_penalty (i, sys_count, start, end,
                            me->left_page_sys_ + me->right_page_sys_ - 
cur_system);
          cur_system += sys_count;
          line_f += get_line_force (i, sys_count, start, end);
          line_pen += get_line_penalty (i, sys_count, start, end);
        }
      else
        {
          if (cur_system == me->left_page_sys_)
            break_pen += all_[i].penalty_;
          cur_system++;
        }
    }

  /* TODO: make line vs. page weighting configurable */
  me->demerits_ = me->force_ * me->force_ + line_f * line_f * 4.0 + fabs 
(me->force_ - prev_f);
  if (isinf (line_f)) /* make failed line-breaking very unattractive */
    me->demerits_ = (me->force_ + 20000.0) * 10;
  if (isinf (me->force_)) /* failed page breaking not quite as bad */
    me->demerits_ = 20000.0;

  if (me->page_number_ == 1 && scm_is_bool (book_->paper_->c_variable 
("first-page-right")))
    me->demerits_ += (to_boolean (book_->paper_->c_variable 
("first-page-right"))
                      && me->right_page_sys_ == 0) ? -10000 : 10000;

  me->demerits_ += prev_dem + break_pen + all_[breaks_[end].sys_].penalty_
                + breaks_[end].penalty_ + line_pen;
}

void
Optimal_breaking::line_breaker_args (vsize i, int *start, int *end)
{
  assert (all_[i].pscore_);
  assert (next_system (*start) <= (int)i && (int)i <= breaks_[*end].sys_);

  int start_col = 0;
  int end_col = -1;

  if (breaks_[*start].sys_ == (int)i)
    start_col = breaks_[*start].score_break_;
  if (breaks_[*end].sys_ == (int)i)
    end_col = breaks_[*end].score_break_;

  assert (end_col && end_col <= all_[breaks_[*end].sys_].score_break_count_);
  *start = start_col;
  *end = end_col;
}

Real
Optimal_breaking::get_page_penalty (vsize i, int sys_count, int start, int end, 
int sys_num)
{
  line_breaker_args (i, &start, &end);
  return line_breaking_[i].get_page_penalty (start, end, sys_count, sys_num);
}

vector<Column_x_positions>
Optimal_breaking::get_line_breaks (vsize i, int sys_count, int start, int end)
{
  line_breaker_args (i, &start, &end);
  return line_breaking_[i].get_solution (start, end, sys_count);
}

Real
Optimal_breaking::get_line_break_dems (vsize i, int sys_count, int start, int 
end)
{
  line_breaker_args (i, &start, &end);
  return line_breaking_[i].get_demerits (start, end, sys_count);
}

Real
Optimal_breaking::get_line_force (vsize i, int sys_count, int start, int end)
{
  line_breaker_args (i, &start, &end);
  return line_breaking_[i].get_force (start, end, sys_count);
}

Real
Optimal_breaking::get_line_penalty (vsize i, int sys_count, int start, int end)
{
  line_breaker_args (i, &start, &end);
  return line_breaking_[i].get_penalty (start, end, sys_count);
}

vsize
Optimal_breaking::get_min_systems (vsize i, int start, int end)
{
  line_breaker_args (i, &start, &end);
  return line_breaking_[i].get_min_systems (start, end);
}

vsize
Optimal_breaking::get_max_systems (vsize i, int start, int end)
{
  line_breaker_args (i, &start, &end);
  return line_breaking_[i].get_max_systems (start, end);
}

Real
Optimal_breaking::page_height (int page_num, bool last)
{
  SCM mod = scm_c_resolve_module ("scm page");
  SCM calc_height = scm_c_module_lookup (mod, "calc-printable-height");
  SCM make_page = scm_c_module_lookup (mod, "make-page");

  calc_height = scm_variable_ref (calc_height);
  make_page = scm_variable_ref (make_page);

  SCM page = scm_apply_0 (make_page, scm_list_n (
                  book_->self_scm (),
                  ly_symbol2scm ("page-number"), scm_from_int (page_num),
                  ly_symbol2scm ("is-last"), scm_from_bool (last),
                  SCM_UNDEFINED));
  SCM height = scm_apply_1 (calc_height, page, SCM_EOL);
  return scm_to_double (height);
}

void
Optimal_breaking::best_page (vector<System_spec> const &sys,
                             Optimal_break_node *me)
{
  Drul_array<Page_spacing> page;

  vector<System_spec> rep_sys;

  bool last = me->break_pos_ == (int) breaks_.size () - 1;
  bool ragged_all = to_boolean (book_->paper_->c_variable ("ragged-bottom"));
  bool ragged_last = to_boolean (book_->paper_->c_variable 
("ragged-last-bottom"));
  bool ragged = ragged_all || (last && ragged_last);

  page[LEFT].page_height_ = page_height (me->page_number_, last);
  page[RIGHT].page_height_ = page_height (me->page_number_ + 1, last);

  /* repeat each score for the number of systems it has */
  for (vsize i = 0; i < sys.size (); i++)
    for (vsize j = 0; j < me->div_[i]; j++)
      rep_sys.push_back (sys[i]);

  page[LEFT].fill_page (rep_sys);

  if (ragged && !isinf(page[LEFT].force_))
    me->force_ = max (0.0, page[LEFT].force_);
  else
    me->force_ = fabs (page[LEFT].force_);
  if (!last)
    me->force_ += robust_scm2double (book_->paper_->c_variable 
("blank-page-force"), 0.0);
  else
    me->force_ += robust_scm2double (book_->paper_->c_variable 
("blank-last-page-force"), 0.0);

  if (me->page_number_ == 1)
    {
      me->left_page_sys_ = 0;
      me->right_page_sys_ = rep_sys.size ();
      me->page_number_ = 0; /* because, for this configuration, we start on the 
right page */
    }
  else
    {
      me->left_page_sys_ = rep_sys.size ();
      me->right_page_sys_ = 0;
    }

  calc_demerits (me);

  /* move things over one system at a time */
  Optimal_break_node cur = *me;
  page[RIGHT].spring_space_ = page[RIGHT].page_height_;
  for (vsize i = rep_sys.size () - 1; i > 0; i--)
    {
      page[LEFT].shift_system (rep_sys[i], rep_sys[i-1], &page[RIGHT]);

      if (isinf (page[RIGHT].force_)) /* it's only going to get worse if we add 
more systems */
        return;
      if (isinf (page[LEFT].force_))
        continue;

      page[LEFT].force_ = ragged_all ? max (0.0, page[LEFT].force_) : fabs 
(page[LEFT].force_);
      page[RIGHT].force_ = ragged    ? max (0.0, page[RIGHT].force_) : fabs 
(page[RIGHT].force_);

      cur.force_ = fabs (page[LEFT].force_) + fabs (page[RIGHT].force_)
                 + fabs (page[LEFT].force_ - page[RIGHT].force_);
      cur.left_page_sys_ = i;
      cur.right_page_sys_ = rep_sys.size () - i;
      calc_demerits (&cur);
      if (cur.demerits_ < me->demerits_)
        {
          *me = cur;
          if (!me->page_number_)
            me->page_number_ = 1;
        }
    }
}


void
Optimal_breaking::calc_subproblem(int i)
{
  int end = i + 1;
  Optimal_break_node best;
  Optimal_break_node cur;

  cur.break_pos_ = end;
  for (int start = end - 1; start >= 0; start--)
    {
      int p_num = (start == 0) ? 1 : state_[start-1].page_number_ + 2;
      vector<System_spec> systems (all_.begin () + next_system (start),
                                   all_.begin () + breaks_[end].sys_ + 1);

      Optimal_break_node this_break_best;
      cur.page_number_ = p_num;
      cur.prev_ = start - 1;

      vector<vsize> min_systems, max_systems;
      int min_system_count = 0, max_system_count = 0;
      calc_system_count_bounds (start, end, &min_systems, &max_systems);

      for (vsize i = 0; i < min_systems.size (); i++)
        {
          min_system_count += min_systems[i];
          max_system_count += max_systems[i];
        }

      bool ok_page = true;
      for (int sys_count = min_system_count; sys_count <= max_system_count && 
ok_page; sys_count++)
        {
          vector<vector<vsize> > div;
          vector<vsize> blank;
          divide_systems (sys_count, min_systems, max_systems, &div, &blank);

          for (vsize d = 0; d < div.size (); d++)
            {
              cur.div_ = div[d];
              best_page (systems, &cur);

              Real line_f = 0;
              for (vsize j = 0; j < systems.size (); j++)
                if (systems[j].pscore_)
                  line_f += get_line_force (j + next_system(start), div[d][j], 
start, end);

              if (isinf (cur.force_))
                {
                  ok_page = false;
                  break;
                }

              cur.line_demerits_ = line_f;
              if (isinf(this_break_best.demerits_) || cur.demerits_ < 
this_break_best.demerits_)
                {
                  this_break_best = cur;
                  this_break_best.div_ = div[d];
                }
            }
        }
      if (isinf (cur.demerits_) && start == end - 1)
        {
          programming_error (_f ("couldn't break between %d and %d", start, 
end));
          assert (0);
        }
      /*message (_f ("the page between %d and %d has prev=%d, force=%f, dem=%f, 
line_dem=%f",
                      start, end, this_break_best.prev_+1, 
this_break_best.force_,
                      this_break_best.demerits_, 
this_break_best.line_demerits_));*/

      if (isinf (best.demerits_) || this_break_best.demerits_ < best.demerits_)
        best = this_break_best;
    }
  state_.push_back (best);
}

SCM
Optimal_breaking::solve ()
{
  state_.clear ();
  message (_f ("Calculating page and line breaks (%d possible page breaks)...",
               (int)breaks_.size () - 1) + " ");
  for (vsize i = 0; i < breaks_.size () - 1; i++)
    {
      calc_subproblem (i);
      progress_indication (string ("[") + to_string (i + 1) + "]");
    }
  progress_indication ("\n");

  vector<Optimal_break_node> breaking;
  int i = state_.size () - 1;
  while (i >= 0)
    {
      breaking.push_back (state_[i]);
      i = state_[i].prev_;
    }
  reverse (breaking);
  SCM systems = make_lines (breaking);
  return make_pages (breaking, systems);
}

/* do the line breaking in all the scores and return a big list of systems */
SCM
Optimal_breaking::make_lines (vector<Optimal_break_node> const &soln)
{
  int total_n_systems = 0;
  for (vsize n = 0; n < soln.size (); n++)
    {
      vsize start = n > 0 ? soln[n-1].break_pos_ : 0;
      vsize end = soln[n].break_pos_;
      int n_systems = 0;

      /* break each score into pieces */
      for (vsize i = next_system (start); (int) i <= breaks_[end].sys_; i++)
        {
          vsize d = i - next_system (start);
          if (all_[i].pscore_)
            {
              vector<Column_x_positions> line_brk;
              Real dems = get_line_force (i, soln[n].div_[d], start, end);
              if (isinf (dems))
                  ::message (_("WTF? I chose an infinite-demerits line 
breaking"));

              line_brk = get_line_breaks (i, soln[n].div_[d], start, end);
              all_[i].pscore_->root_system ()->break_into_pieces (line_brk);
              n_systems += line_brk.size ();
            }
          else
            n_systems++;
        }
      assert (n_systems == soln[n].left_page_sys_ + soln[n].right_page_sys_);
      total_n_systems += n_systems;
    }
  SCM ret = SCM_EOL;
  SCM *tail = &ret;
  for (vsize i = 0; i < all_.size (); i++)
    {
      if (all_[i].pscore_)
        for (SCM s = scm_vector_to_list (all_[i].pscore_->root_system 
()->get_paper_systems ());
              scm_is_pair (s); s = scm_cdr (s))
          {
            *tail = scm_cons (scm_car (s), SCM_EOL);
            tail = SCM_CDRLOC (*tail);
          }
      else
        {
          *tail = scm_cons (all_[i].prob_->self_scm (), SCM_EOL);
          all_[i].prob_->unprotect ();
          tail = SCM_CDRLOC (*tail);
        }
    }
  assert (scm_to_int (scm_length (ret)) == total_n_systems);
  return ret;
}

SCM
Optimal_breaking::make_pages (vector<Optimal_break_node> const &soln, SCM 
systems)
{
  SCM prev_page = SCM_BOOL_F;
  SCM make_page = ly_lily_module_constant ("make-page-from-systems");
  SCM book = book_->self_scm ();
  bool ragged_all = to_boolean (book_->paper_->c_variable ("ragged-bottom"));
  bool ragged_last = to_boolean (book_->paper_->c_variable 
("ragged-last-bottom"));
  SCM ret = SCM_EOL;

  for (vsize i = 0; i < soln.size (); i++)
    {
      if (i > 0 || soln[i].left_page_sys_)
        {
          SCM page_num = scm_from_int (soln[i].page_number_);
          SCM last = scm_from_bool (i == soln.size () - 1 && 
soln[i].right_page_sys_ == 0);
          SCM ragged = scm_from_bool (ragged_all || (to_boolean (last) && 
ragged_last));
          SCM lines = SCM_EOL;
          for (int j = 0; j < soln[i].left_page_sys_; j++, systems = 
scm_cdr(systems))
            lines = scm_cons (scm_car (systems), lines);
          lines = scm_reverse (lines);

          SCM page = scm_apply_0 (make_page,
                      scm_list_n (book, lines, page_num, prev_page, ragged, 
last, SCM_UNDEFINED));
          ret = scm_cons (page, ret);
        }

      if (i < soln.size () - 1 || soln[i].right_page_sys_)
        {
          SCM page_num = scm_from_int (soln[i].page_number_ + 1);
          SCM last = scm_from_bool (i == soln.size () - 1);
          SCM lines = SCM_EOL;
          SCM ragged = scm_from_bool (ragged_all || (to_boolean (last) && 
ragged_last));
          for (int j = 0; j < soln[i].right_page_sys_; j++, systems = 
scm_cdr(systems))
            lines = scm_cons (scm_car (systems), lines);
          lines = scm_reverse (lines);

          SCM page = scm_apply_0 (make_page,
                      scm_list_n (book, lines, page_num, prev_page, ragged, 
last, SCM_UNDEFINED));
          ret = scm_cons (page, ret);
        }
    }
  return scm_reverse (ret);
}

/* unlike the default page breaker, we store our penalties in the system that
 * ends the page */
void
Optimal_breaking::add_break_penalty (Prob *pb)
{
  if (all_.empty ())
    return;

  int sys = all_.size () - 1;
  Real pen = robust_scm2double (pb->get_property ("penalty"), 0.0);

  if (all_.back ().pscore_)
    {
      /* check if there's already a breakpoint there */
      if (breaks_.back ().sys_ == sys
          && breaks_.back ().score_break_ == all_.back ().score_break_count_)
        breaks_.back ().penalty_ += pen;
      else
        breaks_.push_back (Break_position (sys, all_[sys].score_break_count_, 
pen));
    }
  else
    breaks_.push_back (Break_position (sys, -1, pen));
}

void
Optimal_breaking::create_system_list ()
{
  breaks_.push_back (Break_position ());

  SCM specs = book_->get_system_specs ();
  for (SCM s = specs; s != SCM_EOL; s = scm_cdr (s))
    {
      if (Paper_score *ps = dynamic_cast<Paper_score*> (unsmob_music_output 
(scm_car (s))))
        {
          /* add a breakpoint at the end of the last score, if necessary */
          if (all_.size () && all_.back ().pscore_)
            breaks_.push_back (Break_position (all_.size () - 1,
                                               all_.back 
().score_break_count_));

          vector<int> score_brk = find_page_break_indices (ps);
          all_.push_back (System_spec (ps, score_brk.size () + 1));

          for (vsize i = 0; i < score_brk.size(); i++)
            breaks_.push_back (Break_position (all_.size () - 1, i + 1));

          /* include a line breaker at the start of the score */
          score_brk.insert (score_brk.begin (), 0);
          line_breaking_.push_back (Constrained_breaking (score_brk));
          line_breaking_.back ().set_pscore (ps);
        }
      else
        {
          Prob *pb = unsmob_prob (scm_car (s));
          assert (pb);

          pb->protect ();
          add_break_penalty (pb);
          all_.push_back (System_spec (pb));
          line_breaking_.push_back (Constrained_breaking ());
        }
    }

  /* add the ending breakpoint */
  if (all_.back ().pscore_)
    breaks_.push_back (Break_position (all_.size () - 1, all_.back 
().score_break_count_));
  else
    breaks_.push_back (Break_position (all_.size () - 1));

  /* add extra title space */
  Real bet = scm_to_double (book_->paper_->c_variable ("between-title-space"));
  Real after = scm_to_double (book_->paper_->c_variable ("after-title-space"));
  Real before = scm_to_double (book_->paper_->c_variable 
("before-title-space"));
  for (vsize i = 0; i < all_.size () - 1; i++)
    {
      bool title1 = !all_[i].pscore_
                    && to_boolean (all_[i].prob_->get_property ("is-title"));
      bool title2 = !all_[i+1].pscore_
                    && to_boolean (all_[i+1].prob_->get_property ("is-title"));

      if (title1 && !all_[i].penalty_)
        all_[i].penalty_ = 10000;
      if (title1 && title2)
        all_[i].between_size_ += bet;
      else if (title1)
        all_[i].between_size_ += before;
      else if (title2)
        all_[i+1].between_size_ += after;
    }
}

vector<int>
Optimal_breaking::find_page_break_indices(Paper_score *pscore)
{
  vector<Grob*> all = pscore->root_system ()->columns ();
  vector<int> ret;

  /* we don't include breaks at the beginning and end */
  for (vsize i = 0; i < all.size () - 1; i++)
    if (Item::is_breakable (all[i]) &&
        scm_is_number (all[i]->get_property ("page-penalty")) &&
        robust_scm2double (all[i]->get_property ("page-penalty"), 0) < 0)
      ret.push_back(i);

  return ret;
}

void
Optimal_breaking::calc_system_count_bounds (vsize start, vsize end,
                                            vector<vsize> *min,
                                            vector<vsize> *max)
{
  for (int i = next_system (start); i <= breaks_[end].sys_; i++)
    {
      if (all_[i].pscore_)
        {
          min->push_back (get_min_systems (i, start, end));
          max->push_back (get_max_systems (i, start, end));
        }
      else
        {
          min->push_back (1);
          max->push_back (1);
        }
    }
}

/* calculate all possible ways of dividing system_count between the 
System_specs */
void
Optimal_breaking::divide_systems (int system_count,
                                  vector<vsize> const &min_sys,
                                  vector<vsize> const &max_sys,
                                  vector<vector<vsize> > *result,
                                  vector<vsize> *cur_division)
{
  vsize my_index = cur_division->size ();
  int others_min = 0, others_max = 0;

  for (vsize i = my_index + 1; i < min_sys.size (); i++)
    {
      others_min += min_sys[i];
      others_max += max_sys[i];
    }
  vsize real_min = max ((int)min_sys[my_index], system_count - others_max);
  vsize real_max = min ((int)max_sys[my_index], system_count - others_min);

  assert (real_min <= real_max);
  for (vsize i = real_min; i <= real_max; i++)
    {
      cur_division->push_back (i);
      if (my_index == min_sys.size () - 1)
        result->push_back (*cur_division);
      else
        divide_systems (system_count - i, min_sys, max_sys, result, 
cur_division);
      cur_division->pop_back ();
    }
}

/*
  constrained-breaking.hh -- declare a line breaker that
  supports limits on the number of systems

  source file of the GNU LilyPond music typesetter

  (c) 2006 Han-Wen Nienhuys <address@hidden>
*/

#ifndef CONSTRAINED_BREAKING_HH
#define CONSTRAINED_BREAKING_HH

#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
  */
  int prev_;

  /** unlike the Gourlay breaker, this is the sum of all demerits up to,
   * and including, this line */
  Real demerits_;
  Real force_;
  Real penalty_;
  Column_x_positions line_config_;

  Constrained_break_node ()
  {
    prev_ = -1;
    demerits_ = infinity_f;
    force_ = infinity_f;
    penalty_ = 0;
    line_config_.satisfies_constraints_ = false;
  }

  void print () const
  {
    printf ("prev break %d, demerits %f\n",
            prev_, demerits_);
  }
};

/**
   A dynamic programming solution to breaking scores into lines
*/
class Constrained_breaking : public Break_algorithm
{
  public:
    std::vector<Column_x_positions> do_solve ();
    Constrained_breaking ();
    Constrained_breaking (std::vector<int> const &start_col_posns);

    std::vector<Column_x_positions> 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);

    /* 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);

  private:
    int valid_systems_;
    int systems_;

    /* the (i,j)th entry is the column configuration for breaking between
     * columns i and j */
    std::vector<Column_x_positions> cols_;
    int 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 */
    std::vector<std::vector<Constrained_break_node> > state_;

    vector<int> start_;         /* the columns at which we might be asked to 
start breaking */
    vector<int> starting_breakpoints_; /* the corresponding index in breaks_ */

    vector<Grob*> all_;
    std::vector<int> breaks_;

    void prepare_solution (int start, int end, int sys_count, int *rank, int 
*brk);

    void combine_demerits (Column_x_positions const &, Column_x_positions const 
&,
                           Real *force, Real *pen, Real *dem) const;

    bool calc_subproblem(int start, int systems, int max_break_index);
    void resize ();
};
#endif /* CONSTRAINED_BREAKING_HH */
/*
  optimal-breaking.hh -- break lines and pages optimally
  for a whole Paper_book.

  source file of the GNU LilyPond music typesetter

  (c) 2006 Han-Wen Nienhuys <address@hidden>
*/

#ifndef OPTIMAL_BREAKING_HH
#define OPTIMAL_BREAKING_HH

#include "constrained-breaking.hh"
#include "lily-guile.hh"

/* Either a paper-score, markup or header. For paper scores, we record the
 * size of a single system. For the others, we record the size of the entire
 * thing. */
struct System_spec
{
  System_spec (Paper_score*, int);
  System_spec (Prob*);
  System_spec ();

  Real size_;
  Real between_size_; /* just like size_ but we ignore it for the last system */
  Real between_space_;
  Real penalty_;      /* we need a break penalty here as well as in
                       * Break_position because the break positions are only at
                       * possible page turns -- this could be a page break w/o
                       * a page turn */
  Paper_score *pscore_;
  Prob *prob_;

  int score_break_count_; /* for scores, this is the number of start-points our 
line-
                           * breaker has */
};

struct Break_position
{
  int sys_; /* the index in our all_ list */
  int score_break_; /* if sys_ is a score, then we start at the score_brk_'th
                     * possible page-break in the score */
  Real penalty_;

  Break_position(int s=-1, int brk=-1, Real pen=0)
  {
    sys_ = s;
    score_break_ = brk;
    penalty_ = pen;
  }
};

/**
 * A much simplified rods-and-springs problem.
 */
struct Page_spacing
{
  Real force_;
  Real page_height_;
  Real rod_height_;
  Real spring_len_;
  Real spring_space_;
  Real inverse_spring_k_;

  Page_spacing ()
    {
      force_ = page_height_ = rod_height_ = spring_len_ = 0;
      spring_space_ = inverse_spring_k_ = 0;
    }

  void calc_force ();
  void fill_page (vector<System_spec> const &sys);

  void shift_system (const System_spec &sys,
                     const System_spec &prev,
                     Page_spacing *to);
};

/**
   Helper to trace back an optimal path
*/
struct Optimal_break_node
{
  int prev_;
  int page_number_;

  Real force_;

  /** unlike the Gourlay breaker, this is the sum of all demerits up to,
   * and including, this page */
  Real demerits_;
  Real line_demerits_;
  int break_pos_; /* index into breaks_ */

  vector<vsize> div_;  /* our division of systems between scores on this page */
  int left_page_sys_, right_page_sys_;

  Optimal_break_node ()
  {
    prev_ = -1;
    left_page_sys_ = right_page_sys_ = -1;
    line_demerits_ = force_ = 0;
    demerits_ = infinity_f;
    page_number_ = 0;
  }

  void print () const
  {
    printf ("prev break %d, demerits %f\n",
            prev_, demerits_);
  }
};

/**
   A dynamic programming solution to breaking pages
*/
class Optimal_breaking
{
  public:
    SCM solve ();

    Optimal_breaking (Paper_book *pb);

  private:
    std::vector<Optimal_break_node> state_;
    std::vector<Break_position> breaks_;
    std::vector<System_spec> all_;
    std::vector<Constrained_breaking> line_breaking_;

    Paper_book *book_;

    Real page_height (int page_number, bool last);
    int next_system (int starting_break_index) const;

    void best_page (std::vector<System_spec> const &sys,
                    Optimal_break_node *me);

    void create_system_list ();
    std::vector<int> find_page_break_indices (Paper_score *pscore);
    void add_break_penalty (Prob *pb);

    SCM make_lines (vector<Optimal_break_node> const &breaks);
    SCM make_pages (vector<Optimal_break_node> const &breaks, SCM systems);

    void calc_system_count_bounds (vsize start, vsize end,
                                   vector<vsize> *min,
                                   vector<vsize> *max);

    void divide_systems (int system_count, vector<vsize> const &min,
                                           vector<vsize> const &max,
                                           vector<vector<vsize> > *result,
                                           vector<vsize> *cur);

    void calc_demerits (Optimal_break_node *me);

    void calc_subproblem (int);

    void line_breaker_args (vsize i, int *break_st, int *break_end);
    Real get_page_penalty (vsize sys, int sys_count, int break_st, int 
break_end, int sys_num);
    Real get_line_break_dems (vsize sys, int sys_count, int break_st, int 
break_end);
    Real get_line_force (vsize sys, int sys_count, int break_st, int break_end);
    Real get_line_penalty (vsize sys, int sys_count, int break_st, int 
break_end);
    vsize get_min_systems (vsize sys, int break_start, int break_end);
    vsize get_max_systems (vsize sys, int break_start, int break_end);
    std::vector<Column_x_positions> get_line_breaks (vsize sys, int sys_count, 
int st, int end);
};
#endif /* OPTIMAL_BREAKING_HH */
Index: lily/break-algorithm.cc
===================================================================
RCS file: /sources/lilypond/lilypond/lily/break-algorithm.cc,v
retrieving revision 1.54
diff -u -r1.54 break-algorithm.cc
--- lily/break-algorithm.cc     11 Feb 2006 11:35:18 -0000      1.54
+++ lily/break-algorithm.cc     19 Feb 2006 21:31:13 -0000
@@ -89,7 +89,7 @@
 }
 
 vector<Column_x_positions>
-Break_algorithm::solve () const
+Break_algorithm::solve ()
 {
   vector<Column_x_positions> h= do_solve ();
 
Index: lily/gourlay-breaking.cc
===================================================================
RCS file: /sources/lilypond/lilypond/lily/gourlay-breaking.cc,v
retrieving revision 1.93
diff -u -r1.93 gourlay-breaking.cc
--- lily/gourlay-breaking.cc    11 Feb 2006 11:35:18 -0000      1.93
+++ lily/gourlay-breaking.cc    19 Feb 2006 21:31:14 -0000
@@ -75,7 +75,7 @@
    inspiration.
 */
 vector<Column_x_positions>
-Gourlay_breaking::do_solve () const
+Gourlay_breaking::do_solve ()
 {
   vector<Break_node> optimal_paths;
   vector<Grob*> all
Index: lily/paper-book.cc
===================================================================
RCS file: /sources/lilypond/lilypond/lily/paper-book.cc,v
retrieving revision 1.132
diff -u -r1.132 paper-book.cc
--- lily/paper-book.cc  15 Feb 2006 13:02:17 -0000      1.132
+++ lily/paper-book.cc  19 Feb 2006 21:31:15 -0000
@@ -381,7 +381,7 @@
 
   pages_ = SCM_EOL;
   SCM proc = paper_->c_variable ("page-breaking");
-  pages_ = scm_apply_0 (proc, scm_list_2 (systems (), self_scm ()));
+  pages_ = scm_apply_0 (proc, scm_list_1(self_scm ()));
   return pages_;
 }
 
Index: lily/paper-score.cc
===================================================================
RCS file: /sources/lilypond/lilypond/lily/paper-score.cc,v
retrieving revision 1.97
diff -u -r1.97 paper-score.cc
--- lily/paper-score.cc 15 Feb 2006 02:06:30 -0000      1.97
+++ lily/paper-score.cc 19 Feb 2006 21:31:15 -0000
@@ -28,7 +28,7 @@
   layout_ = layout;
   system_ = 0;
   systems_ = SCM_EOL;
-  paper_systems_ = SCM_EOL;
+  paper_systems_ = SCM_BOOL_F;
 }
 
 Paper_score::Paper_score (Paper_score const &s)
@@ -91,11 +91,6 @@
   pc.back ()->set_property ("breakable", SCM_BOOL_T);
 
   system_->pre_processing ();
-
-  vector<Column_x_positions> breaking = calc_breaking ();
-  system_->break_into_pieces (breaking);
-
-  paper_systems_ = system_->get_paper_systems ();
 }
 
 System *
@@ -111,8 +106,14 @@
 }
 
 SCM
-Paper_score::get_paper_systems () const
+Paper_score::get_paper_systems ()
 {
+  if (paper_systems_ == SCM_BOOL_F)
+    {
+      vector<Column_x_positions> breaking = calc_breaking ();
+      system_->break_into_pieces (breaking);
+      paper_systems_ = system_->get_paper_systems ();
+    }
   return paper_systems_;
 }
 
Index: lily/system.cc
===================================================================
RCS file: /sources/lilypond/lilypond/lily/system.cc,v
retrieving revision 1.138
diff -u -r1.138 system.cc
--- lily/system.cc      11 Feb 2006 11:35:17 -0000      1.138
+++ lily/system.cc      19 Feb 2006 21:31:19 -0000
@@ -204,8 +204,8 @@
 {
   for (vsize i = 0; i < breaking.size (); i++)
     {
-      System *system = dynamic_cast<System *> (clone (i));
-      system->rank_ = i;
+      System *system = dynamic_cast<System *> (clone (broken_intos_.size ()));
+      system->rank_ = broken_intos_.size ();
 
       vector<Grob*> c (breaking[i].cols_);
       pscore_->typeset_system (system);
Index: lily/include/break-algorithm.hh
===================================================================
RCS file: /sources/lilypond/lilypond/lily/include/break-algorithm.hh,v
retrieving revision 1.31
diff -u -r1.31 break-algorithm.hh
--- lily/include/break-algorithm.hh     11 Feb 2006 11:35:17 -0000      1.31
+++ lily/include/break-algorithm.hh     19 Feb 2006 21:31:19 -0000
@@ -30,14 +30,14 @@
 
   Simple_spacer_wrapper *generate_spacing_problem (vector<Grob*> const &,
                                                   Interval) const;
-  virtual vector<Column_x_positions> do_solve () const = 0;
+  virtual vector<Column_x_positions> do_solve () = 0;
 
 public:
   virtual ~Break_algorithm ();
   Simple_spacer *(*get_line_spacer) ();
   Break_algorithm ();
   void set_pscore (Paper_score *);
-  vector<Column_x_positions> solve () const;
+  vector<Column_x_positions> solve ();
 };
 
 #endif // BREAK_HH
Index: lily/include/gourlay-breaking.hh
===================================================================
RCS file: /sources/lilypond/lilypond/lily/include/gourlay-breaking.hh,v
retrieving revision 1.21
diff -u -r1.21 gourlay-breaking.hh
--- lily/include/gourlay-breaking.hh    11 Feb 2006 11:35:16 -0000      1.21
+++ lily/include/gourlay-breaking.hh    19 Feb 2006 21:31:19 -0000
@@ -16,7 +16,7 @@
 */
 struct Gourlay_breaking : public Break_algorithm
 {
-  vector<Column_x_positions> do_solve () const;
+  vector<Column_x_positions> do_solve ();
   Gourlay_breaking ();
   Real combine_demerits (Column_x_positions const &, Column_x_positions const 
&) const;
 };
Index: lily/include/paper-score.hh
===================================================================
RCS file: /sources/lilypond/lilypond/lily/include/paper-score.hh,v
retrieving revision 1.39
diff -u -r1.39 paper-score.hh
--- lily/include/paper-score.hh 11 Feb 2006 11:35:16 -0000      1.39
+++ lily/include/paper-score.hh 19 Feb 2006 21:31:19 -0000
@@ -30,7 +30,7 @@
   void typeset_system (System *);
   vector<Column_x_positions> calc_breaking ();
 
-  SCM get_paper_systems () const;
+  SCM get_paper_systems ();
 protected:
   virtual void process ();
   virtual void derived_mark () const;
Index: ly/paper-defaults.ly
===================================================================
RCS file: /sources/lilypond/lilypond/ly/paper-defaults.ly,v
retrieving revision 1.22
diff -u -r1.22 paper-defaults.ly
--- ly/paper-defaults.ly        10 Feb 2006 12:27:42 -0000      1.22
+++ ly/paper-defaults.ly        19 Feb 2006 21:31:19 -0000
@@ -57,6 +57,12 @@
     %%
     between-system-padding = #(* 4 mm)
 
+    %%
+    %% the assumed system height that we use for page-breaking
+    system-height = #8
+    blank-page-force = 15
+    blank-last-page-force = 0
+
     after-title-space = 5 \mm
     before-title-space = 10 \mm
     between-title-space = 2 \mm
Index: scm/layout-page-layout.scm
===================================================================
RCS file: /sources/lilypond/lilypond/scm/layout-page-layout.scm,v
retrieving revision 1.15
diff -u -r1.15 layout-page-layout.scm
--- scm/layout-page-layout.scm  16 Feb 2006 02:17:51 -0000      1.15
+++ scm/layout-page-layout.scm  19 Feb 2006 21:31:20 -0000
@@ -136,6 +136,150 @@
   (if (ly:output-def-lookup layout 'write-page-layout #f)
       (write-page-breaks pages)))
 
+(define-public (space-systems paper-book page-height lines ragged?)
+  (define paper (ly:paper-book-paper paper-book))
+  (let* ((global-inter-system-space
+          (ly:output-def-lookup paper 'between-system-space))
+         (top-space
+          (ly:output-def-lookup paper 'page-top-space))
+         (global-fixed-dist (ly:output-def-lookup paper 
'between-system-padding))
+         
+         (system-vector (list->vector
+                         (append lines
+                                 (if (<= (length lines) 1)
+                                     '(#f)
+                                     '()))))
+         (staff-extents
+          (list->vector
+           (append (map paper-system-staff-extents lines)
+                   (if (<= (length lines) 1)
+                       '((0 . 0))
+                       '()))))
+
+         (real-extents
+          (list->vector
+           (append
+            (map
+             (lambda (sys) (paper-system-extent sys Y)) lines)
+            (if (<= (length lines) 1)
+                '((0 .  0))
+                '()))))
+         
+         (system-count (vector-length real-extents))
+         (topskip (max
+                   (+
+                    top-space
+                    (interval-end (vector-ref staff-extents 0)))
+                   (interval-end (vector-ref real-extents 0))
+                   ))
+         (last-system (vector-ref system-vector (1- system-count)))
+         (bottom-space (if (ly:prob? last-system)
+                           (ly:prob-property last-system 'bottom-space 0.0)
+                           0.0))
+         (space-left (- page-height
+                        bottom-space
+                        (apply + (map interval-length
+                                      (vector->list real-extents)))))
+
+         (space (- page-height
+                   topskip
+                   bottom-space
+                   (-  (interval-start
+                        (vector-ref real-extents (1- system-count))))))
+
+         (calc-spring
+          (lambda (idx)
+            (let* (
+                   (upper-system (vector-ref system-vector idx))
+                   (between-space (ly:prob-property upper-system 'next-space
+                                                            
global-inter-system-space))
+                   (fixed-dist (ly:prob-property upper-system 'next-padding
+                                                         global-fixed-dist))
+                   
+                   (this-system-ext (vector-ref staff-extents idx))
+                   (next-system-ext (vector-ref staff-extents (1+ idx)))
+                   (fixed (max 0 (- (+ (interval-end next-system-ext)
+                                       fixed-dist)
+                                    (interval-start this-system-ext))))
+                   (title1? (and (vector-ref system-vector idx)
+                                 (paper-system-title? (vector-ref 
system-vector idx)
+                                                           )))
+                   (title2? (and
+                             (vector-ref system-vector (1+ idx))
+                             (paper-system-title? (vector-ref system-vector 
(1+ idx)))))
+                   (ideal (+
+                           (cond
+                            ((and title2? title1?)
+                             (ly:output-def-lookup paper 'between-title-space))
+                            (title1?
+                             (ly:output-def-lookup paper 'after-title-space))
+                            (title2?
+                             (ly:output-def-lookup paper 'before-title-space))
+                            (else between-space))
+                           fixed))
+                   (hooke (/ 1 (- ideal fixed))))
+              (list ideal hooke))))
+
+         (springs (map calc-spring (iota (1- system-count))))
+         (calc-rod
+          (lambda (idx)
+            (let* (
+                   (upper-system (vector-ref system-vector idx))
+                   (fixed-dist (ly:prob-property upper-system 'next-padding
+                                                         global-fixed-dist))
+                   (this-system-ext (vector-ref real-extents idx))
+                   (next-system-ext (vector-ref real-extents (1+ idx)))
+                   
+                   (distance (max  (- (+ (interval-end next-system-ext)
+                                         fixed-dist)
+                                      (interval-start this-system-ext)
+                                      ) 0))
+                   (entry (list idx (1+ idx) distance)))
+              entry)))
+         (rods (map calc-rod (iota (1- system-count))))
+
+         ;; we don't set ragged based on amount space left.
+         ;; ragged-bottomlast = ##T is much more predictable
+         (result (ly:solve-spring-rod-problem
+                  springs rods space
+                  ragged?))
+
+         (force (car result))
+         (positions
+          (map (lambda (y)
+                 (+ y topskip))
+               (cdr  result))))
+
+    (if #f ;; debug.
+        (begin
+          (display (list "\n# systems: " system-count
+                         "\nreal-ext" real-extents "\nstaff-ext" staff-extents
+                         "\ninterscore" global-inter-system-space
+                         "\nspace-left" space-left
+                         "\nspring,rod" springs rods
+                         "\ntopskip " topskip
+                         " space " space
+                         "\npage-height" page-height
+                         "\nragged" ragged?
+                         "\nforce" force
+                         "\nres" (cdr result)
+                         "\npositions" positions "\n"))))
+
+    (cons force positions)))
+
+(define-public (make-page-from-systems p-book lines p-num prev ragged? last?)
+  (let*
+    ((page (make-page
+            p-book
+            'lines lines
+            'page-number p-num
+            'prev prev
+            'is-last last?))
+     (height (page-printable-height page))
+     (posns (cdr (space-systems p-book height lines ragged?))))
+    (page-set-property! page 'configuration posns)
+    page))
+
 ;; Optimal distribution of
 ;; lines over pages; line breaks are a given.
 
@@ -145,14 +289,14 @@
 ;; - separate function for word-wrap style breaking?
 ;; - ragged-bottom? ragged-last-bottom?
 
-(define-public (optimal-page-breaks lines paper-book)
+(define-public (optimal-page-breaks paper-book)
   "Return pages as a list starting with 1st page. Each page is a 'page Prob."
 
   (define MAXPENALTY 1e9)
   (define paper (ly:paper-book-paper paper-book))
+  (define lines (ly:paper-book-systems paper-book))
 
   ;; ugh.
-  (define page-alist (layout->page-init (ly:paper-book-paper paper-book))) 
   (define scopes (ly:paper-book-scopes paper-book))
   (define force-equalization-factor #f)
   (define (get-path node done)
@@ -181,136 +325,6 @@
                                         inter-system-space))
         user)))
 
-  (define (space-systems page-height lines ragged?)
-    (let* ((global-inter-system-space
-           (ly:output-def-lookup paper 'between-system-space))
-          (top-space
-           (ly:output-def-lookup paper 'page-top-space))
-          (global-fixed-dist (ly:output-def-lookup paper 
'between-system-padding))
-          
-          (system-vector (list->vector
-                          (append lines
-                                  (if (= (length lines) 1)
-                                      '(#f)
-                                      '()))))
-          (staff-extents
-           (list->vector
-            (append (map paper-system-staff-extents lines)
-                    (if (= (length lines) 1)
-                        '((0 . 0))
-                        '()))))
-
-          (real-extents
-           (list->vector
-            (append
-             (map
-              (lambda (sys) (paper-system-extent sys Y)) lines)
-             (if (= (length lines) 1)
-                 '((0 .  0))
-                 '()))))
-          
-          (system-count (vector-length real-extents))
-          (topskip (max
-                    (+
-                     top-space
-                     (interval-end (vector-ref staff-extents 0)))
-                    (interval-end (vector-ref real-extents 0))
-                    ))
-          (last-system (vector-ref system-vector (1- system-count)))
-          (bottom-space (if (ly:prob? last-system)
-                            (ly:prob-property last-system 'bottom-space 0.0)
-                            0.0))
-          (space-left (- page-height
-                         bottom-space
-                         (apply + (map interval-length
-                                       (vector->list real-extents)))))
-
-          (space (- page-height
-                    topskip
-                    bottom-space
-                    (-  (interval-start
-                         (vector-ref real-extents (1- system-count))))))
-
-          (calc-spring
-           (lambda (idx)
-             (let* (
-                    (upper-system (vector-ref system-vector idx))
-                    (between-space (ly:prob-property upper-system 'next-space
-                                                             
global-inter-system-space))
-                    (fixed-dist (ly:prob-property upper-system 'next-padding
-                                                          global-fixed-dist))
-                    
-                    (this-system-ext (vector-ref staff-extents idx))
-                    (next-system-ext (vector-ref staff-extents (1+ idx)))
-                    (fixed (max 0 (- (+ (interval-end next-system-ext)
-                                        fixed-dist)
-                                     (interval-start this-system-ext))))
-                    (title1? (and (vector-ref system-vector idx)
-                                  (paper-system-title? (vector-ref 
system-vector idx)
-                                                            )))
-                    (title2? (and
-                              (vector-ref system-vector (1+ idx))
-                              (paper-system-title? (vector-ref system-vector 
(1+ idx)))))
-                    (ideal (+
-                            (cond
-                             ((and title2? title1?)
-                              (ly:output-def-lookup paper 
'between-title-space))
-                             (title1?
-                              (ly:output-def-lookup paper 'after-title-space))
-                             (title2?
-                              (ly:output-def-lookup paper 'before-title-space))
-                             (else between-space))
-                            fixed))
-                    (hooke (/ 1 (- ideal fixed))))
-               (list ideal hooke))))
-
-          (springs (map calc-spring (iota (1- system-count))))
-          (calc-rod
-           (lambda (idx)
-             (let* (
-                    (upper-system (vector-ref system-vector idx))
-                    (fixed-dist (ly:prob-property upper-system 'next-padding
-                                                          global-fixed-dist))
-                    (this-system-ext (vector-ref real-extents idx))
-                    (next-system-ext (vector-ref real-extents (1+ idx)))
-                    
-                    (distance (max  (- (+ (interval-end next-system-ext)
-                                          fixed-dist)
-                                       (interval-start this-system-ext)
-                                       ) 0))
-                    (entry (list idx (1+ idx) distance)))
-               entry)))
-          (rods (map calc-rod (iota (1- system-count))))
-
-          ;; we don't set ragged based on amount space left.
-          ;; ragged-bottomlast = ##T is much more predictable
-          (result (ly:solve-spring-rod-problem
-                   springs rods space
-                   ragged?))
-
-          (force (car result))
-          (positions
-           (map (lambda (y)
-                  (+ y topskip))
-                (cdr  result))))
-
-      (if #f ;; debug.
-         (begin
-           (display (list "\n# systems: " system-count
-                          "\nreal-ext" real-extents "\nstaff-ext" staff-extents
-                          "\ninterscore" global-inter-system-space
-                          "\nspace-left" space-left
-                          "\nspring,rod" springs rods
-                          "\ntopskip " topskip
-                          " space " space
-                          "\npage-height" page-height
-                          "\nragged" ragged?
-                          "\nforce" force
-                          "\nres" (cdr result)
-                          "\npositions" positions "\n"))))
-
-      (cons force positions)))
-
   (define (walk-paths done-lines best-paths current-lines  last? current-best)
     "Return the best optimal-page-break-node that contains
 CURRENT-LINES.  DONE-LINES.reversed ++ CURRENT-LINES is a consecutive
@@ -324,8 +338,7 @@
                               (1+ (page-page-number (car best-paths)))))
 
           (this-page (make-page
-                      page-alist
-                      'paper-book paper-book
+                        paper-book
                       'is-last last?
                       'page-number this-page-num))
 
@@ -335,7 +348,7 @@
                        (and ragged-last?
                             last?)))
            (height (page-printable-height this-page))
-          (vertical-spacing (space-systems height current-lines ragged?))
+          (vertical-spacing (space-systems paper-book height current-lines 
ragged?))
           (satisfied-constraints (car vertical-spacing))
            (force (if satisfied-constraints
                      (if (and last? ragged-last?)
Index: scm/page.scm
===================================================================
RCS file: /sources/lilypond/lilypond/scm/page.scm,v
retrieving revision 1.8
diff -u -r1.8 page.scm
--- scm/page.scm        16 Feb 2006 02:17:51 -0000      1.8
+++ scm/page.scm        19 Feb 2006 21:31:20 -0000
@@ -37,10 +37,11 @@
 
 (define page-module (current-module))
 
-(define (make-page init  . args)
+(define (make-page p-book  . args)
   (let*
       ((p (apply ly:make-prob (append
-                              (list 'page init)
+                              (list 'page (layout->page-init 
(ly:paper-book-paper p-book))
+                         'paper-book p-book)
                               args))))
 
     (page-set-property! p 'head-stencil (page-header p))
@@ -196,7 +197,6 @@
       ((p-book (page-property page 'paper-book))
        (layout (ly:paper-book-paper p-book))
        (scopes (ly:paper-book-scopes p-book))
-       (lines (page-lines page))
        (number (page-page-number page))
        (last? (page-property page 'is-last))
        )
@@ -379,9 +379,6 @@
   (let*
       ((p-book (page-property page 'paper-book))
        (layout (ly:paper-book-paper p-book))
-       (scopes (ly:paper-book-scopes p-book))
-       (number (page-page-number page))
-       (last? (page-property page 'is-last))
        (h (- (ly:output-def-lookup layout 'paper-height)
               (ly:output-def-lookup layout 'top-margin)
               (ly:output-def-lookup layout 'bottom-margin)))

reply via email to

[Prev in Thread] Current Thread [Next in Thread]