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--2007 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_;
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 create_system_list ();
105 find_chunks_and_breaks (is_break);
108 Page_breaking::~Page_breaking ()
113 Page_breaking::ragged () const
119 Page_breaking::ragged_last () const
125 Page_breaking::page_top_space () const
127 return page_top_space_;
131 Page_breaking::system_count () const
133 return system_count_;
136 /* translate indices into breaks_ into start-end parameters for the line breaker */
138 Page_breaking::line_breaker_args (vsize sys,
139 Break_position const &start,
140 Break_position const &end,
141 vsize *line_breaker_start,
142 vsize *line_breaker_end)
144 assert (system_specs_[sys].pscore_);
145 assert (next_system (start) <= sys && sys <= end.system_spec_index_);
147 if (start.system_spec_index_ == sys)
148 *line_breaker_start = start.score_break_;
150 *line_breaker_start = 0;
152 if (end.system_spec_index_ == sys)
153 *line_breaker_end = end.score_break_;
155 *line_breaker_end = VPOS;
159 Page_breaking::break_into_pieces (vsize start_break, vsize end_break,
160 Line_division const &div)
162 vector<Break_position> chunks = chunk_list (start_break, end_break);
163 bool ignore_div = false;
164 if (chunks.size () != div.size () + 1)
166 programming_error ("did not find a valid page breaking configuration");
170 for (vsize i = 0; i + 1 < chunks.size (); i++)
172 vsize sys = next_system (chunks[i]);
173 if (system_specs_[sys].pscore_)
177 line_breaker_args (sys, chunks[i], chunks[i+1], &start, &end);
179 vector<Column_x_positions> pos = ignore_div
180 ? line_breaking_[sys].best_solution (start, end)
181 : line_breaking_[sys].solve (start, end, div[i]);
182 system_specs_[sys].pscore_->root_system ()->break_into_pieces (pos);
188 Page_breaking::systems ()
191 for (vsize sys = 0; sys < system_specs_.size (); sys++)
193 if (system_specs_[sys].pscore_)
195 system_specs_[sys].pscore_->root_system ()
196 ->do_break_substitution_and_fixup_refpoints ();
197 SCM lines = system_specs_[sys].pscore_->root_system ()
198 ->get_broken_system_grobs ();
199 ret = scm_cons (lines, ret);
203 Prob *pb = system_specs_[sys].prob_;
204 ret = scm_cons (scm_list_1 (pb->self_scm ()), ret);
208 return scm_append (scm_reverse (ret));
212 Page_breaking::page_height (int page_num, bool last) const
214 SCM mod = scm_c_resolve_module ("scm page");
215 SCM calc_height = scm_c_module_lookup (mod, "calc-printable-height");
216 SCM make_page = scm_c_module_lookup (mod, "make-page");
218 calc_height = scm_variable_ref (calc_height);
219 make_page = scm_variable_ref (make_page);
221 SCM page = scm_apply_0 (make_page, scm_list_n (
223 ly_symbol2scm ("page-number"), scm_from_int (page_num),
224 ly_symbol2scm ("is-last"), scm_from_bool (last),
226 SCM height = scm_apply_1 (calc_height, page, SCM_EOL);
227 return scm_to_double (height) - page_top_space_;
231 Page_breaking::breakpoint_property (vsize breakpoint, char const *str)
233 Break_position const &pos = breaks_[breakpoint];
235 if (pos.system_spec_index_ == VPOS)
237 if (system_specs_[pos.system_spec_index_].pscore_)
238 return pos.col_->get_property (str);
239 return system_specs_[pos.system_spec_index_].prob_->get_property (str);
243 Page_breaking::make_pages (vector<vsize> lines_per_page, SCM systems)
245 SCM layout_module = scm_c_resolve_module ("scm layout-page-layout");
246 SCM page_module = scm_c_resolve_module ("scm page");
248 SCM make_page = scm_c_module_lookup (layout_module, "stretch-and-draw-page");
249 SCM page_stencil = scm_c_module_lookup (page_module, "page-stencil");
250 make_page = scm_variable_ref (make_page);
251 page_stencil = scm_variable_ref (page_stencil);
253 SCM book = book_->self_scm ();
254 int first_page_number
255 = robust_scm2int (book_->paper_->c_variable ("first-page-number"), 1);
257 SCM label_page_table = SCM_EOL;
259 for (vsize i = 0; i < lines_per_page.size (); i++)
261 SCM page_num = scm_from_int (i + first_page_number);
262 SCM last = scm_from_bool (i == lines_per_page.size () - 1);
263 SCM rag = scm_from_bool (ragged () || (to_boolean (last)
265 SCM line_count = scm_from_int (lines_per_page[i]);
266 SCM lines = scm_list_head (systems, line_count);
267 SCM page = scm_apply_0 (make_page,
268 scm_list_n (book, lines, page_num,
269 rag, last, SCM_UNDEFINED));
272 for (SCM l = lines ; scm_is_pair (l) ; l = scm_cdr (l))
274 SCM labels = SCM_EOL;
275 if (Grob * line = unsmob_grob (scm_car (l)))
277 System *system = dynamic_cast<System*> (line);
278 labels = system->get_property ("labels");
280 else if (Prob *prob = unsmob_prob (scm_car (l)))
281 labels = prob->get_property ("labels");
283 for (SCM lbls = labels ; scm_is_pair (lbls) ; lbls = scm_cdr (lbls))
284 label_page_table = scm_cons (scm_cons (scm_car (lbls), page_num),
288 scm_apply_1 (page_stencil, page, SCM_EOL);
289 ret = scm_cons (page, ret);
290 systems = scm_list_tail (systems, line_count);
292 book_->paper_->set_variable (ly_symbol2scm ("label-page-table"), label_page_table);
293 ret = scm_reverse (ret);
297 /* The page-turn-page-breaker needs to have a line-breaker between any two
298 columns with non-NULL page-turn-permission.
300 The optimal-breaker needs to have a line-breaker between any two columns
301 with page-break-permission = 'force.
303 By using a grob predicate, we can accommodate both of these uses.
306 Page_breaking::create_system_list ()
308 SCM specs = book_->get_system_specs ();
309 for (SCM s = specs; scm_is_pair (s); s = scm_cdr (s))
311 if (Paper_score *ps = dynamic_cast<Paper_score*> (unsmob_music_output (scm_car (s))))
313 SCM system_count = ps->layout ()->c_variable ("system-count");
315 if (scm_is_number (system_count))
316 s = scm_append (scm_list_3 (scm_list_1 (scm_car (s)),
317 scm_vector_to_list (ps->get_paper_systems ()),
320 system_specs_.push_back (System_spec (ps));
324 Prob *pb = unsmob_prob (scm_car (s));
328 system_specs_.push_back (System_spec (pb));
334 Page_breaking::find_chunks_and_breaks (Break_predicate is_break)
336 SCM force_sym = ly_symbol2scm ("force");
338 chunks_.push_back (Break_position ());
339 breaks_.push_back (Break_position ());
341 for (vsize i = 0; i < system_specs_.size (); i++)
343 if (system_specs_[i].pscore_)
346 = system_specs_[i].pscore_->root_system ()->used_columns ();
347 vector<vsize> line_breaker_columns;
348 line_breaker_columns.push_back (0);
350 for (vsize j = 1; j < cols.size (); j++)
352 bool last = (j == cols.size () - 1);
353 bool break_point = is_break (cols[j]);
354 bool chunk_end = cols[j]->get_property ("page-break-permission") == force_sym;
355 Break_position cur_pos = Break_position (i,
356 line_breaker_columns.size (),
360 if (break_point || (i == system_specs_.size () - 1 && last))
361 breaks_.push_back (cur_pos);
362 if (chunk_end || last)
363 chunks_.push_back (cur_pos);
365 if ((break_point || chunk_end) && !last)
366 line_breaker_columns.push_back (j);
368 line_breaking_.push_back (Constrained_breaking (system_specs_[i].pscore_, line_breaker_columns));
372 /* TODO: we want some way of applying Break_p to a prob? */
373 if (i == system_specs_.size () - 1)
374 breaks_.push_back (Break_position (i));
376 chunks_.push_back (Break_position (i));
378 /* FIXME: shouldn't we push a Null_breaker or similar dummy
380 line_breaking_.push_back (Constrained_breaking (NULL));
385 vector<Break_position>
386 Page_breaking::chunk_list (vsize start_index, vsize end_index)
388 Break_position start = breaks_[start_index];
389 Break_position end = breaks_[end_index];
392 for (; i < chunks_.size () && chunks_[i] <= start; i++)
395 vector<Break_position> ret;
396 ret.push_back (start);
397 for (; i < chunks_.size () && chunks_[i] < end; i++)
398 ret.push_back (chunks_[i]);
404 Page_breaking::min_system_count (vsize start, vsize end)
406 vector<Break_position> chunks = chunk_list (start, end);
407 Line_division div = system_count_bounds (chunks, true);
410 for (vsize i = 0; i < div.size (); i++)
416 Page_breaking::max_system_count (vsize start, vsize end)
418 vector<Break_position> chunks = chunk_list (start, end);
419 Line_division div = system_count_bounds (chunks, false);
422 for (vsize i = 0; i < div.size (); i++)
427 Page_breaking::Line_division
428 Page_breaking::system_count_bounds (vector<Break_position> const &chunks,
431 assert (chunks.size () >= 2);
434 ret.resize (chunks.size () - 1, 1);
436 for (vsize i = 0; i + 1 < chunks.size (); i++)
438 vsize sys = next_system (chunks[i]);
439 if (system_specs_[sys].pscore_)
443 line_breaker_args (sys, chunks[i], chunks[i+1], &start, &end);
445 ? line_breaking_[sys].min_system_count (start, end)
446 : line_breaking_[sys].max_system_count (start, end);
454 Page_breaking::set_current_breakpoints (vsize start,
457 Line_division lower_bound,
458 Line_division upper_bound)
460 system_count_ = system_count;
461 current_chunks_ = chunk_list (start, end);
462 current_start_breakpoint_ = start;
463 current_end_breakpoint_ = end;
464 clear_line_details_cache ();
466 if (!lower_bound.size ())
467 lower_bound = system_count_bounds (current_chunks_, true);
468 if (!upper_bound.size ())
469 upper_bound = system_count_bounds (current_chunks_, false);
471 assert (lower_bound.size () == current_chunks_.size () - 1);
472 assert (upper_bound.size () == current_chunks_.size () - 1);
474 Line_division work_in_progress;
475 current_configurations_.clear ();
476 line_divisions_rec (system_count,
481 /* we only consider a constant number of configurations. Otherwise,
482 this becomes slow when there are many small scores. The constant
483 5 is somewhat arbitrary. */
484 if (current_configurations_.size () > 5)
486 vector<pair<Real,vsize> > dems_and_indices;
488 for (vsize i = 0; i < current_configurations_.size (); i++)
490 cache_line_details (i);
492 for (vsize j = 0; j < cached_line_details_.size (); j++)
493 dem += cached_line_details_[j].force_ * cached_line_details_[j].force_
494 + cached_line_details_[j].break_penalty_;
496 dems_and_indices.push_back (pair<Real,vsize> (dem, i));
498 vector_sort (dems_and_indices, less<pair<Real,vsize> > ());
500 vector<Line_division> best_5_configurations;
501 for (vsize i = 0; i < 5; i++)
502 best_5_configurations.push_back (current_configurations_[dems_and_indices[i].second]);
504 clear_line_details_cache ();
505 current_configurations_ = best_5_configurations;
510 Page_breaking::set_to_ideal_line_configuration (vsize start, vsize end)
512 current_chunks_ = chunk_list (start, end);
513 current_start_breakpoint_ = start;
514 current_end_breakpoint_ = end;
515 clear_line_details_cache ();
519 for (vsize i = 0; i+1 < current_chunks_.size (); i++)
521 vsize sys = next_system (current_chunks_[i]);
522 if (system_specs_[sys].pscore_)
524 line_breaker_args (sys, current_chunks_[i], current_chunks_[i+1], &start, &end);
525 div.push_back (line_breaking_[sys].best_solution (start, end).size ());
530 system_count_ += div.back ();
532 current_configurations_.clear ();
533 current_configurations_.push_back (div);
537 Page_breaking::current_configuration_count () const
539 return current_configurations_.size ();
543 Page_breaking::cache_line_details (vsize configuration_index)
545 if (cached_configuration_index_ != configuration_index)
547 cached_configuration_index_ = configuration_index;
548 SCM padding_scm = book_->paper_->c_variable ("page-breaking-between-system-padding");
549 if (!scm_is_number (padding_scm))
550 padding_scm = book_->paper_->c_variable ("between-system-padding");
551 Real padding = robust_scm2double (padding_scm, 0.0);
553 Line_division &div = current_configurations_[configuration_index];
554 uncompressed_line_details_.clear ();
555 for (vsize i = 0; i + 1 < current_chunks_.size (); i++)
557 vsize sys = next_system (current_chunks_[i]);
558 if (system_specs_[sys].pscore_)
562 line_breaker_args (sys, current_chunks_[i], current_chunks_[i+1], &start, &end);
564 vector<Line_details> details = line_breaking_[sys].line_details (start, end, div[i]);
565 uncompressed_line_details_.insert (uncompressed_line_details_.end (), details.begin (), details.end ());
569 assert (div[i] == 1);
570 uncompressed_line_details_.push_back (Line_details (system_specs_[sys].prob_));
571 uncompressed_line_details_.back ().padding_ =
572 robust_scm2double (system_specs_[sys].prob_->get_property ("next-padding"),
576 cached_line_details_ = compress_lines (uncompressed_line_details_);
581 Page_breaking::clear_line_details_cache ()
583 cached_configuration_index_ = VPOS;
584 cached_line_details_.clear ();
585 uncompressed_line_details_.clear ();
589 Page_breaking::line_divisions_rec (vsize system_count,
590 Line_division const &min_sys,
591 Line_division const &max_sys,
592 Line_division *cur_division)
594 vsize my_index = cur_division->size ();
595 vsize others_min = 0;
596 vsize others_max = 0;
598 for (vsize i = my_index + 1; i < min_sys.size (); i++)
600 others_min += min_sys[i];
601 others_max += max_sys[i];
603 others_max = min (others_max, system_count);
604 vsize real_min = max (min_sys[my_index], system_count - others_max);
605 vsize real_max = min (max_sys[my_index], system_count - others_min);
607 if (real_min > real_max || real_min <= 0)
609 /* this should never happen within a recursive call. If it happens
610 at all, it means that we were called with an unsolvable problem
611 and we should return an empty result */
612 assert (my_index == 0);
616 for (vsize i = real_min; i <= real_max; i++)
618 cur_division->push_back (i);
619 if (my_index == min_sys.size () - 1)
620 current_configurations_.push_back (*cur_division);
622 line_divisions_rec (system_count - i, min_sys, max_sys, cur_division);
623 cur_division->pop_back ();
628 Page_breaking::min_page_count (vsize configuration, vsize first_page_num)
631 Real cur_rod_height = 0;
632 Real cur_spring_height = 0;
633 Real cur_page_height = page_height (first_page_num, false);
635 cache_line_details (configuration);
636 for (vsize i = 0; i < cached_line_details_.size (); i++)
638 Real ext_len = cached_line_details_[i].extent_.length ();
639 Real next_rod_height = cur_rod_height + ext_len
640 + ((cur_rod_height > 0) ? cached_line_details_[i].padding_: 0);
641 Real next_spring_height = cur_spring_height + cached_line_details_[i].space_;
642 Real next_height = next_rod_height + (ragged () ? next_spring_height : 0);
645 if ((next_height > cur_page_height && cur_rod_height > 0)
647 && cached_line_details_[i-1].page_permission_ == ly_symbol2scm ("force")))
649 cur_rod_height = ext_len;
650 cur_spring_height = cached_line_details_[i].space_;
651 cur_page_height = page_height (first_page_num + ret, false);
656 cur_rod_height = next_rod_height;
657 cur_spring_height = next_spring_height;
661 /* there are two potential problems with the last page (because we didn't know
662 it was the last page until after we managed to fit all the systems to it):
663 - we are ragged-last but the last page has a compressed spring
664 - the value returned by page_height (num, true) is smaller than the
665 value returned by page_height (num, false) and it causes the page not to
668 In either case, we just need to add one more page. This is because the last
669 line will always fit on the extra page and by adding one more page to the
670 end, the previous page is no longer the last page, so our previous
671 calculations that treated it as a non-last page were ok.
674 cur_page_height = page_height (first_page_num + ret - 1, true);
675 Real cur_height = cur_rod_height + ((ragged_last () || ragged ()) ? cur_spring_height : 0);
676 if (cur_height > cur_page_height
677 /* don't increase the page count if the last page had only one system */
678 && cur_rod_height > cached_line_details_.back ().extent_.length ())
681 assert (ret <= cached_line_details_.size ());
686 Page_breaking::space_systems_on_n_pages (vsize configuration, vsize n, vsize first_page_num)
688 Page_spacing_result ret;
690 cache_line_details (configuration);
691 bool valid_n = (n >= min_page_count (configuration, first_page_num)
692 && n <= cached_line_details_.size ());
695 programming_error ("number of pages is out of bounds");
697 if (n == 1 && valid_n)
698 ret = space_systems_on_1_page (cached_line_details_,
699 page_height (first_page_num, is_last ()),
700 ragged () || (is_last () && ragged_last ()));
701 else if (n == 2 && valid_n)
702 ret = space_systems_on_2_pages (configuration, first_page_num);
705 Page_spacer ps (cached_line_details_, first_page_num, this);
709 return finalize_spacing_result (configuration, ret);
713 Page_breaking::blank_page_penalty () const
718 penalty_sym = ly_symbol2scm ("blank-last-page-force");
719 else if (ends_score ())
720 penalty_sym = ly_symbol2scm ("blank-after-score-page-force");
722 penalty_sym = ly_symbol2scm ("blank-page-force");
724 Break_position const &pos = breaks_[current_end_breakpoint_];
725 if (Paper_score *ps = system_specs_[pos.system_spec_index_].pscore_)
726 return robust_scm2double (ps->layout ()->lookup_variable (penalty_sym), 0.0);
728 return robust_scm2double (book_->paper_->lookup_variable (penalty_sym), 0.0);
732 Page_breaking::space_systems_on_n_or_one_more_pages (vsize configuration, vsize n, vsize first_page_num)
734 Page_spacing_result n_res;
735 Page_spacing_result m_res;
737 cache_line_details (configuration);
738 vsize min_p_count = min_page_count (configuration, first_page_num);
739 bool valid_n = n >= min_p_count || n <= cached_line_details_.size ();
742 programming_error ("both page counts are out of bounds");
744 if (n == 1 && valid_n)
746 bool rag = ragged () || (is_last () && ragged_last ());
747 Real height = page_height (first_page_num, is_last ());
749 if (1 >= min_p_count)
750 n_res = space_systems_on_1_page (cached_line_details_, height, rag);
751 if (1 < cached_line_details_.size ())
752 m_res = space_systems_on_2_pages (configuration, first_page_num);
756 Page_spacer ps (cached_line_details_, first_page_num, this);
758 if (n >= min_p_count || !valid_n)
759 n_res = ps.solve (n);
760 if (n < cached_line_details_.size () || !valid_n)
761 m_res = ps.solve (n+1);
764 m_res = finalize_spacing_result (configuration, m_res);
765 n_res = finalize_spacing_result (configuration, n_res);
767 Real penalty = blank_page_penalty ();
768 n_res.demerits_ += penalty;
770 if (n_res.force_.size ())
771 n_res.force_.back () += penalty;
773 return (m_res.demerits_ < n_res.demerits_) ? m_res : n_res;
777 Page_breaking::space_systems_on_best_pages (vsize configuration, vsize first_page_num)
779 vsize min_p_count = min_page_count (configuration, first_page_num);
780 Real odd_pages_penalty = blank_page_penalty ();
782 cache_line_details (configuration);
783 Page_spacer ps (cached_line_details_, first_page_num, this);
784 Page_spacing_result best = ps.solve (min_p_count);
785 best.force_.back () += (min_p_count % 2) ? odd_pages_penalty : 0;
786 best.demerits_ += (min_p_count % 2) ? odd_pages_penalty : 0;
788 for (vsize i = min_p_count+1; i <= cached_line_details_.size (); i++)
790 Page_spacing_result cur = ps.solve (i);
791 cur.demerits_ += (i % 2) ? odd_pages_penalty : 0;
792 if (cur.demerits_ < best.demerits_)
796 return finalize_spacing_result (configuration, best);
800 Page_breaking::space_systems_with_fixed_number_per_page (vsize configuration,
801 int systems_per_page,
802 vsize first_page_num)
804 Page_spacing_result res;
805 Page_spacing space (page_height (first_page_num, false), page_top_space_);
808 vsize page_first_line = 0;
810 cache_line_details (configuration);
811 while (line < cached_line_details_.size ())
815 space.resize (page_height (first_page_num + page, false));
817 int system_count_on_this_page = 0;
818 while (system_count_on_this_page < systems_per_page
819 && line < cached_line_details_.size ())
821 space.append_system (cached_line_details_[line]);
822 system_count_on_this_page += cached_line_details_[line].compressed_nontitle_lines_count_;
826 res.systems_per_page_.push_back (line - page_first_line);
827 res.force_.push_back (space.force_);
828 res.penalty_ += cached_line_details_[line-1].page_penalty_;
829 page_first_line = line;
832 /* Recalculate forces for the last page because we know now that is
833 was really the last page. */
834 space.resize (page_height (first_page_num + page, true));
835 res.force_.back () = space.force_;
836 return finalize_spacing_result (configuration, res);
840 Page_breaking::pack_systems_on_least_pages (vsize configuration, vsize first_page_num)
842 Page_spacing_result res;
844 vsize page_first_line = 0;
845 Page_spacing space (page_height (first_page_num, false), page_top_space_);
847 cache_line_details (configuration);
848 for (vsize line = 0; line < cached_line_details_.size (); line++)
850 Real prev_force = space.force_;
851 space.append_system (cached_line_details_[line]);
852 if ((line > page_first_line)
853 && (isinf (space.force_)
855 && (cached_line_details_[line-1].page_permission_ == ly_symbol2scm ("force")))))
857 res.systems_per_page_.push_back (line - page_first_line);
858 res.force_.push_back (prev_force);
859 res.penalty_ += cached_line_details_[line-1].page_penalty_;
861 space.resize (page_height (first_page_num + page, false));
863 space.append_system (cached_line_details_[line]);
864 page_first_line = line;
867 if (line == cached_line_details_.size () - 1)
869 /* This is the last line */
870 /* When the last page height was computed, we did not know yet that it
871 * was the last one. If the systems put on it don't fit anymore, the last
872 * system is moved to a new page */
873 space.resize (page_height (first_page_num + page, true));
874 if ((line > page_first_line) && (isinf (space.force_)))
876 res.systems_per_page_.push_back (line - page_first_line);
877 res.force_.push_back (prev_force);
878 /* the last page containing the last line */
879 space.resize (page_height (first_page_num + page + 1, true));
881 space.append_system (cached_line_details_[line]);
882 res.systems_per_page_.push_back (1);
883 res.force_.push_back (space.force_);
884 res.penalty_ += cached_line_details_[line-1].page_penalty_;
885 res.penalty_ += cached_line_details_[line].page_penalty_;
889 res.systems_per_page_.push_back (line + 1 - page_first_line);
890 res.force_.push_back (space.force_);
891 res.penalty_ += cached_line_details_[line].page_penalty_;
895 return finalize_spacing_result (configuration, res);
898 /* Calculate demerits and fix res.systems_per_page_ so that
899 it refers to the original line numbers, not the ones given by compress_lines (). */
901 Page_breaking::finalize_spacing_result (vsize configuration, Page_spacing_result res)
903 if (res.force_.empty ())
906 cache_line_details (configuration);
907 res.systems_per_page_ = uncompress_solution (res.systems_per_page_, cached_line_details_);
910 Real line_penalty = 0;
912 Real page_weighting = robust_scm2double (book_->paper_->c_variable ("page-spacing-weight"), 10);
914 for (vsize i = 0; i < uncompressed_line_details_.size (); i++)
916 line_force += uncompressed_line_details_[i].force_ * uncompressed_line_details_[i].force_;
917 line_penalty += uncompressed_line_details_[i].break_penalty_;
920 for (vsize i = 0; i < res.force_.size (); i++)
922 Real f = res.force_[i];
923 if (isinf (f) && res.systems_per_page_[i] == 1)
929 /* for a while we tried averaging page and line forces across pages instead
930 of summing them, but it caused a problem: if there is a single page
931 with a very bad page force (for example because of a forced page break),
932 the page breaker will put in a _lot_ of pages so that the bad force
933 becomes averaged out over many pages. */
934 res.demerits_ = line_force + line_penalty + (page_force + res.penalty_) * page_weighting;
939 /* the cases for page_count = 1 or 2 can be done in O (n) time. Since they
940 are by far the most common cases, we have special functions for them.
942 space_systems_on_1_page has a different calling convention than most of the
943 space_systems functions. This is because space_systems_on_1_page is (unlike
944 the other space_systems functions) sometimes called on subsets of a full
947 Page_breaking::space_systems_on_1_page (vector<Line_details> const &lines, Real page_height, bool ragged)
949 Page_spacing space (page_height, page_top_space_);
950 Page_spacing_result ret;
952 for (vsize i = 0; i < lines.size (); i++)
953 space.append_system (lines[i]);
955 ret.systems_per_page_.push_back (lines.size ());
956 ret.force_.push_back (ragged ? min (space.force_, 0.0) : space.force_);
957 ret.penalty_ = lines.back ().page_penalty_ + lines.back ().turn_penalty_;
959 /* don't do finalize_spacing_result () because we are only an internal function */
964 Page_breaking::space_systems_on_2_pages (vsize configuration, vsize first_page_num)
966 Real page1_height = page_height (first_page_num, false);
967 Real page2_height = page_height (first_page_num + 1, is_last ());
968 bool ragged1 = ragged ();
969 bool ragged2 = ragged () || (is_last () && ragged_last ());
971 /* if there is a forced break, this reduces to 2 1-page problems */
972 cache_line_details (configuration);
973 for (vsize i = 0; i + 1 < cached_line_details_.size (); i++)
974 if (cached_line_details_[i].page_permission_ == ly_symbol2scm ("force"))
976 vector<Line_details> lines1 (cached_line_details_.begin (), cached_line_details_.begin () + i + 1);
977 vector<Line_details> lines2 (cached_line_details_.begin () + i + 1, cached_line_details_.end ());
978 Page_spacing_result p1 = space_systems_on_1_page (lines1, page1_height, ragged1);
979 Page_spacing_result p2 = space_systems_on_1_page (lines2, page2_height, ragged2);
981 p1.systems_per_page_.push_back (p2.systems_per_page_[0]);
982 p1.force_.push_back (p2.force_[0]);
983 p1.penalty_ += p2.penalty_ - cached_line_details_[i].turn_penalty_;
987 vector<Real> page1_force;
988 vector<Real> page2_force;
989 Page_spacing page1 (page1_height, page_top_space_);
990 Page_spacing page2 (page2_height, page_top_space_);
992 page1_force.resize (cached_line_details_.size () - 1, infinity_f);
993 page2_force.resize (cached_line_details_.size () - 1, infinity_f);
995 /* find the page 1 and page 2 forces for each page-breaking position */
996 for (vsize i = 0; i < page1_force.size (); i++)
998 page1.append_system (cached_line_details_[i]);
999 page2.prepend_system (cached_line_details_[cached_line_details_.size () - 1 - i]);
1000 page1_force[i] = (ragged1 && page1.force_ < 0 && i > 0) ? infinity_f : page1.force_;
1003 page2_force[page2_force.size () - 1 - i] =
1004 (page2.force_ < 0 && i + 1 < page1_force.size ()) ? infinity_f : 0;
1006 page2_force[page2_force.size () - 1 - i] = page2.force_;
1009 /* find the position that minimises the sum of the page forces */
1010 vsize best_sys_count = 1;
1011 Real best_demerits = infinity_f;
1012 for (vsize i = 0; i < page1_force.size (); i++)
1014 Real f = page1_force[i] * page1_force[i] + page2_force[i] * page2_force[i];
1015 Real uneven = 2 * (page1_force[i] - page2_force[i]);
1016 Real dem = uneven * uneven + f
1017 + cached_line_details_[i+1].page_penalty_
1018 + cached_line_details_.back ().page_penalty_ + cached_line_details_.back ().turn_penalty_;
1019 if (dem < best_demerits)
1021 best_demerits = dem;
1022 best_sys_count = i+1;
1026 Page_spacing_result ret;
1027 ret.systems_per_page_.push_back (best_sys_count);
1028 ret.systems_per_page_.push_back (cached_line_details_.size () - best_sys_count);
1029 ret.force_.push_back (page1_force[best_sys_count-1]);
1030 ret.force_.push_back (page2_force[best_sys_count-1]);
1031 ret.penalty_ = cached_line_details_[best_sys_count-1].page_penalty_
1032 + cached_line_details_.back ().page_penalty_
1033 + cached_line_details_.back ().turn_penalty_;
1035 /* don't do finalize_spacing_result () because we are only an internal function */
1040 Page_breaking::all_lines_stretched (vsize configuration)
1042 cache_line_details (configuration);
1043 for (vsize i = 0; i < cached_line_details_.size (); i++)
1044 if (cached_line_details_[i].force_ < 0)
1050 Page_breaking::Line_division
1051 Page_breaking::current_configuration (vsize configuration_index) const
1053 return current_configurations_[configuration_index];
1057 Page_breaking::is_last () const
1059 return current_end_breakpoint_ == last_break_position ();
1063 Page_breaking::ends_score () const
1065 return breaks_[current_end_breakpoint_].score_ender_;
1069 Page_breaking::last_break_position () const
1071 return breaks_.size () - 1;