2 constrained-breaking.cc -- implement a line breaker that
3 support limits on the number of systems
5 source file of the GNU LilyPond music typesetter
7 (c) 2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
10 #include "constrained-breaking.hh"
14 #include "paper-column.hh"
15 #include "paper-score.hh"
16 #include "output-def.hh"
17 #include "simple-spacer.hh"
20 #include "international.hh"
23 print_constrained_break_nodes (vector<Constrained_break_node> const &arr)
25 for (vsize i = 0; i < arr.size (); i++)
27 printf ("node %d: ", (int)i);
33 We use the following optimal substructure. Let W(A) be our weight function.
35 Let A_{k,n} = (a_{k,n,1}, ... a_{k,n,k}) be the optimal set of line breaks
36 for k systems and n potential breakpoints. a_{k,n,k} = n (it is the end of
39 Then A_{k+1, m} is contructed from
40 min_ {k < j < m} ( W(A_{k,j} :: m) )
41 where by A::m we denote appending m to the list A
44 /* start and sys here are indexed from 0.
45 * max_break is indexed from starting_breakpoints_[start]
46 * (for max_break, starting_breakpoints_[start] is the beginning
47 * of the piece; the smallest value we should ever see here is
48 * starting_breakpoints_[start] + 1) */
50 Constrained_breaking::calc_subproblem (int start, int sys, int max_break)
52 assert (sys < systems_);
53 assert (start < (int)start_.size ());
54 assert (max_break < (int)breaks_.size ());
56 bool found_something = false;
57 int start_col = starting_breakpoints_[start];
58 vector<Constrained_break_node> &st = state_[start];
59 int rank = breaks_.size () - start_col;
60 int max_index = max_break - start_col;
61 for (int j=sys; j < max_index; j++)
63 if (0 == sys && j > 0)
64 break; /* the first line cannot have its first break after the beginning */
66 Column_x_positions const &cur = cols_[(j + start_col)*cols_rank_ + max_break];
67 Column_x_positions prev;
72 prev = st[(sys-1) * rank + j].line_config_;
73 prev_dem = st[(sys-1) * rank + j].demerits_;
79 combine_demerits(prev, cur, &force, &pen, &dem);
84 if (isinf (st[sys*rank + max_index].demerits_)
85 || dem < st[sys*rank + max_index].demerits_)
87 found_something = true;
88 st[sys*rank + max_index].demerits_ = dem;
89 st[sys*rank + max_index].force_ = force;
90 st[sys*rank + max_index].penalty_ = pen;
91 st[sys*rank + max_index].prev_ = j;
92 st[sys*rank + max_index].line_config_ = cur;
95 return found_something;
98 vector<Column_x_positions>
99 Constrained_breaking::do_solve ()
103 programming_error (_f ("no system number set in constrained-breaking"));
108 return get_solution(0, systems_, -1);
112 Constrained_breaking::resize ()
114 if (!breaks_.size ())
116 bool ragged_right = to_boolean (pscore_->layout ()->c_variable ("ragged-right"));
117 bool ragged_last = to_boolean (pscore_->layout ()->c_variable ("ragged-last"));
119 /* do all the rod/spring problems */
120 breaks_ = find_break_indices ();
121 cols_rank_ = breaks_.size ();
122 all_ = pscore_->root_system ()->columns ();
123 cols_.resize (breaks_.size () * breaks_.size ());
124 for (vsize i = 0; i < breaks_.size () - 1; i++)
125 for (vsize j = i + 1; j < breaks_.size (); j++)
127 vector<Grob*> line (all_.begin () + breaks_[i],
128 all_.begin() + breaks_[j] + 1);
130 line[0] = dynamic_cast<Item *> (line[0])->find_prebroken_piece (RIGHT);
131 line.back () = dynamic_cast<Item *> (line.back ())->find_prebroken_piece (LEFT);
133 cols_[i*cols_rank_ + j].cols_ = line;
135 /* we have no idea what line this will be -- only whether it is the first */
136 Interval line_dims = line_dimensions_int (pscore_->layout (), i);
137 Simple_spacer_wrapper *sp = generate_spacing_problem (line, line_dims);
139 /* TODO: support raggedness */
140 bool last = j == breaks_.size () - 1;
141 bool ragged = ragged_right || (last && ragged_last);
142 sp->solve (&cols_[i*cols_rank_ + j], ragged);
144 if (!cols_[i*cols_rank_ + j].satisfies_constraints_)
149 /* work out all the starting indices */
150 for (vsize i = 0; i < start_.size (); i++)
153 for (j = 0; j < breaks_.size () - 1 && breaks_[j] < start_[i]; j++)
155 starting_breakpoints_.push_back (j);
156 start_[i] = breaks_[j];
158 state_.resize (start_.size ());
161 for (vsize i = 0; i < state_.size (); i++)
162 state_[i].resize((breaks_.size () - starting_breakpoints_[i]) * systems_);
164 /* fill out the matrices */
165 for (vsize i = 0; i < state_.size (); i++)
166 for (int j = valid_systems_; j < systems_; j++)
167 for (vsize k = starting_breakpoints_[i] + j + 1; k < breaks_.size (); k++)
168 if (!calc_subproblem (i, j, k))
169 break; /* if we couldn't break this, it is too cramped already */
170 valid_systems_ = systems_;
173 vector<Column_x_positions>
174 Constrained_breaking::get_solution (int start, int end, int sys_count)
177 prepare_solution (start, end, sys_count, &rank, &brk);
179 vector<Constrained_break_node> const &st = state_[start];
180 vector<Column_x_positions> ret;
182 for (int sys = sys_count-1; sys >= 0; sys--)
185 ret.push_back( st[sys*rank + brk].line_config_ );
186 brk = st[sys*rank + brk].prev_;
194 Constrained_breaking::get_demerits (int start, int end, int sys_count)
197 prepare_solution (start, end, sys_count, &rank, &brk);
199 return state_[start][(sys_count-1)*rank + brk].demerits_;
203 Constrained_breaking::get_force (int start, int end, int sys_count)
206 prepare_solution (start, end, sys_count, &rank, &brk);
207 vector<Constrained_break_node> const &st = state_[start];
210 for (int sys = sys_count-1; sys >= 0 && brk >= 0; sys--)
212 f += fabs (st[sys*rank + brk].force_);
213 brk = st[sys*rank + brk].prev_;
222 Constrained_breaking::get_penalty (int start, int end, int sys_count)
225 prepare_solution (start, end, sys_count, &rank, &brk);
227 return state_[start][(sys_count-1)*rank + brk].penalty_;
231 Constrained_breaking::get_page_penalty (int start, int end, int sys_count, int sys_num)
234 prepare_solution (start, end, sys_count, &rank, &brk);
237 for (sys = sys_count-1; sys > sys_num; sys--)
238 brk = state_[start][sys*rank + brk].prev_;
240 if (brk < 0) /* we didn't satisfy constraints */
242 vector<Grob*> &cols = state_[start][sys*rank + brk].line_config_.cols_;
246 Grob *pc = cols.back ();
249 SCM pen = pc->get_property ("page-penalty");
250 if (scm_is_number (pen) && fabs (scm_to_double (pen)) < 10000)
251 return scm_to_double (pen);
257 Constrained_breaking::get_min_systems (int start, int end)
260 prepare_solution (start, end, 1, &rank, &brk);
262 vector<Constrained_break_node> const &st = state_[start];
264 /* sys_count < rank : rank is the # of breakpoints, we can't have more systems */
265 for (sys_count = 0; sys_count < rank; sys_count++)
267 if (sys_count >= valid_systems_)
269 systems_ = sys_count + 3;
272 if (!isinf (st[sys_count*rank + brk].force_))
273 return sys_count + 1;
275 /* no possible breaks satisfy constraints */
280 Constrained_breaking::get_max_systems (int start, int end)
282 int brk = (end < 0 || end >= (int)start_.size ()) ? breaks_.size () - 1 : start_[end];
283 return brk - starting_breakpoints_[start];
287 Constrained_breaking::prepare_solution (int start, int end, int sys_count, int *rank, int *brk)
289 assert (start < (int)start_.size () && end <= (int)start_.size ());
290 assert (end < 0 || start < end);
291 assert (sys_count > 0);
293 if (sys_count >= valid_systems_)
295 systems_ = sys_count;
298 if (end == (int)start_.size ())
301 *rank = breaks_.size () - starting_breakpoints_[start];
302 *brk = end < 0 ? breaks_.size () - 1 : starting_breakpoints_[end];
303 *brk -= starting_breakpoints_[start];
306 Constrained_breaking::Constrained_breaking ()
308 valid_systems_ = systems_ = 0;
309 start_.push_back (0);
312 Constrained_breaking::Constrained_breaking (vector<int> const &start):
315 valid_systems_ = systems_ = 0;
319 Constrained_breaking::combine_demerits (Column_x_positions const &prev,
320 Column_x_positions const &col,
323 Real *demerits) const
326 if (col.cols_.empty () || !col.satisfies_constraints_)
332 Grob *pc = col.cols_.back ();
335 SCM pen = pc->get_property ("penalty");
336 if (scm_is_number (pen) && fabs (scm_to_double (pen)) < 10000)
337 *penalty += scm_to_double (pen);
341 *demerits = (*force) * (*force) + abs (prev.force_ - *force) + *penalty;