]> git.donarmstrong.com Git - lilypond.git/blobdiff - lily/constrained-breaking.cc
resolve merge
[lilypond.git] / lily / constrained-breaking.cc
index 3a56aa47541190b7bd2f6e2669d5292f7e4d1fac..f6f84b80907714907d218525deb8c868a95f397e 100644 (file)
@@ -1,10 +1,20 @@
 /*
-  constrained-breaking.cc -- implement a line breaker that
-  support limits on the number of systems
+  This file is part of LilyPond, the GNU music typesetter.
 
-  source file of the GNU LilyPond music typesetter
+  Copyright (C) 2006--2011 Joe Neeman <joeneeman@gmail.com>
 
-  (c) 2006--2009 Joe Neeman <joeneeman@gmail.com>
+  LilyPond is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, either version 3 of the License, or
+  (at your option) any later version.
+
+  LilyPond is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "constrained-breaking.hh"
@@ -12,6 +22,7 @@
 #include "international.hh"
 #include "main.hh"
 #include "output-def.hh"
+#include "page-layout-problem.hh"
 #include "paper-column.hh"
 #include "paper-score.hh"
 #include "simple-spacer.hh"
@@ -21,7 +32,7 @@
 /*
   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
+  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)
 
@@ -173,10 +184,13 @@ Constrained_breaking::solve (vsize start, vsize end, vsize sys_count)
             {
               if (brk != end_brk)
                 {
-                  warning (_ ("cannot find line breaking that satisfies constraints" ));
+                 brk = st.at (brk, sys).prev_;
+                 sys--;
+                  warning (_ ("cannot find line breaking that satisfies constraints"));
                   ret.push_back (space_line (brk, end_brk));
                 }
-              /* build up the good solution */
+
+              /* build up the good part of the solution */
               for (vsize cur_sys = sys; cur_sys != VPOS; cur_sys--)
                 {
                  vsize prev_brk = st.at (brk, cur_sys).prev_;
@@ -236,16 +250,52 @@ Constrained_breaking::best_solution (vsize start, vsize end)
 std::vector<Line_details>
 Constrained_breaking::line_details (vsize start, vsize end, vsize sys_count)
 {
-  vsize brk = prepare_solution (start, end, sys_count);
+  vsize end_brk = prepare_solution (start, end, sys_count);
   Matrix<Constrained_break_node> const &st = state_[start];
   vector<Line_details> ret;
 
-  for (int sys = sys_count-1; sys >= 0 && brk != VPOS; sys--)
+  /* This loop structure is C&Ped from solve(). */
+  /* find the first solution that satisfies constraints */
+  for (vsize sys = sys_count-1; sys != VPOS; sys--)
     {
-      ret.push_back (st.at (brk, sys).details_);
-      brk = st.at (brk, sys).prev_;
+      for (vsize brk = end_brk; brk != VPOS; brk--)
+        {
+          if (!isinf (st.at (brk, sys).details_.force_))
+            {
+              if (brk != end_brk)
+               {
+                 /*
+                   During initialize(), we only fill out a
+                   Line_details for lines that are valid (ie. not too
+                   long), otherwise line breaking becomes O(n^3).
+                   In case sys_count is such that no valid solution
+                   is found, we need to fill in the Line_details.
+                 */
+                 Line_details details;
+                 brk = st.at (brk, sys).prev_;
+                 sys--;
+                 fill_line_details (&details, brk, end_brk);
+                 ret.push_back (details);
+               }
+
+              /* build up the good part of the solution */
+              for (vsize cur_sys = sys; cur_sys != VPOS; cur_sys--)
+                {
+                 vsize prev_brk = st.at (brk, cur_sys).prev_;
+                  assert (brk != VPOS);
+                  ret.push_back (st.at (brk, cur_sys).details_);
+                  brk = prev_brk;
+                }
+              reverse (ret);
+              return ret;
+            }
+        }
     }
-  reverse (ret);
+
+  /* if we get to here, just put everything on one line */
+  Line_details details;
+  fill_line_details (&details, 0, end_brk);
+  ret.push_back (details);
   return ret;
 }
 
@@ -330,19 +380,62 @@ Constrained_breaking::initialize ()
 
   ragged_right_ = to_boolean (pscore_->layout ()->c_variable ("ragged-right"));
   ragged_last_ = to_boolean (pscore_->layout ()->c_variable ("ragged-last"));
-      
+  system_system_space_ = 0;
+  system_markup_space_ = 0;
+  system_system_padding_ = 0;
+  system_system_min_distance_ = 0;
+  score_system_padding_ = 0;
+  score_system_min_distance_ = 0;
+  score_markup_padding_ = 0;
+  score_markup_min_distance_ = 0;
+
   Output_def *l = pscore_->layout ();
-  System *sys = pscore_->root_system ();
-  Real space = robust_scm2double (l->c_variable ("ideal-system-space"), 0);
-  SCM padding_scm = l->c_variable ("page-breaking-between-system-padding");
-  if (!scm_is_number (padding_scm))
-    padding_scm = l->c_variable ("between-system-padding");
-  Real padding = robust_scm2double (padding_scm, 0.0);
+
+  SCM spacing_spec = l->c_variable ("system-system-spacing");
+  SCM between_scores_spec = l->c_variable ("score-system-spacing");
+  SCM title_spec = l->c_variable ("score-markup-spacing");
+  SCM page_breaking_spacing_spec = l->c_variable ("page-breaking-system-system-spacing");
+
+  Page_layout_problem::read_spacing_spec (spacing_spec,
+                                         &system_system_space_,
+                                         ly_symbol2scm ("basic-distance"));
+  Page_layout_problem::read_spacing_spec (page_breaking_spacing_spec,
+                                         &system_system_space_,
+                                         ly_symbol2scm ("basic-distance"));
+  Page_layout_problem::read_spacing_spec (title_spec,
+                                         &system_markup_space_,
+                                         ly_symbol2scm ("basic-distance"));
+
+  Page_layout_problem::read_spacing_spec (spacing_spec,
+                                         &system_system_padding_,
+                                         ly_symbol2scm ("padding"));
+  Page_layout_problem::read_spacing_spec (between_scores_spec,
+                                         &score_system_padding_,
+                                         ly_symbol2scm ("padding"));
+  Page_layout_problem::read_spacing_spec (page_breaking_spacing_spec,
+                                         &system_system_padding_,
+                                         ly_symbol2scm ("padding"));
+  Page_layout_problem::read_spacing_spec (title_spec,
+                                         &score_markup_padding_,
+                                         ly_symbol2scm ("padding"));
+
+  Page_layout_problem::read_spacing_spec (between_scores_spec,
+                                         &score_system_min_distance_,
+                                         ly_symbol2scm ("minimum-distance"));
+  Page_layout_problem::read_spacing_spec (spacing_spec,
+                                         &system_system_min_distance_,
+                                         ly_symbol2scm ("minimum-distance"));
+  Page_layout_problem::read_spacing_spec (page_breaking_spacing_spec,
+                                         &system_system_min_distance_,
+                                         ly_symbol2scm ("minimum-distance"));
+  Page_layout_problem::read_spacing_spec (title_spec,
+                                         &score_markup_min_distance_,
+                                         ly_symbol2scm ("minimum-distance"));
 
   Interval first_line = line_dimensions_int (pscore_->layout (), 0);
   Interval other_lines = line_dimensions_int (pscore_->layout (), 1);
   /* do all the rod/spring problems */
-  breaks_ = pscore_->find_break_indices ();
+  breaks_ = pscore_->get_break_indices ();
   all_ = pscore_->root_system ()->used_columns ();
   lines_.resize (breaks_.size (), breaks_.size (), Line_details ());
   vector<Real> forces = get_line_forces (all_,
@@ -353,9 +446,6 @@ Constrained_breaking::initialize ()
     {
       for (vsize j = i + 1; j < breaks_.size (); j++)
        {
-         int start = Paper_column::get_rank (all_[breaks_[i]]);
-         int end = Paper_column::get_rank (all_[breaks_[j]]);
-         Interval extent = sys->pure_height (sys, start, end);
          bool last = j == breaks_.size () - 1;
          bool ragged = ragged_right_ || (last && ragged_last_);
          Line_details &line = lines_.at (j, i);
@@ -366,28 +456,7 @@ Constrained_breaking::initialize ()
          if (isinf (line.force_))
            break;
 
-         Grob *c = all_[breaks_[j]];
-         line.break_penalty_ = robust_scm2double (c->get_property ("line-break-penalty"), 0);
-         line.page_penalty_ = robust_scm2double (c->get_property ("page-break-penalty"), 0);
-         line.turn_penalty_ = robust_scm2double (c->get_property ("page-turn-penalty"), 0);
-         line.break_permission_ = c->get_property ("line-break-permission");
-         line.page_permission_ = c->get_property ("page-break-permission");
-         line.turn_permission_ = c->get_property ("page-turn-permission");
-         
-         /* turn permission should always be stricter than page permission
-            and page permission should always be stricter than line permission */
-         line.page_permission_ = min_permission (line.break_permission_,
-                                                 line.page_permission_);
-         line.turn_permission_ = min_permission (line.page_permission_,
-                                                 line.turn_permission_);
-
-         line.extent_ = (extent.is_empty ()
-                         || isnan (extent[LEFT])
-                         || isnan (extent[RIGHT]))
-           ? Interval (0, 0) : extent;
-         line.padding_ = padding;
-         line.space_ = space;
-         line.inverse_hooke_ = extent.length () + space;
+         fill_line_details (&line, i, j);
        }
     }
 
@@ -403,6 +472,60 @@ Constrained_breaking::initialize ()
   state_.resize (start_.size ());
 }
 
+/*
+  Fills out all of the information contained in a Line_details,
+  except for information about horizontal spacing.
+*/
+void
+Constrained_breaking::fill_line_details (Line_details *const out, vsize start, vsize end)
+{
+  int start_rank = Paper_column::get_rank (all_[breaks_[start]]);
+  int end_rank = Paper_column::get_rank (all_[breaks_[end]]);
+  System *sys = pscore_->root_system ();
+  Interval begin_of_line_extent = sys->begin_of_line_pure_height (start_rank, end_rank);
+  Interval rest_of_line_extent = sys->rest_of_line_pure_height (start_rank, end_rank);
+  bool last = (end == breaks_.size () - 1);
+
+  Grob *c = all_[breaks_[end]];
+  out->last_column_ = c;
+  out->break_penalty_ = robust_scm2double (c->get_property ("line-break-penalty"), 0);
+  out->page_penalty_ = robust_scm2double (c->get_property ("page-break-penalty"), 0);
+  out->turn_penalty_ = robust_scm2double (c->get_property ("page-turn-penalty"), 0);
+  out->break_permission_ = c->get_property ("line-break-permission");
+  out->page_permission_ = c->get_property ("page-break-permission");
+  out->turn_permission_ = c->get_property ("page-turn-permission");
+
+  /* turn permission should always be stricter than page permission
+     and page permission should always be stricter than line permission */
+  out->page_permission_ = min_permission (out->break_permission_,
+                                         out->page_permission_);
+  out->turn_permission_ = min_permission (out->page_permission_,
+                                         out->turn_permission_);
+
+  begin_of_line_extent = (begin_of_line_extent.is_empty ()
+                         || isnan (begin_of_line_extent[LEFT])
+                         || isnan (begin_of_line_extent[RIGHT]))
+    ? Interval (0, 0) : begin_of_line_extent;
+  rest_of_line_extent = (rest_of_line_extent.is_empty ()
+                        || isnan (rest_of_line_extent[LEFT])
+                        || isnan (rest_of_line_extent[RIGHT]))
+    ? Interval (0, 0) : rest_of_line_extent;
+  out->shape_ = Line_shape (begin_of_line_extent, rest_of_line_extent);
+  out->padding_ = last ? score_system_padding_ : system_system_padding_;
+  out->title_padding_ = score_markup_padding_;
+  out->min_distance_ = last ? score_system_min_distance_ : system_system_min_distance_;
+  out->title_min_distance_ = score_markup_min_distance_;
+  out->space_ = system_system_space_;
+  out->title_space_ = system_markup_space_;
+  out->inverse_hooke_ = out->full_height () + system_system_space_;
+
+  out->footnotes_ = sys->get_footnotes_in_range (start_rank, end_rank);
+
+  out->refpoint_extent_ = sys->pure_refpoint_extent (start_rank, end_rank);
+  if (out->refpoint_extent_.is_empty ())
+    out->refpoint_extent_ = Interval (0, 0);
+}
+
 Real
 Constrained_breaking::combine_demerits (Real force, Real prev_force)
 {
@@ -412,3 +535,90 @@ Constrained_breaking::combine_demerits (Real force, Real prev_force)
   return force * force + (prev_force - force) * (prev_force - force);
 }
 
+Line_details::Line_details (Prob *pb, Output_def *paper)
+{
+  SCM spec = paper->c_variable ("markup-system-spacing");
+  SCM title_spec = paper->c_variable ("markup-markup-spacing");
+  padding_ = 0;
+  title_padding_ = 0;
+  min_distance_ = 0;
+  title_min_distance_ = 0;
+  space_ = 0;
+  title_space_ = 0;
+  Page_layout_problem::read_spacing_spec (spec, &space_, ly_symbol2scm ("basic-distance"));
+  Page_layout_problem::read_spacing_spec (title_spec, &title_space_, ly_symbol2scm ("basic-distance"));
+  Page_layout_problem::read_spacing_spec (spec, &padding_, ly_symbol2scm ("padding"));
+  Page_layout_problem::read_spacing_spec (title_spec, &title_padding_, ly_symbol2scm ("padding"));
+  Page_layout_problem::read_spacing_spec (spec, &min_distance_, ly_symbol2scm ("minimum-distance"));
+  Page_layout_problem::read_spacing_spec (title_spec, &title_min_distance_, ly_symbol2scm ("minimum-distance"));
+
+  SCM footnotes = pb->get_property ("footnotes");
+  if (scm_is_pair (footnotes))
+    for (SCM s = footnotes; scm_is_pair (s); s = scm_cdr (s))
+      footnotes_.push_back (unsmob_stencil (scm_car (s)));
+
+  last_column_ = 0;
+  force_ = 0;
+  Interval stencil_extent = unsmob_stencil (pb->get_property ("stencil"))->extent (Y_AXIS);
+  shape_ = Line_shape (stencil_extent, stencil_extent); // pretend it goes all the way across
+  tallness_ = 0;
+  bottom_padding_ = 0;
+  inverse_hooke_ = 1.0;
+  break_permission_ = ly_symbol2scm ("allow");
+  page_permission_ = pb->get_property ("page-break-permission");
+  turn_permission_ = pb->get_property ("page-turn-permission");
+  break_penalty_ = 0;
+  page_penalty_ = robust_scm2double (pb->get_property ("page-break-penalty"), 0);
+  turn_penalty_ = robust_scm2double (pb->get_property ("page-turn-penalty"), 0);
+  title_ = to_boolean (pb->get_property ("is-title"));
+  compressed_lines_count_ = 1;
+  compressed_nontitle_lines_count_ = title_ ? 0 : 1;
+  SCM last_scm  = pb->get_property ("last-markup-line");
+  last_markup_line_  = to_boolean (last_scm);
+  SCM first_scm = pb->get_property ("first-markup-line");
+  first_markup_line_ = to_boolean (first_scm);
+  tight_spacing_ = to_boolean (pb->get_property ("tight-spacing"));
+  refpoint_extent_ = Interval (0, 0);
+}
+
+Real
+Line_details::full_height () const
+{
+  Interval ret;
+  ret.unite(shape_.begin_);
+  ret.unite(shape_.rest_);
+  return ret.length();
+}
+
+Real
+Line_details::tallness () const
+{
+  return tallness_;
+}
+
+Real
+Line_details::spring_length (Line_details const &next_line) const
+{
+  // space_ measures the spring which goes from the bottom refpoint
+  // of this to the top refpoint of next_line. We want to return
+  // the stretchable space between the bottom of this's extent to
+  // the top of next_line's extent.
+  Real refpoint_dist = tallness_ + refpoint_extent_[DOWN] - next_line.refpoint_extent_[UP];
+  Real space = next_line.title_ ? title_space_ : space_;
+  return max (0.0, space - refpoint_dist);
+}
+
+Line_shape::Line_shape (Interval begin, Interval rest)
+{
+  begin_ = begin;
+  rest_ = rest;
+}
+
+Line_shape
+Line_shape::piggyback (Line_shape mount, Real padding) const
+{
+  Real elevation = max (begin_[UP]-mount.begin_[DOWN], rest_[UP]-mount.rest_[DOWN]);
+  Interval begin = Interval (begin_[DOWN], elevation + mount.begin_[UP] + padding);
+  Interval rest = Interval (rest_[DOWN], elevation + mount.rest_[UP] + padding);
+  return Line_shape (begin, rest);
+}