Nitpick: ly:spanner-bound grob name slur -> spanner.
[lilypond.git] / lily / page-breaking.cc
blob4f096d8178e1a692e1097d9a28c13d0f40b52d61
1 /*
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>
8 */
11 This is a utility class for page-breaking algorithms. There are some complex
12 parts of this class, some of which are useful to understand if you intend
13 to write a page breaking algorithm (ie. a subclass of Page_breaking). Most
14 of these complexities were introduced in order to break the problem of
15 page-breaking into simpler subproblems and to hide some of the bookkeeping
16 complexities of page breaking from the page breaking algorithms.
18 COMPRESSED LINES
19 There are several functions that actually distribute systems across pages
20 (for example, the space_systems_XXX and pack_systems_XXX functions). If
21 each of these functions had to handle \noPageBreak, it would be a mess.
22 Therefore, we handle \noPageBreak by "compressing" the list of systems
23 before doing any layout: we concatenate any two systems separated by a
24 \noPageBreak into a single system. The page-breaking functions can do their
25 magic without encountering a \noPageBreak; we then "uncompress" the systems
26 at the end. We almost always work with cached_line_details_, which are
27 "compressed."
29 CHUNKS
30 The basic operation of a page breaking algorithm is to repeatedly request
31 some systems from the line-breaker and place those systems on some pages.
32 With each repetition, the page breaking algorithm asks the line-breaker for
33 some systems that it thinks will help it achieve a better layout. The
34 Page_breaking class provides functionality to facilitate this in the case
35 that the page breaking algorithm only cares about the number of systems.
37 Even if a page breaking algorithm only cares number of systems, there may
38 be many ways to satisfy its request. For example, in a piece with 2 scores
39 and a request for 10 systems, we could return 5 systems from each score or
40 4 from the first and 6 from the second. Even within a score, we might
41 want to try several different line breaking configurations with a fixed
42 system count; if there is a forced \pageBreak, for example, we might wish
43 to tweak the number of systems on both sides of the \pageBreak independently.
45 The Page_breaking class takes care of finding these configurations. It
46 divides the piece into "chunks" and sets up the line-breaker in such a way
47 that the number of systems in each chunk can be modified independently.
48 Chunks never cross score boundaries; each title and markup is its own chunk.
49 When a page breaking algorithm requests a number of systems, the Page_breaker
50 stores an array of potential configurations, which the page breaking
51 algorithm can iterate over using current_configuration(vsize).
53 LINE_DIVISION
54 A Line_division is simply a way of storing the exact way in which the
55 total number of systems is distributed among chunks. Note that a
56 Line_division may not (in fact, usually will not) describe all of the chunks
57 in the entire book. Rather, it will describe the subset of chunks that lie
58 between some fixed starting and ending point. This subset of chunks changes
59 whenever a page breaking algorithm asks to consider a different pair of
60 starting and ending breakpoints. In particular, a Line_division should be
61 discarded after a call to set_current_breakpoints, since that Line_division
62 refers to a subset of chunks which might be different from the current
63 subset of chunks under consideration.
66 #include "page-breaking.hh"
68 #include "international.hh"
69 #include "item.hh"
70 #include "output-def.hh"
71 #include "page-spacing.hh"
72 #include "paper-book.hh"
73 #include "paper-score.hh"
74 #include "paper-system.hh"
75 #include "system.hh"
76 #include "warn.hh"
78 /* for each forbidden page break, merge the systems around it into one
79 system. */
80 static vector<Line_details>
81 compress_lines (const vector<Line_details> &orig)
83 vector<Line_details> ret;
85 for (vsize i = 0; i < orig.size (); i++)
87 if (ret.size () && !scm_is_symbol (ret.back ().page_permission_))
89 Line_details const &old = ret.back ();
90 Line_details compressed = orig[i];
91 compressed.extent_[DOWN] = old.extent_[DOWN];
92 compressed.extent_[UP] = old.extent_[UP] + orig[i].extent_.length () + old.padding_;
93 compressed.space_ += old.space_;
94 compressed.inverse_hooke_ += old.inverse_hooke_;
96 compressed.compressed_lines_count_ = old.compressed_lines_count_ + 1;
97 compressed.compressed_nontitle_lines_count_ =
98 old.compressed_nontitle_lines_count_ + (compressed.title_ ? 0 : 1);
100 compressed.title_ = compressed.title_ && old.title_;
101 ret.back () = compressed;
103 else
105 ret.push_back (orig[i]);
106 ret.back ().force_ = 0;
109 return ret;
112 /* translate the number of systems-per-page into something meaningful for
113 the uncompressed lines.
115 static vector<vsize>
116 uncompress_solution (vector<vsize> const &systems_per_page,
117 vector<Line_details> const &compressed)
119 vector<vsize> ret;
120 vsize start_sys = 0;
122 for (vsize i = 0; i < systems_per_page.size (); i++)
124 int compressed_count = 0;
125 for (vsize j = start_sys; j < start_sys + systems_per_page[i]; j++)
126 compressed_count += compressed[j].compressed_lines_count_ - 1;
128 ret.push_back (systems_per_page[i] + compressed_count);
129 start_sys += systems_per_page[i];
131 return ret;
134 /* for Page_breaking, the start index (when we are dealing with the stuff
135 between a pair of breakpoints) refers to the break_ index of the end of
136 the previous page. So the system index of the start of the current page
137 could either be the same as the end of the previous page or one more than
138 it. */
140 /* Turn a break index into the sys index that starts the next page */
141 vsize
142 Page_breaking::next_system (Break_position const &break_pos) const
144 vsize sys = break_pos.system_spec_index_;
146 if (sys == VPOS) /* beginning of the book */
147 return 0;
148 if (system_specs_[sys].pscore_ && !break_pos.score_ender_)
149 return sys; /* the score overflows the previous page */
150 return sys + 1; /* this page starts with a new System_spec */
153 Page_breaking::Page_breaking (Paper_book *pb, Break_predicate is_break)
155 book_ = pb;
156 system_count_ = 0;
157 ragged_ = to_boolean (pb->paper_->c_variable ("ragged-bottom"));
158 ragged_last_ = to_boolean (pb->paper_->c_variable ("ragged-last-bottom"));
159 page_top_space_ = robust_scm2double (pb->paper_->c_variable ("page-top-space"), 0);
160 systems_per_page_ = max (0, robust_scm2int (pb->paper_->c_variable ("systems-per-page"), 0));
161 max_systems_per_page_ = max (0, robust_scm2int (pb->paper_->c_variable ("max-systems-per-page"), 0));
162 min_systems_per_page_ = max (0, robust_scm2int (pb->paper_->c_variable ("min-systems-per-page"), 0));
164 if (systems_per_page_ && (max_systems_per_page_ || min_systems_per_page_))
166 warning (_f ("ignoring min-systems-per-page and max-systems-per-page because systems-per-page was set"));
167 min_systems_per_page_ = max_systems_per_page_ = 0;
169 if (max_systems_per_page_ && min_systems_per_page_ > max_systems_per_page_)
171 warning (_f ("min-systems-per-page is larger than max-systems-per-page, ignoring both values"));
172 min_systems_per_page_ = max_systems_per_page_ = 0;
175 create_system_list ();
176 find_chunks_and_breaks (is_break);
179 Page_breaking::~Page_breaking ()
183 bool
184 Page_breaking::ragged () const
186 return ragged_;
189 bool
190 Page_breaking::ragged_last () const
192 return ragged_last_;
196 Page_breaking::systems_per_page () const
198 return systems_per_page_;
202 Page_breaking::max_systems_per_page () const
204 return max_systems_per_page_;
208 Page_breaking::min_systems_per_page () const
210 return min_systems_per_page_;
213 Real
214 Page_breaking::page_top_space () const
216 return page_top_space_;
219 vsize
220 Page_breaking::system_count () const
222 return system_count_;
225 bool
226 Page_breaking::too_many_lines (int line_count) const
228 return max_systems_per_page () > 0 && line_count > max_systems_per_page ();
231 bool
232 Page_breaking::too_few_lines (int line_count) const
234 return line_count < min_systems_per_page ();
237 Real
238 Page_breaking::line_count_penalty (int line_count) const
240 if (too_many_lines (line_count))
241 return (line_count - max_systems_per_page ()) * TERRIBLE_SPACING_PENALTY;
242 if (too_few_lines (line_count))
243 return (min_systems_per_page () - line_count) * TERRIBLE_SPACING_PENALTY;
245 return 0;
249 Page_breaking::line_count_status (int line_count) const
251 if (too_many_lines (line_count))
252 return SYSTEM_COUNT_TOO_MANY;
253 if (too_few_lines (line_count))
254 return SYSTEM_COUNT_TOO_FEW;
256 return SYSTEM_COUNT_OK;
259 /* translate indices into breaks_ into start-end parameters for the line breaker */
260 void
261 Page_breaking::line_breaker_args (vsize sys,
262 Break_position const &start,
263 Break_position const &end,
264 vsize *line_breaker_start,
265 vsize *line_breaker_end)
267 assert (system_specs_[sys].pscore_);
268 assert (next_system (start) <= sys && sys <= end.system_spec_index_);
270 if (start.system_spec_index_ == sys)
271 *line_breaker_start = start.score_break_;
272 else
273 *line_breaker_start = 0;
275 if (end.system_spec_index_ == sys)
276 *line_breaker_end = end.score_break_;
277 else
278 *line_breaker_end = VPOS;
281 void
282 Page_breaking::break_into_pieces (vsize start_break, vsize end_break,
283 Line_division const &div)
285 vector<Break_position> chunks = chunk_list (start_break, end_break);
286 bool ignore_div = false;
287 if (chunks.size () != div.size () + 1)
289 programming_error ("did not find a valid page breaking configuration");
290 ignore_div = true;
293 for (vsize i = 0; i + 1 < chunks.size (); i++)
295 vsize sys = next_system (chunks[i]);
296 if (system_specs_[sys].pscore_)
298 vsize start;
299 vsize end;
300 line_breaker_args (sys, chunks[i], chunks[i+1], &start, &end);
302 vector<Column_x_positions> pos = ignore_div
303 ? line_breaking_[sys].best_solution (start, end)
304 : line_breaking_[sys].solve (start, end, div[i]);
305 system_specs_[sys].pscore_->root_system ()->break_into_pieces (pos);
311 Page_breaking::systems ()
313 SCM ret = SCM_EOL;
314 for (vsize sys = 0; sys < system_specs_.size (); sys++)
316 if (system_specs_[sys].pscore_)
318 system_specs_[sys].pscore_->root_system ()
319 ->do_break_substitution_and_fixup_refpoints ();
320 SCM lines = system_specs_[sys].pscore_->root_system ()
321 ->get_broken_system_grobs ();
322 ret = scm_cons (lines, ret);
324 else
326 Prob *pb = system_specs_[sys].prob_;
327 ret = scm_cons (scm_list_1 (pb->self_scm ()), ret);
328 pb->unprotect ();
331 return scm_append (scm_reverse (ret));
334 Real
335 Page_breaking::page_height (int page_num, bool last) const
337 bool last_part = ly_scm2bool (book_->paper_->c_variable ("is-last-bookpart"));
338 SCM mod = scm_c_resolve_module ("scm page");
339 SCM calc_height = scm_c_module_lookup (mod, "calc-printable-height");
340 SCM make_page = scm_c_module_lookup (mod, "make-page");
342 calc_height = scm_variable_ref (calc_height);
343 make_page = scm_variable_ref (make_page);
345 SCM page = scm_apply_0 (make_page, scm_list_n (
346 book_->self_scm (),
347 ly_symbol2scm ("page-number"), scm_from_int (page_num),
348 ly_symbol2scm ("is-last-bookpart"), scm_from_bool (last_part),
349 ly_symbol2scm ("is-bookpart-last-page"), scm_from_bool (last),
350 SCM_UNDEFINED));
351 SCM height = scm_apply_1 (calc_height, page, SCM_EOL);
352 return scm_to_double (height) - page_top_space_;
356 Page_breaking::breakpoint_property (vsize breakpoint, char const *str)
358 Break_position const &pos = breaks_[breakpoint];
360 if (pos.system_spec_index_ == VPOS)
361 return SCM_EOL;
362 if (system_specs_[pos.system_spec_index_].pscore_)
363 return pos.col_->get_property (str);
364 return system_specs_[pos.system_spec_index_].prob_->get_property (str);
368 Page_breaking::make_pages (vector<vsize> lines_per_page, SCM systems)
370 SCM layout_module = scm_c_resolve_module ("scm layout-page-layout");
371 SCM page_module = scm_c_resolve_module ("scm page");
373 SCM make_page = scm_c_module_lookup (layout_module, "stretch-and-draw-page");
374 SCM page_stencil = scm_c_module_lookup (page_module, "page-stencil");
375 make_page = scm_variable_ref (make_page);
376 page_stencil = scm_variable_ref (page_stencil);
378 SCM book = book_->self_scm ();
379 int first_page_number
380 = robust_scm2int (book_->paper_->c_variable ("first-page-number"), 1);
381 bool last_bookpart = ly_scm2bool (book_->paper_->c_variable ("is-last-bookpart"));
382 SCM ret = SCM_EOL;
383 SCM label_page_table = book_->top_paper ()->c_variable ("label-page-table");
384 if (label_page_table == SCM_UNDEFINED)
385 label_page_table = SCM_EOL;
387 for (vsize i = 0; i < lines_per_page.size (); i++)
389 SCM page_num = scm_from_int (i + first_page_number);
390 bool partbook_last_page = (i == lines_per_page.size () - 1);
391 SCM rag = scm_from_bool (ragged () || ( partbook_last_page && ragged_last ()));
392 SCM line_count = scm_from_int (lines_per_page[i]);
393 SCM lines = scm_list_head (systems, line_count);
394 SCM page = scm_apply_0 (make_page,
395 scm_list_n (book, lines, page_num, rag,
396 scm_from_bool (last_bookpart),
397 scm_from_bool (partbook_last_page),
398 SCM_UNDEFINED));
399 /* collect labels */
400 for (SCM l = lines ; scm_is_pair (l) ; l = scm_cdr (l))
402 SCM labels = SCM_EOL;
403 if (Grob * line = unsmob_grob (scm_car (l)))
405 System *system = dynamic_cast<System*> (line);
406 labels = system->get_property ("labels");
408 else if (Prob *prob = unsmob_prob (scm_car (l)))
409 labels = prob->get_property ("labels");
411 for (SCM lbls = labels ; scm_is_pair (lbls) ; lbls = scm_cdr (lbls))
412 label_page_table = scm_cons (scm_cons (scm_car (lbls), page_num),
413 label_page_table);
416 scm_apply_1 (page_stencil, page, SCM_EOL);
417 ret = scm_cons (page, ret);
418 systems = scm_list_tail (systems, line_count);
420 book_->top_paper ()->set_variable (ly_symbol2scm ("label-page-table"), label_page_table);
421 ret = scm_reverse (ret);
422 return ret;
425 /* The page-turn-page-breaker needs to have a line-breaker between any two
426 columns with non-NULL page-turn-permission.
428 The optimal-breaker needs to have a line-breaker between any two columns
429 with page-break-permission = 'force.
431 By using a grob predicate, we can accommodate both of these uses.
433 void
434 Page_breaking::create_system_list ()
436 SCM specs = book_->get_system_specs ();
437 for (SCM s = specs; scm_is_pair (s); s = scm_cdr (s))
439 if (Paper_score *ps = dynamic_cast<Paper_score*> (unsmob_music_output (scm_car (s))))
441 SCM system_count = ps->layout ()->c_variable ("system-count");
443 if (scm_is_number (system_count))
444 s = scm_append (scm_list_3 (scm_list_1 (scm_car (s)),
445 scm_vector_to_list (ps->get_paper_systems ()),
446 scm_cdr (s)));
447 else
448 system_specs_.push_back (System_spec (ps));
450 else
452 Prob *pb = unsmob_prob (scm_car (s));
453 assert (pb);
455 pb->protect ();
456 system_specs_.push_back (System_spec (pb));
461 void
462 Page_breaking::find_chunks_and_breaks (Break_predicate is_break)
464 SCM force_sym = ly_symbol2scm ("force");
466 chunks_.push_back (Break_position ());
467 breaks_.push_back (Break_position ());
469 for (vsize i = 0; i < system_specs_.size (); i++)
471 if (system_specs_[i].pscore_)
473 vector<Grob*> cols
474 = system_specs_[i].pscore_->root_system ()->used_columns ();
475 vector<vsize> line_breaker_columns;
476 line_breaker_columns.push_back (0);
478 for (vsize j = 1; j < cols.size (); j++)
480 bool last = (j == cols.size () - 1);
481 bool break_point = is_break (cols[j]);
482 bool chunk_end = cols[j]->get_property ("page-break-permission") == force_sym;
483 Break_position cur_pos = Break_position (i,
484 line_breaker_columns.size (),
485 cols[j],
486 last);
488 if (break_point || (i == system_specs_.size () - 1 && last))
489 breaks_.push_back (cur_pos);
490 if (chunk_end || last)
491 chunks_.push_back (cur_pos);
493 if ((break_point || chunk_end) && !last)
494 line_breaker_columns.push_back (j);
496 line_breaking_.push_back (Constrained_breaking (system_specs_[i].pscore_, line_breaker_columns));
498 else
500 /* TODO: we want some way of applying Break_p to a prob? */
501 if (i == system_specs_.size () - 1)
502 breaks_.push_back (Break_position (i));
504 chunks_.push_back (Break_position (i));
506 /* FIXME: shouldn't we push a Null_breaker or similar dummy
507 class? --hwn */
508 line_breaking_.push_back (Constrained_breaking (NULL));
513 vector<Break_position>
514 Page_breaking::chunk_list (vsize start_index, vsize end_index)
516 Break_position start = breaks_[start_index];
517 Break_position end = breaks_[end_index];
519 vsize i = 0;
520 for (; i < chunks_.size () && chunks_[i] <= start; i++)
523 vector<Break_position> ret;
524 ret.push_back (start);
525 for (; i < chunks_.size () && chunks_[i] < end; i++)
526 ret.push_back (chunks_[i]);
527 ret.push_back (end);
528 return ret;
531 vsize
532 Page_breaking::min_system_count (vsize start, vsize end)
534 vector<Break_position> chunks = chunk_list (start, end);
535 Line_division div = system_count_bounds (chunks, true);
536 vsize ret = 0;
538 for (vsize i = 0; i < div.size (); i++)
539 ret += div[i];
540 return ret;
543 vsize
544 Page_breaking::max_system_count (vsize start, vsize end)
546 vector<Break_position> chunks = chunk_list (start, end);
547 Line_division div = system_count_bounds (chunks, false);
548 vsize ret = 0;
550 for (vsize i = 0; i < div.size (); i++)
551 ret += div[i];
552 return ret;
555 Page_breaking::Line_division
556 Page_breaking::system_count_bounds (vector<Break_position> const &chunks,
557 bool min)
559 assert (chunks.size () >= 2);
561 Line_division ret;
562 ret.resize (chunks.size () - 1, 1);
564 for (vsize i = 0; i + 1 < chunks.size (); i++)
566 vsize sys = next_system (chunks[i]);
567 if (system_specs_[sys].pscore_)
569 vsize start;
570 vsize end;
571 line_breaker_args (sys, chunks[i], chunks[i+1], &start, &end);
572 ret[i] = min
573 ? line_breaking_[sys].min_system_count (start, end)
574 : line_breaking_[sys].max_system_count (start, end);
578 return ret;
581 void
582 Page_breaking::set_current_breakpoints (vsize start,
583 vsize end,
584 vsize system_count,
585 Line_division lower_bound,
586 Line_division upper_bound)
588 system_count_ = system_count;
589 current_chunks_ = chunk_list (start, end);
590 current_start_breakpoint_ = start;
591 current_end_breakpoint_ = end;
592 clear_line_details_cache ();
594 if (!lower_bound.size ())
595 lower_bound = system_count_bounds (current_chunks_, true);
596 if (!upper_bound.size ())
597 upper_bound = system_count_bounds (current_chunks_, false);
599 assert (lower_bound.size () == current_chunks_.size () - 1);
600 assert (upper_bound.size () == current_chunks_.size () - 1);
602 Line_division work_in_progress;
603 current_configurations_.clear ();
604 line_divisions_rec (system_count,
605 lower_bound,
606 upper_bound,
607 &work_in_progress);
609 /* we only consider a constant number of configurations. Otherwise,
610 this becomes slow when there are many small scores. The constant
611 5 is somewhat arbitrary. */
612 if (current_configurations_.size () > 5)
614 vector<pair<Real,vsize> > dems_and_indices;
616 for (vsize i = 0; i < current_configurations_.size (); i++)
618 cache_line_details (i);
619 Real dem = 0;
620 for (vsize j = 0; j < cached_line_details_.size (); j++)
621 dem += cached_line_details_[j].force_ * cached_line_details_[j].force_
622 + cached_line_details_[j].break_penalty_;
624 dems_and_indices.push_back (pair<Real,vsize> (dem, i));
626 vector_sort (dems_and_indices, less<pair<Real,vsize> > ());
628 vector<Line_division> best_5_configurations;
629 for (vsize i = 0; i < 5; i++)
630 best_5_configurations.push_back (current_configurations_[dems_and_indices[i].second]);
632 clear_line_details_cache ();
633 current_configurations_ = best_5_configurations;
637 void
638 Page_breaking::set_to_ideal_line_configuration (vsize start, vsize end)
640 current_chunks_ = chunk_list (start, end);
641 current_start_breakpoint_ = start;
642 current_end_breakpoint_ = end;
643 clear_line_details_cache ();
644 system_count_ = 0;
646 Line_division div;
647 for (vsize i = 0; i+1 < current_chunks_.size (); i++)
649 vsize sys = next_system (current_chunks_[i]);
650 if (system_specs_[sys].pscore_)
652 line_breaker_args (sys, current_chunks_[i], current_chunks_[i+1], &start, &end);
653 div.push_back (line_breaking_[sys].best_solution (start, end).size ());
655 else
656 div.push_back (1);
658 system_count_ += div.back ();
660 current_configurations_.clear ();
661 current_configurations_.push_back (div);
664 vsize
665 Page_breaking::current_configuration_count () const
667 return current_configurations_.size ();
670 void
671 Page_breaking::cache_line_details (vsize configuration_index)
673 if (cached_configuration_index_ != configuration_index)
675 cached_configuration_index_ = configuration_index;
676 SCM padding_scm = book_->paper_->c_variable ("page-breaking-between-system-padding");
677 if (!scm_is_number (padding_scm))
678 padding_scm = book_->paper_->c_variable ("between-system-padding");
679 Real padding = robust_scm2double (padding_scm, 0.0);
681 Line_division &div = current_configurations_[configuration_index];
682 uncompressed_line_details_.clear ();
683 for (vsize i = 0; i + 1 < current_chunks_.size (); i++)
685 vsize sys = next_system (current_chunks_[i]);
686 if (system_specs_[sys].pscore_)
688 vsize start;
689 vsize end;
690 line_breaker_args (sys, current_chunks_[i], current_chunks_[i+1], &start, &end);
692 vector<Line_details> details = line_breaking_[sys].line_details (start, end, div[i]);
693 uncompressed_line_details_.insert (uncompressed_line_details_.end (), details.begin (), details.end ());
695 else
697 assert (div[i] == 1);
698 uncompressed_line_details_.push_back (Line_details (system_specs_[sys].prob_));
699 uncompressed_line_details_.back ().padding_ =
700 robust_scm2double (system_specs_[sys].prob_->get_property ("next-padding"),
701 padding);
704 cached_line_details_ = compress_lines (uncompressed_line_details_);
708 void
709 Page_breaking::clear_line_details_cache ()
711 cached_configuration_index_ = VPOS;
712 cached_line_details_.clear ();
713 uncompressed_line_details_.clear ();
716 void
717 Page_breaking::line_divisions_rec (vsize system_count,
718 Line_division const &min_sys,
719 Line_division const &max_sys,
720 Line_division *cur_division)
722 vsize my_index = cur_division->size ();
723 vsize others_min = 0;
724 vsize others_max = 0;
726 for (vsize i = my_index + 1; i < min_sys.size (); i++)
728 others_min += min_sys[i];
729 others_max += max_sys[i];
731 others_max = min (others_max, system_count);
732 vsize real_min = max (min_sys[my_index], system_count - others_max);
733 vsize real_max = min (max_sys[my_index], system_count - others_min);
735 if (real_min > real_max || real_min <= 0)
737 /* this should never happen within a recursive call. If it happens
738 at all, it means that we were called with an unsolvable problem
739 and we should return an empty result */
740 assert (my_index == 0);
741 return;
744 for (vsize i = real_min; i <= real_max; i++)
746 cur_division->push_back (i);
747 if (my_index == min_sys.size () - 1)
748 current_configurations_.push_back (*cur_division);
749 else
750 line_divisions_rec (system_count - i, min_sys, max_sys, cur_division);
751 cur_division->pop_back ();
755 vsize
756 Page_breaking::min_page_count (vsize configuration, vsize first_page_num)
758 vsize ret = 1;
759 Real cur_rod_height = 0;
760 Real cur_spring_height = 0;
761 Real cur_page_height = page_height (first_page_num, false);
762 int line_count = 0;
764 cache_line_details (configuration);
765 for (vsize i = 0; i < cached_line_details_.size (); i++)
767 Real ext_len = cached_line_details_[i].extent_.length ();
768 Real next_rod_height = cur_rod_height + ext_len
769 + ((cur_rod_height > 0) ? cached_line_details_[i].padding_: 0);
770 Real next_spring_height = cur_spring_height + cached_line_details_[i].space_;
771 Real next_height = next_rod_height + (ragged () ? next_spring_height : 0);
772 int next_line_count = line_count + cached_line_details_[i].compressed_nontitle_lines_count_;
774 if ((!too_few_lines (line_count) && (next_height > cur_page_height && cur_rod_height > 0))
775 || too_many_lines (next_line_count)
776 || (i > 0
777 && cached_line_details_[i-1].page_permission_ == ly_symbol2scm ("force")))
779 line_count = cached_line_details_[i].compressed_nontitle_lines_count_;
780 cur_rod_height = ext_len;
781 cur_spring_height = cached_line_details_[i].space_;
782 cur_page_height = page_height (first_page_num + ret, false);
783 ret++;
785 else
787 cur_rod_height = next_rod_height;
788 cur_spring_height = next_spring_height;
789 line_count = next_line_count;
793 /* there are two potential problems with the last page (because we didn't know
794 it was the last page until after we managed to fit all the systems to it):
795 - we are ragged-last but the last page has a compressed spring
796 - the value returned by page_height (num, true) is smaller than the
797 value returned by page_height (num, false) and it causes the page not to
798 fit.
800 In either case, we just need to add one more page. This is because the last
801 line will always fit on the extra page and by adding one more page to the
802 end, the previous page is no longer the last page, so our previous
803 calculations that treated it as a non-last page were ok.
806 cur_page_height = page_height (first_page_num + ret - 1, true);
807 Real cur_height = cur_rod_height + ((ragged_last () || ragged ()) ? cur_spring_height : 0);
808 if (!too_few_lines (line_count - cached_line_details_.back ().compressed_nontitle_lines_count_)
809 && cur_height > cur_page_height
810 /* don't increase the page count if the last page had only one system */
811 && cur_rod_height > cached_line_details_.back ().extent_.length ())
812 ret++;
814 assert (ret <= cached_line_details_.size ());
815 return ret;
818 // If systems_per_page_ is positive, we don't really try to space on N pages;
819 // we just put the requested number of systems on each page and penalize
820 // if the result doesn't have N pages.
821 Page_spacing_result
822 Page_breaking::space_systems_on_n_pages (vsize configuration, vsize n, vsize first_page_num)
824 Page_spacing_result ret;
826 if (systems_per_page_ > 0)
828 Page_spacing_result ret = space_systems_with_fixed_number_per_page (configuration, first_page_num);
829 ret.demerits_ += (ret.force_.size () == n) ? 0 : BAD_SPACING_PENALTY;
830 return ret;
833 cache_line_details (configuration);
834 bool valid_n = (n >= min_page_count (configuration, first_page_num)
835 && n <= cached_line_details_.size ());
837 if (!valid_n)
838 programming_error ("number of pages is out of bounds");
840 if (n == 1 && valid_n)
841 ret = space_systems_on_1_page (cached_line_details_,
842 page_height (first_page_num, is_last ()),
843 ragged () || (is_last () && ragged_last ()));
844 else if (n == 2 && valid_n)
845 ret = space_systems_on_2_pages (configuration, first_page_num);
846 else
848 Page_spacer ps (cached_line_details_, first_page_num, this);
849 ret = ps.solve (n);
852 return finalize_spacing_result (configuration, ret);
855 Real
856 Page_breaking::blank_page_penalty () const
858 SCM penalty_sym;
860 if (is_last ())
861 penalty_sym = ly_symbol2scm ("blank-last-page-force");
862 else if (ends_score ())
863 penalty_sym = ly_symbol2scm ("blank-after-score-page-force");
864 else
865 penalty_sym = ly_symbol2scm ("blank-page-force");
867 Break_position const &pos = breaks_[current_end_breakpoint_];
868 if (Paper_score *ps = system_specs_[pos.system_spec_index_].pscore_)
869 return robust_scm2double (ps->layout ()->lookup_variable (penalty_sym), 0.0);
871 return robust_scm2double (book_->paper_->lookup_variable (penalty_sym), 0.0);
874 // If systems_per_page_ is positive, we don't really try to space on N
875 // or N+1 pages; see the comment to space_systems_on_n_pages.
876 Page_spacing_result
877 Page_breaking::space_systems_on_n_or_one_more_pages (vsize configuration, vsize n, vsize first_page_num)
879 Page_spacing_result n_res;
880 Page_spacing_result m_res;
882 if (systems_per_page_ > 0)
884 Page_spacing_result ret = space_systems_with_fixed_number_per_page (configuration, first_page_num);
885 ret.demerits_ += (ret.force_.size () == n || ret.force_.size () == (n-1)) ? 0 : BAD_SPACING_PENALTY;
886 return ret;
889 cache_line_details (configuration);
890 vsize min_p_count = min_page_count (configuration, first_page_num);
891 bool valid_n = n >= min_p_count || n <= cached_line_details_.size ();
893 if (!valid_n)
894 programming_error ("both page counts are out of bounds");
896 if (n == 1 && valid_n)
898 bool rag = ragged () || (is_last () && ragged_last ());
899 Real height = page_height (first_page_num, is_last ());
901 if (1 >= min_p_count)
902 n_res = space_systems_on_1_page (cached_line_details_, height, rag);
903 if (1 < cached_line_details_.size ())
904 m_res = space_systems_on_2_pages (configuration, first_page_num);
906 else
908 Page_spacer ps (cached_line_details_, first_page_num, this);
910 if (n >= min_p_count || !valid_n)
911 n_res = ps.solve (n);
912 if (n < cached_line_details_.size () || !valid_n)
913 m_res = ps.solve (n+1);
916 m_res = finalize_spacing_result (configuration, m_res);
917 n_res = finalize_spacing_result (configuration, n_res);
919 Real penalty = blank_page_penalty ();
920 n_res.demerits_ += penalty;
922 if (n_res.force_.size ())
923 n_res.force_.back () += penalty;
925 return (m_res.demerits_ < n_res.demerits_) ? m_res : n_res;
928 Page_spacing_result
929 Page_breaking::space_systems_on_best_pages (vsize configuration, vsize first_page_num)
931 vsize min_p_count = min_page_count (configuration, first_page_num);
932 Real odd_pages_penalty = blank_page_penalty ();
934 cache_line_details (configuration);
935 Page_spacer ps (cached_line_details_, first_page_num, this);
936 Page_spacing_result best = ps.solve (min_p_count);
937 best.force_.back () += (min_p_count % 2) ? odd_pages_penalty : 0;
938 best.demerits_ += (min_p_count % 2) ? odd_pages_penalty : 0;
940 for (vsize i = min_p_count+1; i <= cached_line_details_.size (); i++)
942 Page_spacing_result cur = ps.solve (i);
943 cur.demerits_ += (i % 2) ? odd_pages_penalty : 0;
944 if (cur.demerits_ < best.demerits_)
945 best = cur;
948 return finalize_spacing_result (configuration, best);
951 Page_spacing_result
952 Page_breaking::space_systems_with_fixed_number_per_page (vsize configuration,
953 vsize first_page_num)
955 Page_spacing_result res;
956 Page_spacing space (page_height (first_page_num, false), page_top_space_);
957 vsize line = 0;
958 vsize page = 0;
959 vsize page_first_line = 0;
961 cache_line_details (configuration);
962 while (line < cached_line_details_.size ())
964 page++;
965 space.clear ();
966 space.resize (page_height (first_page_num + page, false));
968 int system_count_on_this_page = 0;
969 while (system_count_on_this_page < systems_per_page_
970 && line < cached_line_details_.size ())
972 Line_details const &cur_line = cached_line_details_[line];
973 space.append_system (cur_line);
974 system_count_on_this_page += cur_line.compressed_nontitle_lines_count_;
975 line++;
977 if (cur_line.page_permission_ == ly_symbol2scm ("force"))
978 break;
981 res.systems_per_page_.push_back (line - page_first_line);
983 res.force_.push_back (space.force_);
984 res.penalty_ += cached_line_details_[line-1].page_penalty_;
985 if (system_count_on_this_page != systems_per_page_)
987 res.penalty_ += TERRIBLE_SPACING_PENALTY;
988 res.system_count_status_ |= ((system_count_on_this_page < systems_per_page_))
989 ? SYSTEM_COUNT_TOO_FEW : SYSTEM_COUNT_TOO_MANY;
992 page_first_line = line;
995 /* Recalculate forces for the last page because we know now that is
996 really the last page. */
997 space.resize (page_height (first_page_num + page, true));
998 res.force_.back () = space.force_;
999 return finalize_spacing_result (configuration, res);
1002 Page_spacing_result
1003 Page_breaking::pack_systems_on_least_pages (vsize configuration, vsize first_page_num)
1005 // TODO: add support for min/max-systems-per-page.
1006 Page_spacing_result res;
1007 vsize page = 0;
1008 vsize page_first_line = 0;
1009 Page_spacing space (page_height (first_page_num, false), page_top_space_);
1011 cache_line_details (configuration);
1012 for (vsize line = 0; line < cached_line_details_.size (); line++)
1014 Real prev_force = space.force_;
1015 space.append_system (cached_line_details_[line]);
1016 if ((line > page_first_line)
1017 && (isinf (space.force_)
1018 || ((line > 0)
1019 && (cached_line_details_[line-1].page_permission_ == ly_symbol2scm ("force")))))
1021 res.systems_per_page_.push_back (line - page_first_line);
1022 res.force_.push_back (prev_force);
1023 res.penalty_ += cached_line_details_[line-1].page_penalty_;
1024 page++;
1025 space.resize (page_height (first_page_num + page, false));
1026 space.clear ();
1027 space.append_system (cached_line_details_[line]);
1028 page_first_line = line;
1031 if (line == cached_line_details_.size () - 1)
1033 /* This is the last line */
1034 /* When the last page height was computed, we did not know yet that it
1035 * was the last one. If the systems put on it don't fit anymore, the last
1036 * system is moved to a new page */
1037 space.resize (page_height (first_page_num + page, true));
1038 if ((line > page_first_line) && (isinf (space.force_)))
1040 res.systems_per_page_.push_back (line - page_first_line);
1041 res.force_.push_back (prev_force);
1042 /* the last page containing the last line */
1043 space.resize (page_height (first_page_num + page + 1, true));
1044 space.clear ();
1045 space.append_system (cached_line_details_[line]);
1046 res.systems_per_page_.push_back (1);
1047 res.force_.push_back (space.force_);
1048 res.penalty_ += cached_line_details_[line-1].page_penalty_;
1049 res.penalty_ += cached_line_details_[line].page_penalty_;
1051 else
1053 res.systems_per_page_.push_back (line + 1 - page_first_line);
1054 res.force_.push_back (space.force_);
1055 res.penalty_ += cached_line_details_[line].page_penalty_;
1059 return finalize_spacing_result (configuration, res);
1062 /* Calculate demerits and fix res.systems_per_page_ so that
1063 it refers to the original line numbers, not the ones given by compress_lines (). */
1064 Page_spacing_result
1065 Page_breaking::finalize_spacing_result (vsize configuration, Page_spacing_result res)
1067 if (res.force_.empty ())
1068 return res;
1070 cache_line_details (configuration);
1071 res.systems_per_page_ = uncompress_solution (res.systems_per_page_, cached_line_details_);
1073 Real line_force = 0;
1074 Real line_penalty = 0;
1075 Real page_demerits = res.penalty_;
1076 Real page_weighting = robust_scm2double (book_->paper_->c_variable ("page-spacing-weight"), 10);
1078 for (vsize i = 0; i < uncompressed_line_details_.size (); i++)
1080 line_force += uncompressed_line_details_[i].force_ * uncompressed_line_details_[i].force_;
1081 line_penalty += uncompressed_line_details_[i].break_penalty_;
1084 for (vsize i = 0; i < res.force_.size (); i++)
1086 Real f = res.force_[i];
1088 page_demerits += min(f*f, BAD_SPACING_PENALTY);
1091 /* for a while we tried averaging page and line forces across pages instead
1092 of summing them, but it caused a problem: if there is a single page
1093 with a very bad page force (for example because of a forced page break),
1094 the page breaker will put in a _lot_ of pages so that the bad force
1095 becomes averaged out over many pages. */
1096 res.demerits_ = line_force + line_penalty + page_demerits * page_weighting;
1097 return res;
1101 /* the cases for page_count = 1 or 2 can be done in O (n) time. Since they
1102 are by far the most common cases, we have special functions for them.
1104 space_systems_on_1_page has a different calling convention than most of the
1105 space_systems functions. This is because space_systems_on_1_page is (unlike
1106 the other space_systems functions) sometimes called on subsets of a full
1107 configuration. */
1108 Page_spacing_result
1109 Page_breaking::space_systems_on_1_page (vector<Line_details> const &lines, Real page_height, bool ragged)
1111 Page_spacing space (page_height, page_top_space_);
1112 Page_spacing_result ret;
1113 int line_count = 0;
1115 for (vsize i = 0; i < lines.size (); i++)
1117 space.append_system (lines[i]);
1118 line_count += lines[i].compressed_nontitle_lines_count_;
1121 ret.systems_per_page_.push_back (lines.size ());
1122 ret.force_.push_back (ragged ? min (space.force_, 0.0) : space.force_);
1123 ret.penalty_ = line_count_penalty (line_count) + lines.back ().page_penalty_ + lines.back ().turn_penalty_;
1124 ret.system_count_status_ |= line_count_status (line_count);
1126 /* don't do finalize_spacing_result () because we are only an internal function */
1127 return ret;
1130 Page_spacing_result
1131 Page_breaking::space_systems_on_2_pages (vsize configuration, vsize first_page_num)
1133 Real page1_height = page_height (first_page_num, false);
1134 Real page2_height = page_height (first_page_num + 1, is_last ());
1135 bool ragged1 = ragged ();
1136 bool ragged2 = ragged () || (is_last () && ragged_last ());
1138 /* if there is a forced break, this reduces to 2 1-page problems */
1139 cache_line_details (configuration);
1140 for (vsize i = 0; i + 1 < cached_line_details_.size (); i++)
1141 if (cached_line_details_[i].page_permission_ == ly_symbol2scm ("force"))
1143 vector<Line_details> lines1 (cached_line_details_.begin (), cached_line_details_.begin () + i + 1);
1144 vector<Line_details> lines2 (cached_line_details_.begin () + i + 1, cached_line_details_.end ());
1145 Page_spacing_result p1 = space_systems_on_1_page (lines1, page1_height, ragged1);
1146 Page_spacing_result p2 = space_systems_on_1_page (lines2, page2_height, ragged2);
1148 p1.systems_per_page_.push_back (p2.systems_per_page_[0]);
1149 p1.force_.push_back (p2.force_[0]);
1150 p1.penalty_ += p2.penalty_ - cached_line_details_[i].turn_penalty_;
1151 return p1;
1154 vector<Real> page1_force;
1155 vector<Real> page2_force;
1157 // page1_penalty and page2_penalty store the penalties based
1158 // on min-systems-per-page and max-systems-per-page.
1159 vector<Real> page1_penalty;
1160 vector<Real> page2_penalty;
1162 // page1_status and page2_status keep track of whether the min-systems-per-page
1163 // and max-systems-per-page constraints are satisfied.
1164 vector<int> page1_status;
1165 vector<int> page2_status;
1167 Page_spacing page1 (page1_height, page_top_space_);
1168 Page_spacing page2 (page2_height, page_top_space_);
1169 int page1_line_count = 0;
1170 int page2_line_count = 0;
1172 page1_force.resize (cached_line_details_.size () - 1, infinity_f);
1173 page2_force.resize (cached_line_details_.size () - 1, infinity_f);
1174 page1_penalty.resize (cached_line_details_.size () - 1, infinity_f);
1175 page2_penalty.resize (cached_line_details_.size () - 1, infinity_f);
1176 page1_status.resize (cached_line_details_.size () - 1, 0);
1177 page2_status.resize (cached_line_details_.size () - 1, 0);
1179 /* find the page 1 and page 2 forces for each page-breaking position */
1180 for (vsize i = 0; i < page1_force.size (); i++)
1182 page1.append_system (cached_line_details_[i]);
1183 page2.prepend_system (cached_line_details_[cached_line_details_.size () - 1 - i]);
1184 page1_line_count += cached_line_details_[i].compressed_nontitle_lines_count_;
1185 page2_line_count += cached_line_details_[cached_line_details_.size () - 1 - i].compressed_nontitle_lines_count_;
1187 page1_force[i] = (ragged1 && page1.force_ < 0 && i > 0) ? infinity_f : page1.force_;
1188 page1_penalty[i] = line_count_penalty (page1_line_count);
1189 page1_status[i] = line_count_status (page1_line_count);
1191 if (ragged2)
1192 page2_force[page2_force.size () - 1 - i] =
1193 (page2.force_ < 0 && i + 1 < page1_force.size ()) ? infinity_f : 0;
1194 else
1195 page2_force[page2_force.size () - 1 - i] = page2.force_;
1196 page2_penalty[page2_penalty.size () - 1 - i] = line_count_penalty (page2_line_count);
1197 page2_status[page2_penalty.size () - 1 - i] = line_count_status (page2_line_count);
1200 /* find the position that minimises the sum of the page forces */
1201 vsize best_sys_count = 1;
1202 Real best_demerits = infinity_f;
1203 for (vsize i = 0; i < page1_force.size (); i++)
1205 Real f = page1_force[i] * page1_force[i] + page2_force[i] * page2_force[i];
1207 // NOTE: we treat max-systems-per-page and min-systems-per-page as soft
1208 // constraints. That is, we penalize harshly when they don't happen
1209 // but we don't completely reject.
1210 Real dem = f
1211 + page1_penalty[i] + page2_penalty[i]
1212 + cached_line_details_[i+1].page_penalty_
1213 + cached_line_details_.back ().page_penalty_ + cached_line_details_.back ().turn_penalty_;
1214 if (dem < best_demerits)
1216 best_demerits = dem;
1217 best_sys_count = i+1;
1221 Page_spacing_result ret;
1222 ret.systems_per_page_.push_back (best_sys_count);
1223 ret.systems_per_page_.push_back (cached_line_details_.size () - best_sys_count);
1224 ret.force_.push_back (page1_force[best_sys_count-1]);
1225 ret.force_.push_back (page2_force[best_sys_count-1]);
1226 ret.system_count_status_ = page1_status[best_sys_count-1] | page2_status[best_sys_count-1];
1227 ret.penalty_ = cached_line_details_[best_sys_count-1].page_penalty_
1228 + cached_line_details_.back ().page_penalty_
1229 + cached_line_details_.back ().turn_penalty_
1230 + page1_penalty[best_sys_count-1] + page2_penalty[best_sys_count-1];
1232 /* don't do finalize_spacing_result () because we are only an internal function */
1233 return ret;
1236 bool
1237 Page_breaking::all_lines_stretched (vsize configuration)
1239 cache_line_details (configuration);
1240 for (vsize i = 0; i < cached_line_details_.size (); i++)
1241 if (cached_line_details_[i].force_ < 0)
1242 return false;
1244 return true;
1247 Page_breaking::Line_division
1248 Page_breaking::current_configuration (vsize configuration_index) const
1250 return current_configurations_[configuration_index];
1253 bool
1254 Page_breaking::is_last () const
1256 return current_end_breakpoint_ == last_break_position ();
1259 bool
1260 Page_breaking::ends_score () const
1262 return breaks_[current_end_breakpoint_].score_ender_;
1265 vsize
1266 Page_breaking::last_break_position () const
1268 return breaks_.size () - 1;