Web: Fix Typo - Issue 1147
[lilypond/patrick.git] / lily / page-breaking.cc
blob83ffd90fd584a5e4f6a708ca0ac8f7d0137a74f3
1 /*
2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2006--2010 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/>.
21 This is a utility class for page-breaking algorithms. There are some complex
22 parts of this class, some of which are useful to understand if you intend
23 to write a page breaking algorithm (ie. a subclass of Page_breaking). Most
24 of these complexities were introduced in order to break the problem of
25 page-breaking into simpler subproblems and to hide some of the bookkeeping
26 complexities of page breaking from the page breaking algorithms.
28 COMPRESSED LINES
29 There are several functions that actually distribute systems across pages
30 (for example, the space_systems_XXX and pack_systems_XXX functions). If
31 each of these functions had to handle \noPageBreak, it would be a mess.
32 Therefore, we handle \noPageBreak by "compressing" the list of systems
33 before doing any layout: we concatenate any two systems separated by a
34 \noPageBreak into a single system. The page-breaking functions can do their
35 magic without encountering a \noPageBreak; we then "uncompress" the systems
36 at the end. We almost always work with cached_line_details_, which are
37 "compressed."
39 CHUNKS
40 The basic operation of a page breaking algorithm is to repeatedly request
41 some systems from the line-breaker and place those systems on some pages.
42 With each repetition, the page breaking algorithm asks the line-breaker for
43 some systems that it thinks will help it achieve a better layout. The
44 Page_breaking class provides functionality to facilitate this in the case
45 that the page breaking algorithm only cares about the number of systems.
47 Even if a page breaking algorithm only cares number of systems, there may
48 be many ways to satisfy its request. For example, in a piece with 2 scores
49 and a request for 10 systems, we could return 5 systems from each score or
50 4 from the first and 6 from the second. Even within a score, we might
51 want to try several different line breaking configurations with a fixed
52 system count; if there is a forced \pageBreak, for example, we might wish
53 to tweak the number of systems on both sides of the \pageBreak independently.
55 The Page_breaking class takes care of finding these configurations. It
56 divides the piece into "chunks" and sets up the line-breaker in such a way
57 that the number of systems in each chunk can be modified independently.
58 Chunks never cross score boundaries; each title and markup is its own chunk.
59 When a page breaking algorithm requests a number of systems, the Page_breaker
60 stores an array of potential configurations, which the page breaking
61 algorithm can iterate over using current_configuration(vsize).
63 LINE_DIVISION
64 A Line_division is simply a way of storing the exact way in which the
65 total number of systems is distributed among chunks. Note that a
66 Line_division may not (in fact, usually will not) describe all of the chunks
67 in the entire book. Rather, it will describe the subset of chunks that lie
68 between some fixed starting and ending point. This subset of chunks changes
69 whenever a page breaking algorithm asks to consider a different pair of
70 starting and ending breakpoints. In particular, a Line_division should be
71 discarded after a call to set_current_breakpoints, since that Line_division
72 refers to a subset of chunks which might be different from the current
73 subset of chunks under consideration.
76 #include "page-breaking.hh"
78 #include "international.hh"
79 #include "item.hh"
80 #include "output-def.hh"
81 #include "page-layout-problem.hh"
82 #include "page-spacing.hh"
83 #include "paper-book.hh"
84 #include "paper-score.hh"
85 #include "paper-system.hh"
86 #include "system.hh"
87 #include "warn.hh"
89 /* for each forbidden page break, merge the systems around it into one
90 system. */
91 static vector<Line_details>
92 compress_lines (const vector<Line_details> &orig)
94 vector<Line_details> ret;
96 for (vsize i = 0; i < orig.size (); i++)
98 if (ret.size () && !scm_is_symbol (ret.back ().page_permission_))
100 Line_details const &old = ret.back ();
101 Line_details compressed = orig[i];
103 "padding" is the padding below the current line. The padding below
104 "old" (taking into account whether "old" is a title) is already
105 accounted for in the extent of the compressed line. The padding
106 below the compressed line, therefore, should depend on whether its
107 bottom-most line is a title or not.
109 Real padding = 0;
110 if (!orig[i].tight_spacing_)
111 padding = orig[i].title_ ? old.title_padding_ : old.padding_;
113 compressed.shape_ = old.shape_.piggyback (orig[i].shape_, padding);
114 compressed.space_ += old.space_;
115 compressed.inverse_hooke_ += old.inverse_hooke_;
117 compressed.compressed_lines_count_ = old.compressed_lines_count_ + 1;
118 compressed.compressed_nontitle_lines_count_ =
119 old.compressed_nontitle_lines_count_ + (compressed.title_ ? 0 : 1);
121 // compressed.title_ is true if and only if the first of its
122 // compressed lines was a title.
123 compressed.title_ = old.title_;
124 ret.back () = compressed;
126 else
128 ret.push_back (orig[i]);
129 ret.back ().force_ = 0;
132 return ret;
135 /* translate the number of systems-per-page into something meaningful for
136 the uncompressed lines.
138 static vector<vsize>
139 uncompress_solution (vector<vsize> const &systems_per_page,
140 vector<Line_details> const &compressed)
142 vector<vsize> ret;
143 vsize start_sys = 0;
145 for (vsize i = 0; i < systems_per_page.size (); i++)
147 int compressed_count = 0;
148 for (vsize j = start_sys; j < start_sys + systems_per_page[i]; j++)
149 compressed_count += compressed[j].compressed_lines_count_ - 1;
151 ret.push_back (systems_per_page[i] + compressed_count);
152 start_sys += systems_per_page[i];
154 return ret;
157 /* for Page_breaking, the start index (when we are dealing with the stuff
158 between a pair of breakpoints) refers to the break_ index of the end of
159 the previous page. So the system index of the start of the current page
160 could either be the same as the end of the previous page or one more than
161 it. */
163 /* Turn a break index into the sys index that starts the next page */
164 vsize
165 Page_breaking::next_system (Break_position const &break_pos) const
167 vsize sys = break_pos.system_spec_index_;
169 if (sys == VPOS) /* beginning of the book */
170 return 0;
171 if (system_specs_[sys].pscore_ && !break_pos.score_ender_)
172 return sys; /* the score overflows the previous page */
173 return sys + 1; /* this page starts with a new System_spec */
176 Page_breaking::Page_breaking (Paper_book *pb, Break_predicate is_break)
178 book_ = pb;
179 system_count_ = 0;
180 ragged_ = to_boolean (pb->paper_->c_variable ("ragged-bottom"));
181 ragged_last_ = to_boolean (pb->paper_->c_variable ("ragged-last-bottom"));
182 systems_per_page_ = max (0, robust_scm2int (pb->paper_->c_variable ("systems-per-page"), 0));
183 max_systems_per_page_ = max (0, robust_scm2int (pb->paper_->c_variable ("max-systems-per-page"), 0));
184 min_systems_per_page_ = max (0, robust_scm2int (pb->paper_->c_variable ("min-systems-per-page"), 0));
185 orphan_penalty_ = robust_scm2int (pb->paper_->c_variable ("orphan-penalty"), 100000);
187 if (systems_per_page_ && (max_systems_per_page_ || min_systems_per_page_))
189 warning (_f ("ignoring min-systems-per-page and max-systems-per-page because systems-per-page was set"));
190 min_systems_per_page_ = max_systems_per_page_ = 0;
192 if (max_systems_per_page_ && min_systems_per_page_ > max_systems_per_page_)
194 warning (_f ("min-systems-per-page is larger than max-systems-per-page, ignoring both values"));
195 min_systems_per_page_ = max_systems_per_page_ = 0;
198 create_system_list ();
199 find_chunks_and_breaks (is_break);
202 Page_breaking::~Page_breaking ()
206 bool
207 Page_breaking::ragged () const
209 return ragged_;
212 bool
213 Page_breaking::ragged_last () const
215 return ragged_last_;
219 Page_breaking::systems_per_page () const
221 return systems_per_page_;
225 Page_breaking::max_systems_per_page () const
227 return max_systems_per_page_;
231 Page_breaking::min_systems_per_page () const
233 return min_systems_per_page_;
236 vsize
237 Page_breaking::system_count () const
239 return system_count_;
242 bool
243 Page_breaking::too_many_lines (int line_count) const
245 return max_systems_per_page () > 0 && line_count > max_systems_per_page ();
248 bool
249 Page_breaking::too_few_lines (int line_count) const
251 return line_count < min_systems_per_page ();
254 Real
255 Page_breaking::line_count_penalty (int line_count) const
257 if (too_many_lines (line_count))
258 return (line_count - max_systems_per_page ()) * TERRIBLE_SPACING_PENALTY;
259 if (too_few_lines (line_count))
260 return (min_systems_per_page () - line_count) * TERRIBLE_SPACING_PENALTY;
262 return 0;
266 Page_breaking::line_count_status (int line_count) const
268 if (too_many_lines (line_count))
269 return SYSTEM_COUNT_TOO_MANY;
270 if (too_few_lines (line_count))
271 return SYSTEM_COUNT_TOO_FEW;
273 return SYSTEM_COUNT_OK;
276 /* translate indices into breaks_ into start-end parameters for the line breaker */
277 void
278 Page_breaking::line_breaker_args (vsize sys,
279 Break_position const &start,
280 Break_position const &end,
281 vsize *line_breaker_start,
282 vsize *line_breaker_end)
284 assert (system_specs_[sys].pscore_);
285 assert (next_system (start) <= sys && sys <= end.system_spec_index_);
287 if (start.system_spec_index_ == sys)
288 *line_breaker_start = start.score_break_;
289 else
290 *line_breaker_start = 0;
292 if (end.system_spec_index_ == sys)
293 *line_breaker_end = end.score_break_;
294 else
295 *line_breaker_end = VPOS;
298 void
299 Page_breaking::break_into_pieces (vsize start_break, vsize end_break,
300 Line_division const &div)
302 vector<Break_position> chunks = chunk_list (start_break, end_break);
303 bool ignore_div = false;
304 if (chunks.size () != div.size () + 1)
306 programming_error ("did not find a valid page breaking configuration");
307 ignore_div = true;
310 for (vsize i = 0; i + 1 < chunks.size (); i++)
312 vsize sys = next_system (chunks[i]);
313 if (system_specs_[sys].pscore_)
315 vsize start;
316 vsize end;
317 line_breaker_args (sys, chunks[i], chunks[i+1], &start, &end);
319 vector<Column_x_positions> pos = ignore_div
320 ? line_breaking_[sys].best_solution (start, end)
321 : line_breaking_[sys].solve (start, end, div[i]);
322 system_specs_[sys].pscore_->root_system ()->break_into_pieces (pos);
328 Page_breaking::systems ()
330 SCM ret = SCM_EOL;
331 for (vsize sys = 0; sys < system_specs_.size (); sys++)
333 if (system_specs_[sys].pscore_)
335 system_specs_[sys].pscore_->root_system ()
336 ->do_break_substitution_and_fixup_refpoints ();
337 SCM lines = system_specs_[sys].pscore_->root_system ()
338 ->get_broken_system_grobs ();
339 ret = scm_cons (lines, ret);
341 else
343 Prob *pb = system_specs_[sys].prob_;
344 ret = scm_cons (scm_list_1 (pb->self_scm ()), ret);
345 pb->unprotect ();
348 return scm_append (scm_reverse (ret));
352 Page_breaking::make_page (int page_num, bool last) const
354 bool last_part = ly_scm2bool (book_->paper_->c_variable ("is-last-bookpart"));
355 SCM mod = scm_c_resolve_module ("scm page");
356 SCM make_page_scm = scm_c_module_lookup (mod, "make-page");
358 make_page_scm = scm_variable_ref (make_page_scm);
360 return scm_apply_0 (make_page_scm,
361 scm_list_n (book_->self_scm (),
362 ly_symbol2scm ("page-number"), scm_from_int (page_num),
363 ly_symbol2scm ("is-last-bookpart"), scm_from_bool (last_part),
364 ly_symbol2scm ("is-bookpart-last-page"), scm_from_bool (last),
365 SCM_UNDEFINED));
368 Real
369 Page_breaking::page_height (int page_num, bool last) const
371 SCM mod = scm_c_resolve_module ("scm page");
372 SCM page = make_page (page_num, last);
373 SCM calc_height = scm_c_module_lookup (mod, "calc-printable-height");
374 calc_height = scm_variable_ref (calc_height);
376 SCM height = scm_apply_1 (calc_height, page, SCM_EOL);
377 return scm_to_double (height);
381 Page_breaking::breakpoint_property (vsize breakpoint, char const *str)
383 Break_position const &pos = breaks_[breakpoint];
385 if (pos.system_spec_index_ == VPOS)
386 return SCM_EOL;
387 if (system_specs_[pos.system_spec_index_].pscore_)
388 return pos.col_->get_property (str);
389 return system_specs_[pos.system_spec_index_].prob_->get_property (str);
393 Page_breaking::get_page_configuration (SCM systems, int page_num, bool ragged, bool last)
395 SCM dummy_page = make_page (page_num, last);
396 Page_layout_problem layout (book_, dummy_page, systems);
397 return scm_is_pair (systems) ? layout.solution (ragged) : SCM_EOL;
401 Page_breaking::draw_page (SCM systems, SCM configuration, int page_num, bool last)
403 // Create a stencil for each system.
404 SCM paper_systems = SCM_EOL;
405 for (SCM s = scm_reverse (systems); scm_is_pair (s); s = scm_cdr (s))
407 SCM paper_system = scm_car (s);
408 if (Grob *g = unsmob_grob (scm_car (s)))
410 System *sys = dynamic_cast<System*> (g);
411 paper_system = sys->get_paper_system ();
414 paper_systems = scm_cons (paper_system, paper_systems);
417 // Create the page and draw it.
418 SCM page = make_page (page_num, last);
419 SCM page_module = scm_c_resolve_module ("scm page");
420 SCM page_stencil = scm_c_module_lookup (page_module, "page-stencil");
421 page_stencil = scm_variable_ref (page_stencil);
423 Prob *p = unsmob_prob (page);
424 p->set_property ("lines", paper_systems);
425 p->set_property ("configuration", configuration);
426 scm_apply_1 (page_stencil, page, SCM_EOL);
428 return page;
432 Page_breaking::make_pages (vector<vsize> lines_per_page, SCM systems)
434 int first_page_number
435 = robust_scm2int (book_->paper_->c_variable ("first-page-number"), 1);
436 SCM ret = SCM_EOL;
437 SCM label_page_table = book_->top_paper ()->c_variable ("label-page-table");
438 if (label_page_table == SCM_UNDEFINED)
439 label_page_table = SCM_EOL;
441 // Build a list of (systems . configuration) pairs. Note that we lay out
442 // the staves and find the configurations before drawing anything. Some
443 // grobs (like tuplet brackets) look at their neighbours while drawing
444 // themselves. If this happens before the neighbouring staves have
445 // been laid out, bad side-effects could happen (in particular,
446 // Align_interface::align_to_ideal_distances might be called).
447 SCM systems_and_configs = SCM_EOL;
449 for (vsize i = 0; i < lines_per_page.size (); i++)
451 int page_num = i + first_page_number;
452 bool bookpart_last_page = (i == lines_per_page.size () - 1);
453 bool rag = ragged () || (bookpart_last_page && ragged_last ());
454 SCM line_count = scm_from_int (lines_per_page[i]);
455 SCM lines = scm_list_head (systems, line_count);
456 SCM config = get_page_configuration (lines, page_num, rag, bookpart_last_page);
458 systems_and_configs = scm_cons (scm_cons (lines, config), systems_and_configs);
459 systems = scm_list_tail (systems, line_count);
462 // Now it's safe to make the pages.
463 int page_num = first_page_number + lines_per_page.size () - 1;
464 for (SCM s = systems_and_configs; scm_is_pair (s); s = scm_cdr (s))
466 SCM lines = scm_caar (s);
467 SCM config = scm_cdar (s);
468 bool bookpart_last_page = (s == systems_and_configs);
469 SCM page = draw_page (lines, config, page_num, bookpart_last_page);
471 /* collect labels */
472 SCM page_num_scm = scm_from_int (page_num);
473 for (SCM l = lines ; scm_is_pair (l) ; l = scm_cdr (l))
475 SCM labels = SCM_EOL;
476 if (Grob * line = unsmob_grob (scm_car (l)))
478 System *system = dynamic_cast<System*> (line);
479 labels = system->get_property ("labels");
481 else if (Prob *prob = unsmob_prob (scm_car (l)))
482 labels = prob->get_property ("labels");
484 for (SCM lbls = labels ; scm_is_pair (lbls) ; lbls = scm_cdr (lbls))
485 label_page_table = scm_cons (scm_cons (scm_car (lbls), page_num_scm),
486 label_page_table);
489 ret = scm_cons (page, ret);
490 --page_num;
492 book_->top_paper ()->set_variable (ly_symbol2scm ("label-page-table"), label_page_table);
493 return ret;
496 /* The page-turn-page-breaker needs to have a line-breaker between any two
497 columns with non-NULL page-turn-permission.
499 The optimal-breaker needs to have a line-breaker between any two columns
500 with page-break-permission = 'force.
502 By using a grob predicate, we can accommodate both of these uses.
504 void
505 Page_breaking::create_system_list ()
507 SCM specs = book_->get_system_specs ();
508 for (SCM s = specs; scm_is_pair (s); s = scm_cdr (s))
510 if (Paper_score *ps = dynamic_cast<Paper_score*> (unsmob_music_output (scm_car (s))))
512 system_specs_.push_back (System_spec (ps));
514 else
516 Prob *pb = unsmob_prob (scm_car (s));
517 assert (pb);
519 pb->protect ();
520 system_specs_.push_back (System_spec (pb));
525 void
526 Page_breaking::find_chunks_and_breaks (Break_predicate is_break)
528 SCM force_sym = ly_symbol2scm ("force");
530 chunks_.push_back (Break_position ());
531 breaks_.push_back (Break_position ());
533 for (vsize i = 0; i < system_specs_.size (); i++)
535 if (system_specs_[i].pscore_)
537 vector<Grob*> cols = system_specs_[i].pscore_->root_system ()->used_columns ();
538 vector<Grob*> forced_line_break_cols;
540 SCM system_count = system_specs_[i].pscore_->layout ()->c_variable ("system-count");
541 if (scm_is_number (system_count))
543 // With system-count given, the line configuration for
544 // this score is fixed. We need to ensure that chunk
545 // boundaries only occur at line breaks.
546 Constrained_breaking breaking (system_specs_[i].pscore_);
547 vector<Line_details> details = breaking.line_details (0, VPOS, scm_to_int (system_count));
549 for (vsize j = 0; j < details.size (); j++)
550 forced_line_break_cols.push_back (details[j].last_column_);
553 int last_forced_line_break_idx = 0;
554 vsize forced_line_break_idx = 0;
555 vector<vsize> line_breaker_columns;
556 line_breaker_columns.push_back (0);
558 for (vsize j = 1; j < cols.size (); j++)
560 if (forced_line_break_cols.size ())
562 if (forced_line_break_idx >= forced_line_break_cols.size ()
563 || forced_line_break_cols[forced_line_break_idx] != cols[j])
564 continue;
565 else
566 forced_line_break_idx++;
569 bool last = (j == cols.size () - 1);
570 bool break_point = is_break (cols[j]);
571 bool chunk_end = cols[j]->get_property ("page-break-permission") == force_sym;
572 Break_position cur_pos = Break_position (i,
573 line_breaker_columns.size (),
574 cols[j],
575 last);
577 // NOTE: even in the breaks_ list, forced_line_count_
578 // refers to the number of lines between a
579 // Break_position and the start of that /chunk/. This
580 // is needed for system_count_bounds to work correctly,
581 // since it mixes Break_positions from breaks_ and
582 // chunks_.
583 if (scm_is_number (system_count))
584 cur_pos.forced_line_count_ = forced_line_break_idx - last_forced_line_break_idx;
586 if (break_point || (i == system_specs_.size () - 1 && last))
587 breaks_.push_back (cur_pos);
588 if (chunk_end || last)
590 chunks_.push_back (cur_pos);
591 last_forced_line_break_idx = forced_line_break_idx;
594 if ((break_point || chunk_end) && !last)
595 line_breaker_columns.push_back (j);
597 line_breaking_.push_back (Constrained_breaking (system_specs_[i].pscore_, line_breaker_columns));
599 else
601 /* TODO: we want some way of applying Break_p to a prob? */
602 if (i == system_specs_.size () - 1)
603 breaks_.push_back (Break_position (i));
605 chunks_.push_back (Break_position (i));
607 /* FIXME: shouldn't we push a Null_breaker or similar dummy
608 class? --hwn */
609 line_breaking_.push_back (Constrained_breaking (NULL));
614 vector<Break_position>
615 Page_breaking::chunk_list (vsize start_index, vsize end_index)
617 Break_position start = breaks_[start_index];
618 Break_position end = breaks_[end_index];
620 vsize i = 0;
621 for (; i < chunks_.size () && chunks_[i] <= start; i++)
624 vector<Break_position> ret;
625 ret.push_back (start);
626 for (; i < chunks_.size () && chunks_[i] < end; i++)
627 ret.push_back (chunks_[i]);
628 ret.push_back (end);
629 return ret;
632 vsize
633 Page_breaking::min_system_count (vsize start, vsize end)
635 vector<Break_position> chunks = chunk_list (start, end);
636 Line_division div = system_count_bounds (chunks, true);
637 vsize ret = 0;
639 for (vsize i = 0; i < div.size (); i++)
640 ret += div[i];
641 return ret;
644 vsize
645 Page_breaking::max_system_count (vsize start, vsize end)
647 vector<Break_position> chunks = chunk_list (start, end);
648 Line_division div = system_count_bounds (chunks, false);
649 vsize ret = 0;
651 for (vsize i = 0; i < div.size (); i++)
652 ret += div[i];
653 return ret;
656 Page_breaking::Line_division
657 Page_breaking::system_count_bounds (vector<Break_position> const &chunks,
658 bool min)
660 assert (chunks.size () >= 2);
662 Line_division ret;
663 ret.resize (chunks.size () - 1, 1);
665 for (vsize i = 0; i + 1 < chunks.size (); i++)
667 vsize sys = next_system (chunks[i]);
669 if (chunks[i+1].forced_line_count_)
670 ret[i] = chunks[i+1].forced_line_count_;
671 else if (system_specs_[sys].pscore_)
673 vsize start;
674 vsize end;
675 line_breaker_args (sys, chunks[i], chunks[i+1], &start, &end);
676 ret[i] = min
677 ? line_breaking_[sys].min_system_count (start, end)
678 : line_breaking_[sys].max_system_count (start, end);
682 return ret;
685 void
686 Page_breaking::set_current_breakpoints (vsize start,
687 vsize end,
688 vsize system_count,
689 Line_division lower_bound,
690 Line_division upper_bound)
692 system_count_ = system_count;
693 current_chunks_ = chunk_list (start, end);
694 current_start_breakpoint_ = start;
695 current_end_breakpoint_ = end;
696 clear_line_details_cache ();
698 if (!lower_bound.size ())
699 lower_bound = system_count_bounds (current_chunks_, true);
700 if (!upper_bound.size ())
701 upper_bound = system_count_bounds (current_chunks_, false);
703 assert (lower_bound.size () == current_chunks_.size () - 1);
704 assert (upper_bound.size () == current_chunks_.size () - 1);
706 Line_division work_in_progress;
707 current_configurations_.clear ();
708 line_divisions_rec (system_count,
709 lower_bound,
710 upper_bound,
711 &work_in_progress);
713 /* we only consider a constant number of configurations. Otherwise,
714 this becomes slow when there are many small scores. The constant
715 5 is somewhat arbitrary. */
716 if (current_configurations_.size () > 5)
718 vector<pair<Real,vsize> > dems_and_indices;
720 for (vsize i = 0; i < current_configurations_.size (); i++)
722 cache_line_details (i);
723 Real dem = 0;
724 for (vsize j = 0; j < cached_line_details_.size (); j++)
725 dem += cached_line_details_[j].force_ * cached_line_details_[j].force_
726 + cached_line_details_[j].break_penalty_;
728 dems_and_indices.push_back (pair<Real,vsize> (dem, i));
730 vector_sort (dems_and_indices, less<pair<Real,vsize> > ());
732 vector<Line_division> best_5_configurations;
733 for (vsize i = 0; i < 5; i++)
734 best_5_configurations.push_back (current_configurations_[dems_and_indices[i].second]);
736 clear_line_details_cache ();
737 current_configurations_ = best_5_configurations;
741 void
742 Page_breaking::set_to_ideal_line_configuration (vsize start, vsize end)
744 current_chunks_ = chunk_list (start, end);
745 current_start_breakpoint_ = start;
746 current_end_breakpoint_ = end;
747 clear_line_details_cache ();
748 system_count_ = 0;
750 Line_division div;
751 for (vsize i = 0; i+1 < current_chunks_.size (); i++)
753 vsize sys = next_system (current_chunks_[i]);
755 if (current_chunks_[i+1].forced_line_count_)
756 div.push_back (current_chunks_[i+1].forced_line_count_);
757 else if (system_specs_[sys].pscore_)
759 line_breaker_args (sys, current_chunks_[i], current_chunks_[i+1], &start, &end);
760 div.push_back (line_breaking_[sys].best_solution (start, end).size ());
762 else
763 div.push_back (1);
765 system_count_ += div.back ();
767 current_configurations_.clear ();
768 current_configurations_.push_back (div);
771 vsize
772 Page_breaking::current_configuration_count () const
774 return current_configurations_.size ();
777 void
778 Page_breaking::cache_line_details (vsize configuration_index)
780 if (cached_configuration_index_ != configuration_index)
782 cached_configuration_index_ = configuration_index;
784 Line_division &div = current_configurations_[configuration_index];
785 uncompressed_line_details_.clear ();
786 for (vsize i = 0; i + 1 < current_chunks_.size (); i++)
788 vsize sys = next_system (current_chunks_[i]);
789 if (system_specs_[sys].pscore_)
791 vsize start;
792 vsize end;
793 line_breaker_args (sys, current_chunks_[i], current_chunks_[i+1], &start, &end);
795 vector<Line_details> details = line_breaking_[sys].line_details (start, end, div[i]);
796 uncompressed_line_details_.insert (uncompressed_line_details_.end (), details.begin (), details.end ());
798 else
800 assert (div[i] == 1);
801 uncompressed_line_details_.push_back (Line_details (system_specs_[sys].prob_, book_->paper_));
804 cached_line_details_ = compress_lines (uncompressed_line_details_);
808 void
809 Page_breaking::clear_line_details_cache ()
811 cached_configuration_index_ = VPOS;
812 cached_line_details_.clear ();
813 uncompressed_line_details_.clear ();
816 void
817 Page_breaking::line_divisions_rec (vsize system_count,
818 Line_division const &min_sys,
819 Line_division const &max_sys,
820 Line_division *cur_division)
822 vsize my_index = cur_division->size ();
823 vsize others_min = 0;
824 vsize others_max = 0;
826 for (vsize i = my_index + 1; i < min_sys.size (); i++)
828 others_min += min_sys[i];
829 others_max += max_sys[i];
831 others_max = min (others_max, system_count);
832 vsize real_min = max (min_sys[my_index], system_count - others_max);
833 vsize real_max = min (max_sys[my_index], system_count - others_min);
835 if (real_min > real_max || real_min <= 0)
837 /* this should never happen within a recursive call. If it happens
838 at all, it means that we were called with an unsolvable problem
839 and we should return an empty result */
840 assert (my_index == 0);
841 return;
844 for (vsize i = real_min; i <= real_max; i++)
846 cur_division->push_back (i);
847 if (my_index == min_sys.size () - 1)
848 current_configurations_.push_back (*cur_division);
849 else
850 line_divisions_rec (system_count - i, min_sys, max_sys, cur_division);
851 cur_division->pop_back ();
855 void
856 Page_breaking::compute_line_heights ()
858 Real prev_hanging = 0;
859 Real prev_hanging_begin = 0;
860 Real prev_hanging_rest = 0;
861 for (vsize i = 0; i < cached_line_details_.size (); i++)
863 Line_shape shape = cached_line_details_[i].shape_;
864 Real a = shape.begin_[UP];
865 Real b = shape.rest_[UP];
866 Real midline_hanging = max (prev_hanging_begin + a, prev_hanging_rest + b);
867 Real hanging_begin = midline_hanging - shape.begin_[DOWN];
868 Real hanging_rest = midline_hanging - shape.rest_[DOWN];
869 Real hanging = max (hanging_begin, hanging_rest);
870 cached_line_details_[i].tallness_ = hanging - prev_hanging;
871 prev_hanging = hanging;
872 prev_hanging_begin = hanging_begin;
873 prev_hanging_rest = hanging_rest;
877 vsize
878 Page_breaking::min_page_count (vsize configuration, vsize first_page_num)
880 vsize ret = 1;
881 vsize page_starter = 0;
882 Real cur_rod_height = 0;
883 Real cur_spring_height = 0;
884 Real cur_page_height = page_height (first_page_num, false);
885 int line_count = 0;
887 cache_line_details (configuration);
888 compute_line_heights ();
890 if (cached_line_details_.size ())
891 cur_page_height -= min_whitespace_at_top_of_page (cached_line_details_[0]);
893 for (vsize i = 0; i < cached_line_details_.size (); i++)
895 Real padding = 0;
896 Real ext_len;
897 if (cur_rod_height > 0)
899 if (!cached_line_details_[i].tight_spacing_)
900 padding = (cached_line_details_[i].title_
901 ? cached_line_details_[i - 1].title_padding_
902 : cached_line_details_[i - 1].padding_);
903 ext_len = cached_line_details_[i].tallness_;
905 else
907 ext_len = cached_line_details_[i].full_height();
909 Real next_rod_height = cur_rod_height + ext_len + padding;
910 Real next_spring_height = cur_spring_height + cached_line_details_[i].space_;
911 Real next_height = next_rod_height + (ragged () ? next_spring_height : 0)
912 + min_whitespace_at_bottom_of_page (cached_line_details_[i]);
913 int next_line_count = line_count + cached_line_details_[i].compressed_nontitle_lines_count_;
915 if ((!too_few_lines (line_count) && (next_height > cur_page_height && cur_rod_height > 0))
916 || too_many_lines (next_line_count)
917 || (i > 0
918 && cached_line_details_[i-1].page_permission_ == ly_symbol2scm ("force")))
920 line_count = cached_line_details_[i].compressed_nontitle_lines_count_;
921 cur_rod_height = cached_line_details_[i].full_height();
922 cur_spring_height = cached_line_details_[i].space_;
923 page_starter = i;
925 cur_page_height = page_height (first_page_num + ret, false);
926 cur_page_height -= min_whitespace_at_top_of_page (cached_line_details_[i]);
928 ret++;
930 else
932 cur_rod_height = next_rod_height;
933 cur_spring_height = next_spring_height;
934 line_count = next_line_count;
938 /* there are two potential problems with the last page (because we didn't know
939 it was the last page until after we managed to fit all the systems to it):
940 - we are ragged-last but the last page has a compressed spring
941 - the value returned by page_height (num, true) is smaller than the
942 value returned by page_height (num, false) and it causes the page not to
943 fit.
945 In either case, we just need to add one more page. This is because the last
946 line will always fit on the extra page and by adding one more page to the
947 end, the previous page is no longer the last page, so our previous
948 calculations that treated it as a non-last page were ok.
951 cur_page_height = page_height (first_page_num + ret - 1, true);
952 cur_page_height -= min_whitespace_at_top_of_page (cached_line_details_[page_starter]);
953 cur_page_height -= min_whitespace_at_bottom_of_page (cached_line_details_.back ());
955 Real cur_height = cur_rod_height + ((ragged_last () || ragged ()) ? cur_spring_height : 0);
956 if (!too_few_lines (line_count - cached_line_details_.back ().compressed_nontitle_lines_count_)
957 && cur_height > cur_page_height
958 /* don't increase the page count if the last page had only one system */
959 && cur_rod_height > cached_line_details_.back ().full_height ())
960 ret++;
962 assert (ret <= cached_line_details_.size ());
963 return ret;
966 // If systems_per_page_ is positive, we don't really try to space on N pages;
967 // we just put the requested number of systems on each page and penalize
968 // if the result doesn't have N pages.
969 Page_spacing_result
970 Page_breaking::space_systems_on_n_pages (vsize configuration, vsize n, vsize first_page_num)
972 Page_spacing_result ret;
974 if (systems_per_page_ > 0)
976 Page_spacing_result ret = space_systems_with_fixed_number_per_page (configuration, first_page_num);
977 ret.demerits_ += (ret.force_.size () == n) ? 0 : BAD_SPACING_PENALTY;
978 return ret;
981 cache_line_details (configuration);
982 bool valid_n = (n >= min_page_count (configuration, first_page_num)
983 && n <= cached_line_details_.size ());
985 if (!valid_n)
986 programming_error ("number of pages is out of bounds");
988 if (n == 1 && valid_n)
989 ret = space_systems_on_1_page (cached_line_details_,
990 page_height (first_page_num, is_last ()),
991 ragged () || (is_last () && ragged_last ()));
992 else if (n == 2 && valid_n)
993 ret = space_systems_on_2_pages (configuration, first_page_num);
994 else
996 Page_spacer ps (cached_line_details_, first_page_num, this);
997 ret = ps.solve (n);
1000 return finalize_spacing_result (configuration, ret);
1003 Real
1004 Page_breaking::blank_page_penalty () const
1006 SCM penalty_sym;
1008 if (is_last ())
1009 penalty_sym = ly_symbol2scm ("blank-last-page-force");
1010 else if (ends_score ())
1011 penalty_sym = ly_symbol2scm ("blank-after-score-page-force");
1012 else
1013 penalty_sym = ly_symbol2scm ("blank-page-force");
1015 Break_position const &pos = breaks_[current_end_breakpoint_];
1016 if (Paper_score *ps = system_specs_[pos.system_spec_index_].pscore_)
1017 return robust_scm2double (ps->layout ()->lookup_variable (penalty_sym), 0.0);
1019 return robust_scm2double (book_->paper_->lookup_variable (penalty_sym), 0.0);
1022 // If systems_per_page_ is positive, we don't really try to space on N
1023 // or N+1 pages; see the comment to space_systems_on_n_pages.
1024 Page_spacing_result
1025 Page_breaking::space_systems_on_n_or_one_more_pages (vsize configuration, vsize n, vsize first_page_num,
1026 Real penalty_for_fewer_pages)
1028 Page_spacing_result n_res;
1029 Page_spacing_result m_res;
1031 if (systems_per_page_ > 0)
1033 Page_spacing_result ret = space_systems_with_fixed_number_per_page (configuration, first_page_num);
1034 ret.demerits_ += (ret.force_.size () == n || ret.force_.size () == (n-1)) ? 0 : BAD_SPACING_PENALTY;
1035 return ret;
1038 cache_line_details (configuration);
1039 vsize min_p_count = min_page_count (configuration, first_page_num);
1040 bool valid_n = n >= min_p_count || n <= cached_line_details_.size ();
1042 if (!valid_n)
1043 programming_error ("both page counts are out of bounds");
1045 if (n == 1 && valid_n)
1047 bool rag = ragged () || (is_last () && ragged_last ());
1048 Real height = page_height (first_page_num, is_last ());
1050 if (1 >= min_p_count)
1051 n_res = space_systems_on_1_page (cached_line_details_, height, rag);
1052 if (1 < cached_line_details_.size ())
1053 m_res = space_systems_on_2_pages (configuration, first_page_num);
1055 else
1057 Page_spacer ps (cached_line_details_, first_page_num, this);
1059 if (n >= min_p_count || !valid_n)
1060 n_res = ps.solve (n);
1061 if (n < cached_line_details_.size () || !valid_n)
1062 m_res = ps.solve (n+1);
1065 m_res = finalize_spacing_result (configuration, m_res);
1066 n_res = finalize_spacing_result (configuration, n_res);
1068 Real page_spacing_weight = robust_scm2double (book_->paper_->c_variable ("page-spacing-weight"), 10);
1069 n_res.demerits_ += penalty_for_fewer_pages * page_spacing_weight;
1071 if (n_res.force_.size ())
1072 n_res.force_.back () += penalty_for_fewer_pages;
1074 return (m_res.demerits_ < n_res.demerits_) ? m_res : n_res;
1077 Page_spacing_result
1078 Page_breaking::space_systems_on_best_pages (vsize configuration, vsize first_page_num)
1080 vsize min_p_count = min_page_count (configuration, first_page_num);
1082 cache_line_details (configuration);
1083 Page_spacer ps (cached_line_details_, first_page_num, this);
1084 Page_spacing_result best = ps.solve (min_p_count);
1086 for (vsize i = min_p_count+1; i <= cached_line_details_.size (); i++)
1088 Page_spacing_result cur = ps.solve (i);
1089 if (cur.demerits_ < best.demerits_)
1090 best = cur;
1093 Page_spacing_result ret = finalize_spacing_result (configuration, best);
1094 return ret;
1097 Page_spacing_result
1098 Page_breaking::space_systems_with_fixed_number_per_page (vsize configuration,
1099 vsize first_page_num)
1101 Page_spacing_result res;
1102 Page_spacing space (page_height (first_page_num, false), this);
1103 vsize line = 0;
1104 vsize page = 0;
1105 vsize page_first_line = 0;
1107 cache_line_details (configuration);
1108 while (line < cached_line_details_.size ())
1110 page++;
1111 space.clear ();
1112 space.resize (page_height (first_page_num + page, false));
1114 int system_count_on_this_page = 0;
1115 while (system_count_on_this_page < systems_per_page_
1116 && line < cached_line_details_.size ())
1118 Line_details const &cur_line = cached_line_details_[line];
1119 space.append_system (cur_line);
1120 system_count_on_this_page += cur_line.compressed_nontitle_lines_count_;
1121 line++;
1123 if (cur_line.page_permission_ == ly_symbol2scm ("force"))
1124 break;
1127 res.systems_per_page_.push_back (line - page_first_line);
1129 res.force_.push_back (space.force_);
1130 res.penalty_ += cached_line_details_[line-1].page_penalty_;
1131 if (system_count_on_this_page != systems_per_page_)
1133 res.penalty_ += TERRIBLE_SPACING_PENALTY;
1134 res.system_count_status_ |= ((system_count_on_this_page < systems_per_page_))
1135 ? SYSTEM_COUNT_TOO_FEW : SYSTEM_COUNT_TOO_MANY;
1138 page_first_line = line;
1141 /* Recalculate forces for the last page because we know now that is
1142 really the last page. */
1143 space.resize (page_height (first_page_num + page, true));
1144 res.force_.back () = space.force_;
1145 return finalize_spacing_result (configuration, res);
1148 Page_spacing_result
1149 Page_breaking::pack_systems_on_least_pages (vsize configuration, vsize first_page_num)
1151 // TODO: add support for min/max-systems-per-page.
1152 Page_spacing_result res;
1153 vsize page = 0;
1154 vsize page_first_line = 0;
1155 Page_spacing space (page_height (first_page_num, false), this);
1157 cache_line_details (configuration);
1158 compute_line_heights ();
1159 for (vsize line = 0; line < cached_line_details_.size (); line++)
1161 Real prev_force = space.force_;
1162 space.append_system (cached_line_details_[line]);
1163 if ((line > page_first_line)
1164 && (isinf (space.force_)
1165 || ((line > 0)
1166 && (cached_line_details_[line-1].page_permission_ == ly_symbol2scm ("force")))))
1168 res.systems_per_page_.push_back (line - page_first_line);
1169 res.force_.push_back (prev_force);
1170 res.penalty_ += cached_line_details_[line-1].page_penalty_;
1171 page++;
1172 space.resize (page_height (first_page_num + page, false));
1173 space.clear ();
1174 space.append_system (cached_line_details_[line]);
1175 page_first_line = line;
1178 if (line == cached_line_details_.size () - 1)
1180 /* This is the last line */
1181 /* When the last page height was computed, we did not know yet that it
1182 * was the last one. If the systems put on it don't fit anymore, the last
1183 * system is moved to a new page */
1184 space.resize (page_height (first_page_num + page, true));
1185 if ((line > page_first_line) && (isinf (space.force_)))
1187 res.systems_per_page_.push_back (line - page_first_line);
1188 res.force_.push_back (prev_force);
1189 /* the last page containing the last line */
1190 space.resize (page_height (first_page_num + page + 1, true));
1191 space.clear ();
1192 space.append_system (cached_line_details_[line]);
1193 res.systems_per_page_.push_back (1);
1194 res.force_.push_back (space.force_);
1195 res.penalty_ += cached_line_details_[line-1].page_penalty_;
1196 res.penalty_ += cached_line_details_[line].page_penalty_;
1198 else
1200 res.systems_per_page_.push_back (line + 1 - page_first_line);
1201 res.force_.push_back (space.force_);
1202 res.penalty_ += cached_line_details_[line].page_penalty_;
1206 return finalize_spacing_result (configuration, res);
1209 /* Calculate demerits and fix res.systems_per_page_ so that
1210 it refers to the original line numbers, not the ones given by compress_lines (). */
1211 Page_spacing_result
1212 Page_breaking::finalize_spacing_result (vsize configuration, Page_spacing_result res)
1214 if (res.force_.empty ())
1215 return res;
1217 cache_line_details (configuration);
1218 res.systems_per_page_ = uncompress_solution (res.systems_per_page_, cached_line_details_);
1220 Real line_force = 0;
1221 Real line_penalty = 0;
1222 Real page_demerits = res.penalty_;
1223 Real page_weighting = robust_scm2double (book_->paper_->c_variable ("page-spacing-weight"), 10);
1225 for (vsize i = 0; i < uncompressed_line_details_.size (); i++)
1227 line_force += uncompressed_line_details_[i].force_ * uncompressed_line_details_[i].force_;
1228 line_penalty += uncompressed_line_details_[i].break_penalty_;
1231 for (vsize i = 0; i < res.force_.size (); i++)
1233 Real f = res.force_[i];
1235 page_demerits += min(f*f, BAD_SPACING_PENALTY);
1238 /* for a while we tried averaging page and line forces across pages instead
1239 of summing them, but it caused a problem: if there is a single page
1240 with a very bad page force (for example because of a forced page break),
1241 the page breaker will put in a _lot_ of pages so that the bad force
1242 becomes averaged out over many pages. */
1243 res.demerits_ = line_force + line_penalty + page_demerits * page_weighting;
1244 return res;
1248 /* the cases for page_count = 1 or 2 can be done in O (n) time. Since they
1249 are by far the most common cases, we have special functions for them.
1251 space_systems_on_1_page has a different calling convention than most of the
1252 space_systems functions. This is because space_systems_on_1_page is (unlike
1253 the other space_systems functions) sometimes called on subsets of a full
1254 configuration. */
1255 Page_spacing_result
1256 Page_breaking::space_systems_on_1_page (vector<Line_details> const &lines, Real page_height, bool ragged)
1258 Page_spacing space (page_height, this);
1259 Page_spacing_result ret;
1260 int line_count = 0;
1262 for (vsize i = 0; i < lines.size (); i++)
1264 space.append_system (lines[i]);
1265 line_count += lines[i].compressed_nontitle_lines_count_;
1268 ret.systems_per_page_.push_back (lines.size ());
1269 ret.force_.push_back (ragged ? min (space.force_, 0.0) : space.force_);
1270 ret.penalty_ = line_count_penalty (line_count) + lines.back ().page_penalty_ + lines.back ().turn_penalty_;
1271 ret.system_count_status_ |= line_count_status (line_count);
1273 /* don't do finalize_spacing_result () because we are only an internal function */
1274 return ret;
1277 Page_spacing_result
1278 Page_breaking::space_systems_on_2_pages (vsize configuration, vsize first_page_num)
1280 Real page1_height = page_height (first_page_num, false);
1281 Real page2_height = page_height (first_page_num + 1, is_last ());
1282 bool ragged1 = ragged ();
1283 bool ragged2 = ragged () || (is_last () && ragged_last ());
1285 /* if there is a forced break, this reduces to 2 1-page problems */
1286 cache_line_details (configuration);
1287 for (vsize i = 0; i + 1 < cached_line_details_.size (); i++)
1288 if (cached_line_details_[i].page_permission_ == ly_symbol2scm ("force"))
1290 vector<Line_details> lines1 (cached_line_details_.begin (), cached_line_details_.begin () + i + 1);
1291 vector<Line_details> lines2 (cached_line_details_.begin () + i + 1, cached_line_details_.end ());
1292 Page_spacing_result p1 = space_systems_on_1_page (lines1, page1_height, ragged1);
1293 Page_spacing_result p2 = space_systems_on_1_page (lines2, page2_height, ragged2);
1295 p1.systems_per_page_.push_back (p2.systems_per_page_[0]);
1296 p1.force_.push_back (p2.force_[0]);
1297 p1.penalty_ += p2.penalty_ - cached_line_details_[i].turn_penalty_;
1298 p1.system_count_status_ |= p2.system_count_status_;
1299 return p1;
1302 vector<Real> page1_force;
1303 vector<Real> page2_force;
1305 // page1_penalty and page2_penalty store the penalties based
1306 // on min-systems-per-page and max-systems-per-page.
1307 vector<Real> page1_penalty;
1308 vector<Real> page2_penalty;
1310 // page1_status and page2_status keep track of whether the min-systems-per-page
1311 // and max-systems-per-page constraints are satisfied.
1312 vector<int> page1_status;
1313 vector<int> page2_status;
1315 Page_spacing page1 (page1_height, this);
1316 Page_spacing page2 (page2_height, this);
1317 int page1_line_count = 0;
1318 int page2_line_count = 0;
1320 page1_force.resize (cached_line_details_.size () - 1, infinity_f);
1321 page2_force.resize (cached_line_details_.size () - 1, infinity_f);
1322 page1_penalty.resize (cached_line_details_.size () - 1, infinity_f);
1323 page2_penalty.resize (cached_line_details_.size () - 1, infinity_f);
1324 page1_status.resize (cached_line_details_.size () - 1, 0);
1325 page2_status.resize (cached_line_details_.size () - 1, 0);
1327 /* find the page 1 and page 2 forces for each page-breaking position */
1328 for (vsize i = 0; i < page1_force.size (); i++)
1330 page1.append_system (cached_line_details_[i]);
1331 page2.prepend_system (cached_line_details_[cached_line_details_.size () - 1 - i]);
1332 page1_line_count += cached_line_details_[i].compressed_nontitle_lines_count_;
1333 page2_line_count += cached_line_details_[cached_line_details_.size () - 1 - i].compressed_nontitle_lines_count_;
1335 page1_force[i] = (ragged1 && page1.force_ < 0 && i > 0) ? infinity_f : page1.force_;
1336 page1_penalty[i] = line_count_penalty (page1_line_count);
1337 page1_status[i] = line_count_status (page1_line_count);
1339 if (ragged2)
1340 page2_force[page2_force.size () - 1 - i] =
1341 (page2.force_ < 0 && i + 1 < page1_force.size ()) ? infinity_f : 0;
1342 else
1343 page2_force[page2_force.size () - 1 - i] = page2.force_;
1344 page2_penalty[page2_penalty.size () - 1 - i] = line_count_penalty (page2_line_count);
1345 page2_status[page2_penalty.size () - 1 - i] = line_count_status (page2_line_count);
1348 /* find the position that minimises the sum of the page forces */
1349 vsize best_sys_count = 1;
1350 Real best_demerits = infinity_f;
1351 for (vsize i = 0; i < page1_force.size (); i++)
1353 Real f = page1_force[i] * page1_force[i] + page2_force[i] * page2_force[i];
1355 // NOTE: we treat max-systems-per-page and min-systems-per-page as soft
1356 // constraints. That is, we penalize harshly when they don't happen
1357 // but we don't completely reject.
1358 Real dem = f
1359 + page1_penalty[i] + page2_penalty[i]
1360 + cached_line_details_[i+1].page_penalty_
1361 + cached_line_details_.back ().page_penalty_ + cached_line_details_.back ().turn_penalty_;
1362 if (dem < best_demerits)
1364 best_demerits = dem;
1365 best_sys_count = i+1;
1369 Page_spacing_result ret;
1370 ret.systems_per_page_.push_back (best_sys_count);
1371 ret.systems_per_page_.push_back (cached_line_details_.size () - best_sys_count);
1372 ret.force_.push_back (page1_force[best_sys_count-1]);
1373 ret.force_.push_back (page2_force[best_sys_count-1]);
1374 ret.system_count_status_ = page1_status[best_sys_count-1] | page2_status[best_sys_count-1];
1375 ret.penalty_ = cached_line_details_[best_sys_count-1].page_penalty_
1376 + cached_line_details_.back ().page_penalty_
1377 + cached_line_details_.back ().turn_penalty_
1378 + page1_penalty[best_sys_count-1] + page2_penalty[best_sys_count-1];
1380 /* don't do finalize_spacing_result () because we are only an internal function */
1381 return ret;
1384 bool
1385 Page_breaking::all_lines_stretched (vsize configuration)
1387 cache_line_details (configuration);
1388 for (vsize i = 0; i < cached_line_details_.size (); i++)
1389 if (cached_line_details_[i].force_ < 0)
1390 return false;
1392 return true;
1395 Page_breaking::Line_division
1396 Page_breaking::current_configuration (vsize configuration_index) const
1398 return current_configurations_[configuration_index];
1401 bool
1402 Page_breaking::is_last () const
1404 return current_end_breakpoint_ == last_break_position ();
1407 bool
1408 Page_breaking::ends_score () const
1410 return breaks_[current_end_breakpoint_].score_ender_;
1413 vsize
1414 Page_breaking::last_break_position () const
1416 return breaks_.size () - 1;
1419 // This gives the minimum distance between the top of the
1420 // printable area (ie. the bottom of the top-margin) and
1421 // the extent box of the topmost system.
1422 Real
1423 Page_breaking::min_whitespace_at_top_of_page (Line_details const &line) const
1425 SCM first_system_spacing = book_->paper_->c_variable ("top-system-spacing");
1426 if (line.title_)
1427 first_system_spacing = book_->paper_->c_variable ("top-title-spacing");
1429 Real min_distance = -infinity_f;
1430 Real padding = 0;
1432 Page_layout_problem::read_spacing_spec (first_system_spacing,
1433 &min_distance,
1434 ly_symbol2scm ("minimum-distance"));
1435 Page_layout_problem::read_spacing_spec (first_system_spacing,
1436 &padding,
1437 ly_symbol2scm ("padding"));
1439 // FIXME: take into account the height of the header
1440 Real translate = max (line.shape_.begin_[UP], line.shape_.rest_[UP]);
1441 return max (0.0, max (padding, min_distance - translate));
1444 Real
1445 Page_breaking::min_whitespace_at_bottom_of_page (Line_details const &line) const
1447 SCM last_system_spacing = book_->paper_->c_variable ("bottom-system-spacing");
1448 Real min_distance = -infinity_f;
1449 Real padding = 0;
1451 Page_layout_problem::read_spacing_spec (last_system_spacing,
1452 &min_distance,
1453 ly_symbol2scm ("minimum-distance"));
1454 Page_layout_problem::read_spacing_spec (last_system_spacing,
1455 &padding,
1456 ly_symbol2scm ("padding"));
1458 // FIXME: take into account the height of the footer
1459 Real translate = min (line.shape_.begin_[DOWN], line.shape_.rest_[DOWN]);
1460 return max (0.0, max (padding, min_distance + translate));
1464 Page_breaking::orphan_penalty () const
1466 return orphan_penalty_;