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_;
39 compressed.title_ = old.title_;
41 /* we don't need the force_ field for the vertical spacing,
42 so we use force_ = n to signal that the line was compressed,
43 reducing the number of lines by n (and force_ = 0 otherwise).
44 This makes uncompression much easier. */
45 compressed.force_ = old.force_ + 1;
46 ret.back () = compressed;
50 ret.push_back (orig[i]);
51 ret.back ().force_ = 0;
57 /* translate the number of systems-per-page into something meaningful for
58 the uncompressed lines.
61 uncompress_solution (vector<vsize> const &systems_per_page,
62 vector<Line_details> const &compressed)
67 for (vsize i = 0; i < systems_per_page.size (); i++)
69 int compressed_count = 0;
70 for (vsize j = start_sys; j < start_sys + systems_per_page[i]; j++)
71 compressed_count += (int)compressed[j].force_;
73 ret.push_back (systems_per_page[i] + compressed_count);
74 start_sys += systems_per_page[i];
79 /* for Page_breaking, the start index (when we are dealing with the stuff
80 between a pair of breakpoints) refers to the break_ index of the end of
81 the previous page. So the system index of the start of the current page
82 could either be the same as the end of the previous page or one more than
85 /* Turn a break index into the sys index that starts the next page */
87 Page_breaking::next_system (Break_position const &break_pos) const
89 vsize sys = break_pos.system_spec_index_;
91 if (sys == VPOS) /* beginning of the book */
93 if (system_specs_[sys].pscore_ && !break_pos.score_ender_)
94 return sys; /* the score overflows the previous page */
95 return sys + 1; /* this page starts with a new System_spec */
98 Page_breaking::Page_breaking (Paper_book *pb, Break_predicate is_break)
102 ragged_ = to_boolean (pb->paper_->c_variable ("ragged-bottom"));
103 ragged_last_ = to_boolean (pb->paper_->c_variable ("ragged-last-bottom"));
104 page_top_space_ = robust_scm2double (pb->paper_->c_variable ("page-top-space"), 0);
105 create_system_list ();
106 find_chunks_and_breaks (is_break);
109 Page_breaking::~Page_breaking ()
114 Page_breaking::ragged () const
120 Page_breaking::ragged_last () const
126 Page_breaking::page_top_space () const
128 return page_top_space_;
132 Page_breaking::system_count () const
134 return system_count_;
137 /* translate indices into breaks_ into start-end parameters for the line breaker */
139 Page_breaking::line_breaker_args (vsize sys,
140 Break_position const &start,
141 Break_position const &end,
142 vsize *line_breaker_start,
143 vsize *line_breaker_end)
145 assert (system_specs_[sys].pscore_);
146 assert (next_system (start) <= sys && sys <= end.system_spec_index_);
148 if (start.system_spec_index_ == sys)
149 *line_breaker_start = start.score_break_;
151 *line_breaker_start = 0;
153 if (end.system_spec_index_ == sys)
154 *line_breaker_end = end.score_break_;
156 *line_breaker_end = VPOS;
160 Page_breaking::break_into_pieces (vsize start_break, vsize end_break,
161 Line_division const &div)
163 vector<Break_position> chunks = chunk_list (start_break, end_break);
164 bool ignore_div = false;
165 if (chunks.size () != div.size () + 1)
167 programming_error ("did not find a valid page breaking configuration");
171 for (vsize i = 0; i + 1 < chunks.size (); i++)
173 vsize sys = next_system (chunks[i]);
174 if (system_specs_[sys].pscore_)
178 line_breaker_args (sys, chunks[i], chunks[i+1], &start, &end);
180 vector<Column_x_positions> pos = ignore_div
181 ? line_breaking_[sys].best_solution (start, end)
182 : line_breaking_[sys].solve (start, end, div[i]);
183 system_specs_[sys].pscore_->root_system ()->break_into_pieces (pos);
189 Page_breaking::systems ()
192 for (vsize sys = 0; sys < system_specs_.size (); sys++)
194 if (system_specs_[sys].pscore_)
196 system_specs_[sys].pscore_->root_system ()
197 ->do_break_substitution_and_fixup_refpoints ();
198 SCM lines = system_specs_[sys].pscore_->root_system ()
199 ->get_broken_system_grobs ();
200 ret = scm_cons (lines, ret);
204 Prob *pb = system_specs_[sys].prob_;
205 ret = scm_cons (scm_list_1 (pb->self_scm ()), ret);
209 return scm_append (scm_reverse (ret));
213 Page_breaking::page_height (int page_num, bool last) const
215 SCM mod = scm_c_resolve_module ("scm page");
216 SCM calc_height = scm_c_module_lookup (mod, "calc-printable-height");
217 SCM make_page = scm_c_module_lookup (mod, "make-page");
219 calc_height = scm_variable_ref (calc_height);
220 make_page = scm_variable_ref (make_page);
222 SCM page = scm_apply_0 (make_page, scm_list_n (
224 ly_symbol2scm ("page-number"), scm_from_int (page_num),
225 ly_symbol2scm ("is-last"), scm_from_bool (last),
227 SCM height = scm_apply_1 (calc_height, page, SCM_EOL);
228 return scm_to_double (height) - page_top_space_;
232 Page_breaking::breakpoint_property (vsize breakpoint, char const *str)
234 Break_position const &pos = breaks_[breakpoint];
236 if (pos.system_spec_index_ == VPOS)
238 if (system_specs_[pos.system_spec_index_].pscore_)
239 return pos.col_->get_property (str);
240 return system_specs_[pos.system_spec_index_].prob_->get_property (str);
244 Page_breaking::make_pages (vector<vsize> lines_per_page, SCM systems)
246 SCM layout_module = scm_c_resolve_module ("scm layout-page-layout");
247 SCM page_module = scm_c_resolve_module ("scm page");
249 SCM make_page = scm_c_module_lookup (layout_module, "stretch-and-draw-page");
250 SCM page_stencil = scm_c_module_lookup (page_module, "page-stencil");
251 make_page = scm_variable_ref (make_page);
252 page_stencil = scm_variable_ref (page_stencil);
254 SCM book = book_->self_scm ();
255 int first_page_number
256 = robust_scm2int (book_->paper_->c_variable ("first-page-number"), 1);
258 SCM label_page_table = SCM_EOL;
260 for (vsize i = 0; i < lines_per_page.size (); i++)
262 SCM page_num = scm_from_int (i + first_page_number);
263 SCM last = scm_from_bool (i == lines_per_page.size () - 1);
264 SCM rag = scm_from_bool (ragged () || (to_boolean (last)
266 SCM line_count = scm_from_int (lines_per_page[i]);
267 SCM lines = scm_list_head (systems, line_count);
268 SCM page = scm_apply_0 (make_page,
269 scm_list_n (book, lines, page_num,
270 rag, last, SCM_UNDEFINED));
273 for (SCM l = lines ; scm_is_pair (l) ; l = scm_cdr (l))
275 SCM labels = SCM_EOL;
276 if (Grob * line = unsmob_grob (scm_car (l)))
278 System *system = dynamic_cast<System*> (line);
279 labels = system->get_property ("labels");
281 else if (Prob *prob = unsmob_prob (scm_car (l)))
282 labels = prob->get_property ("labels");
284 for (SCM lbls = labels ; scm_is_pair (lbls) ; lbls = scm_cdr (lbls))
285 label_page_table = scm_cons (scm_cons (scm_car (lbls), page_num),
289 scm_apply_1 (page_stencil, page, SCM_EOL);
290 ret = scm_cons (page, ret);
291 systems = scm_list_tail (systems, line_count);
293 book_->paper_->set_variable (ly_symbol2scm ("label-page-table"), label_page_table);
294 ret = scm_reverse (ret);
298 /* The page-turn-page-breaker needs to have a line-breaker between any two
299 columns with non-NULL page-turn-permission.
301 The optimal-breaker needs to have a line-breaker between any two columns
302 with page-break-permission = 'force.
304 By using a grob predicate, we can accommodate both of these uses.
307 Page_breaking::create_system_list ()
309 SCM specs = book_->get_system_specs ();
310 for (SCM s = specs; scm_is_pair (s); s = scm_cdr (s))
312 if (Paper_score *ps = dynamic_cast<Paper_score*> (unsmob_music_output (scm_car (s))))
314 SCM system_count = ps->layout ()->c_variable ("system-count");
316 if (scm_is_number (system_count))
317 s = scm_append (scm_list_3 (scm_list_1 (scm_car (s)),
318 scm_vector_to_list (ps->get_paper_systems ()),
321 system_specs_.push_back (System_spec (ps));
325 Prob *pb = unsmob_prob (scm_car (s));
329 system_specs_.push_back (System_spec (pb));
335 Page_breaking::find_chunks_and_breaks (Break_predicate is_break)
337 SCM force_sym = ly_symbol2scm ("force");
339 chunks_.push_back (Break_position ());
340 breaks_.push_back (Break_position ());
342 for (vsize i = 0; i < system_specs_.size (); i++)
344 if (system_specs_[i].pscore_)
347 = system_specs_[i].pscore_->root_system ()->used_columns ();
348 vector<vsize> line_breaker_columns;
349 line_breaker_columns.push_back (0);
351 for (vsize j = 1; j < cols.size (); j++)
353 bool last = (j == cols.size () - 1);
354 bool break_point = is_break (cols[j]);
355 bool chunk_end = cols[j]->get_property ("page-break-permission") == force_sym;
356 Break_position cur_pos = Break_position (i,
357 line_breaker_columns.size (),
361 if (break_point || (i == system_specs_.size () - 1 && last))
362 breaks_.push_back (cur_pos);
363 if (chunk_end || last)
364 chunks_.push_back (cur_pos);
366 if ((break_point || chunk_end) && !last)
367 line_breaker_columns.push_back (j);
369 line_breaking_.push_back (Constrained_breaking (system_specs_[i].pscore_, line_breaker_columns));
373 /* TODO: we want some way of applying Break_p to a prob? */
374 if (i == system_specs_.size () - 1)
375 breaks_.push_back (Break_position (i));
377 chunks_.push_back (Break_position (i));
379 /* FIXME: shouldn't we push a Null_breaker or similar dummy
381 line_breaking_.push_back (Constrained_breaking (NULL));
386 vector<Break_position>
387 Page_breaking::chunk_list (vsize start_index, vsize end_index)
389 Break_position start = breaks_[start_index];
390 Break_position end = breaks_[end_index];
393 for (; i < chunks_.size () && chunks_[i] <= start; i++)
396 vector<Break_position> ret;
397 ret.push_back (start);
398 for (; i < chunks_.size () && chunks_[i] < end; i++)
399 ret.push_back (chunks_[i]);
405 Page_breaking::min_system_count (vsize start, vsize end)
407 vector<Break_position> chunks = chunk_list (start, end);
408 Line_division div = system_count_bounds (chunks, true);
411 for (vsize i = 0; i < div.size (); i++)
417 Page_breaking::max_system_count (vsize start, vsize end)
419 vector<Break_position> chunks = chunk_list (start, end);
420 Line_division div = system_count_bounds (chunks, false);
423 for (vsize i = 0; i < div.size (); i++)
428 Page_breaking::Line_division
429 Page_breaking::system_count_bounds (vector<Break_position> const &chunks,
432 assert (chunks.size () >= 2);
435 ret.resize (chunks.size () - 1, 1);
437 for (vsize i = 0; i + 1 < chunks.size (); i++)
439 vsize sys = next_system (chunks[i]);
440 if (system_specs_[sys].pscore_)
444 line_breaker_args (sys, chunks[i], chunks[i+1], &start, &end);
446 ? line_breaking_[sys].min_system_count (start, end)
447 : line_breaking_[sys].max_system_count (start, end);
455 Page_breaking::set_current_breakpoints (vsize start,
458 Line_division lower_bound,
459 Line_division upper_bound)
461 system_count_ = system_count;
462 current_chunks_ = chunk_list (start, end);
463 current_start_breakpoint_ = start;
464 current_end_breakpoint_ = end;
465 clear_line_details_cache ();
467 if (!lower_bound.size ())
468 lower_bound = system_count_bounds (current_chunks_, true);
469 if (!upper_bound.size ())
470 upper_bound = system_count_bounds (current_chunks_, false);
472 assert (lower_bound.size () == current_chunks_.size () - 1);
473 assert (upper_bound.size () == current_chunks_.size () - 1);
475 Line_division work_in_progress;
476 current_configurations_.clear ();
477 line_divisions_rec (system_count,
482 /* we only consider a constant number of configurations. Otherwise,
483 this becomes slow when there are many small scores. The constant
484 5 is somewhat arbitrary. */
485 if (current_configurations_.size () > 5)
487 vector<pair<Real,vsize> > dems_and_indices;
489 for (vsize i = 0; i < current_configurations_.size (); i++)
491 cache_line_details (i);
493 for (vsize j = 0; j < cached_line_details_.size (); j++)
494 dem += cached_line_details_[j].force_ * cached_line_details_[j].force_
495 + cached_line_details_[j].break_penalty_;
497 dems_and_indices.push_back (pair<Real,vsize> (dem, i));
499 vector_sort (dems_and_indices, less<pair<Real,vsize> > ());
501 vector<Line_division> best_5_configurations;
502 for (vsize i = 0; i < 5; i++)
503 best_5_configurations.push_back (current_configurations_[dems_and_indices[i].second]);
505 clear_line_details_cache ();
506 current_configurations_ = best_5_configurations;
511 Page_breaking::set_to_ideal_line_configuration (vsize start, vsize end)
513 current_chunks_ = chunk_list (start, end);
514 current_start_breakpoint_ = start;
515 current_end_breakpoint_ = end;
516 clear_line_details_cache ();
520 for (vsize i = 0; i+1 < current_chunks_.size (); i++)
522 vsize sys = next_system (current_chunks_[i]);
523 if (system_specs_[sys].pscore_)
525 line_breaker_args (sys, current_chunks_[i], current_chunks_[i+1], &start, &end);
526 div.push_back (line_breaking_[sys].best_solution (start, end).size ());
531 system_count_ += div.back ();
533 current_configurations_.clear ();
534 current_configurations_.push_back (div);
538 Page_breaking::current_configuration_count () const
540 return current_configurations_.size ();
544 Page_breaking::cache_line_details (vsize configuration_index)
546 if (cached_configuration_index_ != configuration_index)
548 cached_configuration_index_ = configuration_index;
549 SCM padding_scm = book_->paper_->c_variable ("page-breaking-between-system-padding");
550 if (!scm_is_number (padding_scm))
551 padding_scm = book_->paper_->c_variable ("between-system-padding");
552 Real padding = robust_scm2double (padding_scm, 0.0);
554 Line_division &div = current_configurations_[configuration_index];
555 uncompressed_line_details_.clear ();
556 for (vsize i = 0; i + 1 < current_chunks_.size (); i++)
558 vsize sys = next_system (current_chunks_[i]);
559 if (system_specs_[sys].pscore_)
563 line_breaker_args (sys, current_chunks_[i], current_chunks_[i+1], &start, &end);
565 vector<Line_details> details = line_breaking_[sys].line_details (start, end, div[i]);
566 uncompressed_line_details_.insert (uncompressed_line_details_.end (), details.begin (), details.end ());
570 assert (div[i] == 1);
571 uncompressed_line_details_.push_back (Line_details (system_specs_[sys].prob_));
572 uncompressed_line_details_.back ().padding_ =
573 robust_scm2double (system_specs_[sys].prob_->get_property ("next-padding"),
577 cached_line_details_ = compress_lines (uncompressed_line_details_);
582 Page_breaking::clear_line_details_cache ()
584 cached_configuration_index_ = VPOS;
585 cached_line_details_.clear ();
586 uncompressed_line_details_.clear ();
590 Page_breaking::line_divisions_rec (vsize system_count,
591 Line_division const &min_sys,
592 Line_division const &max_sys,
593 Line_division *cur_division)
595 vsize my_index = cur_division->size ();
596 vsize others_min = 0;
597 vsize others_max = 0;
599 for (vsize i = my_index + 1; i < min_sys.size (); i++)
601 others_min += min_sys[i];
602 others_max += max_sys[i];
604 others_max = min (others_max, system_count);
605 vsize real_min = max (min_sys[my_index], system_count - others_max);
606 vsize real_max = min (max_sys[my_index], system_count - others_min);
608 if (real_min > real_max || real_min <= 0)
610 /* this should never happen within a recursive call. If it happens
611 at all, it means that we were called with an unsolvable problem
612 and we should return an empty result */
613 assert (my_index == 0);
617 for (vsize i = real_min; i <= real_max; i++)
619 cur_division->push_back (i);
620 if (my_index == min_sys.size () - 1)
621 current_configurations_.push_back (*cur_division);
623 line_divisions_rec (system_count - i, min_sys, max_sys, cur_division);
624 cur_division->pop_back ();
629 Page_breaking::min_page_count (vsize configuration, vsize first_page_num)
632 Real cur_rod_height = 0;
633 Real cur_spring_height = 0;
634 Real cur_page_height = page_height (first_page_num, false);
636 cache_line_details (configuration);
637 for (vsize i = 0; i < cached_line_details_.size (); i++)
639 Real ext_len = cached_line_details_[i].extent_.length ();
640 Real next_rod_height = cur_rod_height + ext_len
641 + ((cur_rod_height > 0) ? cached_line_details_[i].padding_: 0);
642 Real next_spring_height = cur_spring_height + cached_line_details_[i].space_;
643 Real next_height = next_rod_height + (ragged () ? next_spring_height : 0);
646 if ((next_height > cur_page_height && cur_rod_height > 0)
648 && cached_line_details_[i-1].page_permission_ == ly_symbol2scm ("force")))
650 cur_rod_height = ext_len;
651 cur_spring_height = cached_line_details_[i].space_;
652 cur_page_height = page_height (first_page_num + ret, false);
657 cur_rod_height = next_rod_height;
658 cur_spring_height = next_spring_height;
662 /* there are two potential problems with the last page (because we didn't know
663 it was the last page until after we managed to fit all the systems to it):
664 - we are ragged-last but the last page has a compressed spring
665 - the value returned by page_height (num, true) is smaller than the
666 value returned by page_height (num, false) and it causes the page not to
669 In either case, we just need to add one more page. This is because the last
670 line will always fit on the extra page and by adding one more page to the
671 end, the previous page is no longer the last page, so our previous
672 calculations that treated it as a non-last page were ok.
675 cur_page_height = page_height (first_page_num + ret - 1, true);
676 Real cur_height = cur_rod_height + ((ragged_last () || ragged ()) ? cur_spring_height : 0);
677 if (cur_height > cur_page_height
678 /* don't increase the page count if the last page had only one system */
679 && cur_rod_height > cached_line_details_.back ().extent_.length ())
682 assert (ret <= cached_line_details_.size ());
687 Page_breaking::space_systems_on_n_pages (vsize configuration, vsize n, vsize first_page_num)
689 Page_spacing_result ret;
690 assert (n >= min_page_count (configuration, first_page_num));
692 cache_line_details (configuration);
694 if (n == 1 && n <= cached_line_details_.size ())
695 ret = space_systems_on_1_page (cached_line_details_,
696 page_height (first_page_num, is_last ()),
697 ragged () || (is_last () && ragged_last ()));
698 else if (n == 2 && n <= cached_line_details_.size ())
699 ret = space_systems_on_2_pages (configuration, first_page_num);
702 Page_spacer ps (cached_line_details_, first_page_num, this);
706 return finalize_spacing_result (configuration, ret);
710 Page_breaking::blank_page_penalty () const
712 SCM penalty_sym = is_last () ? ly_symbol2scm ("blank-last-page-force") : ly_symbol2scm ("blank-page-force");
713 return robust_scm2double (book_->paper_->lookup_variable (penalty_sym), 0.0);
717 Page_breaking::space_systems_on_n_or_one_more_pages (vsize configuration, vsize n, vsize first_page_num)
719 Page_spacing_result n_res;
720 Page_spacing_result m_res;
722 cache_line_details (configuration);
723 vsize min_p_count = min_page_count (configuration, first_page_num);
727 bool rag = ragged () || (is_last () && ragged_last ());
728 Real height = page_height (first_page_num, is_last ());
730 if (1 >= min_p_count)
731 n_res = space_systems_on_1_page (cached_line_details_, height, rag);
732 if (1 < cached_line_details_.size ())
733 m_res = space_systems_on_2_pages (configuration, first_page_num);
737 Page_spacer ps (cached_line_details_, first_page_num, this);
739 if (n >= min_p_count)
740 n_res = ps.solve (n);
741 if (n < cached_line_details_.size ())
742 m_res = ps.solve (n+1);
745 m_res = finalize_spacing_result (configuration, m_res);
746 n_res = finalize_spacing_result (configuration, n_res);
748 Real penalty = blank_page_penalty ();
749 n_res.demerits_ += penalty;
751 if (n_res.force_.size ())
752 n_res.force_.back () += penalty;
754 return (m_res.demerits_ < n_res.demerits_) ? m_res : n_res;
758 Page_breaking::space_systems_on_best_pages (vsize configuration, vsize first_page_num)
760 vsize min_p_count = min_page_count (configuration, first_page_num);
761 Real odd_pages_penalty = blank_page_penalty ();
763 cache_line_details (configuration);
764 Page_spacer ps (cached_line_details_, first_page_num, this);
765 Page_spacing_result best = ps.solve (min_p_count);
766 best.force_.back () += (min_p_count % 2) ? odd_pages_penalty : 0;
767 best.demerits_ += (min_p_count % 2) ? odd_pages_penalty : 0;
769 for (vsize i = min_p_count+1; i <= cached_line_details_.size (); i++)
771 Page_spacing_result cur = ps.solve (i);
772 cur.demerits_ += (i % 2) ? odd_pages_penalty : 0;
773 if (cur.demerits_ < best.demerits_)
777 return finalize_spacing_result (configuration, best);
781 Page_breaking::pack_systems_on_least_pages (vsize configuration, vsize first_page_num)
783 Page_spacing_result res;
785 vsize page_first_line = 0;
786 Page_spacing space (page_height (first_page_num, false), page_top_space_);
788 cache_line_details (configuration);
789 for (vsize line = 0; line < cached_line_details_.size (); line++)
791 Real prev_force = space.force_;
792 space.append_system (cached_line_details_[line]);
793 if ((line > page_first_line)
794 && (isinf (space.force_)
796 && (cached_line_details_[line-1].page_permission_ == ly_symbol2scm ("force")))))
798 res.systems_per_page_.push_back (line - page_first_line);
799 res.force_.push_back (prev_force);
800 res.penalty_ += cached_line_details_[line-1].page_penalty_;
802 space.resize (page_height (first_page_num + page, false));
804 space.append_system (cached_line_details_[line]);
805 page_first_line = line;
808 if (line == cached_line_details_.size () - 1)
810 /* This is the last line */
811 /* When the last page height was computed, we did not know yet that it
812 * was the last one. If the systems put on it don't fit anymore, the last
813 * system is moved to a new page */
814 space.resize (page_height (first_page_num + page, true));
815 if ((line > page_first_line) && (isinf (space.force_)))
817 res.systems_per_page_.push_back (line - page_first_line);
818 res.force_.push_back (prev_force);
819 /* the last page containing the last line */
820 space.resize (page_height (first_page_num + page + 1, true));
822 space.append_system (cached_line_details_[line]);
823 res.systems_per_page_.push_back (1);
824 res.force_.push_back (space.force_);
825 res.penalty_ += cached_line_details_[line-1].page_penalty_;
826 res.penalty_ += cached_line_details_[line].page_penalty_;
830 res.systems_per_page_.push_back (line + 1 - page_first_line);
831 res.force_.push_back (space.force_);
832 res.penalty_ += cached_line_details_[line].page_penalty_;
836 return finalize_spacing_result (configuration, res);
839 /* Calculate demerits and fix res.systems_per_page_ so that
840 it refers to the original line numbers, not the ones given by compress_lines (). */
842 Page_breaking::finalize_spacing_result (vsize configuration, Page_spacing_result res)
844 if (res.force_.empty ())
847 cache_line_details (configuration);
848 res.systems_per_page_ = uncompress_solution (res.systems_per_page_, cached_line_details_);
851 Real line_penalty = 0;
853 Real page_weighting = robust_scm2double (book_->paper_->c_variable ("page-spacing-weight"), 10);
855 for (vsize i = 0; i < uncompressed_line_details_.size (); i++)
857 line_force += uncompressed_line_details_[i].force_ * uncompressed_line_details_[i].force_;
858 line_penalty += uncompressed_line_details_[i].break_penalty_;
861 for (vsize i = 0; i < res.force_.size (); i++)
863 Real f = res.force_[i];
864 if (isinf (f) && res.systems_per_page_[i] == 1)
870 /* for a while we tried averaging page and line forces across pages instead
871 of summing them, but it caused a problem: if there is a single page
872 with a very bad page force (for example because of a forced page break),
873 the page breaker will put in a _lot_ of pages so that the bad force
874 becomes averaged out over many pages. */
875 res.demerits_ = line_force + line_penalty + (page_force + res.penalty_) * page_weighting;
880 /* the cases for page_count = 1 or 2 can be done in O (n) time. Since they
881 are by far the most common cases, we have special functions for them.
883 space_systems_on_1_page has a different calling convention than most of the
884 space_systems functions. This is because space_systems_on_1_page is (unlike
885 the other space_systems functions) sometimes called on subsets of a full
888 Page_breaking::space_systems_on_1_page (vector<Line_details> const &lines, Real page_height, bool ragged)
890 Page_spacing space (page_height, page_top_space_);
891 Page_spacing_result ret;
893 for (vsize i = 0; i < lines.size (); i++)
894 space.append_system (lines[i]);
896 ret.systems_per_page_.push_back (lines.size ());
897 ret.force_.push_back (ragged ? min (space.force_, 0.0) : space.force_);
898 ret.penalty_ = lines.back ().page_penalty_ + lines.back ().turn_penalty_;
900 /* don't do finalize_spacing_result () because we are only an internal function */
905 Page_breaking::space_systems_on_2_pages (vsize configuration, vsize first_page_num)
907 Real page1_height = page_height (first_page_num, false);
908 Real page2_height = page_height (first_page_num + 1, is_last ());
909 bool ragged1 = ragged ();
910 bool ragged2 = ragged () || (is_last () && ragged_last ());
912 /* if there is a forced break, this reduces to 2 1-page problems */
913 cache_line_details (configuration);
914 for (vsize i = 0; i + 1 < cached_line_details_.size (); i++)
915 if (cached_line_details_[i].page_permission_ == ly_symbol2scm ("force"))
917 vector<Line_details> lines1 (cached_line_details_.begin (), cached_line_details_.begin () + i + 1);
918 vector<Line_details> lines2 (cached_line_details_.begin () + i + 1, cached_line_details_.end ());
919 Page_spacing_result p1 = space_systems_on_1_page (lines1, page1_height, ragged1);
920 Page_spacing_result p2 = space_systems_on_1_page (lines2, page2_height, ragged2);
922 p1.systems_per_page_.push_back (p2.systems_per_page_[0]);
923 p1.force_.push_back (p2.force_[0]);
924 p1.penalty_ += p2.penalty_ - cached_line_details_[i].turn_penalty_;
928 vector<Real> page1_force;
929 vector<Real> page2_force;
930 Page_spacing page1 (page1_height, page_top_space_);
931 Page_spacing page2 (page2_height, page_top_space_);
933 page1_force.resize (cached_line_details_.size () - 1, infinity_f);
934 page2_force.resize (cached_line_details_.size () - 1, infinity_f);
936 /* find the page 1 and page 2 forces for each page-breaking position */
937 for (vsize i = 0; i < page1_force.size (); i++)
939 page1.append_system (cached_line_details_[i]);
940 page2.prepend_system (cached_line_details_[cached_line_details_.size () - 1 - i]);
941 page1_force[i] = (ragged1 && page1.force_ < 0 && i > 0) ? infinity_f : page1.force_;
944 page2_force[page2_force.size () - 1 - i] =
945 (page2.force_ < 0 && i + 1 < page1_force.size ()) ? infinity_f : 0;
947 page2_force[page2_force.size () - 1 - i] = page2.force_;
950 /* find the position that minimises the sum of the page forces */
951 vsize best_sys_count = 1;
952 Real best_demerits = infinity_f;
953 for (vsize i = 0; i < page1_force.size (); i++)
955 Real f = page1_force[i] * page1_force[i] + page2_force[i] * page2_force[i];
956 Real uneven = 2 * (page1_force[i] - page2_force[i]);
957 Real dem = uneven * uneven + f
958 + cached_line_details_[i+1].page_penalty_
959 + cached_line_details_.back ().page_penalty_ + cached_line_details_.back ().turn_penalty_;
960 if (dem < best_demerits)
963 best_sys_count = i+1;
967 Page_spacing_result ret;
968 ret.systems_per_page_.push_back (best_sys_count);
969 ret.systems_per_page_.push_back (cached_line_details_.size () - best_sys_count);
970 ret.force_.push_back (page1_force[best_sys_count-1]);
971 ret.force_.push_back (page2_force[best_sys_count-1]);
972 ret.penalty_ = cached_line_details_[best_sys_count-1].page_penalty_
973 + cached_line_details_.back ().page_penalty_
974 + cached_line_details_.back ().turn_penalty_;
976 /* don't do finalize_spacing_result () because we are only an internal function */
981 Page_breaking::all_lines_stretched (vsize configuration)
983 cache_line_details (configuration);
984 for (vsize i = 0; i < cached_line_details_.size (); i++)
985 if (cached_line_details_[i].force_ < 0)
991 Page_breaking::Line_division
992 Page_breaking::current_configuration (vsize configuration_index) const
994 return current_configurations_[configuration_index];
998 Page_breaking::is_last () const
1000 return current_end_breakpoint_ == last_break_position ();
1004 Page_breaking::last_break_position () const
1006 return breaks_.size () - 1;