]> git.donarmstrong.com Git - lilypond.git/commitdiff
* lily/include/constrained-breaking.hh (class
authorHan-Wen Nienhuys <hanwen@xs4all.nl>
Tue, 21 Feb 2006 17:55:44 +0000 (17:55 +0000)
committerHan-Wen Nienhuys <hanwen@xs4all.nl>
Tue, 21 Feb 2006 17:55:44 +0000 (17:55 +0000)
Constrained_breaking): new file.

* lily/constrained-breaking.cc (resize): new file.

* lily/gourlay-breaking.cc (do_solve): ragged-last iso raggedlast

ChangeLog
THANKS
lily/constrained-breaking.cc [new file with mode: 0644]
lily/gourlay-breaking.cc
lily/include/constrained-breaking.hh [new file with mode: 0644]
lily/paper-score.cc
python/convertrules.py

index 5c9c839fc3aca22c92465dcee875765825e9f552..482d5e568a63590de87f85f24113dabbae1ffec8 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 2006-02-21  Han-Wen Nienhuys  <hanwen@xs4all.nl>
 
+       * lily/include/constrained-breaking.hh (class
+       Constrained_breaking): new file.
+
+       * lily/constrained-breaking.cc (resize): new file.
+
+       * lily/gourlay-breaking.cc (do_solve): ragged-last iso raggedlast
+
        * Documentation/user/advanced-notation.itely (Clusters): insert
        end ignore too.
 
diff --git a/THANKS b/THANKS
index d2c1f7bcf0049de08633258d2a7470663c25e70f..3a08e00d3ffa2e837b3659751b1d4f8c0f7016e2 100644 (file)
--- a/THANKS
+++ b/THANKS
@@ -23,8 +23,8 @@ Sven Axelsson
 Werner Lemberg
 Yoshinobu Ishizaki
 
-SPONSORS
 
+SPONSORS
 
 Aaron Mehl
 Basil Crow
diff --git a/lily/constrained-breaking.cc b/lily/constrained-breaking.cc
new file mode 100644 (file)
index 0000000..8b5e11c
--- /dev/null
@@ -0,0 +1,343 @@
+/*
+  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 <hanwen@xs4all.nl>
+*/
+
+#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 ("ragged-last"));
+
+      /* 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;
+}
+
index dfbd0b0f5c3ae714058939b81f0a5e2a637c54af..a02b4a553371ff798556a11992a5ddcaf1761880 100644 (file)
@@ -87,7 +87,7 @@ Gourlay_breaking::do_solve () const
   optimal_paths.push_back (first_node);
 
   bool ragged_right = to_boolean (pscore_->layout ()->c_variable ("ragged-right"));
-  bool ragged_last = to_boolean (pscore_->layout ()->c_variable ("raggedlast"));
+  bool ragged_last = to_boolean (pscore_->layout ()->c_variable ("ragged-last"));
 
   Real worst_force = 0.0;
   for (vsize break_idx = 1; break_idx < breaks.size (); break_idx++)
diff --git a/lily/include/constrained-breaking.hh b/lily/include/constrained-breaking.hh
new file mode 100644 (file)
index 0000000..11965e8
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+  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 <hanwen@xs4all.nl>
+*/
+
+#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 */
index 5f7e1540df1be0fa81ba6c17826cef5b50d99bda..330bf3ae4965dde62c1b75e4023ab747bd7ae420 100644 (file)
@@ -63,8 +63,16 @@ Paper_score::calc_breaking ()
 {
   Break_algorithm *algorithm = 0;
   vector<Column_x_positions> sol;
-
-  algorithm = new Gourlay_breaking;
+  
+  int system_count = robust_scm2int (layout ()->c_variable ("system-count"), 0);
+  if (system_count)
+    {
+      Constrained_breaking *b = new Constrained_breaking;
+      algorithm = b;
+    }
+  else
+    algorithm = new Gourlay_breaking;
+  
   algorithm->set_pscore (this);
   sol = algorithm->solve ();
   delete algorithm;
index 9f3a492ea0ad81b53fa42a03d3dee50e2b319c8b..3d89f22f609f0bf22040814ba1026087957a7406 100644 (file)
@@ -2705,6 +2705,7 @@ def conv (str):
                ('betweensystempadding', 'between-system-padding'),
                ('pagetopspace', 'page-top-space'),
                ('raggedright', 'ragged-right'),
+               ('raggedlast', 'ragged-last'),
                ('raggedbottom', 'ragged-bottom'),
                ('raggedlastbottom', 'ragged-last-bottom'),
                ('aftertitlespace', 'after-title-space'),