2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2006--2009 Joe Neeman <joeneeman@gmail.com>
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
20 #include "page-spacing.hh"
23 #include "page-breaking.hh"
27 Page_spacing::calc_force ()
29 Real height = page_height_
30 - breaker_->min_whitespace_at_top_of_page (first_line_)
31 - breaker_->min_whitespace_at_bottom_of_page (last_line_);
33 if (rod_height_ + last_line_.bottom_padding_ >= height)
36 force_ = (height - rod_height_ - last_line_.bottom_padding_ - spring_len_)
37 / max (0.1, inverse_spring_k_);
41 Page_spacing::resize (Real new_height)
43 page_height_ = new_height;
48 Page_spacing::append_system (const Line_details &line)
53 rod_height_ += line.title_ ? last_line_.title_padding_ : last_line_.padding_;
55 rod_height_ += line.extent_.length ();
56 spring_len_ += line.space_;
57 inverse_spring_k_ += line.inverse_hooke_;
65 Page_spacing::prepend_system (const Line_details &line)
68 rod_height_ += first_line_.title_ ? line.title_padding_ : line.padding_;
72 rod_height_ += line.extent_.length ();
73 spring_len_ += line.space_;
74 inverse_spring_k_ += line.inverse_hooke_;
82 Page_spacing::clear ()
84 force_ = rod_height_ = spring_len_ = 0;
85 inverse_spring_k_ = 0;
89 Page_spacer::Page_spacer (vector<Line_details> const &lines, vsize first_page_num, Page_breaking const *breaker)
92 first_page_num_ = first_page_num;
95 ragged_ = breaker->ragged ();
96 ragged_last_ = breaker->is_last () && breaker->ragged_last ();
100 Page_spacer::solve (vsize page_count)
102 if (page_count > max_page_count_)
105 Page_spacing_result ret;
107 vsize system = lines_.size () - 1;
108 vsize extra_systems = 0;
109 vsize extra_pages = 0;
111 if (isinf (state_.at (system, page_count-1).demerits_))
113 programming_error ("tried to space systems on a bad number of pages");
114 /* Usually, this means that we tried to cram too many systems into
115 to few pages. To avoid crashing, we look for the largest number of
116 systems that we can fit properly onto the right number of pages.
117 All the systems that don't fit get tacked onto the last page.
120 for (i = system; isinf (state_.at (i, page_count-1).demerits_) && i; i--)
125 extra_systems = system - i;
130 /* try chopping off pages from the end */
132 for (j = page_count; j && isinf (state_.at (system, j-1).demerits_); j--)
137 extra_pages = page_count - j;
141 return Page_spacing_result (); /* couldn't salvage it -- probably going to crash */
145 ret.force_.resize (page_count);
146 ret.systems_per_page_.resize (page_count);
147 ret.penalty_ = state_.at (system, page_count-1).penalty_
148 + lines_.back ().page_penalty_ + lines_.back ().turn_penalty_;
151 for (vsize p = page_count; p--;)
153 assert (system != VPOS);
155 Page_spacing_node const &ps = state_.at (system, p);
156 ret.force_[p] = ps.force_;
157 ret.demerits_ += ps.force_ * ps.force_;
159 ret.systems_per_page_[p] = system + 1;
161 ret.systems_per_page_[p] = system - ps.prev_;
167 ret.systems_per_page_.back () += extra_systems;
168 ret.force_.back () = BAD_SPACING_PENALTY;
172 ret.force_.insert (ret.force_.end (), extra_pages, BAD_SPACING_PENALTY);
173 ret.systems_per_page_.insert (ret.systems_per_page_.end (), extra_pages, 0);
180 Page_spacer::resize (vsize page_count)
182 assert (page_count > 0);
184 if (max_page_count_ >= page_count)
187 state_.resize (lines_.size (), page_count, Page_spacing_node ());
188 for (vsize page = max_page_count_; page < page_count; page++)
189 for (vsize line = page; line < lines_.size (); line++)
190 if (!calc_subproblem (page, line))
193 max_page_count_ = page_count;
196 // Carries out one step in the dynamic programming algorithm for putting systems
197 // on a fixed number of pages. One call to this routine calculates the best
198 // configuration for putting lines 0 through LINE-1 on PAGE+1 pages, provided that
199 // we have previously called calc_subproblem(page-1, k) for every k < LINE.
201 // This algorithm is similar to the constrained-breaking algorithm.
203 Page_spacer::calc_subproblem (vsize page, vsize line)
205 bool last = line == lines_.size () - 1;
206 Page_spacing space (breaker_->page_height (page + first_page_num_, last),
208 Page_spacing_node &cur = state_.at (line, page);
209 bool ragged = ragged_ || (ragged_last_ && last);
212 for (vsize page_start = line+1; page_start > page && page_start--;)
214 Page_spacing_node const *prev = page > 0 ? &state_.at (page_start-1, page-1) : 0;
216 space.prepend_system (lines_[page_start]);
218 // This 'if' statement is a little hard to parse. It won't consider this configuration
219 // if it is overfull unless the current configuration is the first one with this start
220 // point. We also make an exception (and consider this configuration) if the previous
221 // configuration we tried had fewer lines than min-systems-per-page.
222 if (!breaker_->too_few_lines (line_count)
224 && (isinf (space.force_) || (space.force_ < 0 && ragged)))
227 line_count += lines_[page_start].compressed_nontitle_lines_count_;
228 if (page > 0 || page_start == 0)
230 // If the last page is ragged, set its force to zero. This way, we will leave
231 // the last page half-empty rather than trying to balance things out
232 // (which only makes sense in non-ragged situations).
233 if (line == lines_.size () - 1 && ragged && last && space.force_ > 0)
236 Real demerits = space.force_ * space.force_;
237 /* If a single line is taller than a page, we need to consider it as
238 a possible solution (but we give it a very bad score). */
239 if (isinf (space.force_) && page_start == line)
240 demerits = BAD_SPACING_PENALTY;
241 demerits += (prev ? prev->demerits_ : 0);
243 Real penalty = breaker_->line_count_penalty (line_count);
245 penalty += lines_[page_start-1].page_penalty_
246 + (page % 2 == 0) ? lines_[page_start-1].turn_penalty_ : 0;
248 /* Deal with widow/orphan lines */
249 /* Last line of paragraph is first line on the new page */
250 if ((page_start > 0) &&
251 (page_start < lines_.size ()) &&
252 (lines_[page_start].last_markup_line_))
253 penalty += breaker_->orphan_penalty ();
254 /* First line of paragraph is last line on the previous page */
255 if ((page_start > 0) &&
256 (page_start < lines_.size ()) &&
257 (lines_[page_start-1].first_markup_line_))
258 penalty += breaker_->orphan_penalty ();
261 if (demerits < cur.demerits_ || page_start == line)
263 cur.demerits_ = demerits;
264 cur.force_ = space.force_;
265 cur.penalty_ = penalty + (prev ? prev->penalty_ : 0);
266 cur.system_count_status_ = breaker_->line_count_status (line_count)
267 | (prev ? prev->system_count_status_ : 0);
268 cur.prev_ = page_start - 1;
273 && lines_[page_start-1].page_permission_ == ly_symbol2scm ("force"))
276 return !isinf (cur.demerits_);