1 /*
2 This file is part of LilyPond, the GNU music typesetter.
3
4 Copyright (C) 2006--2020 Joe Neeman <joeneeman@gmail.com>
5
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /*
21 This is a utility class for page-breaking algorithms. There are some complex
22 parts of this class, some of which are useful to understand if you intend
23 to write a page breaking algorithm (ie. a subclass of Page_breaking). Most
24 of these complexities were introduced in order to break the problem of
25 page-breaking into simpler subproblems and to hide some of the bookkeeping
26 complexities of page breaking from the page breaking algorithms.
27
28 COMPRESSED LINES
29 There are several functions that actually distribute systems across pages
30 (for example, the space_systems_XXX and pack_systems_XXX functions). If
31 each of these functions had to handle \noPageBreak, it would be a mess.
32 Therefore, we handle \noPageBreak by "compressing" the list of systems
33 before doing any layout: we concatenate any two systems separated by a
34 \noPageBreak into a single system. The page-breaking functions can do their
35 magic without encountering a \noPageBreak; we then "uncompress" the systems
36 at the end. We almost always work with cached_line_details_, which are
37 "compressed."
38
39 CHUNKS
40 The basic operation of a page breaking algorithm is to repeatedly request
41 some systems from the line-breaker and place those systems on some pages.
42 With each repetition, the page breaking algorithm asks the line-breaker for
43 some systems that it thinks will help it achieve a better layout. The
44 Page_breaking class provides functionality to facilitate this in the case
45 that the page breaking algorithm only cares about the number of systems.
46
47 Even if a page breaking algorithm only cares number of systems, there may
48 be many ways to satisfy its request. For example, in a piece with 2 scores
49 and a request for 10 systems, we could return 5 systems from each score or
50 4 from the first and 6 from the second. Even within a score, we might
51 want to try several different line breaking configurations with a fixed
52 system count; if there is a forced \pageBreak, for example, we might wish
53 to tweak the number of systems on both sides of the \pageBreak independently.
54
55 The Page_breaking class takes care of finding these configurations. It
56 divides the piece into "chunks" and sets up the line-breaker in such a way
57 that the number of systems in each chunk can be modified independently.
58 Chunks never cross score boundaries; each title and markup is its own chunk.
59 When a page breaking algorithm requests a number of systems, the Page_breaker
60 stores an array of potential configurations, which the page breaking
61 algorithm can iterate over using current_configuration(vsize).
62
63 LINE_DIVISION
64 A Line_division is simply a way of storing the exact way in which the
65 total number of systems is distributed among chunks. Note that a
66 Line_division may not (in fact, usually will not) describe all of the chunks
67 in the entire book. Rather, it will describe the subset of chunks that lie
68 between some fixed starting and ending point. This subset of chunks changes
69 whenever a page breaking algorithm asks to consider a different pair of
70 starting and ending breakpoints. In particular, a Line_division should be
71 discarded after a call to set_current_breakpoints, since that Line_division
72 refers to a subset of chunks which might be different from the current
73 subset of chunks under consideration.
74
75 HOW TO WRITE A PAGE BREAKING ALGORITHM
76 All page breakers supported by this class work more-or-less in the same way.
77 First, they request a particular number of systems by saying
78 set_current_breakpoints (0, last_break_position (), system_count)
79 (never mind what the first two arguments do, I'll get to them later).
80 Alternatively, you can do
81 set_to_ideal_line_configuration (0, last_break_position ()),
82 and the number of systems will be automatically chosen according to what
83 the line breaker wants.
84
85 If there are multiple scores, there will be many different ways to achieve
86 a certain number of lines. You can see how many alternatives are available
87 with current_configuration_count (). For every i from 0 to
88 current_configuration_count ()-1, you can see the line division of the
89 corresponding configuration with current_configuration (i), or you can try
90 out various page configurations with one of the space_systems_xxx or
91 pack_systems_xxx functions. The first argument to each of these functions
92 is the configuration index.
93
94 When you're done trying out configurations and you've picked the one
95 you want, do
96 break_into_pieces (0, last_break_position (), line_division_that_you_want);
97 return make_pages (systems_per_page, systems ());
98 where systems_per_page is a vector of numbers telling how many systems are
99 on each page. You can get your systems_per_page vector by looking inside
100 the Page_spacing_results that are returned by space_systems_xxx or
101 pack_systems_xxx.
102
103 A note on performance: set_current_breakpoints is EXPONENTIALLY SLOW unless
104 you constrain it by giving it a lower or an upper bound on the configurations
105 it looks for. Optimal_page_breaking, for example, works by trying
106 out a bunch of configurations, increasing the system count by one, trying
107 again and so on. Each time we increase the system count, we assume that the
108 best new configurations are going to be elementwise larger than the
109 best configuration for the previous system count (in other words, we're going
110 to get a new configuration just by adding an extra line to sone score
111 and leaving the rest the same). Therefore, we pass the best previous line
112 division as an lower bound to set_current_breakpoints.
113
114 Now you should be in a position to understand Optimal_page_breaking::solve.
115 Go ahead and read that before finding out, in the next paragraph,
116 what the first two arguments to set_current_breakpoints do.
117
118 "BREAKS"
119 Sometimes, it's useful to run this whole page-breaking machinery on a subset
120 of the book. To do this, you can mark certain "breaks" in the book (a poor
121 choice of name, perhaps, since a "break" here is different from a page break)
122 and you can run page breaking between any two breaks. You mark your breaks
123 by providing a Break_predicate (and, if you want, a Prob_break_predicate)
124 to Page_breaking's constructor. You then choose a subset of your book
125 by passing the starting and ending breaks to set_current_breakpoints. You
126 can see an example of this in Page_turn_page_breaking, where there is a break
127 everywhere that a page turn is allowed.
128 */
129
130 #include "page-breaking.hh"
131
132 #include "international.hh"
133 #include "item.hh"
134 #include "line-interface.hh"
135 #include "output-def.hh"
136 #include "page-layout-problem.hh"
137 #include "page-spacing.hh"
138 #include "paper-book.hh"
139 #include "paper-column.hh"
140 #include "paper-score.hh"
141 #include "paper-system.hh"
142 #include "text-interface.hh"
143 #include "system.hh"
144 #include "warn.hh"
145
146 using std::pair;
147 using std::vector;
148
149 /* for each forbidden page break, merge the systems around it into one
150 system. */
151 static vector<Line_details>
compress_lines(const vector<Line_details> & orig)152 compress_lines (const vector<Line_details> &orig)
153 {
154 vector<Line_details> ret;
155
156 for (vsize i = 0; i < orig.size (); i++)
157 {
158 if (ret.size () && !scm_is_symbol (ret.back ().page_permission_))
159 {
160 Line_details const &old = ret.back ();
161 Line_details compressed = orig[i];
162 /*
163 We must account for the padding between the lines that we are compressing.
164 The padding values come from "old," which is the upper system here. Note
165 the meaning of tight-spacing: if a system has tight-spacing, then the padding
166 _before_ it is ignored.
167 */
168 Real padding = 0;
169 if (!orig[i].tight_spacing_)
170 padding = orig[i].title_ ? old.title_padding_ : old.padding_;
171
172 // FIXME: double check these. Doesn't foo.piggyback (bar) mean
173 // that foo goes on top?
174 // TODO: break out a Line_details::piggyback from here?
175 compressed.shape_ = old.shape_.piggyback (orig[i].shape_, padding);
176 compressed.refpoint_extent_[UP] = old.refpoint_extent_[UP];
177 compressed.refpoint_extent_[DOWN] += compressed.shape_.rest_[UP] - old.shape_.rest_[UP];
178 compressed.space_ += old.space_;
179 compressed.inverse_hooke_ += old.inverse_hooke_;
180
181 compressed.compressed_lines_count_ = old.compressed_lines_count_ + 1;
182 compressed.compressed_nontitle_lines_count_
183 = old.compressed_nontitle_lines_count_ + (compressed.title_ ? 0 : 1);
184
185 // compressed.title_ is true if and only if the first of its
186 // compressed lines was a title.
187 compressed.title_ = old.title_;
188
189 // adds footnotes of one line to the footnotes of another
190 compressed.footnote_heights_.insert (compressed.footnote_heights_.begin (),
191 old.footnote_heights_.begin (),
192 old.footnote_heights_.end ());
193 compressed.in_note_heights_.insert (compressed.in_note_heights_.begin (),
194 old.in_note_heights_.begin (),
195 old.in_note_heights_.end ());
196
197 ret.back () = compressed;
198 }
199 else
200 {
201 ret.push_back (orig[i]);
202 }
203 }
204 return ret;
205 }
206
207 /* translate the number of systems-per-page into something meaningful for
208 the uncompressed lines.
209 */
210 static vector<vsize>
uncompress_solution(vector<vsize> const & systems_per_page,vector<Line_details> const & compressed)211 uncompress_solution (vector<vsize> const &systems_per_page,
212 vector<Line_details> const &compressed)
213 {
214 vector<vsize> ret;
215 vsize start_sys = 0;
216
217 for (vsize i = 0; i < systems_per_page.size (); i++)
218 {
219 int compressed_count = 0;
220 for (vsize j = start_sys; j < start_sys + systems_per_page[i]; j++)
221 compressed_count += compressed[j].compressed_lines_count_ - 1;
222
223 ret.push_back (systems_per_page[i] + compressed_count);
224 start_sys += systems_per_page[i];
225 }
226 return ret;
227 }
228
229 /* for Page_breaking, the start index (when we are dealing with the stuff
230 between a pair of breakpoints) refers to the break_ index of the end of
231 the previous page. So the system index of the start of the current page
232 could either be the same as the end of the previous page or one more than
233 it. */
234
235 /* Turn a break index into the sys index that starts the next page */
236 vsize
next_system(Break_position const & break_pos) const237 Page_breaking::next_system (Break_position const &break_pos) const
238 {
239 vsize sys = break_pos.system_spec_index_;
240
241 if (sys == VPOS) /* beginning of the book */
242 return 0;
243 if (system_specs_[sys].pscore_ && !break_pos.score_ender_)
244 return sys; /* the score overflows the previous page */
245 return sys + 1; /* this page starts with a new System_spec */
246 }
247
Page_breaking(Paper_book * pb,Break_predicate is_break,Prob_break_predicate prob_break)248 Page_breaking::Page_breaking (Paper_book *pb, Break_predicate is_break, Prob_break_predicate prob_break)
249 {
250 book_ = pb;
251 system_count_ = 0;
252 paper_height_ = from_scm<double> (pb->paper_->c_variable ("paper-height"), 1.0);
253 ragged_ = from_scm<bool> (pb->paper_->c_variable ("ragged-bottom"));
254 ragged_last_ = from_scm<bool> (pb->paper_->c_variable ("ragged-last-bottom"));
255 systems_per_page_ = std::max (0, from_scm (pb->paper_->c_variable ("systems-per-page"), 0));
256 max_systems_per_page_ = std::max (0, from_scm (pb->paper_->c_variable ("max-systems-per-page"), 0));
257 min_systems_per_page_ = std::max (0, from_scm (pb->paper_->c_variable ("min-systems-per-page"), 0));
258 orphan_penalty_ = from_scm (pb->paper_->c_variable ("orphan-penalty"), 100000);
259
260 Stencil footnote_separator = Page_layout_problem::get_footnote_separator_stencil (pb->paper_);
261
262 if (!footnote_separator.is_empty ())
263 {
264 Interval separator_extent = footnote_separator.extent (Y_AXIS);
265 Real separator_span = separator_extent.length ();
266
267 footnote_separator_stencil_height_ = separator_span;
268 }
269 else
270 footnote_separator_stencil_height_ = 0.0;
271
272 footnote_padding_ = from_scm<double> (pb->paper_->c_variable ("footnote-padding"), 0.0);
273 in_note_padding_ = from_scm<double> (pb->paper_->c_variable ("in-note-padding"), 0.0);
274 footnote_footer_padding_ = from_scm<double> (pb->paper_->c_variable ("footnote-footer-padding"), 0.0);
275
276 footnote_number_raise_ = from_scm<double> (pb->paper_->c_variable ("footnote-number-raise"), 0.0);
277
278 if (systems_per_page_ && (max_systems_per_page_ || min_systems_per_page_))
279 {
280 warning (_f ("ignoring min-systems-per-page and max-systems-per-page because systems-per-page was set"));
281 min_systems_per_page_ = max_systems_per_page_ = 0;
282 }
283 if (max_systems_per_page_ && min_systems_per_page_ > max_systems_per_page_)
284 {
285 warning (_f ("min-systems-per-page is larger than max-systems-per-page, ignoring both values"));
286 min_systems_per_page_ = max_systems_per_page_ = 0;
287 }
288
289 create_system_list ();
290 find_chunks_and_breaks (is_break, prob_break);
291 }
292
~Page_breaking()293 Page_breaking::~Page_breaking ()
294 {
295 }
296
297 bool
ragged() const298 Page_breaking::ragged () const
299 {
300 return ragged_;
301 }
302
303 bool
ragged_last() const304 Page_breaking::ragged_last () const
305 {
306 return ragged_last_;
307 }
308
309 int
systems_per_page() const310 Page_breaking::systems_per_page () const
311 {
312 return systems_per_page_;
313 }
314
315 int
max_systems_per_page() const316 Page_breaking::max_systems_per_page () const
317 {
318 if (systems_per_page_)
319 return systems_per_page_;
320 return max_systems_per_page_;
321 }
322
323 int
min_systems_per_page() const324 Page_breaking::min_systems_per_page () const
325 {
326 if (systems_per_page_)
327 return systems_per_page_;
328 return min_systems_per_page_;
329 }
330
331 vsize
system_count() const332 Page_breaking::system_count () const
333 {
334 return system_count_;
335 }
336
337 Real
footnote_separator_stencil_height() const338 Page_breaking::footnote_separator_stencil_height () const
339 {
340 return footnote_separator_stencil_height_;
341 }
342
343 Real
in_note_padding() const344 Page_breaking::in_note_padding () const
345 {
346 return in_note_padding_;
347 }
348
349 Real
footnote_padding() const350 Page_breaking::footnote_padding () const
351 {
352 return footnote_padding_;
353 }
354
355 Real
footnote_footer_padding() const356 Page_breaking::footnote_footer_padding () const
357 {
358 return footnote_footer_padding_;
359 }
360
361 Real
footnote_number_raise() const362 Page_breaking::footnote_number_raise () const
363 {
364 return footnote_number_raise_;
365 }
366
367 bool
too_many_lines(int line_count) const368 Page_breaking::too_many_lines (int line_count) const
369 {
370 return max_systems_per_page () > 0 && line_count > max_systems_per_page ();
371 }
372
373 bool
too_few_lines(int line_count) const374 Page_breaking::too_few_lines (int line_count) const
375 {
376 return line_count < min_systems_per_page ();
377 }
378
379 Real
line_count_penalty(int line_count) const380 Page_breaking::line_count_penalty (int line_count) const
381 {
382 if (too_many_lines (line_count))
383 return (line_count - max_systems_per_page ()) * TERRIBLE_SPACING_PENALTY;
384 if (too_few_lines (line_count))
385 return (min_systems_per_page () - line_count) * TERRIBLE_SPACING_PENALTY;
386
387 return 0;
388 }
389
390 int
line_count_status(int line_count) const391 Page_breaking::line_count_status (int line_count) const
392 {
393 if (too_many_lines (line_count))
394 return SYSTEM_COUNT_TOO_MANY;
395 if (too_few_lines (line_count))
396 return SYSTEM_COUNT_TOO_FEW;
397
398 return SYSTEM_COUNT_OK;
399 }
400
401 /* translate indices into breaks_ into start-end parameters for the line breaker */
402 void
line_breaker_args(vsize sys,Break_position const & start,Break_position const & end,vsize * line_breaker_start,vsize * line_breaker_end)403 Page_breaking::line_breaker_args (vsize sys,
404 Break_position const &start,
405 Break_position const &end,
406 vsize *line_breaker_start,
407 vsize *line_breaker_end)
408 {
409 assert (system_specs_[sys].pscore_);
410 assert (next_system (start) <= sys && sys <= end.system_spec_index_);
411
412 if (start.system_spec_index_ == sys)
413 *line_breaker_start = start.score_break_;
414 else
415 *line_breaker_start = 0;
416
417 if (end.system_spec_index_ == sys)
418 *line_breaker_end = end.score_break_;
419 else
420 *line_breaker_end = VPOS;
421 }
422
423 void
break_into_pieces(vsize start_break,vsize end_break,Line_division const & div)424 Page_breaking::break_into_pieces (vsize start_break, vsize end_break,
425 Line_division const &div)
426 {
427 vector<Break_position> chunks = chunk_list (start_break, end_break);
428 bool ignore_div = false;
429 if (chunks.size () != div.size () + 1)
430 {
431 programming_error ("did not find a valid page breaking configuration");
432 ignore_div = true;
433 }
434
435 for (vsize i = 0; i + 1 < chunks.size (); i++)
436 {
437 vsize sys = next_system (chunks[i]);
438 if (system_specs_[sys].pscore_)
439 {
440 vsize start;
441 vsize end;
442 line_breaker_args (sys, chunks[i], chunks[i + 1], &start, &end);
443
444 vector<Column_x_positions> pos = ignore_div
445 ? line_breaking_[sys].best_solution (start, end)
446 : line_breaking_[sys].solve (start, end, div[i]);
447 system_specs_[sys].pscore_->root_system ()->break_into_pieces (pos);
448 }
449 }
450 }
451
452 SCM
systems()453 Page_breaking::systems ()
454 {
455 SCM ret = SCM_EOL;
456 for (vsize sys = 0; sys < system_specs_.size (); sys++)
457 {
458 if (system_specs_[sys].pscore_)
459 {
460 system_specs_[sys].pscore_->root_system ()
461 ->do_break_substitution_and_fixup_refpoints ();
462 SCM lines = system_specs_[sys].pscore_->root_system ()
463 ->get_broken_system_grobs ();
464 ret = scm_cons (lines, ret);
465 }
466 else if (Prob *pb = system_specs_[sys].prob_)
467 {
468 ret = scm_cons (scm_list_1 (pb->self_scm ()), ret);
469 pb->unprotect ();
470 }
471 }
472 return scm_append (scm_reverse_x (ret, SCM_EOL));
473 }
474
475 /* returns a Prob for the page */
476 SCM
make_page(int page_num,bool last) const477 Page_breaking::make_page (int page_num, bool last) const
478 {
479 bool last_part = ly_scm2bool (book_->paper_->c_variable ("is-last-bookpart"));
480 SCM mod = scm_c_resolve_module ("scm page");
481 SCM make_page_scm = scm_c_module_lookup (mod, "make-page");
482
483 make_page_scm = scm_variable_ref (make_page_scm);
484
485 return scm_apply_0 (make_page_scm,
486 scm_list_n (book_->self_scm (),
487 ly_symbol2scm ("page-number"), to_scm (page_num),
488 ly_symbol2scm ("is-last-bookpart"), scm_from_bool (last_part),
489 ly_symbol2scm ("is-bookpart-last-page"), scm_from_bool (last),
490 SCM_UNDEFINED));
491 }
492
493 // Returns the total height of the paper, including margins and
494 // space for the header/footer. This is an upper bound on
495 // page_height, and it doesn't depend on the current page.
496 Real
paper_height() const497 Page_breaking::paper_height () const
498 {
499 return paper_height_;
500 }
501
502 Real
page_height(int page_num,bool last) const503 Page_breaking::page_height (int page_num, bool last) const
504 {
505 // The caches allow us to store the page heights for any
506 // non-negative page numbers. We use a negative value in the
507 // cache to signal that that position has not yet been initialized.
508 // This means that we won't cache properly if page_num is negative or
509 // if calc_height returns a negative number. But that's likely to
510 // be rare, so it shouldn't affect performance.
511 vector<Real> &cache = last ? last_page_height_cache_ : page_height_cache_;
512 if (page_num >= 0 && (int) cache.size () > page_num && cache[page_num] >= 0)
513 return cache[page_num];
514 else
515 {
516 SCM mod = scm_c_resolve_module ("scm page");
517 SCM page = make_page (page_num, last);
518 SCM calc_height = scm_c_module_lookup (mod, "calc-printable-height");
519 calc_height = scm_variable_ref (calc_height);
520
521 SCM height_scm = scm_apply_1 (calc_height, page, SCM_EOL);
522 Real height = scm_to_double (height_scm);
523
524 if (page_num >= 0)
525 {
526 if ((int) cache.size () <= page_num)
527 cache.resize (page_num + 1, -1);
528 cache[page_num] = height;
529 }
530 return height;
531 }
532 }
533
534 SCM
breakpoint_property(vsize breakpoint,char const * str)535 Page_breaking::breakpoint_property (vsize breakpoint, char const *str)
536 {
537 Break_position const &pos = breaks_[breakpoint];
538
539 if (pos.system_spec_index_ == VPOS)
540 return SCM_EOL;
541 if (system_specs_[pos.system_spec_index_].pscore_)
542 return get_property (pos.col_, str);
543 return get_property (system_specs_[pos.system_spec_index_].prob_, str);
544 }
545
546 SCM
get_page_configuration(SCM systems,int page_num,bool ragged,bool last)547 Page_breaking::get_page_configuration (SCM systems, int page_num, bool ragged, bool last)
548 {
549 SCM dummy_page = make_page (page_num, last);
550 Page_layout_problem layout (book_, dummy_page, systems);
551 return scm_is_pair (systems) ? layout.solution (ragged) : SCM_EOL;
552 }
553
554 /* Return a Prob as SCM value encompassing the given systems. */
555 SCM
draw_page(SCM systems,SCM configuration,int page_num,bool last)556 Page_breaking::draw_page (SCM systems, SCM configuration, int page_num, bool last)
557 {
558 // Create a stencil for each system.
559 SCM paper_systems = SCM_EOL;
560 for (SCM s = systems; scm_is_pair (s); s = scm_cdr (s))
561 {
562 SCM paper_system = scm_car (s);
563 if (Grob *g = unsmob<Grob> (scm_car (s)))
564 {
565 System *sys = dynamic_cast<System *> (g);
566 paper_system = sys->get_paper_system ();
567 }
568
569 paper_systems = scm_cons (paper_system, paper_systems);
570 }
571 paper_systems = scm_reverse_x (paper_systems, SCM_EOL);
572
573 // Create the page and draw it.
574 SCM page = make_page (page_num, last);
575
576 Prob *p = unsmob<Prob> (page);
577 set_property (p, "lines", paper_systems);
578 set_property (p, "configuration", configuration);
579
580 Stencil *foot_p = unsmob<Stencil> (get_property (p, "foot-stencil"));
581 Stencil foot = foot_p ? *foot_p : Stencil ();
582
583 SCM footnotes = Page_layout_problem::get_footnotes_from_lines (systems);
584
585 foot = Page_layout_problem::add_footnotes_to_footer (footnotes, foot, book_);
586
587 set_property (p, "foot-stencil", foot.smobbed_copy ());
588
589 return page;
590 }
591
592 SCM
make_pages(const vector<vsize> & lines_per_page,SCM systems)593 Page_breaking::make_pages (const vector<vsize> &lines_per_page, SCM systems)
594 {
595 if (scm_is_null (systems))
596 return SCM_EOL;
597
598 int first_page_number
599 = from_scm (book_->paper_->c_variable ("first-page-number"), 1);
600 SCM ret = SCM_EOL;
601 bool reset_footnotes_on_new_page = from_scm<bool> (book_->top_paper ()->c_variable ("reset-footnotes-on-new-page"));
602 SCM label_page_table = book_->top_paper ()->c_variable ("label-page-table");
603 if (SCM_UNBNDP (label_page_table))
604 label_page_table = SCM_EOL;
605
606 // Build a list of (systems configuration . footnote-count) triples.
607 // Note that we lay out the staves and find the configurations,
608 // but we do not draw anything in this function. It is important
609 // that all staves are laid out vertically before any are drawn; some
610 // grobs (like tuplet brackets) look at their neighbours while drawing
611 // themselves. If this happens before the neighbouring staves have
612 // been laid out, bad side-effects could happen (in particular,
613 // Align_interface::align_to_ideal_distances might be called).
614 SCM systems_configs_fncounts = SCM_EOL;
615 vsize footnote_count = 0;
616 Real last_page_force = 0;
617
618 const vsize page_count = lines_per_page.size ();
619 for (vsize i = 0; i < page_count; i++)
620 {
621 int page_num = first_page_number + static_cast<int> (i);
622 bool bookpart_last_page = (i == page_count - 1);
623 bool rag = ragged () || (bookpart_last_page && ragged_last ());
624 SCM line_count = to_scm (lines_per_page[i]);
625 SCM lines = scm_list_head (systems, line_count);
626
627 int rank_on_page = 0;
628 for (SCM line = lines; scm_is_pair (line); line = scm_cdr (line))
629 {
630 System *sys = unsmob<System> (scm_car (line));
631 if (sys)
632 {
633 set_property (sys, "rank-on-page", to_scm (rank_on_page));
634 set_property (sys, "page-number", to_scm (page_num));
635 rank_on_page++;
636 }
637 }
638
639 vsize fn_lines = Page_layout_problem::get_footnote_count (lines);
640 Page_layout_problem::add_footnotes_to_lines (lines, reset_footnotes_on_new_page ? 0 : footnote_count, book_);
641
642 SCM config = SCM_EOL;
643 SCM dummy_page = make_page (page_num, bookpart_last_page);
644 Page_layout_problem layout (book_, dummy_page, lines);
645 if (!scm_is_pair (systems))
646 config = SCM_EOL;
647 else if (rag && !ragged ())
648 // If we're ragged-last but not ragged, make the last page
649 // have the same force as the previous page.
650 config = layout.fixed_force_solution (last_page_force);
651 else
652 config = layout.solution (rag);
653
654 if ((ragged () && layout.force () < 0.0)
655 || std::isinf (layout.force ()))
656 warning (_f ("page %d has been compressed", page_num));
657 else
658 last_page_force = layout.force ();
659
660 systems_configs_fncounts = scm_cons (scm_cons (lines, config), systems_configs_fncounts);
661 footnote_count += fn_lines;
662 systems = scm_list_tail (systems, line_count);
663 }
664
665 // TODO: previously, the following loop caused the systems to be
666 // drawn. Now that we no longer draw anything in Page_breaking,
667 // it is safe to merge these two loops.
668 int page_num = first_page_number + static_cast<int> (page_count) - 1;
669 for (SCM s = systems_configs_fncounts; scm_is_pair (s); s = scm_cdr (s))
670 {
671 SCM lines = scm_caar (s);
672 SCM config = scm_cdar (s);
673
674 bool bookpart_last_page = scm_is_eq (s, systems_configs_fncounts);
675 SCM page = draw_page (lines, config, page_num, bookpart_last_page);
676 /* collect labels */
677 SCM page_num_scm = to_scm (page_num);
678 for (SCM l = lines; scm_is_pair (l); l = scm_cdr (l))
679 {
680 SCM labels = SCM_EOL;
681 if (Grob *line = unsmob<Grob> (scm_car (l)))
682 {
683 System *system = dynamic_cast<System *> (line);
684 labels = get_property (system, "labels");
685 }
686 else if (Prob *prob = unsmob<Prob> (scm_car (l)))
687 labels = get_property (prob, "labels");
688
689 for (SCM lbls = labels; scm_is_pair (lbls); lbls = scm_cdr (lbls))
690 label_page_table = scm_cons (scm_cons (scm_car (lbls), page_num_scm),
691 label_page_table);
692 }
693
694 ret = scm_cons (page, ret);
695 --page_num;
696 }
697
698 // By reversing the table, we ensure that duplicated labels (eg. those
699 // straddling a page turn) will appear in the table with their last
700 // occurence first.
701 label_page_table = scm_reverse_x (label_page_table, SCM_EOL);
702 book_->top_paper ()->set_variable (ly_symbol2scm ("label-page-table"), label_page_table);
703 return ret;
704 }
705
706 void
create_system_list()707 Page_breaking::create_system_list ()
708 {
709 SCM specs = book_->get_system_specs ();
710 for (SCM s = specs; scm_is_pair (s); s = scm_cdr (s))
711 {
712 if (Paper_score *ps = unsmob<Paper_score> (scm_car (s)))
713 {
714 system_specs_.push_back (System_spec (ps));
715 }
716 else
717 {
718 Prob *pb = unsmob<Prob> (scm_car (s));
719 assert (pb);
720
721 pb->protect ();
722 system_specs_.push_back (System_spec (pb));
723 }
724 }
725 if (!system_specs_.size ())
726 system_specs_.push_back (System_spec ());
727 }
728
729 /* The page-turn-page-breaker needs to have a line-breaker between any two
730 columns with non-NULL page-turn-permission.
731
732 The optimal-breaker needs to have a line-breaker between any two columns
733 with page-break-permission = 'force.
734
735 By using a grob predicate, we can accommodate both of these uses.
736
737 is_break indicates if the column is an allowed page turn.
738 */
739 void
find_chunks_and_breaks(Break_predicate is_break,Prob_break_predicate prob_is_break)740 Page_breaking::find_chunks_and_breaks (Break_predicate is_break, Prob_break_predicate prob_is_break)
741 {
742 SCM force_sym = ly_symbol2scm ("force");
743
744 chunks_.push_back (Break_position ());
745 breaks_.push_back (Break_position ());
746
747 for (vsize i = 0; i < system_specs_.size (); i++)
748 {
749 if (system_specs_[i].pscore_)
750 {
751 vector<Paper_column *> cols = system_specs_[i].pscore_->root_system ()->used_columns ();
752 vector<Paper_column *> forced_line_break_cols;
753
754 SCM system_count = system_specs_[i].pscore_->layout ()->c_variable ("system-count");
755 if (scm_is_number (system_count))
756 {
757 // With system-count given, the line configuration for
758 // this score is fixed. We need to ensure that chunk
759 // boundaries only occur at line breaks.
760 Constrained_breaking breaking (system_specs_[i].pscore_);
761 vector<Line_details> details = breaking.line_details (0, VPOS, scm_to_int (system_count));
762
763 for (vsize j = 0; j < details.size (); j++)
764 forced_line_break_cols.push_back (details[j].last_column_);
765 }
766
767 vsize last_forced_line_break_idx = 0;
768 vsize forced_line_break_idx = 0;
769 vector<vsize> line_breaker_columns;
770 line_breaker_columns.push_back (0);
771
772 for (vsize j = 0; j < cols.size (); j++)
773 {
774 if (forced_line_break_cols.size ())
775 {
776 if (forced_line_break_idx >= forced_line_break_cols.size ()
777 || forced_line_break_cols[forced_line_break_idx] != cols[j])
778 continue;
779 else
780 forced_line_break_idx++;
781 }
782
783 bool last = (j == cols.size () - 1);
784 bool break_point = is_break && j > 0 && is_break (cols[j]);
785 bool chunk_end = scm_is_eq (get_property (cols[j], "page-break-permission"), force_sym);
786 Break_position cur_pos = Break_position (i,
787 line_breaker_columns.size (),
788 cols[j],
789 last);
790
791 // NOTE: even in the breaks_ list, forced_line_count_
792 // refers to the number of lines between a
793 // Break_position and the start of that /chunk/. This
794 // is needed for system_count_bounds to work correctly,
795 // since it mixes Break_positions from breaks_ and
796 // chunks_.
797 if (scm_is_number (system_count))
798 cur_pos.forced_line_count_ = forced_line_break_idx - last_forced_line_break_idx;
799
800 if (break_point || (i == system_specs_.size () - 1 && last))
801 breaks_.push_back (cur_pos);
802 if (chunk_end || last)
803 {
804 chunks_.push_back (cur_pos);
805 last_forced_line_break_idx = forced_line_break_idx;
806 }
807
808 if ((break_point || chunk_end) && !last)
809 line_breaker_columns.push_back (j);
810 }
811 line_breaking_.push_back (Constrained_breaking (system_specs_[i].pscore_, line_breaker_columns));
812 }
813 else if (system_specs_[i].prob_)
814 {
815 bool break_point = prob_is_break && prob_is_break (system_specs_[i].prob_);
816 if (break_point || i == system_specs_.size () - 1)
817 breaks_.push_back (Break_position (i));
818
819 chunks_.push_back (Break_position (i));
820
821 /* FIXME: shouldn't we push a Null_breaker or similar dummy
822 class? --hwn */
823 line_breaking_.push_back (Constrained_breaking (NULL));
824 }
825 }
826 }
827
828 vector<Break_position>
chunk_list(vsize start_index,vsize end_index) const829 Page_breaking::chunk_list (vsize start_index, vsize end_index) const
830 {
831 Break_position start = breaks_[start_index];
832 Break_position end = breaks_[end_index];
833
834 // TODO: could use binary search.
835 vsize i = 0;
836 for (; i < chunks_.size () && chunks_[i] <= start; i++)
837 ;
838
839 vector<Break_position> ret;
840 ret.push_back (start);
841 for (; i < chunks_.size () && chunks_[i] < end; i++)
842 ret.push_back (chunks_[i]);
843 ret.push_back (end);
844 return ret;
845 }
846
847 // Returns the minimum number of _non-title_ lines.
848 vsize
min_system_count(vsize start,vsize end)849 Page_breaking::min_system_count (vsize start, vsize end)
850 {
851 vector<Break_position> chunks = chunk_list (start, end);
852 Line_division div = system_count_bounds (chunks, true);
853 vsize ret = 0;
854
855 for (vsize i = 0; i < div.size (); i++)
856 ret += div[i];
857 return ret;
858 }
859
860 // Returns the maximum number of _non-title_ lines.
861 vsize
max_system_count(vsize start,vsize end)862 Page_breaking::max_system_count (vsize start, vsize end)
863 {
864 vector<Break_position> chunks = chunk_list (start, end);
865 Line_division div = system_count_bounds (chunks, false);
866 vsize ret = 0;
867
868 for (vsize i = 0; i < div.size (); i++)
869 ret += div[i];
870 return ret;
871 }
872
873 // The numbers returned by this function represent either
874 // the maximum or minimum number of _non-title_ lines
875 // per chunk.
876 Page_breaking::Line_division
system_count_bounds(vector<Break_position> const & chunks,bool min)877 Page_breaking::system_count_bounds (vector<Break_position> const &chunks,
878 bool min)
879 {
880 assert (chunks.size () >= 2);
881
882 Line_division ret;
883 ret.resize (chunks.size () - 1, 0);
884
885 for (vsize i = 0; i + 1 < chunks.size (); i++)
886 {
887 vsize sys = next_system (chunks[i]);
888
889 if (chunks[i + 1].forced_line_count_)
890 ret[i] = chunks[i + 1].forced_line_count_;
891 else if (system_specs_[sys].pscore_)
892 {
893 vsize start;
894 vsize end;
895 line_breaker_args (sys, chunks[i], chunks[i + 1], &start, &end);
896 ret[i] = min
897 ? line_breaking_[sys].min_system_count (start, end)
898 : line_breaking_[sys].max_system_count (start, end);
899 }
900 }
901
902 return ret;
903 }
904
905 void
set_current_breakpoints(vsize start,vsize end,vsize system_count,Line_division lower_bound,Line_division upper_bound)906 Page_breaking::set_current_breakpoints (vsize start,
907 vsize end,
908 vsize system_count,
909 Line_division lower_bound,
910 Line_division upper_bound)
911 {
912 system_count_ = system_count;
913 current_chunks_ = chunk_list (start, end);
914 current_start_breakpoint_ = start;
915 current_end_breakpoint_ = end;
916 clear_line_details_cache ();
917
918 if (!lower_bound.size ())
919 lower_bound = system_count_bounds (current_chunks_, true);
920 if (!upper_bound.size ())
921 upper_bound = system_count_bounds (current_chunks_, false);
922
923 assert (lower_bound.size () == current_chunks_.size () - 1);
924 assert (upper_bound.size () == current_chunks_.size () - 1);
925
926 Line_division work_in_progress;
927 current_configurations_.clear ();
928 line_divisions_rec (system_count,
929 lower_bound,
930 upper_bound,
931 &work_in_progress);
932
933 /* we only consider a constant number of configurations. Otherwise,
934 this becomes slow when there are many small scores. The constant
935 5 is somewhat arbitrary. */
936 if (current_configurations_.size () > 5)
937 {
938 vector<pair<Real, vsize> > dems_and_indices;
939
940 for (vsize i = 0; i < current_configurations_.size (); i++)
941 {
942 cache_line_details (i);
943 Real dem = 0;
944 for (vsize j = 0; j < cached_line_details_.size (); j++)
945 dem += cached_line_details_[j].force_ * cached_line_details_[j].force_
946 + cached_line_details_[j].break_penalty_;
947
948 dems_and_indices.push_back (pair<Real, vsize> (dem, i));
949 }
950 vector_sort (dems_and_indices, std::less<pair<Real, vsize> > ());
951
952 vector<Line_division> best_5_configurations;
953 for (vsize i = 0; i < 5; i++)
954 best_5_configurations.push_back (current_configurations_[dems_and_indices[i].second]);
955
956 clear_line_details_cache ();
957 current_configurations_ = best_5_configurations;
958 }
959 }
960
961 void
set_to_ideal_line_configuration(vsize start,vsize end)962 Page_breaking::set_to_ideal_line_configuration (vsize start, vsize end)
963 {
964 current_chunks_ = chunk_list (start, end);
965 current_start_breakpoint_ = start;
966 current_end_breakpoint_ = end;
967 clear_line_details_cache ();
968 system_count_ = 0;
969
970 Line_division div;
971 for (vsize i = 0; i + 1 < current_chunks_.size (); i++)
972 {
973 vsize sys = next_system (current_chunks_[i]);
974
975 if (current_chunks_[i + 1].forced_line_count_)
976 div.push_back (current_chunks_[i + 1].forced_line_count_);
977 else if (system_specs_[sys].pscore_)
978 {
979 line_breaker_args (sys, current_chunks_[i], current_chunks_[i + 1], &start, &end);
980 div.push_back (line_breaking_[sys].best_solution (start, end).size ());
981 }
982 else
983 div.push_back (0);
984
985 system_count_ += div.back ();
986 }
987 current_configurations_.clear ();
988 current_configurations_.push_back (div);
989 }
990
991 vsize
current_configuration_count() const992 Page_breaking::current_configuration_count () const
993 {
994 return current_configurations_.size ();
995 }
996
997 void
cache_line_details(vsize configuration_index)998 Page_breaking::cache_line_details (vsize configuration_index)
999 {
1000 if (cached_configuration_index_ != configuration_index)
1001 {
1002 cached_configuration_index_ = configuration_index;
1003
1004 Line_division &div = current_configurations_[configuration_index];
1005 uncompressed_line_details_.clear ();
1006 for (vsize i = 0; i + 1 < current_chunks_.size (); i++)
1007 {
1008 vsize sys = next_system (current_chunks_[i]);
1009 if (system_specs_[sys].pscore_)
1010 {
1011 vsize start;
1012 vsize end;
1013 line_breaker_args (sys, current_chunks_[i], current_chunks_[i + 1], &start, &end);
1014
1015 vector<Line_details> details = line_breaking_[sys].line_details (start, end, div[i]);
1016 uncompressed_line_details_.insert (uncompressed_line_details_.end (), details.begin (), details.end ());
1017 }
1018 else
1019 {
1020 assert (div[i] == 0);
1021 uncompressed_line_details_.push_back (system_specs_[sys].prob_
1022 ? Line_details (system_specs_[sys].prob_, book_->paper_)
1023 : Line_details ());
1024 }
1025 }
1026 cached_line_details_ = compress_lines (uncompressed_line_details_);
1027 calc_line_heights ();
1028 }
1029 }
1030
1031 void
clear_line_details_cache()1032 Page_breaking::clear_line_details_cache ()
1033 {
1034 cached_configuration_index_ = VPOS;
1035 cached_line_details_.clear ();
1036 uncompressed_line_details_.clear ();
1037 }
1038
1039 void
line_divisions_rec(vsize system_count,Line_division const & min_sys,Line_division const & max_sys,Line_division * cur_division)1040 Page_breaking::line_divisions_rec (vsize system_count,
1041 Line_division const &min_sys,
1042 Line_division const &max_sys,
1043 Line_division *cur_division)
1044 {
1045 vsize my_index = cur_division->size ();
1046 vsize others_min = 0;
1047 vsize others_max = 0;
1048
1049 for (vsize i = my_index + 1; i < min_sys.size (); i++)
1050 {
1051 others_min += min_sys[i];
1052 others_max += max_sys[i];
1053 }
1054 others_max = std::min (others_max, system_count);
1055 vsize real_min = std::max (min_sys[my_index], system_count - others_max);
1056
1057 if (system_count < others_min)
1058 {
1059 /* this should never happen within a recursive call. If it happens
1060 at all, it means that we were called with an unsolvable problem
1061 and we should return an empty result */
1062 assert (my_index == 0);
1063 return;
1064 }
1065
1066 vsize real_max = std::min (max_sys[my_index], system_count - others_min);
1067
1068 if (real_min > real_max)
1069 {
1070 /* this should never happen within a recursive call. If it happens
1071 at all, it means that we were called with an unsolvable problem
1072 and we should return an empty result */
1073 assert (my_index == 0);
1074 return;
1075 }
1076
1077 for (vsize i = real_min; i <= real_max; i++)
1078 {
1079 cur_division->push_back (i);
1080 if (my_index == min_sys.size () - 1)
1081 current_configurations_.push_back (*cur_division);
1082 else
1083 line_divisions_rec (system_count - i, min_sys, max_sys, cur_division);
1084 cur_division->pop_back ();
1085 }
1086 }
1087
1088 void
calc_line_heights()1089 Page_breaking::calc_line_heights ()
1090 {
1091 Real prev_hanging = 0;
1092 Real prev_hanging_begin = 0;
1093 Real prev_hanging_rest = 0;
1094
1095 // refpoint_hanging is the y coordinate of the origin of this system.
1096 // It may not be the same as refpoint_extent[UP], which is the
1097 // refpoint of the first spaceable staff in this system.
1098 Real prev_refpoint_hanging = 0;
1099 for (vsize i = 0; i < cached_line_details_.size (); i++)
1100 {
1101 Line_details &cur = cached_line_details_[i];
1102 Line_shape shape = cur.shape_;
1103 Real a = shape.begin_[UP];
1104 Real b = shape.rest_[UP];
1105 bool title = cur.title_;
1106 Real refpoint_hanging = std::max (prev_hanging_begin + a, prev_hanging_rest + b);
1107
1108 if (i > 0)
1109 {
1110 Real padding = 0;
1111 Line_details const &prev = cached_line_details_[i - 1];
1112 if (!cur.tight_spacing_)
1113 padding = title
1114 ? prev.title_padding_
1115 : prev.padding_;
1116 Real min_dist = title
1117 ? prev.title_min_distance_
1118 : prev.min_distance_;
1119 refpoint_hanging = std::max (refpoint_hanging + padding,
1120 prev_refpoint_hanging - prev.refpoint_extent_[DOWN]
1121 + cur.refpoint_extent_[UP] + min_dist);
1122 }
1123
1124 Real hanging_begin = refpoint_hanging - shape.begin_[DOWN];
1125 Real hanging_rest = refpoint_hanging - shape.rest_[DOWN];
1126 Real hanging = std::max (hanging_begin, hanging_rest);
1127 cur.tallness_ = hanging - prev_hanging;
1128 prev_hanging = hanging;
1129 prev_hanging_begin = hanging_begin;
1130 prev_hanging_rest = hanging_rest;
1131 prev_refpoint_hanging = refpoint_hanging;
1132 }
1133 }
1134
1135 vsize
min_page_count(vsize configuration,int first_page_num)1136 Page_breaking::min_page_count (vsize configuration, int first_page_num)
1137 {
1138 int ret = 1;
1139 vsize page_starter = 0;
1140 Real cur_rod_height = 0;
1141 Real cur_spring_height = 0;
1142 Real cur_page_height = page_height (first_page_num, false);
1143 int line_count = 0;
1144
1145 cache_line_details (configuration);
1146
1147 if (cached_line_details_.size ())
1148 cur_page_height -= min_whitespace_at_top_of_page (cached_line_details_[0]);
1149
1150 for (vsize i = 0; i < cached_line_details_.size (); i++)
1151 {
1152 Line_details const &cur = cached_line_details_[i];
1153 Line_details const *const prev = (i > 0) ? &cached_line_details_[i - 1] : 0;
1154 Real ext_len;
1155 if (cur_rod_height > 0)
1156 ext_len = cur.tallness_;
1157 else
1158 ext_len = cur.full_height ();
1159
1160 Real spring_len = (i > 0) ? prev->spring_length (cur) : 0;
1161 Real next_rod_height = cur_rod_height + ext_len;
1162 Real next_spring_height = cur_spring_height + spring_len;
1163 Real next_height = next_rod_height + (ragged () ? next_spring_height : 0)
1164 + min_whitespace_at_bottom_of_page (cur);
1165 int next_line_count = line_count + cur.compressed_nontitle_lines_count_;
1166
1167 if ((!too_few_lines (line_count) && (next_height > cur_page_height && cur_rod_height > 0))
1168 || too_many_lines (next_line_count)
1169 || (prev && scm_is_eq (prev->page_permission_, ly_symbol2scm ("force"))))
1170 {
1171 line_count = cur.compressed_nontitle_lines_count_;
1172 cur_rod_height = cur.full_height ();
1173 cur_spring_height = 0;
1174 page_starter = i;
1175
1176 cur_page_height = page_height (first_page_num + ret, false);
1177 cur_page_height -= min_whitespace_at_top_of_page (cur);
1178
1179 ret++;
1180 }
1181 else
1182 {
1183 cur_rod_height = next_rod_height;
1184 cur_spring_height = next_spring_height;
1185 line_count = next_line_count;
1186 }
1187 }
1188
1189 /* there are two potential problems with the last page (because we didn't know
1190 it was the last page until after we managed to fit all the systems to it):
1191 - we are ragged-last but the last page has a compressed spring
1192 - the value returned by page_height (num, true) is smaller than the
1193 value returned by page_height (num, false) and it causes the page not to
1194 fit.
1195
1196 In either case, we just need to add one more page. This is because the last
1197 line will always fit on the extra page and by adding one more page to the
1198 end, the previous page is no longer the last page, so our previous
1199 calculations that treated it as a non-last page were ok.
1200 */
1201
1202 if (is_last ())
1203 {
1204 cur_page_height = page_height (first_page_num + ret - 1, true);
1205 cur_page_height -= min_whitespace_at_top_of_page (cached_line_details_[page_starter]);
1206 cur_page_height -= min_whitespace_at_bottom_of_page (cached_line_details_.back ());
1207
1208 if (!too_few_lines (line_count - cached_line_details_.back ().compressed_nontitle_lines_count_)
1209 && cur_rod_height > cur_page_height
1210 /* don't increase the page count if the last page had only one system */
1211 && cur_rod_height > cached_line_details_.back ().full_height ())
1212 ret++;
1213 assert (static_cast<vsize> (ret) <= cached_line_details_.size ());
1214 }
1215
1216 return ret;
1217 }
1218
1219 // If systems_per_page_ is positive, we don't really try to space on N pages;
1220 // we just put the requested number of systems on each page and penalize
1221 // if the result doesn't have N pages.
1222 Page_spacing_result
space_systems_on_n_pages(vsize configuration,vsize n,int first_page_num)1223 Page_breaking::space_systems_on_n_pages (vsize configuration, vsize n, int first_page_num)
1224 {
1225 Page_spacing_result ret;
1226
1227 if (systems_per_page_ > 0)
1228 {
1229 Page_spacing_result ret = space_systems_with_fixed_number_per_page (configuration, first_page_num);
1230 ret.demerits_ += (ret.force_.size () == n) ? 0 : BAD_SPACING_PENALTY;
1231 return ret;
1232 }
1233
1234 cache_line_details (configuration);
1235 bool valid_n = (n >= min_page_count (configuration, first_page_num)
1236 && n <= cached_line_details_.size ());
1237
1238 if (!valid_n)
1239 programming_error ("number of pages is out of bounds");
1240
1241 if (n == 1 && valid_n)
1242 ret = space_systems_on_1_page (cached_line_details_,
1243 page_height (first_page_num, is_last ()),
1244 ragged () || (is_last () && ragged_last ()));
1245 else if (n == 2 && valid_n)
1246 ret = space_systems_on_2_pages (configuration, first_page_num);
1247 else
1248 {
1249 Page_spacer ps (cached_line_details_, first_page_num, this);
1250 ret = ps.solve (n);
1251 }
1252
1253 return finalize_spacing_result (configuration, ret);
1254 }
1255
1256 Real
blank_page_penalty() const1257 Page_breaking::blank_page_penalty () const
1258 {
1259 SCM penalty_sym;
1260
1261 if (is_last ())
1262 penalty_sym = ly_symbol2scm ("blank-last-page-penalty");
1263 else if (ends_score ())
1264 penalty_sym = ly_symbol2scm ("blank-after-score-page-penalty");
1265 else
1266 penalty_sym = ly_symbol2scm ("blank-page-penalty");
1267
1268 Break_position const &pos = breaks_[current_end_breakpoint_];
1269 if (Paper_score *ps = system_specs_[pos.system_spec_index_].pscore_)
1270 return from_scm<double> (ps->layout ()->lookup_variable (penalty_sym), 0.0);
1271
1272 return from_scm<double> (book_->paper_->lookup_variable (penalty_sym), 0.0);
1273 }
1274
1275 // If systems_per_page_ is positive, we don't really try to space on N
1276 // or N+1 pages; see the comment to space_systems_on_n_pages.
1277 Page_spacing_result
space_systems_on_n_or_one_more_pages(vsize configuration,vsize n,int first_page_num,Real penalty_for_fewer_pages)1278 Page_breaking::space_systems_on_n_or_one_more_pages (vsize configuration, vsize n, int first_page_num,
1279 Real penalty_for_fewer_pages)
1280 {
1281 Page_spacing_result n_res;
1282 Page_spacing_result m_res;
1283
1284 if (systems_per_page_ > 0)
1285 {
1286 Page_spacing_result ret = space_systems_with_fixed_number_per_page (configuration, first_page_num);
1287 ret.demerits_ += (ret.force_.size () == n || ret.force_.size () == (n - 1)) ? 0 : BAD_SPACING_PENALTY;
1288 return ret;
1289 }
1290
1291 cache_line_details (configuration);
1292 vsize min_p_count = min_page_count (configuration, first_page_num);
1293 bool valid_n = n >= min_p_count || n <= cached_line_details_.size ();
1294
1295 if (!valid_n)
1296 programming_error ("both page counts are out of bounds");
1297
1298 if (n == 1 && valid_n)
1299 {
1300 bool rag = ragged () || (is_last () && ragged_last ());
1301 Real height = page_height (first_page_num, is_last ());
1302
1303 if (1 >= min_p_count)
1304 n_res = space_systems_on_1_page (cached_line_details_, height, rag);
1305 if (1 < cached_line_details_.size ())
1306 m_res = space_systems_on_2_pages (configuration, first_page_num);
1307 }
1308 else
1309 {
1310 Page_spacer ps (cached_line_details_, first_page_num, this);
1311
1312 if (n >= min_p_count || !valid_n)
1313 n_res = ps.solve (n);
1314 if (n < cached_line_details_.size () || !valid_n)
1315 m_res = ps.solve (n + 1);
1316 }
1317
1318 m_res = finalize_spacing_result (configuration, m_res);
1319 n_res = finalize_spacing_result (configuration, n_res);
1320
1321 Real page_spacing_weight = from_scm<double> (book_->paper_->c_variable ("page-spacing-weight"), 10);
1322 n_res.demerits_ += penalty_for_fewer_pages * page_spacing_weight;
1323
1324 if (n_res.force_.size ())
1325 n_res.force_.back () += penalty_for_fewer_pages;
1326
1327 return (m_res.demerits_ < n_res.demerits_) ? m_res : n_res;
1328 }
1329
1330 Page_spacing_result
space_systems_on_best_pages(vsize configuration,int first_page_num)1331 Page_breaking::space_systems_on_best_pages (vsize configuration, int first_page_num)
1332 {
1333 if (systems_per_page_ > 0)
1334 return space_systems_with_fixed_number_per_page (configuration, first_page_num);
1335
1336 cache_line_details (configuration);
1337 Page_spacer ps (cached_line_details_, first_page_num, this);
1338
1339 return finalize_spacing_result (configuration, ps.solve ());
1340 }
1341
1342 Page_spacing_result
space_systems_with_fixed_number_per_page(vsize configuration,int first_page_num)1343 Page_breaking::space_systems_with_fixed_number_per_page (vsize configuration,
1344 int first_page_num)
1345 {
1346 Page_spacing_result res;
1347 Page_spacing space (page_height (first_page_num, false), this);
1348 vsize line = 0;
1349 int page_num = first_page_num;
1350 vsize page_first_line = 0;
1351
1352 cache_line_details (configuration);
1353 while (line < cached_line_details_.size ())
1354 {
1355 page_num++;
1356 space.clear ();
1357 space.resize (page_height (page_num, false));
1358
1359 int system_count_on_this_page = 0;
1360 while (system_count_on_this_page < systems_per_page_
1361 && line < cached_line_details_.size ())
1362 {
1363 Line_details const &cur_line = cached_line_details_[line];
1364 space.append_system (cur_line);
1365 system_count_on_this_page += cur_line.compressed_nontitle_lines_count_;
1366 line++;
1367
1368 if (scm_is_eq (cur_line.page_permission_, ly_symbol2scm ("force")))
1369 break;
1370 }
1371
1372 res.systems_per_page_.push_back (line - page_first_line);
1373
1374 res.force_.push_back (space.force_);
1375 res.penalty_ += cached_line_details_[line - 1].page_penalty_;
1376 if (system_count_on_this_page != systems_per_page_)
1377 {
1378 res.penalty_ += abs (system_count_on_this_page - systems_per_page_) * TERRIBLE_SPACING_PENALTY;
1379 res.system_count_status_ |= ((system_count_on_this_page < systems_per_page_))
1380 ? SYSTEM_COUNT_TOO_FEW : SYSTEM_COUNT_TOO_MANY;
1381 }
1382
1383 page_first_line = line;
1384 }
1385
1386 /* Recalculate forces for the last page because we know now that is
1387 really the last page. */
1388 space.resize (page_height (page_num, true));
1389 res.force_.back () = space.force_;
1390 return finalize_spacing_result (configuration, res);
1391 }
1392
1393 Page_spacing_result
pack_systems_on_least_pages(vsize configuration,int first_page_num)1394 Page_breaking::pack_systems_on_least_pages (vsize configuration, int first_page_num)
1395 {
1396 // TODO: add support for min/max-systems-per-page.
1397 Page_spacing_result res;
1398 int page_num = first_page_num;
1399 vsize page_first_line = 0;
1400 Page_spacing space (page_height (page_num, false), this);
1401
1402 cache_line_details (configuration);
1403 for (vsize line = 0; line < cached_line_details_.size (); line++)
1404 {
1405 Real prev_force = space.force_;
1406 space.append_system (cached_line_details_[line]);
1407 if ((line > page_first_line)
1408 && (std::isinf (space.force_)
1409 || ((line > 0)
1410 && scm_is_eq (cached_line_details_[line - 1].page_permission_,
1411 ly_symbol2scm ("force")))))
1412 {
1413 res.systems_per_page_.push_back (line - page_first_line);
1414 res.force_.push_back (prev_force);
1415 res.penalty_ += cached_line_details_[line - 1].page_penalty_;
1416 page_num++;
1417 space.resize (page_height (page_num, false));
1418 space.clear ();
1419 space.append_system (cached_line_details_[line]);
1420 page_first_line = line;
1421 }
1422
1423 if (line == cached_line_details_.size () - 1)
1424 {
1425 /* This is the last line */
1426 /* When the last page height was computed, we did not know yet that it
1427 * was the last one. If the systems put on it don't fit anymore, the last
1428 * system is moved to a new page */
1429 space.resize (page_height (page_num, true));
1430 if ((line > page_first_line) && (std::isinf (space.force_)))
1431 {
1432 res.systems_per_page_.push_back (line - page_first_line);
1433 res.force_.push_back (prev_force);
1434 /* the last page containing the last line */
1435 space.resize (page_height (page_num + 1, true));
1436 space.clear ();
1437 space.append_system (cached_line_details_[line]);
1438 res.systems_per_page_.push_back (1);
1439 res.force_.push_back (space.force_);
1440 res.penalty_ += cached_line_details_[line - 1].page_penalty_;
1441 res.penalty_ += cached_line_details_[line].page_penalty_;
1442 }
1443 else
1444 {
1445 res.systems_per_page_.push_back (line + 1 - page_first_line);
1446 res.force_.push_back (space.force_);
1447 res.penalty_ += cached_line_details_[line].page_penalty_;
1448 }
1449 }
1450 }
1451 return finalize_spacing_result (configuration, res);
1452 }
1453
1454 /* Calculate demerits and fix res.systems_per_page_ so that
1455 it refers to the original line numbers, not the ones given by compress_lines (). */
1456 Page_spacing_result
finalize_spacing_result(vsize configuration,Page_spacing_result res)1457 Page_breaking::finalize_spacing_result (vsize configuration, Page_spacing_result res)
1458 {
1459 if (res.force_.empty ())
1460 return res;
1461
1462 cache_line_details (configuration);
1463 res.systems_per_page_ = uncompress_solution (res.systems_per_page_, cached_line_details_);
1464
1465 Real line_force = 0;
1466 Real line_penalty = 0;
1467 Real page_demerits = res.penalty_;
1468 Real page_weighting = from_scm<double> (book_->paper_->c_variable ("page-spacing-weight"), 10);
1469
1470 for (vsize i = 0; i < uncompressed_line_details_.size (); i++)
1471 {
1472 line_force += uncompressed_line_details_[i].force_ * uncompressed_line_details_[i].force_;
1473 line_penalty += uncompressed_line_details_[i].break_penalty_;
1474 }
1475
1476 for (vsize i = ragged () ? res.force_.size () - 1 : 0;
1477 i < res.force_.size () - (is_last () && ragged_last ());
1478 i++)
1479 {
1480 Real f = res.force_[i];
1481
1482 page_demerits += std::min (f * f, BAD_SPACING_PENALTY);
1483 }
1484
1485 /* for a while we tried averaging page and line forces across pages instead
1486 of summing them, but it caused a problem: if there is a single page
1487 with a very bad page force (for example because of a forced page break),
1488 the page breaker will put in a _lot_ of pages so that the bad force
1489 becomes averaged out over many pages. */
1490 res.demerits_ = line_force + line_penalty + page_demerits * page_weighting;
1491 return res;
1492
1493 }
1494
1495 /* the cases for page_count = 1 or 2 can be done in O (n) time. Since they
1496 are by far the most common cases, we have special functions for them.
1497
1498 space_systems_on_1_page has a different calling convention than most of the
1499 space_systems functions. This is because space_systems_on_1_page is (unlike
1500 the other space_systems functions) sometimes called on subsets of a full
1501 configuration. */
1502 Page_spacing_result
space_systems_on_1_page(vector<Line_details> const & lines,Real page_height,bool ragged)1503 Page_breaking::space_systems_on_1_page (vector<Line_details> const &lines, Real page_height, bool ragged)
1504 {
1505 Page_spacing space (page_height, this);
1506 Page_spacing_result ret;
1507 int line_count = 0;
1508
1509 for (vsize i = 0; i < lines.size (); i++)
1510 {
1511 space.append_system (lines[i]);
1512 line_count += lines[i].compressed_nontitle_lines_count_;
1513 }
1514
1515 ret.systems_per_page_.push_back (lines.size ());
1516 ret.force_.push_back (ragged ? std::min (space.force_, 0.0) : space.force_);
1517 ret.penalty_ = line_count_penalty (line_count) + lines.back ().page_penalty_ + lines.back ().turn_penalty_;
1518 ret.system_count_status_ |= line_count_status (line_count);
1519
1520 /* don't do finalize_spacing_result () because we are only an internal function */
1521 return ret;
1522 }
1523
1524 Page_spacing_result
space_systems_on_2_pages(vsize configuration,int first_page_num)1525 Page_breaking::space_systems_on_2_pages (vsize configuration, int first_page_num)
1526 {
1527 Real page1_height = page_height (first_page_num, false);
1528 Real page2_height = page_height (first_page_num + 1, is_last ());
1529 bool ragged1 = ragged ();
1530 bool ragged2 = ragged () || (is_last () && ragged_last ());
1531
1532 /* if there is a forced break, this reduces to 2 1-page problems */
1533 cache_line_details (configuration);
1534 for (vsize i = 0; i + 1 < cached_line_details_.size (); i++)
1535 if (scm_is_eq (cached_line_details_[i].page_permission_,
1536 ly_symbol2scm ("force")))
1537 {
1538 vector<Line_details> lines1 (cached_line_details_.begin (), cached_line_details_.begin () + i + 1);
1539 vector<Line_details> lines2 (cached_line_details_.begin () + i + 1, cached_line_details_.end ());
1540 Page_spacing_result p1 = space_systems_on_1_page (lines1, page1_height, ragged1);
1541 Page_spacing_result p2 = space_systems_on_1_page (lines2, page2_height, ragged2);
1542
1543 p1.systems_per_page_.push_back (p2.systems_per_page_[0]);
1544 p1.force_.push_back (p2.force_[0]);
1545 p1.penalty_ += p2.penalty_ - cached_line_details_[i].turn_penalty_;
1546 p1.system_count_status_ |= p2.system_count_status_;
1547 return p1;
1548 }
1549
1550 vector<Real> page1_force;
1551 vector<Real> page2_force;
1552
1553 // page1_penalty and page2_penalty store the penalties based
1554 // on min-systems-per-page and max-systems-per-page.
1555 vector<Real> page1_penalty;
1556 vector<Real> page2_penalty;
1557
1558 // page1_status and page2_status keep track of whether the min-systems-per-page
1559 // and max-systems-per-page constraints are satisfied.
1560 vector<int> page1_status;
1561 vector<int> page2_status;
1562
1563 Page_spacing page1 (page1_height, this);
1564 Page_spacing page2 (page2_height, this);
1565 int page1_line_count = 0;
1566 int page2_line_count = 0;
1567
1568 page1_force.resize (cached_line_details_.size () - 1, infinity_f);
1569 page2_force.resize (cached_line_details_.size () - 1, infinity_f);
1570 page1_penalty.resize (cached_line_details_.size () - 1, infinity_f);
1571 page2_penalty.resize (cached_line_details_.size () - 1, infinity_f);
1572 page1_status.resize (cached_line_details_.size () - 1, 0);
1573 page2_status.resize (cached_line_details_.size () - 1, 0);
1574
1575 /* find the page 1 and page 2 forces for each page-breaking position */
1576 for (vsize i = 0; i < page1_force.size (); i++)
1577 {
1578 page1.append_system (cached_line_details_[i]);
1579 page2.prepend_system (cached_line_details_[cached_line_details_.size () - 1 - i]);
1580 page1_line_count += cached_line_details_[i].compressed_nontitle_lines_count_;
1581 page2_line_count += cached_line_details_[cached_line_details_.size () - 1 - i].compressed_nontitle_lines_count_;
1582
1583 page1_force[i] = (ragged1 && page1.force_ < 0 && i > 0) ? infinity_f : page1.force_;
1584 page1_penalty[i] = line_count_penalty (page1_line_count);
1585 page1_status[i] = line_count_status (page1_line_count);
1586
1587 if (ragged1)
1588 page2_force[page2_force.size () - 1 - i]
1589 = (page2.force_ < 0 && i + 1 < page1_force.size ()) ? infinity_f : 0;
1590 else if (ragged2 && page2.force_ > 0)
1591 page2_force[page2_force.size () - 1 - i] = 0.0;
1592 else
1593 page2_force[page2_force.size () - 1 - i] = page2.force_;
1594 page2_penalty[page2_penalty.size () - 1 - i] = line_count_penalty (page2_line_count);
1595 page2_status[page2_penalty.size () - 1 - i] = line_count_status (page2_line_count);
1596 }
1597
1598 /* find the position that minimises the sum of the page forces */
1599 vsize best_sys_count = 1;
1600 Real best_demerits = infinity_f;
1601 for (vsize i = 0; i < page1_force.size (); i++)
1602 {
1603 Real f = page1_force[i] * page1_force[i] + page2_force[i] * page2_force[i];
1604
1605 // NOTE: we treat max-systems-per-page and min-systems-per-page as soft
1606 // constraints. That is, we penalize harshly when they don't happen
1607 // but we don't completely reject.
1608 Real dem = f
1609 + page1_penalty[i] + page2_penalty[i]
1610 + cached_line_details_[i + 1].page_penalty_
1611 + cached_line_details_.back ().page_penalty_ + cached_line_details_.back ().turn_penalty_;
1612 if (dem < best_demerits)
1613 {
1614 best_demerits = dem;
1615 best_sys_count = i + 1;
1616 }
1617 }
1618
1619 Page_spacing_result ret;
1620 ret.systems_per_page_.push_back (best_sys_count);
1621 ret.systems_per_page_.push_back (cached_line_details_.size () - best_sys_count);
1622 ret.force_.push_back (page1_force[best_sys_count - 1]);
1623 ret.force_.push_back (page2_force[best_sys_count - 1]);
1624 ret.system_count_status_ = page1_status[best_sys_count - 1] | page2_status[best_sys_count - 1];
1625 ret.penalty_ = cached_line_details_[best_sys_count - 1].page_penalty_
1626 + cached_line_details_.back ().page_penalty_
1627 + cached_line_details_.back ().turn_penalty_
1628 + page1_penalty[best_sys_count - 1] + page2_penalty[best_sys_count - 1];
1629
1630 /* don't do finalize_spacing_result () because we are only an internal function */
1631 return ret;
1632 }
1633
1634 bool
all_lines_stretched(vsize configuration)1635 Page_breaking::all_lines_stretched (vsize configuration)
1636 {
1637 cache_line_details (configuration);
1638 for (vsize i = 0; i < cached_line_details_.size (); i++)
1639 if (cached_line_details_[i].force_ < 0)
1640 return false;
1641
1642 return true;
1643 }
1644
1645 Page_breaking::Line_division
current_configuration(vsize configuration_index) const1646 Page_breaking::current_configuration (vsize configuration_index) const
1647 {
1648 return current_configurations_[configuration_index];
1649 }
1650
1651 bool
is_last() const1652 Page_breaking::is_last () const
1653 {
1654 return current_end_breakpoint_ == last_break_position ();
1655 }
1656
1657 bool
ends_score() const1658 Page_breaking::ends_score () const
1659 {
1660 return breaks_[current_end_breakpoint_].score_ender_;
1661 }
1662
1663 vsize
last_break_position() const1664 Page_breaking::last_break_position () const
1665 {
1666 return breaks_.size () - 1;
1667 }
1668
1669 // This gives the minimum distance between the top of the
1670 // printable area (ie. the bottom of the top-margin) and
1671 // the extent box of the topmost system.
1672 Real
min_whitespace_at_top_of_page(Line_details const & line) const1673 Page_breaking::min_whitespace_at_top_of_page (Line_details const &line) const
1674 {
1675 SCM first_system_spacing = book_->paper_->c_variable ("top-system-spacing");
1676 if (line.title_)
1677 first_system_spacing = book_->paper_->c_variable ("top-markup-spacing");
1678
1679 Real min_distance = -infinity_f;
1680 Real padding = 0;
1681
1682 Page_layout_problem::read_spacing_spec (first_system_spacing,
1683 &min_distance,
1684 ly_symbol2scm ("minimum-distance"));
1685 Page_layout_problem::read_spacing_spec (first_system_spacing,
1686 &padding,
1687 ly_symbol2scm ("padding"));
1688
1689 // FIXME: take into account the height of the header
1690 Real translate = std::max (line.shape_.begin_[UP], line.shape_.rest_[UP]);
1691 return std::max (0.0, std::max (padding, min_distance - translate));
1692 }
1693
1694 Real
min_whitespace_at_bottom_of_page(Line_details const & line) const1695 Page_breaking::min_whitespace_at_bottom_of_page (Line_details const &line) const
1696 {
1697 SCM last_system_spacing = book_->paper_->c_variable ("last-bottom-spacing");
1698 Real min_distance = -infinity_f;
1699 Real padding = 0;
1700
1701 Page_layout_problem::read_spacing_spec (last_system_spacing,
1702 &min_distance,
1703 ly_symbol2scm ("minimum-distance"));
1704 Page_layout_problem::read_spacing_spec (last_system_spacing,
1705 &padding,
1706 ly_symbol2scm ("padding"));
1707
1708 // FIXME: take into account the height of the footer
1709 Real translate = std::min (line.shape_.begin_[DOWN], line.shape_.rest_[DOWN]);
1710 return std::max (0.0, std::max (padding, min_distance + translate));
1711 }
1712
1713 int
orphan_penalty() const1714 Page_breaking::orphan_penalty () const
1715 {
1716 return orphan_penalty_;
1717 }
1718