2 page-turn-page-breaking.cc -- implement Page_turn_page_breaking
4 source file of the GNU LilyPond music typesetter
6 (c) 2006--2009 Joe Neeman <joeneeman@gmail.com>
9 #include "page-turn-page-breaking.hh"
11 #include "international.hh"
13 #include "output-def.hh"
14 #include "page-spacing.hh"
15 #include "paper-book.hh"
16 #include "paper-score.hh"
17 #include "paper-system.hh"
24 return scm_is_symbol (g
->get_property ("page-turn-permission"));
27 Page_turn_page_breaking::Page_turn_page_breaking (Paper_book
*pb
)
28 : Page_breaking (pb
, is_break
)
32 Page_turn_page_breaking::~Page_turn_page_breaking ()
36 Page_turn_page_breaking::Break_node
37 Page_turn_page_breaking::put_systems_on_pages (vsize start
,
42 vsize min_p_count
= min_page_count (configuration
, page_number
);
43 bool auto_first
= to_boolean (book_
->paper_
->c_variable ("auto-first-page-number"));
45 /* If [START, END] does not contain an intermediate
46 breakpoint, we may need to consider solutions that result in a bad turn.
47 In this case, we won't abort if the min_page_count is too big */
48 if (start
< end
- 1 && min_p_count
+ (auto_first
? 0 : (page_number
% 2)) > 2)
51 /* if PAGE-NUMBER is odd, we are starting on a right hand page. That is, we
54 - even number of pages + a blank page
57 - odd number of pages + a blank page
58 - even number of pages
60 No matter which case PAGE-NUMBER falls into, we take the second choice if
61 min_p_count has that evenness. (For example, if PAGE-NUMBER is even and
62 min_p_count is even, we don't even consider the blank page option). */
64 Page_spacing_result result
;
65 if (start
== 0 && auto_first
)
68 result
= space_systems_on_n_or_one_more_pages (configuration
, min_p_count
, page_number
);
70 result
= space_systems_on_n_pages (configuration
, min_p_count
, page_number
);
72 else if (page_number
% 2 == min_p_count
% 2)
73 result
= space_systems_on_n_pages (configuration
, min_p_count
, page_number
);
75 result
= space_systems_on_n_or_one_more_pages (configuration
, min_p_count
, page_number
);
78 ret
.prev_
= start
- 1;
80 ret
.page_count_
= result
.force_
.size ();
81 ret
.first_page_number_
= page_number
;
82 if (auto_first
&& start
== 0)
83 ret
.first_page_number_
+= 1 - (ret
.page_count_
% 2);
85 ret
.div_
= current_configuration (configuration
);
86 ret
.system_count_
= result
.systems_per_page_
;
88 ret
.too_many_lines_
= all_lines_stretched (configuration
);
89 ret
.demerits_
= result
.demerits_
;
91 ret
.demerits_
+= state_
[start
-1].demerits_
;
96 /* The number of pages taken up by a Break_node, including
97 the blank page if there is one */
99 Page_turn_page_breaking::total_page_count (Break_node
const &b
)
101 vsize end
= b
.first_page_number_
+ b
.page_count_
;
102 return end
- 1 + (end
% 2) - b
.first_page_number_
;
105 extern bool debug_page_breaking_scoring
;
108 Page_turn_page_breaking::calc_subproblem (vsize ending_breakpoint
)
110 vsize end
= ending_breakpoint
+ 1;
113 Break_node this_start_best
;
114 vsize prev_best_system_count
= 0;
116 for (vsize start
= end
; start
--;)
119 && breakpoint_property (start
+1, "page-turn-permission") == ly_symbol2scm ("force"))
122 if (start
> 0 && best
.demerits_
< state_
[start
-1].demerits_
)
125 int p_num
= robust_scm2int (book_
->paper_
->c_variable ("first-page-number"), 1);
128 /* except possibly for the first page, enforce the fact that first_page_number_
129 should always be even (left hand page).
130 TODO: are there different conventions in right-to-left languages?
132 p_num
= state_
[start
-1].first_page_number_
+ state_
[start
-1].page_count_
;
136 Line_division min_division
;
137 Line_division max_division
;
139 vsize min_sys_count
= min_system_count (start
, end
);
140 vsize max_sys_count
= max_system_count (start
, end
);
141 this_start_best
.demerits_
= infinity_f
;
145 if (debug_page_breaking_scoring
)
146 message (_f ("page-turn-page-breaking: breaking from %d to %d", (int) start
, (int) end
));
148 /* heuristic: we've just added a breakpoint, we'll need at least as
149 many systems as before */
150 min_sys_count
= max (min_sys_count
, prev_best_system_count
);
151 for (vsize sys_count
= min_sys_count
; sys_count
<= max_sys_count
&& ok_page
; sys_count
++)
153 set_current_breakpoints (start
, end
, sys_count
, min_division
, max_division
);
156 for (vsize i
= 0; i
< current_configuration_count (); i
++)
158 cur
= put_systems_on_pages (start
, end
, i
, p_num
);
160 if (isinf (cur
.demerits_
)
161 || (cur
.page_count_
+ (p_num
% 2) > 2
162 && (!isinf (this_start_best
.demerits_
))
163 && total_page_count (cur
) > total_page_count (this_start_best
)))
169 if (cur
.demerits_
< this_start_best
.demerits_
)
171 if (debug_page_breaking_scoring
)
172 print_break_node (cur
);
175 this_start_best
= cur
;
176 prev_best_system_count
= sys_count
;
178 /* heuristic: if we increase the number of systems, we can bound the
179 division from below by our current best division */
180 min_division
= current_configuration (i
);
183 if (!found
&& this_start_best
.too_many_lines_
)
186 if (isinf (this_start_best
.demerits_
))
188 assert (!isinf (best
.demerits_
) && start
< end
- 1);
192 if (start
== 0 && end
== 1
193 && this_start_best
.first_page_number_
== 1
194 && this_start_best
.page_count_
> 1)
195 warning (_ ("cannot fit the first page turn onto a single page. "
196 "Consider setting first-page-number to an even number."));
198 if (this_start_best
.demerits_
< best
.demerits_
)
199 best
= this_start_best
;
201 state_
.push_back (best
);
205 Page_turn_page_breaking::solve ()
208 message (_f ("Calculating page and line breaks (%d possible page breaks)...",
209 (int) last_break_position ()));
210 for (vsize i
= 0; i
< last_break_position (); i
++)
213 progress_indication (string ("[") + to_string (i
+ 1) + "]");
215 progress_indication ("\n");
217 vector
<Break_node
> breaking
;
218 int i
= state_
.size () - 1;
221 breaking
.push_back (state_
[i
]);
226 message (_ ("Drawing systems..."));
227 SCM systems
= make_lines (&breaking
);
228 return make_pages (breaking
, systems
);
231 /* do the line breaking in all the scores and return a big list of systems */
233 Page_turn_page_breaking::make_lines (vector
<Break_node
> *psoln
)
235 vector
<Break_node
> &soln
= *psoln
;
236 for (vsize n
= 0; n
< soln
.size (); n
++)
238 vsize start
= n
> 0 ? soln
[n
-1].break_pos_
: 0;
239 vsize end
= soln
[n
].break_pos_
;
241 break_into_pieces (start
, end
, soln
[n
].div_
);
248 Page_turn_page_breaking::make_pages (vector
<Break_node
> const &soln
, SCM systems
)
250 vector
<vsize
> lines_per_page
;
251 for (vsize i
= 0; i
< soln
.size (); i
++)
253 for (vsize j
= 0; j
< soln
[i
].page_count_
; j
++)
254 lines_per_page
.push_back (soln
[i
].system_count_
[j
]);
256 if (i
+ 1 < soln
.size () && (soln
[i
].first_page_number_
+ soln
[i
].page_count_
) % 2)
257 /* add a blank page */
258 lines_per_page
.push_back (0);
261 /* this should only actually modify first-page-number if
262 auto-first-page-number was true. */
263 book_
->paper_
->set_variable (ly_symbol2scm ("first-page-number"),
264 scm_from_int (soln
[0].first_page_number_
));
265 return Page_breaking::make_pages (lines_per_page
, systems
);
269 Page_turn_page_breaking::print_break_node (Break_node
const &node
)
271 int system_count
= 0;
272 for (vsize i
= 0; i
< node
.system_count_
.size (); i
++)
273 system_count
+= node
.system_count_
[i
];
275 message (_f ("break starting at page %d", (int) node
.first_page_number_
));
276 message (_f ("\tdemerits: %f", node
.demerits_
));
277 message (_f ("\tsystem count: %d", system_count
));
278 message (_f ("\tpage count: %d", (int) node
.page_count_
));
279 message (_f ("\tprevious break: %d", (int) node
.prev_
));