]> git.donarmstrong.com Git - lilypond.git/blob - lily/page-breaking.cc
Add high-level documentation about Page_breaking.
[lilypond.git] / lily / page-breaking.cc
1 /*
2   page-breaking.cc -- implement a superclass and utility
3   functions shared by various page-breaking algorithms
4
5   source file of the GNU LilyPond music typesetter
6
7   (c) 2006--2009 Joe Neeman <joeneeman@gmail.com>
8 */
9
10 /*
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.
17
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."
28
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.
36
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.
44
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).
52
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.
64 */
65
66 #include "page-breaking.hh"
67
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"
77
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)
82 {
83   vector<Line_details> ret;
84
85   for (vsize i = 0; i < orig.size (); i++)
86     {
87       if (ret.size () && !scm_is_symbol (ret.back ().page_permission_))
88         {
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_;
95
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);
99
100           compressed.title_ = compressed.title_ && old.title_;
101           ret.back () = compressed;
102         }
103       else
104         {
105           ret.push_back (orig[i]);
106           ret.back ().force_ = 0;
107         }
108     }
109   return ret;
110 }
111
112 /* translate the number of systems-per-page into something meaningful for
113    the uncompressed lines.
114 */
115 static vector<vsize>
116 uncompress_solution (vector<vsize> const &systems_per_page,
117                      vector<Line_details> const &compressed)
118 {
119   vector<vsize> ret;
120   vsize start_sys = 0;
121
122   for (vsize i = 0; i < systems_per_page.size (); i++)
123     {
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;
127
128       ret.push_back (systems_per_page[i] + compressed_count);
129       start_sys += systems_per_page[i];
130     }
131   return ret;
132 }
133
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. */
139
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
143 {
144   vsize sys = break_pos.system_spec_index_;
145
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 */
151 }
152
153 Page_breaking::Page_breaking (Paper_book *pb, Break_predicate is_break)
154 {
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));
163
164   if (systems_per_page_ && (max_systems_per_page_ || min_systems_per_page_))
165     {
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;
168     }
169   if (max_systems_per_page_ && min_systems_per_page_ > max_systems_per_page_)
170     {
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;
173     }
174
175   create_system_list ();
176   find_chunks_and_breaks (is_break);
177 }
178
179 Page_breaking::~Page_breaking ()
180 {
181 }
182
183 bool
184 Page_breaking::ragged () const
185 {
186   return ragged_;
187 }
188
189 bool
190 Page_breaking::ragged_last () const
191 {
192   return ragged_last_;
193 }
194
195 int
196 Page_breaking::systems_per_page () const
197 {
198   return systems_per_page_;
199 }
200
201 int
202 Page_breaking::max_systems_per_page () const
203 {
204   return max_systems_per_page_;
205 }
206
207 int
208 Page_breaking::min_systems_per_page () const
209 {
210   return min_systems_per_page_;
211 }
212
213 Real
214 Page_breaking::page_top_space () const
215 {
216   return page_top_space_;
217 }
218
219 vsize
220 Page_breaking::system_count () const
221 {
222   return system_count_;
223 }
224
225 bool
226 Page_breaking::too_many_lines (int line_count) const
227 {
228   return max_systems_per_page () > 0 && line_count > max_systems_per_page ();
229 }
230
231 bool
232 Page_breaking::too_few_lines (int line_count) const
233 {
234   return line_count < min_systems_per_page ();
235 }
236
237 Real
238 Page_breaking::line_count_penalty (int line_count) const
239 {
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;
244
245   return 0;
246 }
247
248 int
249 Page_breaking::line_count_status (int line_count) const
250 {
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;
255
256   return SYSTEM_COUNT_OK;
257 }
258
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)
266 {
267   assert (system_specs_[sys].pscore_);
268   assert (next_system (start) <= sys && sys <= end.system_spec_index_);
269
270   if (start.system_spec_index_ == sys)
271     *line_breaker_start = start.score_break_;
272   else
273     *line_breaker_start = 0;
274
275   if (end.system_spec_index_ == sys)
276     *line_breaker_end = end.score_break_;
277   else
278     *line_breaker_end = VPOS;
279 }
280
281 void
282 Page_breaking::break_into_pieces (vsize start_break, vsize end_break,
283                                   Line_division const &div)
284 {
285   vector<Break_position> chunks = chunk_list (start_break, end_break);
286   bool ignore_div = false;
287   if (chunks.size () != div.size () + 1)
288     {
289       programming_error ("did not find a valid page breaking configuration");
290       ignore_div = true;
291     }
292
293   for (vsize i = 0; i + 1 < chunks.size (); i++)
294     {
295       vsize sys = next_system (chunks[i]);
296       if (system_specs_[sys].pscore_)
297         {
298           vsize start;
299           vsize end;
300           line_breaker_args (sys, chunks[i], chunks[i+1], &start, &end);
301
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);
306         }
307     }
308 }
309
310 SCM
311 Page_breaking::systems ()
312 {
313   SCM ret = SCM_EOL;
314   for (vsize sys = 0; sys < system_specs_.size (); sys++)
315     {
316       if (system_specs_[sys].pscore_)
317         {
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);
323         }
324       else
325         {
326           Prob *pb = system_specs_[sys].prob_;
327           ret = scm_cons (scm_list_1 (pb->self_scm ()), ret);
328           pb->unprotect ();
329         }
330     }
331   return scm_append (scm_reverse (ret));
332 }
333
334 Real
335 Page_breaking::page_height (int page_num, bool last) const
336 {
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");
341
342   calc_height = scm_variable_ref (calc_height);
343   make_page = scm_variable_ref (make_page);
344
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_;
353 }
354
355 SCM
356 Page_breaking::breakpoint_property (vsize breakpoint, char const *str)
357 {
358   Break_position const &pos = breaks_[breakpoint];
359
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);
365 }
366
367 SCM
368 Page_breaking::make_pages (vector<vsize> lines_per_page, SCM systems)
369 {
370   SCM layout_module = scm_c_resolve_module ("scm layout-page-layout");
371   SCM page_module = scm_c_resolve_module ("scm page");
372
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);
377
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;
386
387   for (vsize i = 0; i < lines_per_page.size (); i++)
388     {
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))
401         {
402           SCM labels = SCM_EOL;
403           if (Grob * line = unsmob_grob (scm_car (l)))
404             {
405               System *system = dynamic_cast<System*> (line);
406               labels = system->get_property ("labels");
407             }
408           else if (Prob *prob = unsmob_prob (scm_car (l)))
409             labels = prob->get_property ("labels");
410
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);
414         }
415
416       scm_apply_1 (page_stencil, page, SCM_EOL);
417       ret = scm_cons (page, ret);
418       systems = scm_list_tail (systems, line_count);
419     }
420   book_->top_paper ()->set_variable (ly_symbol2scm ("label-page-table"), label_page_table);
421   ret = scm_reverse (ret);
422   return ret;
423 }
424
425 /* The page-turn-page-breaker needs to have a line-breaker between any two
426    columns with non-NULL page-turn-permission.
427
428    The optimal-breaker needs to have a line-breaker between any two columns
429    with page-break-permission = 'force.
430
431    By using a grob predicate, we can accommodate both of these uses.
432 */
433 void
434 Page_breaking::create_system_list ()
435 {
436   SCM specs = book_->get_system_specs ();
437   for (SCM s = specs; scm_is_pair (s); s = scm_cdr (s))
438     {
439       if (Paper_score *ps = dynamic_cast<Paper_score*> (unsmob_music_output (scm_car (s))))
440         {
441           SCM system_count = ps->layout ()->c_variable ("system-count");
442
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));
449         }
450       else
451         {
452           Prob *pb = unsmob_prob (scm_car (s));
453           assert (pb);
454
455           pb->protect ();
456           system_specs_.push_back (System_spec (pb));
457         }
458     }
459 }
460
461 void
462 Page_breaking::find_chunks_and_breaks (Break_predicate is_break)
463 {
464   SCM force_sym = ly_symbol2scm ("force");
465
466   chunks_.push_back (Break_position ());
467   breaks_.push_back (Break_position ());
468
469   for (vsize i = 0; i < system_specs_.size (); i++)
470     {
471       if (system_specs_[i].pscore_)
472         {
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);
477
478           for (vsize j = 1; j < cols.size (); j++)
479             {
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);
487
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);
492
493               if ((break_point || chunk_end) && !last)
494                 line_breaker_columns.push_back (j);
495             }
496           line_breaking_.push_back (Constrained_breaking (system_specs_[i].pscore_, line_breaker_columns));
497         }
498       else
499         {
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));
503
504           chunks_.push_back (Break_position (i));
505
506           /* FIXME: shouldn't we push a Null_breaker or similar dummy
507              class? --hwn */
508           line_breaking_.push_back (Constrained_breaking (NULL));
509         }
510     }
511 }
512
513 vector<Break_position>
514 Page_breaking::chunk_list (vsize start_index, vsize end_index)
515 {
516   Break_position start = breaks_[start_index];
517   Break_position end = breaks_[end_index];
518
519   vsize i = 0;
520   for (; i < chunks_.size () && chunks_[i] <= start; i++)
521     ;
522
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;
529 }
530
531 vsize
532 Page_breaking::min_system_count (vsize start, vsize end)
533 {
534   vector<Break_position> chunks = chunk_list (start, end);
535   Line_division div = system_count_bounds (chunks, true);
536   vsize ret = 0;
537
538   for (vsize i = 0; i < div.size (); i++)
539     ret += div[i];
540   return ret;
541 }
542
543 vsize
544 Page_breaking::max_system_count (vsize start, vsize end)
545 {
546   vector<Break_position> chunks = chunk_list (start, end);
547   Line_division div = system_count_bounds (chunks, false);
548   vsize ret = 0;
549
550   for (vsize i = 0; i < div.size (); i++)
551     ret += div[i];
552   return ret;
553 }
554
555 Page_breaking::Line_division
556 Page_breaking::system_count_bounds (vector<Break_position> const &chunks,
557                                     bool min)
558 {
559   assert (chunks.size () >= 2);
560
561   Line_division ret;
562   ret.resize (chunks.size () - 1, 1);
563
564   for (vsize i = 0; i + 1 < chunks.size (); i++)
565     {
566       vsize sys = next_system (chunks[i]);
567       if (system_specs_[sys].pscore_)
568         {
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);
575         }
576     }
577
578   return ret;
579 }
580
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)
587 {
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 ();
593
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);
598
599   assert (lower_bound.size () == current_chunks_.size () - 1);
600   assert (upper_bound.size () == current_chunks_.size () - 1);
601
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);
608
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)
613     {
614       vector<pair<Real,vsize> > dems_and_indices;
615
616       for (vsize i = 0; i < current_configurations_.size (); i++)
617         {
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_;
623
624           dems_and_indices.push_back (pair<Real,vsize> (dem, i));
625         }
626       vector_sort (dems_and_indices, less<pair<Real,vsize> > ());
627
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]);
631
632       clear_line_details_cache ();
633       current_configurations_ = best_5_configurations;
634     }
635 }
636
637 void
638 Page_breaking::set_to_ideal_line_configuration (vsize start, vsize end)
639 {
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;
645
646   Line_division div;
647   for (vsize i = 0; i+1 < current_chunks_.size (); i++)
648     {
649       vsize sys = next_system (current_chunks_[i]);
650       if (system_specs_[sys].pscore_)
651         {
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 ());
654         }
655       else
656         div.push_back (1);
657
658       system_count_ += div.back ();
659     }
660   current_configurations_.clear ();
661   current_configurations_.push_back (div);
662 }
663
664 vsize
665 Page_breaking::current_configuration_count () const
666 {
667   return current_configurations_.size ();
668 }
669
670 void
671 Page_breaking::cache_line_details (vsize configuration_index)
672 {
673   if (cached_configuration_index_ != configuration_index)
674     {
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);
680
681       Line_division &div = current_configurations_[configuration_index];
682       uncompressed_line_details_.clear ();
683       for (vsize i = 0; i + 1 < current_chunks_.size (); i++)
684         {
685           vsize sys = next_system (current_chunks_[i]);
686           if (system_specs_[sys].pscore_)
687             {
688               vsize start;
689               vsize end;
690               line_breaker_args (sys, current_chunks_[i], current_chunks_[i+1], &start, &end);
691
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 ());
694             }
695           else
696             {
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);
702             }
703         }
704       cached_line_details_ = compress_lines (uncompressed_line_details_);
705     }
706 }
707
708 void
709 Page_breaking::clear_line_details_cache ()
710 {
711   cached_configuration_index_ = VPOS;
712   cached_line_details_.clear ();
713   uncompressed_line_details_.clear ();
714 }
715
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)
721 {
722   vsize my_index = cur_division->size ();
723   vsize others_min = 0;
724   vsize others_max = 0;
725
726   for (vsize i = my_index + 1; i < min_sys.size (); i++)
727     {
728       others_min += min_sys[i];
729       others_max += max_sys[i];
730     }
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);
734
735   if (real_min > real_max || real_min <= 0)
736     {
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;
742     }
743
744   for (vsize i = real_min; i <= real_max; i++)
745     {
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 ();
752     }
753 }
754
755 vsize
756 Page_breaking::min_page_count (vsize configuration, vsize first_page_num)
757 {
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;
763
764   cache_line_details (configuration);
765   for (vsize i = 0; i < cached_line_details_.size (); i++)
766     {
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_;
773
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")))
778         {
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++;
784         }
785       else
786         {
787           cur_rod_height = next_rod_height;
788           cur_spring_height = next_spring_height;
789           line_count = next_line_count;
790         }
791     }
792
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.
799
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.
804   */
805
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++;
813
814   assert (ret <= cached_line_details_.size ());
815   return ret;
816 }
817
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)
823 {
824   Page_spacing_result ret;
825
826   if (systems_per_page_ > 0)
827     {
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;
831     }
832
833   cache_line_details (configuration);
834   bool valid_n = (n >= min_page_count (configuration, first_page_num)
835                   && n <= cached_line_details_.size ());
836
837   if (!valid_n)
838     programming_error ("number of pages is out of bounds");
839
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
847     {
848       Page_spacer ps (cached_line_details_, first_page_num, this);
849       ret = ps.solve (n);
850     }
851
852   return finalize_spacing_result (configuration, ret);
853 }
854
855 Real
856 Page_breaking::blank_page_penalty () const
857 {
858   SCM penalty_sym;
859
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");
866
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);
870
871   return robust_scm2double (book_->paper_->lookup_variable (penalty_sym), 0.0);
872 }
873
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)
878 {
879   Page_spacing_result n_res;
880   Page_spacing_result m_res;
881
882   if (systems_per_page_ > 0)
883     {
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;
887     }
888
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 ();
892
893   if (!valid_n)
894     programming_error ("both page counts are out of bounds");
895
896   if (n == 1 && valid_n)
897     {
898       bool rag = ragged () || (is_last () && ragged_last ());
899       Real height = page_height (first_page_num, is_last ());
900
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);
905     }
906   else
907     {
908       Page_spacer ps (cached_line_details_, first_page_num, this);
909       
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);
914     }
915
916   m_res = finalize_spacing_result (configuration, m_res);
917   n_res = finalize_spacing_result (configuration, n_res);
918
919   Real penalty = blank_page_penalty ();
920   n_res.demerits_ += penalty;
921
922   if (n_res.force_.size ())
923     n_res.force_.back () += penalty;
924
925   return (m_res.demerits_ < n_res.demerits_) ? m_res : n_res;
926 }
927
928 Page_spacing_result
929 Page_breaking::space_systems_on_best_pages (vsize configuration, vsize first_page_num)
930 {
931   vsize min_p_count = min_page_count (configuration, first_page_num);
932   Real odd_pages_penalty = blank_page_penalty ();
933
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;
939
940   for (vsize i = min_p_count+1; i <= cached_line_details_.size (); i++)
941     {
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;
946     }
947
948   return finalize_spacing_result (configuration, best);
949 }
950
951 Page_spacing_result
952 Page_breaking::space_systems_with_fixed_number_per_page (vsize configuration,
953                                                          vsize first_page_num)
954 {
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;
960
961   cache_line_details (configuration);
962   while (line < cached_line_details_.size ())
963     {
964       page++;
965       space.clear ();
966       space.resize (page_height (first_page_num + page, false));
967
968       int system_count_on_this_page = 0;
969       while (system_count_on_this_page < systems_per_page_
970              && line < cached_line_details_.size ())
971         {
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++;
976
977           if (cur_line.page_permission_ == ly_symbol2scm ("force"))
978             break;
979         }
980
981       res.systems_per_page_.push_back (line - page_first_line);
982
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_)
986         {
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;
990         }
991
992       page_first_line = line;
993     }
994
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);
1000 }
1001
1002 Page_spacing_result
1003 Page_breaking::pack_systems_on_least_pages (vsize configuration, vsize first_page_num)
1004 {
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_);
1010
1011   cache_line_details (configuration);
1012   for (vsize line = 0; line < cached_line_details_.size (); line++)
1013     {
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")))))
1020         {
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;
1029         }
1030
1031       if (line == cached_line_details_.size () - 1)
1032         {
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_)))
1039             {
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_;
1050             }
1051           else
1052             {
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_;
1056             }
1057         }
1058     }
1059   return finalize_spacing_result (configuration, res);
1060 }
1061
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)
1066 {
1067   if (res.force_.empty ())
1068     return res;
1069
1070   cache_line_details (configuration);
1071   res.systems_per_page_ = uncompress_solution (res.systems_per_page_, cached_line_details_);
1072
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);
1077
1078   for (vsize i = 0; i < uncompressed_line_details_.size (); i++)
1079     {
1080       line_force += uncompressed_line_details_[i].force_ * uncompressed_line_details_[i].force_;
1081       line_penalty += uncompressed_line_details_[i].break_penalty_;
1082     }
1083
1084   for (vsize i = 0; i < res.force_.size (); i++)
1085     {
1086       Real f = res.force_[i];
1087
1088       page_demerits += min(f*f, BAD_SPACING_PENALTY);
1089     }
1090
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;
1098
1099 }
1100
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.
1103
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)
1110 {
1111   Page_spacing space (page_height, page_top_space_);
1112   Page_spacing_result ret;
1113   int line_count = 0;
1114
1115   for (vsize i = 0; i < lines.size (); i++)
1116     {
1117       space.append_system (lines[i]);
1118       line_count += lines[i].compressed_nontitle_lines_count_;
1119     }
1120
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);
1125
1126   /* don't do finalize_spacing_result () because we are only an internal function */
1127   return ret;
1128 }
1129
1130 Page_spacing_result
1131 Page_breaking::space_systems_on_2_pages (vsize configuration, vsize first_page_num)
1132 {
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 ());
1137
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"))
1142       {
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);
1147
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;
1152       }
1153
1154   vector<Real> page1_force;
1155   vector<Real> page2_force;
1156
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;
1161
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;
1166
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;
1171
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);
1178
1179   /* find the page 1 and page 2 forces for each page-breaking position */
1180   for (vsize i = 0; i < page1_force.size (); i++)
1181     {
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_;
1186
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);
1190
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);
1198     }
1199
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++)
1204     {
1205       Real f = page1_force[i] * page1_force[i] + page2_force[i] * page2_force[i];
1206
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)
1215         {
1216           best_demerits = dem;
1217           best_sys_count = i+1;
1218         }
1219     }
1220
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];
1231
1232   /* don't do finalize_spacing_result () because we are only an internal function */
1233   return ret;
1234 }
1235
1236 bool
1237 Page_breaking::all_lines_stretched (vsize configuration)
1238 {
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;
1243
1244   return true;
1245 }
1246
1247 Page_breaking::Line_division
1248 Page_breaking::current_configuration (vsize configuration_index) const
1249 {
1250   return current_configurations_[configuration_index];
1251 }
1252
1253 bool
1254 Page_breaking::is_last () const
1255 {
1256   return current_end_breakpoint_ == last_break_position ();
1257 }
1258
1259 bool
1260 Page_breaking::ends_score () const
1261 {
1262   return breaks_[current_end_breakpoint_].score_ender_;
1263 }
1264
1265 vsize
1266 Page_breaking::last_break_position () const
1267 {
1268   return breaks_.size () - 1;  
1269 }