2 page-breaking.cc -- implement a superclass and utility
3 functions shared by various page-breaking algorithms
5 source file of the GNU LilyPond music typesetter
7 (c) 2006--2009 Joe Neeman <joeneeman@gmail.com>
10 #include "page-breaking.hh"
12 #include "international.hh"
14 #include "output-def.hh"
15 #include "page-spacing.hh"
16 #include "paper-book.hh"
17 #include "paper-score.hh"
18 #include "paper-system.hh"
22 /* for each forbidden page break, merge the systems around it into one
24 static vector<Line_details>
25 compress_lines (const vector<Line_details> &orig)
27 vector<Line_details> ret;
29 for (vsize i = 0; i < orig.size (); i++)
31 if (ret.size () && !scm_is_symbol (ret.back ().page_permission_))
33 Line_details const &old = ret.back ();
34 Line_details compressed = orig[i];
35 compressed.extent_[DOWN] = old.extent_[DOWN];
36 compressed.extent_[UP] = old.extent_[UP] + orig[i].extent_.length () + old.padding_;
37 compressed.space_ += old.space_;
38 compressed.inverse_hooke_ += old.inverse_hooke_;
40 compressed.compressed_lines_count_ = old.compressed_lines_count_ + 1;
41 compressed.compressed_nontitle_lines_count_ =
42 old.compressed_nontitle_lines_count_ + (compressed.title_ ? 0 : 1);
44 compressed.title_ = compressed.title_ && old.title_;
45 ret.back () = compressed;
49 ret.push_back (orig[i]);
50 ret.back ().force_ = 0;
56 /* translate the number of systems-per-page into something meaningful for
57 the uncompressed lines.
60 uncompress_solution (vector<vsize> const &systems_per_page,
61 vector<Line_details> const &compressed)
66 for (vsize i = 0; i < systems_per_page.size (); i++)
68 int compressed_count = 0;
69 for (vsize j = start_sys; j < start_sys + systems_per_page[i]; j++)
70 compressed_count += compressed[j].compressed_lines_count_ - 1;
72 ret.push_back (systems_per_page[i] + compressed_count);
73 start_sys += systems_per_page[i];
78 /* for Page_breaking, the start index (when we are dealing with the stuff
79 between a pair of breakpoints) refers to the break_ index of the end of
80 the previous page. So the system index of the start of the current page
81 could either be the same as the end of the previous page or one more than
84 /* Turn a break index into the sys index that starts the next page */
86 Page_breaking::next_system (Break_position const &break_pos) const
88 vsize sys = break_pos.system_spec_index_;
90 if (sys == VPOS) /* beginning of the book */
92 if (system_specs_[sys].pscore_ && !break_pos.score_ender_)
93 return sys; /* the score overflows the previous page */
94 return sys + 1; /* this page starts with a new System_spec */
97 Page_breaking::Page_breaking (Paper_book *pb, Break_predicate is_break)
101 ragged_ = to_boolean (pb->paper_->c_variable ("ragged-bottom"));
102 ragged_last_ = to_boolean (pb->paper_->c_variable ("ragged-last-bottom"));
103 page_top_space_ = robust_scm2double (pb->paper_->c_variable ("page-top-space"), 0);
104 systems_per_page_ = robust_scm2int (pb->paper_->c_variable ("systems-per-page"), 0);
105 max_systems_per_page_ = robust_scm2int (pb->paper_->c_variable ("max-systems-per-page"), 0);
107 create_system_list ();
108 find_chunks_and_breaks (is_break);
111 Page_breaking::~Page_breaking ()
116 Page_breaking::ragged () const
122 Page_breaking::ragged_last () const
128 Page_breaking::systems_per_page () const
130 return systems_per_page_;
134 Page_breaking::max_systems_per_page () const
136 return max_systems_per_page_;
140 Page_breaking::page_top_space () const
142 return page_top_space_;
146 Page_breaking::system_count () const
148 return system_count_;
151 /* translate indices into breaks_ into start-end parameters for the line breaker */
153 Page_breaking::line_breaker_args (vsize sys,
154 Break_position const &start,
155 Break_position const &end,
156 vsize *line_breaker_start,
157 vsize *line_breaker_end)
159 assert (system_specs_[sys].pscore_);
160 assert (next_system (start) <= sys && sys <= end.system_spec_index_);
162 if (start.system_spec_index_ == sys)
163 *line_breaker_start = start.score_break_;
165 *line_breaker_start = 0;
167 if (end.system_spec_index_ == sys)
168 *line_breaker_end = end.score_break_;
170 *line_breaker_end = VPOS;
174 Page_breaking::break_into_pieces (vsize start_break, vsize end_break,
175 Line_division const &div)
177 vector<Break_position> chunks = chunk_list (start_break, end_break);
178 bool ignore_div = false;
179 if (chunks.size () != div.size () + 1)
181 programming_error ("did not find a valid page breaking configuration");
185 for (vsize i = 0; i + 1 < chunks.size (); i++)
187 vsize sys = next_system (chunks[i]);
188 if (system_specs_[sys].pscore_)
192 line_breaker_args (sys, chunks[i], chunks[i+1], &start, &end);
194 vector<Column_x_positions> pos = ignore_div
195 ? line_breaking_[sys].best_solution (start, end)
196 : line_breaking_[sys].solve (start, end, div[i]);
197 system_specs_[sys].pscore_->root_system ()->break_into_pieces (pos);
203 Page_breaking::systems ()
206 for (vsize sys = 0; sys < system_specs_.size (); sys++)
208 if (system_specs_[sys].pscore_)
210 system_specs_[sys].pscore_->root_system ()
211 ->do_break_substitution_and_fixup_refpoints ();
212 SCM lines = system_specs_[sys].pscore_->root_system ()
213 ->get_broken_system_grobs ();
214 ret = scm_cons (lines, ret);
218 Prob *pb = system_specs_[sys].prob_;
219 ret = scm_cons (scm_list_1 (pb->self_scm ()), ret);
223 return scm_append (scm_reverse (ret));
227 Page_breaking::page_height (int page_num, bool last) const
229 bool last_part = ly_scm2bool (book_->paper_->c_variable ("is-last-bookpart"));
230 SCM mod = scm_c_resolve_module ("scm page");
231 SCM calc_height = scm_c_module_lookup (mod, "calc-printable-height");
232 SCM make_page = scm_c_module_lookup (mod, "make-page");
234 calc_height = scm_variable_ref (calc_height);
235 make_page = scm_variable_ref (make_page);
237 SCM page = scm_apply_0 (make_page, scm_list_n (
239 ly_symbol2scm ("page-number"), scm_from_int (page_num),
240 ly_symbol2scm ("is-last-bookpart"), scm_from_bool (last_part),
241 ly_symbol2scm ("is-bookpart-last-page"), scm_from_bool (last),
243 SCM height = scm_apply_1 (calc_height, page, SCM_EOL);
244 return scm_to_double (height) - page_top_space_;
248 Page_breaking::breakpoint_property (vsize breakpoint, char const *str)
250 Break_position const &pos = breaks_[breakpoint];
252 if (pos.system_spec_index_ == VPOS)
254 if (system_specs_[pos.system_spec_index_].pscore_)
255 return pos.col_->get_property (str);
256 return system_specs_[pos.system_spec_index_].prob_->get_property (str);
260 Page_breaking::make_pages (vector<vsize> lines_per_page, SCM systems)
262 SCM layout_module = scm_c_resolve_module ("scm layout-page-layout");
263 SCM page_module = scm_c_resolve_module ("scm page");
265 SCM make_page = scm_c_module_lookup (layout_module, "stretch-and-draw-page");
266 SCM page_stencil = scm_c_module_lookup (page_module, "page-stencil");
267 make_page = scm_variable_ref (make_page);
268 page_stencil = scm_variable_ref (page_stencil);
270 SCM book = book_->self_scm ();
271 int first_page_number
272 = robust_scm2int (book_->paper_->c_variable ("first-page-number"), 1);
273 bool last_bookpart = ly_scm2bool (book_->paper_->c_variable ("is-last-bookpart"));
275 SCM label_page_table = book_->top_paper ()->c_variable ("label-page-table");
276 if (label_page_table == SCM_UNDEFINED)
277 label_page_table = SCM_EOL;
279 for (vsize i = 0; i < lines_per_page.size (); i++)
281 SCM page_num = scm_from_int (i + first_page_number);
282 bool partbook_last_page = (i == lines_per_page.size () - 1);
283 SCM rag = scm_from_bool (ragged () || ( partbook_last_page && ragged_last ()));
284 SCM line_count = scm_from_int (lines_per_page[i]);
285 SCM lines = scm_list_head (systems, line_count);
286 SCM page = scm_apply_0 (make_page,
287 scm_list_n (book, lines, page_num, rag,
288 scm_from_bool (last_bookpart),
289 scm_from_bool (partbook_last_page),
292 for (SCM l = lines ; scm_is_pair (l) ; l = scm_cdr (l))
294 SCM labels = SCM_EOL;
295 if (Grob * line = unsmob_grob (scm_car (l)))
297 System *system = dynamic_cast<System*> (line);
298 labels = system->get_property ("labels");
300 else if (Prob *prob = unsmob_prob (scm_car (l)))
301 labels = prob->get_property ("labels");
303 for (SCM lbls = labels ; scm_is_pair (lbls) ; lbls = scm_cdr (lbls))
304 label_page_table = scm_cons (scm_cons (scm_car (lbls), page_num),
308 scm_apply_1 (page_stencil, page, SCM_EOL);
309 ret = scm_cons (page, ret);
310 systems = scm_list_tail (systems, line_count);
312 book_->top_paper ()->set_variable (ly_symbol2scm ("label-page-table"), label_page_table);
313 ret = scm_reverse (ret);
317 /* The page-turn-page-breaker needs to have a line-breaker between any two
318 columns with non-NULL page-turn-permission.
320 The optimal-breaker needs to have a line-breaker between any two columns
321 with page-break-permission = 'force.
323 By using a grob predicate, we can accommodate both of these uses.
326 Page_breaking::create_system_list ()
328 SCM specs = book_->get_system_specs ();
329 for (SCM s = specs; scm_is_pair (s); s = scm_cdr (s))
331 if (Paper_score *ps = dynamic_cast<Paper_score*> (unsmob_music_output (scm_car (s))))
333 SCM system_count = ps->layout ()->c_variable ("system-count");
335 if (scm_is_number (system_count))
336 s = scm_append (scm_list_3 (scm_list_1 (scm_car (s)),
337 scm_vector_to_list (ps->get_paper_systems ()),
340 system_specs_.push_back (System_spec (ps));
344 Prob *pb = unsmob_prob (scm_car (s));
348 system_specs_.push_back (System_spec (pb));
354 Page_breaking::find_chunks_and_breaks (Break_predicate is_break)
356 SCM force_sym = ly_symbol2scm ("force");
358 chunks_.push_back (Break_position ());
359 breaks_.push_back (Break_position ());
361 for (vsize i = 0; i < system_specs_.size (); i++)
363 if (system_specs_[i].pscore_)
366 = system_specs_[i].pscore_->root_system ()->used_columns ();
367 vector<vsize> line_breaker_columns;
368 line_breaker_columns.push_back (0);
370 for (vsize j = 1; j < cols.size (); j++)
372 bool last = (j == cols.size () - 1);
373 bool break_point = is_break (cols[j]);
374 bool chunk_end = cols[j]->get_property ("page-break-permission") == force_sym;
375 Break_position cur_pos = Break_position (i,
376 line_breaker_columns.size (),
380 if (break_point || (i == system_specs_.size () - 1 && last))
381 breaks_.push_back (cur_pos);
382 if (chunk_end || last)
383 chunks_.push_back (cur_pos);
385 if ((break_point || chunk_end) && !last)
386 line_breaker_columns.push_back (j);
388 line_breaking_.push_back (Constrained_breaking (system_specs_[i].pscore_, line_breaker_columns));
392 /* TODO: we want some way of applying Break_p to a prob? */
393 if (i == system_specs_.size () - 1)
394 breaks_.push_back (Break_position (i));
396 chunks_.push_back (Break_position (i));
398 /* FIXME: shouldn't we push a Null_breaker or similar dummy
400 line_breaking_.push_back (Constrained_breaking (NULL));
405 vector<Break_position>
406 Page_breaking::chunk_list (vsize start_index, vsize end_index)
408 Break_position start = breaks_[start_index];
409 Break_position end = breaks_[end_index];
412 for (; i < chunks_.size () && chunks_[i] <= start; i++)
415 vector<Break_position> ret;
416 ret.push_back (start);
417 for (; i < chunks_.size () && chunks_[i] < end; i++)
418 ret.push_back (chunks_[i]);
424 Page_breaking::min_system_count (vsize start, vsize end)
426 vector<Break_position> chunks = chunk_list (start, end);
427 Line_division div = system_count_bounds (chunks, true);
430 for (vsize i = 0; i < div.size (); i++)
436 Page_breaking::max_system_count (vsize start, vsize end)
438 vector<Break_position> chunks = chunk_list (start, end);
439 Line_division div = system_count_bounds (chunks, false);
442 for (vsize i = 0; i < div.size (); i++)
447 Page_breaking::Line_division
448 Page_breaking::system_count_bounds (vector<Break_position> const &chunks,
451 assert (chunks.size () >= 2);
454 ret.resize (chunks.size () - 1, 1);
456 for (vsize i = 0; i + 1 < chunks.size (); i++)
458 vsize sys = next_system (chunks[i]);
459 if (system_specs_[sys].pscore_)
463 line_breaker_args (sys, chunks[i], chunks[i+1], &start, &end);
465 ? line_breaking_[sys].min_system_count (start, end)
466 : line_breaking_[sys].max_system_count (start, end);
474 Page_breaking::set_current_breakpoints (vsize start,
477 Line_division lower_bound,
478 Line_division upper_bound)
480 system_count_ = system_count;
481 current_chunks_ = chunk_list (start, end);
482 current_start_breakpoint_ = start;
483 current_end_breakpoint_ = end;
484 clear_line_details_cache ();
486 if (!lower_bound.size ())
487 lower_bound = system_count_bounds (current_chunks_, true);
488 if (!upper_bound.size ())
489 upper_bound = system_count_bounds (current_chunks_, false);
491 assert (lower_bound.size () == current_chunks_.size () - 1);
492 assert (upper_bound.size () == current_chunks_.size () - 1);
494 Line_division work_in_progress;
495 current_configurations_.clear ();
496 line_divisions_rec (system_count,
501 /* we only consider a constant number of configurations. Otherwise,
502 this becomes slow when there are many small scores. The constant
503 5 is somewhat arbitrary. */
504 if (current_configurations_.size () > 5)
506 vector<pair<Real,vsize> > dems_and_indices;
508 for (vsize i = 0; i < current_configurations_.size (); i++)
510 cache_line_details (i);
512 for (vsize j = 0; j < cached_line_details_.size (); j++)
513 dem += cached_line_details_[j].force_ * cached_line_details_[j].force_
514 + cached_line_details_[j].break_penalty_;
516 dems_and_indices.push_back (pair<Real,vsize> (dem, i));
518 vector_sort (dems_and_indices, less<pair<Real,vsize> > ());
520 vector<Line_division> best_5_configurations;
521 for (vsize i = 0; i < 5; i++)
522 best_5_configurations.push_back (current_configurations_[dems_and_indices[i].second]);
524 clear_line_details_cache ();
525 current_configurations_ = best_5_configurations;
530 Page_breaking::set_to_ideal_line_configuration (vsize start, vsize end)
532 current_chunks_ = chunk_list (start, end);
533 current_start_breakpoint_ = start;
534 current_end_breakpoint_ = end;
535 clear_line_details_cache ();
539 for (vsize i = 0; i+1 < current_chunks_.size (); i++)
541 vsize sys = next_system (current_chunks_[i]);
542 if (system_specs_[sys].pscore_)
544 line_breaker_args (sys, current_chunks_[i], current_chunks_[i+1], &start, &end);
545 div.push_back (line_breaking_[sys].best_solution (start, end).size ());
550 system_count_ += div.back ();
552 current_configurations_.clear ();
553 current_configurations_.push_back (div);
557 Page_breaking::current_configuration_count () const
559 return current_configurations_.size ();
563 Page_breaking::cache_line_details (vsize configuration_index)
565 if (cached_configuration_index_ != configuration_index)
567 cached_configuration_index_ = configuration_index;
568 SCM padding_scm = book_->paper_->c_variable ("page-breaking-between-system-padding");
569 if (!scm_is_number (padding_scm))
570 padding_scm = book_->paper_->c_variable ("between-system-padding");
571 Real padding = robust_scm2double (padding_scm, 0.0);
573 Line_division &div = current_configurations_[configuration_index];
574 uncompressed_line_details_.clear ();
575 for (vsize i = 0; i + 1 < current_chunks_.size (); i++)
577 vsize sys = next_system (current_chunks_[i]);
578 if (system_specs_[sys].pscore_)
582 line_breaker_args (sys, current_chunks_[i], current_chunks_[i+1], &start, &end);
584 vector<Line_details> details = line_breaking_[sys].line_details (start, end, div[i]);
585 uncompressed_line_details_.insert (uncompressed_line_details_.end (), details.begin (), details.end ());
589 assert (div[i] == 1);
590 uncompressed_line_details_.push_back (Line_details (system_specs_[sys].prob_));
591 uncompressed_line_details_.back ().padding_ =
592 robust_scm2double (system_specs_[sys].prob_->get_property ("next-padding"),
596 cached_line_details_ = compress_lines (uncompressed_line_details_);
601 Page_breaking::clear_line_details_cache ()
603 cached_configuration_index_ = VPOS;
604 cached_line_details_.clear ();
605 uncompressed_line_details_.clear ();
609 Page_breaking::line_divisions_rec (vsize system_count,
610 Line_division const &min_sys,
611 Line_division const &max_sys,
612 Line_division *cur_division)
614 vsize my_index = cur_division->size ();
615 vsize others_min = 0;
616 vsize others_max = 0;
618 for (vsize i = my_index + 1; i < min_sys.size (); i++)
620 others_min += min_sys[i];
621 others_max += max_sys[i];
623 others_max = min (others_max, system_count);
624 vsize real_min = max (min_sys[my_index], system_count - others_max);
625 vsize real_max = min (max_sys[my_index], system_count - others_min);
627 if (real_min > real_max || real_min <= 0)
629 /* this should never happen within a recursive call. If it happens
630 at all, it means that we were called with an unsolvable problem
631 and we should return an empty result */
632 assert (my_index == 0);
636 for (vsize i = real_min; i <= real_max; i++)
638 cur_division->push_back (i);
639 if (my_index == min_sys.size () - 1)
640 current_configurations_.push_back (*cur_division);
642 line_divisions_rec (system_count - i, min_sys, max_sys, cur_division);
643 cur_division->pop_back ();
648 Page_breaking::min_page_count (vsize configuration, vsize first_page_num)
651 Real cur_rod_height = 0;
652 Real cur_spring_height = 0;
653 Real cur_page_height = page_height (first_page_num, false);
655 cache_line_details (configuration);
656 for (vsize i = 0; i < cached_line_details_.size (); i++)
658 Real ext_len = cached_line_details_[i].extent_.length ();
659 Real next_rod_height = cur_rod_height + ext_len
660 + ((cur_rod_height > 0) ? cached_line_details_[i].padding_: 0);
661 Real next_spring_height = cur_spring_height + cached_line_details_[i].space_;
662 Real next_height = next_rod_height + (ragged () ? next_spring_height : 0);
665 if ((next_height > cur_page_height && cur_rod_height > 0)
667 && cached_line_details_[i-1].page_permission_ == ly_symbol2scm ("force")))
669 cur_rod_height = ext_len;
670 cur_spring_height = cached_line_details_[i].space_;
671 cur_page_height = page_height (first_page_num + ret, false);
676 cur_rod_height = next_rod_height;
677 cur_spring_height = next_spring_height;
681 /* there are two potential problems with the last page (because we didn't know
682 it was the last page until after we managed to fit all the systems to it):
683 - we are ragged-last but the last page has a compressed spring
684 - the value returned by page_height (num, true) is smaller than the
685 value returned by page_height (num, false) and it causes the page not to
688 In either case, we just need to add one more page. This is because the last
689 line will always fit on the extra page and by adding one more page to the
690 end, the previous page is no longer the last page, so our previous
691 calculations that treated it as a non-last page were ok.
694 cur_page_height = page_height (first_page_num + ret - 1, true);
695 Real cur_height = cur_rod_height + ((ragged_last () || ragged ()) ? cur_spring_height : 0);
696 if (cur_height > cur_page_height
697 /* don't increase the page count if the last page had only one system */
698 && cur_rod_height > cached_line_details_.back ().extent_.length ())
701 assert (ret <= cached_line_details_.size ());
705 // If systems_per_page_ is positive, we don't really try to space on N pages;
706 // we just put the requested number of systems on each page and penalize
707 // if the result doesn't have N pages.
709 Page_breaking::space_systems_on_n_pages (vsize configuration, vsize n, vsize first_page_num)
711 Page_spacing_result ret;
713 if (systems_per_page_ > 0)
715 Page_spacing_result ret = space_systems_with_fixed_number_per_page (configuration, first_page_num);
716 ret.demerits_ += (ret.force_.size () == n) ? 0 : BAD_SPACING_PENALTY;
720 cache_line_details (configuration);
721 bool valid_n = (n >= min_page_count (configuration, first_page_num)
722 && n <= cached_line_details_.size ());
725 programming_error ("number of pages is out of bounds");
727 if (n == 1 && valid_n)
728 ret = space_systems_on_1_page (cached_line_details_,
729 page_height (first_page_num, is_last ()),
730 ragged () || (is_last () && ragged_last ()));
731 else if (n == 2 && valid_n)
732 ret = space_systems_on_2_pages (configuration, first_page_num);
735 Page_spacer ps (cached_line_details_, first_page_num, this);
739 return finalize_spacing_result (configuration, ret);
743 Page_breaking::blank_page_penalty () const
748 penalty_sym = ly_symbol2scm ("blank-last-page-force");
749 else if (ends_score ())
750 penalty_sym = ly_symbol2scm ("blank-after-score-page-force");
752 penalty_sym = ly_symbol2scm ("blank-page-force");
754 Break_position const &pos = breaks_[current_end_breakpoint_];
755 if (Paper_score *ps = system_specs_[pos.system_spec_index_].pscore_)
756 return robust_scm2double (ps->layout ()->lookup_variable (penalty_sym), 0.0);
758 return robust_scm2double (book_->paper_->lookup_variable (penalty_sym), 0.0);
761 // If systems_per_page_ is positive, we don't really try to space on N
762 // or N+1 pages; see the comment to space_systems_on_n_pages.
764 Page_breaking::space_systems_on_n_or_one_more_pages (vsize configuration, vsize n, vsize first_page_num)
766 Page_spacing_result n_res;
767 Page_spacing_result m_res;
769 if (systems_per_page_ > 0)
771 Page_spacing_result ret = space_systems_with_fixed_number_per_page (configuration, first_page_num);
772 ret.demerits_ += (ret.force_.size () == n || ret.force_.size () == (n-1)) ? 0 : BAD_SPACING_PENALTY;
776 cache_line_details (configuration);
777 vsize min_p_count = min_page_count (configuration, first_page_num);
778 bool valid_n = n >= min_p_count || n <= cached_line_details_.size ();
781 programming_error ("both page counts are out of bounds");
783 if (n == 1 && valid_n)
785 bool rag = ragged () || (is_last () && ragged_last ());
786 Real height = page_height (first_page_num, is_last ());
788 if (1 >= min_p_count)
789 n_res = space_systems_on_1_page (cached_line_details_, height, rag);
790 if (1 < cached_line_details_.size ())
791 m_res = space_systems_on_2_pages (configuration, first_page_num);
795 Page_spacer ps (cached_line_details_, first_page_num, this);
797 if (n >= min_p_count || !valid_n)
798 n_res = ps.solve (n);
799 if (n < cached_line_details_.size () || !valid_n)
800 m_res = ps.solve (n+1);
803 m_res = finalize_spacing_result (configuration, m_res);
804 n_res = finalize_spacing_result (configuration, n_res);
806 Real penalty = blank_page_penalty ();
807 n_res.demerits_ += penalty;
809 if (n_res.force_.size ())
810 n_res.force_.back () += penalty;
812 return (m_res.demerits_ < n_res.demerits_) ? m_res : n_res;
816 Page_breaking::space_systems_on_best_pages (vsize configuration, vsize first_page_num)
818 vsize min_p_count = min_page_count (configuration, first_page_num);
819 Real odd_pages_penalty = blank_page_penalty ();
821 cache_line_details (configuration);
822 Page_spacer ps (cached_line_details_, first_page_num, this);
823 Page_spacing_result best = ps.solve (min_p_count);
824 best.force_.back () += (min_p_count % 2) ? odd_pages_penalty : 0;
825 best.demerits_ += (min_p_count % 2) ? odd_pages_penalty : 0;
827 for (vsize i = min_p_count+1; i <= cached_line_details_.size (); i++)
829 Page_spacing_result cur = ps.solve (i);
830 cur.demerits_ += (i % 2) ? odd_pages_penalty : 0;
831 if (cur.demerits_ < best.demerits_)
835 return finalize_spacing_result (configuration, best);
839 Page_breaking::space_systems_with_fixed_number_per_page (vsize configuration,
840 vsize first_page_num)
842 Page_spacing_result res;
843 Page_spacing space (page_height (first_page_num, false), page_top_space_);
846 vsize page_first_line = 0;
848 cache_line_details (configuration);
849 while (line < cached_line_details_.size ())
853 space.resize (page_height (first_page_num + page, false));
855 int system_count_on_this_page = 0;
856 while (system_count_on_this_page < systems_per_page_
857 && line < cached_line_details_.size ())
859 Line_details const &cur_line = cached_line_details_[line];
860 space.append_system (cur_line);
861 system_count_on_this_page += cur_line.compressed_nontitle_lines_count_;
864 if (cur_line.page_permission_ == ly_symbol2scm ("force"))
868 res.systems_per_page_.push_back (line - page_first_line);
869 res.force_.push_back (space.force_);
870 res.penalty_ += cached_line_details_[line-1].page_penalty_;
871 page_first_line = line;
874 /* Recalculate forces for the last page because we know now that is
875 was really the last page. */
876 space.resize (page_height (first_page_num + page, true));
877 res.force_.back () = space.force_;
878 return finalize_spacing_result (configuration, res);
882 Page_breaking::pack_systems_on_least_pages (vsize configuration, vsize first_page_num)
884 Page_spacing_result res;
886 vsize page_first_line = 0;
887 Page_spacing space (page_height (first_page_num, false), page_top_space_);
889 cache_line_details (configuration);
890 for (vsize line = 0; line < cached_line_details_.size (); line++)
892 Real prev_force = space.force_;
893 space.append_system (cached_line_details_[line]);
894 if ((line > page_first_line)
895 && (isinf (space.force_)
897 && (cached_line_details_[line-1].page_permission_ == ly_symbol2scm ("force")))))
899 res.systems_per_page_.push_back (line - page_first_line);
900 res.force_.push_back (prev_force);
901 res.penalty_ += cached_line_details_[line-1].page_penalty_;
903 space.resize (page_height (first_page_num + page, false));
905 space.append_system (cached_line_details_[line]);
906 page_first_line = line;
909 if (line == cached_line_details_.size () - 1)
911 /* This is the last line */
912 /* When the last page height was computed, we did not know yet that it
913 * was the last one. If the systems put on it don't fit anymore, the last
914 * system is moved to a new page */
915 space.resize (page_height (first_page_num + page, true));
916 if ((line > page_first_line) && (isinf (space.force_)))
918 res.systems_per_page_.push_back (line - page_first_line);
919 res.force_.push_back (prev_force);
920 /* the last page containing the last line */
921 space.resize (page_height (first_page_num + page + 1, true));
923 space.append_system (cached_line_details_[line]);
924 res.systems_per_page_.push_back (1);
925 res.force_.push_back (space.force_);
926 res.penalty_ += cached_line_details_[line-1].page_penalty_;
927 res.penalty_ += cached_line_details_[line].page_penalty_;
931 res.systems_per_page_.push_back (line + 1 - page_first_line);
932 res.force_.push_back (space.force_);
933 res.penalty_ += cached_line_details_[line].page_penalty_;
937 return finalize_spacing_result (configuration, res);
940 /* Calculate demerits and fix res.systems_per_page_ so that
941 it refers to the original line numbers, not the ones given by compress_lines (). */
943 Page_breaking::finalize_spacing_result (vsize configuration, Page_spacing_result res)
945 if (res.force_.empty ())
948 cache_line_details (configuration);
949 res.systems_per_page_ = uncompress_solution (res.systems_per_page_, cached_line_details_);
952 Real line_penalty = 0;
954 Real page_weighting = robust_scm2double (book_->paper_->c_variable ("page-spacing-weight"), 10);
956 for (vsize i = 0; i < uncompressed_line_details_.size (); i++)
958 line_force += uncompressed_line_details_[i].force_ * uncompressed_line_details_[i].force_;
959 line_penalty += uncompressed_line_details_[i].break_penalty_;
962 for (vsize i = 0; i < res.force_.size (); i++)
964 Real f = res.force_[i];
965 if (isinf (f) && res.systems_per_page_[i] == 1)
971 /* for a while we tried averaging page and line forces across pages instead
972 of summing them, but it caused a problem: if there is a single page
973 with a very bad page force (for example because of a forced page break),
974 the page breaker will put in a _lot_ of pages so that the bad force
975 becomes averaged out over many pages. */
976 res.demerits_ = line_force + line_penalty + (page_force + res.penalty_) * page_weighting;
981 /* the cases for page_count = 1 or 2 can be done in O (n) time. Since they
982 are by far the most common cases, we have special functions for them.
984 space_systems_on_1_page has a different calling convention than most of the
985 space_systems functions. This is because space_systems_on_1_page is (unlike
986 the other space_systems functions) sometimes called on subsets of a full
989 Page_breaking::space_systems_on_1_page (vector<Line_details> const &lines, Real page_height, bool ragged)
991 Page_spacing space (page_height, page_top_space_);
992 Page_spacing_result ret;
994 for (vsize i = 0; i < lines.size (); i++)
995 space.append_system (lines[i]);
997 ret.systems_per_page_.push_back (lines.size ());
998 ret.force_.push_back (ragged ? min (space.force_, 0.0) : space.force_);
999 ret.penalty_ = lines.back ().page_penalty_ + lines.back ().turn_penalty_;
1001 /* don't do finalize_spacing_result () because we are only an internal function */
1006 Page_breaking::space_systems_on_2_pages (vsize configuration, vsize first_page_num)
1008 Real page1_height = page_height (first_page_num, false);
1009 Real page2_height = page_height (first_page_num + 1, is_last ());
1010 bool ragged1 = ragged ();
1011 bool ragged2 = ragged () || (is_last () && ragged_last ());
1013 /* if there is a forced break, this reduces to 2 1-page problems */
1014 cache_line_details (configuration);
1015 for (vsize i = 0; i + 1 < cached_line_details_.size (); i++)
1016 if (cached_line_details_[i].page_permission_ == ly_symbol2scm ("force"))
1018 vector<Line_details> lines1 (cached_line_details_.begin (), cached_line_details_.begin () + i + 1);
1019 vector<Line_details> lines2 (cached_line_details_.begin () + i + 1, cached_line_details_.end ());
1020 Page_spacing_result p1 = space_systems_on_1_page (lines1, page1_height, ragged1);
1021 Page_spacing_result p2 = space_systems_on_1_page (lines2, page2_height, ragged2);
1023 p1.systems_per_page_.push_back (p2.systems_per_page_[0]);
1024 p1.force_.push_back (p2.force_[0]);
1025 p1.penalty_ += p2.penalty_ - cached_line_details_[i].turn_penalty_;
1029 vector<Real> page1_force;
1030 vector<Real> page2_force;
1031 Page_spacing page1 (page1_height, page_top_space_);
1032 Page_spacing page2 (page2_height, page_top_space_);
1034 page1_force.resize (cached_line_details_.size () - 1, infinity_f);
1035 page2_force.resize (cached_line_details_.size () - 1, infinity_f);
1037 /* find the page 1 and page 2 forces for each page-breaking position */
1038 for (vsize i = 0; i < page1_force.size (); i++)
1040 page1.append_system (cached_line_details_[i]);
1041 page2.prepend_system (cached_line_details_[cached_line_details_.size () - 1 - i]);
1042 page1_force[i] = (ragged1 && page1.force_ < 0 && i > 0) ? infinity_f : page1.force_;
1045 page2_force[page2_force.size () - 1 - i] =
1046 (page2.force_ < 0 && i + 1 < page1_force.size ()) ? infinity_f : 0;
1048 page2_force[page2_force.size () - 1 - i] = page2.force_;
1051 /* find the position that minimises the sum of the page forces */
1052 vsize best_sys_count = 1;
1053 Real best_demerits = infinity_f;
1054 for (vsize i = 0; i < page1_force.size (); i++)
1056 Real f = page1_force[i] * page1_force[i] + page2_force[i] * page2_force[i];
1057 Real uneven = 2 * (page1_force[i] - page2_force[i]);
1058 Real dem = uneven * uneven + f
1059 + cached_line_details_[i+1].page_penalty_
1060 + cached_line_details_.back ().page_penalty_ + cached_line_details_.back ().turn_penalty_;
1061 if (dem < best_demerits)
1063 best_demerits = dem;
1064 best_sys_count = i+1;
1068 Page_spacing_result ret;
1069 ret.systems_per_page_.push_back (best_sys_count);
1070 ret.systems_per_page_.push_back (cached_line_details_.size () - best_sys_count);
1071 ret.force_.push_back (page1_force[best_sys_count-1]);
1072 ret.force_.push_back (page2_force[best_sys_count-1]);
1073 ret.penalty_ = cached_line_details_[best_sys_count-1].page_penalty_
1074 + cached_line_details_.back ().page_penalty_
1075 + cached_line_details_.back ().turn_penalty_;
1077 /* don't do finalize_spacing_result () because we are only an internal function */
1082 Page_breaking::all_lines_stretched (vsize configuration)
1084 cache_line_details (configuration);
1085 for (vsize i = 0; i < cached_line_details_.size (); i++)
1086 if (cached_line_details_[i].force_ < 0)
1092 Page_breaking::Line_division
1093 Page_breaking::current_configuration (vsize configuration_index) const
1095 return current_configurations_[configuration_index];
1099 Page_breaking::is_last () const
1101 return current_end_breakpoint_ == last_break_position ();
1105 Page_breaking::ends_score () const
1107 return breaks_[current_end_breakpoint_].score_ender_;
1111 Page_breaking::last_break_position () const
1113 return breaks_.size () - 1;