Workaround for broken MusicXML files (percussion clef in MuseScore)
[lilypond.git] / lily / page-spacing.cc
blobc60f0d88b1d171d3df4608169575ea7cf6331cfa
1 /*
2 page-spacing.cc - implement routines for spacing
3 systems vertically on pages
5 source file of the GNU LilyPond music typesetter
7 (c) 2006--2009 Joe Neeman <joeneeman@gmail.com>
8 */
10 #include "page-spacing.hh"
12 #include "matrix.hh"
13 #include "page-breaking.hh"
14 #include "warn.hh"
16 void
17 Page_spacing::calc_force ()
19 /* If the first system is a title, we add back in the page-top-space. */
20 Real height = first_line_.title_ ? page_height_ + page_top_space_ : page_height_;
22 if (rod_height_ + last_line_.bottom_padding_ >= height)
23 force_ = infinity_f;
24 else
25 force_ = (height - rod_height_ - last_line_.bottom_padding_ - spring_len_)
26 / max (0.1, inverse_spring_k_);
29 void
30 Page_spacing::resize (Real new_height)
32 page_height_ = new_height;
33 calc_force ();
36 void
37 Page_spacing::append_system (const Line_details &line)
39 if (!rod_height_)
40 first_line_ = line;
42 rod_height_ += last_line_.padding_;
44 rod_height_ += line.extent_.length ();
45 spring_len_ += line.space_;
46 inverse_spring_k_ += line.inverse_hooke_;
48 last_line_ = line;
50 calc_force ();
53 void
54 Page_spacing::prepend_system (const Line_details &line)
56 if (rod_height_)
57 rod_height_ += line.padding_;
58 else
59 last_line_ = line;
61 rod_height_ += line.extent_.length ();
62 spring_len_ += line.space_;
63 inverse_spring_k_ += line.inverse_hooke_;
65 first_line_ = line;
67 calc_force ();
70 void
71 Page_spacing::clear ()
73 force_ = rod_height_ = spring_len_ = 0;
74 inverse_spring_k_ = 0;
78 Page_spacer::Page_spacer (vector<Line_details> const &lines, vsize first_page_num, Page_breaking const *breaker)
79 : lines_ (lines)
81 first_page_num_ = first_page_num;
82 breaker_ = breaker;
83 max_page_count_ = 0;
84 ragged_ = breaker->ragged ();
85 ragged_last_ = breaker->is_last () && breaker->ragged_last ();
88 Page_spacing_result
89 Page_spacer::solve (vsize page_count)
91 if (page_count > max_page_count_)
92 resize (page_count);
94 Page_spacing_result ret;
96 vsize system = lines_.size () - 1;
97 vsize extra_systems = 0;
98 vsize extra_pages = 0;
100 if (isinf (state_.at (system, page_count-1).demerits_))
102 programming_error ("tried to space systems on a bad number of pages");
103 /* Usually, this means that we tried to cram too many systems into
104 to few pages. To avoid crashing, we look for the largest number of
105 systems that we can fit properly onto the right number of pages.
106 All the systems that don't fit get tacked onto the last page.
108 vsize i;
109 for (i = system; isinf (state_.at (i, page_count-1).demerits_) && i; i--)
112 if (i)
114 extra_systems = system - i;
115 system = i;
117 else
119 /* try chopping off pages from the end */
120 vsize j;
121 for (j = page_count; j && isinf (state_.at (system, j-1).demerits_); j--)
124 if (j)
126 extra_pages = page_count - j;
127 page_count = j;
129 else
130 return Page_spacing_result (); /* couldn't salvage it -- probably going to crash */
134 ret.force_.resize (page_count);
135 ret.systems_per_page_.resize (page_count);
136 ret.penalty_ = state_.at (system, page_count-1).penalty_
137 + lines_.back ().page_penalty_ + lines_.back ().turn_penalty_;
139 ret.demerits_ = 0;
140 for (vsize p = page_count; p--;)
142 assert (system != VPOS);
144 Page_spacing_node const &ps = state_.at (system, p);
145 ret.force_[p] = ps.force_;
146 ret.demerits_ += ps.force_ * ps.force_;
147 if (p == 0)
148 ret.systems_per_page_[p] = system + 1;
149 else
150 ret.systems_per_page_[p] = system - ps.prev_;
151 system = ps.prev_;
154 if (extra_systems)
156 ret.systems_per_page_.back () += extra_systems;
157 ret.demerits_ += BAD_SPACING_PENALTY;
159 if (extra_pages)
161 ret.force_.insert (ret.force_.end (), extra_pages, BAD_SPACING_PENALTY);
162 ret.systems_per_page_.insert (ret.systems_per_page_.end (), extra_pages, 0);
163 ret.demerits_ += BAD_SPACING_PENALTY;
167 ret.demerits_ += ret.penalty_;
168 return ret;
171 void
172 Page_spacer::resize (vsize page_count)
174 assert (page_count > 0);
176 if (max_page_count_ >= page_count)
177 return;
179 state_.resize (lines_.size (), page_count, Page_spacing_node ());
180 for (vsize page = max_page_count_; page < page_count; page++)
181 for (vsize line = page; line < lines_.size (); line++)
182 if (!calc_subproblem (page, line))
183 break;
185 max_page_count_ = page_count;
188 bool
189 Page_spacer::calc_subproblem (vsize page, vsize line)
191 bool last = line == lines_.size () - 1;
192 Page_spacing space (breaker_->page_height (page + first_page_num_, last),
193 breaker_->page_top_space ());
194 Page_spacing_node &cur = state_.at (line, page);
195 bool ragged = ragged_ || (ragged_last_ && last);
196 int line_count = 0;
198 for (vsize page_start = line+1; page_start > page && page_start--;)
200 Page_spacing_node const *prev = page > 0 ? &state_.at (page_start-1, page-1) : 0;
202 space.prepend_system (lines_[page_start]);
204 // This 'if' statement is a little hard to parse. It won't consider this configuration
205 // if it is overfull unless the current configuration is the first one with this start
206 // point. We also make an exception (and consider this configuration) if the previous
207 // configuration we tried had fewer lines than min-systems-per-page.
208 if (!breaker_->too_few_lines (line_count)
209 && page_start < line
210 && (isinf (space.force_) || (space.force_ < 0 && ragged)))
211 break;
213 line_count += lines_[page_start].compressed_nontitle_lines_count_;
214 if (page > 0 || page_start == 0)
216 if (line == lines_.size () - 1 && ragged && last && space.force_ > 0)
217 space.force_ = 0;
219 Real demerits = space.force_ * space.force_;
220 /* If a single line is taller than a page, we need to consider it as
221 a possible solution (but we give it a very bad score). */
222 if (isinf (space.force_) && page_start == line)
223 demerits = BAD_SPACING_PENALTY;
224 demerits += (prev ? prev->demerits_ : 0);
226 Real penalty = breaker_->line_count_penalty (line_count);
227 if (page_start > 0)
228 penalty += lines_[page_start-1].page_penalty_
229 + (page % 2 == 0) ? lines_[page_start-1].turn_penalty_ : 0;
231 demerits += penalty;
232 if (demerits < cur.demerits_ || page_start == line)
234 cur.demerits_ = demerits;
235 cur.force_ = space.force_;
236 cur.penalty_ = penalty + (prev ? prev->penalty_ : 0);
237 cur.system_count_status_ = breaker_->line_count_status (line_count)
238 | (prev ? prev->system_count_status_ : 0);
239 cur.prev_ = page_start - 1;
243 if (page_start > 0
244 && lines_[page_start-1].page_permission_ == ly_symbol2scm ("force"))
245 break;
247 return !isinf (cur.demerits_);