]> git.donarmstrong.com Git - lilypond.git/blob - lily/optimal-page-breaking.cc
Put systems_per_page_ in an object property instead of passing it around.
[lilypond.git] / lily / optimal-page-breaking.cc
1 /*
2   optimal-page-breaking.cc -- implement a page-breaker that
3   will break pages in such a way that both horizontal and
4   vertical spacing will be acceptable
5
6   source file of the GNU LilyPond music typesetter
7
8   (c) 2006--2009 Joe Neeman <joeneeman@gmail.com>
9 */
10
11 #include "international.hh"
12 #include "optimal-page-breaking.hh"
13 #include "output-def.hh"
14 #include "page-spacing.hh"
15 #include "paper-book.hh"
16 #include "paper-score.hh"
17 #include "prob.hh"
18 #include "system.hh"
19
20 static bool
21 is_break (Grob *)
22 {
23   return false;
24 }
25
26 Optimal_page_breaking::Optimal_page_breaking (Paper_book *pb)
27   : Page_breaking (pb, is_break)
28 {
29 }
30
31 Optimal_page_breaking::~Optimal_page_breaking ()
32 {
33 }
34
35 SCM
36 Optimal_page_breaking::solve ()
37 {
38   vsize end = last_break_position ();
39   vsize max_sys_count = max_system_count (0, end);
40   vsize first_page_num = robust_scm2int (book_->paper_->c_variable ("first-page-number"), 1);
41   SCM forced_page_count = book_->paper_->c_variable ("page-count");
42
43   set_to_ideal_line_configuration (0, end);
44
45   Page_spacing_result best;
46   vsize page_count = robust_scm2int (forced_page_count, 1);
47   Line_division ideal_line_division = current_configuration (0);
48   Line_division best_division = ideal_line_division;
49   vsize min_sys_count = 1;
50   vsize ideal_sys_count = system_count ();
51   
52   if (!scm_is_integer (forced_page_count))
53     {
54       /* find out the ideal number of pages */
55       message (_ ("Finding the ideal number of pages..."));
56   
57       if (systems_per_page () > 0)
58         best = space_systems_with_fixed_number_per_page (0, first_page_num);
59       else
60         best = space_systems_on_best_pages (0, first_page_num);
61
62       page_count = best.systems_per_page_.size ();
63       ideal_sys_count = best.system_count ();
64       min_sys_count = ideal_sys_count - best.systems_per_page_.back ();
65   
66       if (page_count > 1 && best.systems_per_page_[page_count - 2] > 1)
67         min_sys_count -= best.systems_per_page_[page_count - 2];
68
69       min_sys_count = max (min_sys_count, (vsize)1);
70     }
71   else
72     {
73       /* TODO: the following line will spit out programming errors if the
74          ideal line spacing doesn't fit on PAGE_COUNT pages */
75       /* TODO: the interaction between systems_per_page and page_count needs to
76          be considered. */
77       best = space_systems_on_n_pages (0, page_count, first_page_num);
78       min_sys_count = page_count;
79     }
80
81   if (page_count == 1)
82     message (_ ("Fitting music on 1 page..."));
83   else if (scm_is_integer (forced_page_count))
84     message (_f ("Fitting music on %d pages...", (int)page_count));
85   else
86     message (_f ("Fitting music on %d or %d pages...", (int)page_count-1, (int)page_count));
87
88   /* try a smaller number of systems than the ideal number for line breaking */
89   Line_division bound = ideal_line_division;
90   for (vsize sys_count = ideal_sys_count; --sys_count >= min_sys_count;)
91     {
92       Page_spacing_result best_for_this_sys_count;
93       set_current_breakpoints (0, end, sys_count, Line_division (), bound);
94
95       for (vsize i = 0; i < current_configuration_count (); i++)
96         {
97           vsize min_p_count = min_page_count (i, first_page_num);
98           Page_spacing_result cur;
99
100           if (min_p_count == page_count || scm_is_integer (forced_page_count))
101             cur = space_systems_on_n_pages (i, page_count, first_page_num);
102           else
103             cur = space_systems_on_n_or_one_more_pages (i, page_count-1, first_page_num);
104
105           if (cur.demerits_ < best_for_this_sys_count.demerits_ || isinf (best_for_this_sys_count.demerits_))
106             {
107               best_for_this_sys_count = cur;
108               bound = current_configuration (i);
109             }
110         }
111
112       if (best_for_this_sys_count.demerits_ < best.demerits_ || isinf (best.demerits_))
113         {
114           best = best_for_this_sys_count;
115           best_division = bound;
116         }
117
118       /* if the pages are stretched on average, stop trying to reduce sys_count */
119       if (best_for_this_sys_count.page_count () < page_count
120           && best_for_this_sys_count.average_force () > 0)
121         break;
122         
123
124       if (isinf (best_for_this_sys_count.demerits_))
125         break;
126     }
127
128   /* try a larger number of systems than the ideal line breaking number. This
129      is more or less C&P, but the loop bounds make it difficult to try something
130      like do {...} while (flip(&d) != UP). */
131   bound = ideal_line_division;
132   for (vsize sys_count = ideal_sys_count+1; sys_count <= max_sys_count; sys_count++)
133     {
134       Real best_demerits_for_this_sys_count = infinity_f;
135       set_current_breakpoints (0, end, sys_count, bound);
136
137       for (vsize i = 0; i < current_configuration_count (); i++)
138         {
139           vsize min_p_count = min_page_count (i, first_page_num);
140           Page_spacing_result cur;
141
142           if (min_p_count > page_count)
143             continue;
144           else
145             cur = space_systems_on_n_pages (i, page_count, first_page_num);
146
147           if (cur.demerits_ < best.demerits_ || isinf (best.demerits_))
148             {
149               best = cur;
150               best_division = current_configuration (i);
151             }
152
153           if (cur.demerits_ < best_demerits_for_this_sys_count || isinf (best_demerits_for_this_sys_count))
154             {
155               best_demerits_for_this_sys_count = cur.demerits_;
156               bound = current_configuration (i);
157             }
158         }
159       if (isinf (best_demerits_for_this_sys_count))
160         break;
161     }
162
163   message (_ ("Drawing systems..."));
164   break_into_pieces (0, end, best_division);
165   SCM lines = systems ();
166   return make_pages (best.systems_per_page_, lines);
167 }
168