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>
13 * vsize vs. int: casts should not be necessary. Use VPOS iso -1 as
14 magic signaling value?
16 * The specification uses A j, k, n and m as variables.
18 Functions use start,end,sys_count,calc_subproblem as variables. Use the same naming
19 for the specification as for the code.
34 * no spurious * in <slash><star> <star><slash> comments.
40 #include "constrained-breaking.hh"
42 #include "international.hh"
44 #include "output-def.hh"
45 #include "paper-column.hh"
46 #include "paper-score.hh"
47 #include "simple-spacer.hh"
52 print_constrained_break_nodes (vector<Constrained_break_node> const &arr)
54 for (vsize i = 0; i < arr.size (); i++)
56 printf ("node %d: ", (int)i);
62 We use the following optimal substructure. Let W(A) be our weight function.
64 Let A_{k,n} = (a_{k,n,1}, ... a_{k,n,k}) be the optimal set of line breaks
65 for k systems and n potential breakpoints. a_{k,n,k} = n (it is the end of
68 Then A_{k+1, m} is contructed from
69 min_ {k < j < m} ( W(A_{k,j} :: m) )
70 where by A::m we denote appending m to the list A
74 /* start and sys here are indexed from 0.
76 max_break is indexed from starting_breakpoints_[start] (for
77 max_break, starting_breakpoints_[start] is the beginning of the
78 piece; the smallest value we should ever see here is
79 starting_breakpoints_[start] + 1) */
81 Constrained_breaking::calc_subproblem (int start, int sys, int max_break)
83 assert (sys < systems_);
84 assert (start < (int)start_.size ());
85 assert (max_break < (int)breaks_.size ());
87 bool found_something = false;
88 int start_col = starting_breakpoints_[start];
89 vector<Constrained_break_node> &st = state_[start];
90 int rank = breaks_.size () - start_col;
91 int max_index = max_break - start_col;
92 for (int j = sys; j < max_index; j++)
94 if (0 == sys && j > 0)
95 break; /* the first line cannot have its first break after the beginning */
97 Column_x_positions const &cur = cols_[(j + start_col)*cols_rank_ + max_break];
98 Column_x_positions prev;
103 prev = st[(sys-1) * rank + j].line_config_;
104 prev_dem = st[(sys-1) * rank + j].demerits_;
106 if (isinf (prev_dem))
109 Real dem, force, pen;
110 combine_demerits(prev, cur, &force, &pen, &dem);
115 int k = sys*rank + max_index;
116 if (isinf (st[k].demerits_)
117 || dem < st[k].demerits_)
119 found_something = true;
122 TODO: maybe just copy a Constrained_break_node ?
124 st[k].demerits_ = dem;
125 st[k].force_ = force;
126 st[k].penalty_ = pen;
128 st[k].line_config_ = cur;
131 return found_something;
134 vector<Column_x_positions>
135 Constrained_breaking::solve ()
139 programming_error (_f ("no system number set in constrained-breaking"));
140 systems_ = start_.size () / 2;
144 return get_solution (0, systems_, -1);
148 Constrained_breaking::resize ()
150 if (!breaks_.size ())
152 bool ragged_right = to_boolean (pscore_->layout ()->c_variable ("ragged-right"));
153 bool ragged_last = to_boolean (pscore_->layout ()->c_variable ("ragged-last"));
155 /* do all the rod/spring problems */
156 breaks_ = pscore_->find_break_indices ();
157 cols_rank_ = breaks_.size ();
158 all_ = pscore_->root_system ()->columns ();
159 cols_.resize (breaks_.size () * breaks_.size ());
160 for (vsize i = 0; i < breaks_.size () - 1; i++)
161 for (vsize j = i + 1; j < breaks_.size (); j++)
163 vector<Grob*> line (all_.begin () + breaks_[i],
164 all_.begin() + breaks_[j] + 1);
166 line[0] = dynamic_cast<Item *> (line[0])->find_prebroken_piece (RIGHT);
167 line.back () = dynamic_cast<Item *> (line.back ())->find_prebroken_piece (LEFT);
169 cols_[i*cols_rank_ + j].cols_ = line;
171 /* we have no idea what line this will be -- only whether it is the first */
172 Interval line_dims = line_dimensions_int (pscore_->layout (), i);
173 Simple_spacer_wrapper *sp = generate_spacing_problem (line, line_dims);
175 bool last = j == breaks_.size () - 1;
176 bool ragged = ragged_right || (last && ragged_last);
177 sp->solve (&cols_[i*cols_rank_ + j], ragged);
179 if (!cols_[i*cols_rank_ + j].satisfies_constraints_)
184 /* work out all the starting indices */
185 for (vsize i = 0; i < start_.size (); i++)
188 for (j = 0; j < breaks_.size () - 1 && breaks_[j] < start_[i]; j++)
190 starting_breakpoints_.push_back (j);
191 start_[i] = breaks_[j];
193 state_.resize (start_.size ());
196 for (vsize i = 0; i < state_.size (); i++)
197 state_[i].resize((breaks_.size () - starting_breakpoints_[i]) * systems_);
199 /* fill out the matrices */
200 for (vsize i = 0; i < state_.size (); i++)
201 for (int j = valid_systems_; j < systems_; j++)
202 for (vsize k = starting_breakpoints_[i] + j + 1; k < breaks_.size (); k++)
203 if (!calc_subproblem (i, j, k))
204 break; /* if we couldn't break this, it is too cramped already */
206 valid_systems_ = systems_;
209 vector<Column_x_positions>
210 Constrained_breaking::get_solution (int start, int end, int sys_count)
214 prepare_solution (start, end, sys_count, &rank, &brk);
216 vector<Constrained_break_node> const &st = state_[start];
217 vector<Column_x_positions> ret;
219 for (int sys = sys_count-1; sys >= 0; sys--)
222 ret.push_back (st[sys*rank + brk].line_config_);
223 brk = st[sys*rank + brk].prev_;
232 Constrained_breaking::get_demerits (int start, int end, int sys_count)
236 prepare_solution (start, end, sys_count, &rank, &brk);
238 return state_[start][(sys_count-1)*rank + brk].demerits_;
242 Constrained_breaking::get_force (int start, int end, int sys_count)
246 prepare_solution (start, end, sys_count, &rank, &brk);
247 vector<Constrained_break_node> const &st = state_[start];
250 for (int sys = sys_count-1; sys >= 0 && brk >= 0; sys--)
252 f += fabs (st[sys*rank + brk].force_);
253 brk = st[sys*rank + brk].prev_;
262 Constrained_breaking::get_penalty (int start, int end, int sys_count)
266 prepare_solution (start, end, sys_count, &rank, &brk);
268 return state_[start][(sys_count-1)*rank + brk].penalty_;
272 Constrained_breaking::get_page_penalty (int start, int end, int sys_count, int sys_num)
276 prepare_solution (start, end, sys_count, &rank, &brk);
279 for (sys = sys_count-1; sys > sys_num; sys--)
280 brk = state_[start][sys*rank + brk].prev_;
282 if (brk < 0) /* we didn't satisfy constraints */
284 vector<Grob*> &cols = state_[start][sys*rank + brk].line_config_.cols_;
288 Grob *pc = cols.back ();
291 SCM pen = pc->get_property ("page-penalty");
292 if (scm_is_number (pen) && fabs (scm_to_double (pen)) < 10000)
293 return scm_to_double (pen);
299 Constrained_breaking::get_min_systems (int start, int end)
303 prepare_solution (start, end, 1, &rank, &brk);
305 vector<Constrained_break_node> const &st = state_[start];
307 /* sys_count < rank : rank is the # of breakpoints, we can't have more systems */
308 for (sys_count = 0; sys_count < rank; sys_count++)
310 if (sys_count >= valid_systems_)
312 systems_ = sys_count + 3;
315 if (!isinf (st[sys_count*rank + brk].force_))
316 return sys_count + 1;
318 /* no possible breaks satisfy constraints */
323 Constrained_breaking::get_max_systems (int start, int end)
325 int brk = (end < 0 || end >= (int)start_.size ()) ? breaks_.size () - 1 : start_[end];
326 return brk - starting_breakpoints_[start];
330 Constrained_breaking::prepare_solution (vsize start, int end, int sys_count, int *rank, int *brk)
332 assert (start < start_.size () && end <= int (start_.size ()));
333 assert (end < 0 || int (start) < end);
334 assert (sys_count > 0);
336 if (sys_count >= valid_systems_)
338 systems_ = sys_count;
341 if (end == (int)start_.size ())
344 *rank = breaks_.size () - starting_breakpoints_[start];
345 *brk = end < 0 ? breaks_.size () - 1 : starting_breakpoints_[end];
346 *brk -= starting_breakpoints_[start];
349 Constrained_breaking::Constrained_breaking ()
351 valid_systems_ = systems_ = 0;
352 start_.push_back (0);
355 Constrained_breaking::Constrained_breaking (vector<int> const &start)
358 valid_systems_ = systems_ = 0;
362 Constrained_breaking::combine_demerits (Column_x_positions const &prev,
363 Column_x_positions const &col,
366 Real *demerits) const
369 if (col.cols_.empty () || !col.satisfies_constraints_)
375 Grob *pc = col.cols_.back ();
378 SCM pen = pc->get_property ("penalty");
379 if (scm_is_number (pen) && fabs (scm_to_double (pen)) < 10000)
380 *penalty += scm_to_double (pen);
384 *demerits = (*force) * (*force) + abs (prev.force_ - *force) + *penalty;