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--2007 Joe Neeman <joeneeman@gmail.com>
10 #include "page-spacing.hh"
13 #include "page-breaking.hh"
17 Page_spacing::calc_force ()
19 if (rod_height_ + last_line_.bottom_padding_ >= page_height_)
22 force_ = (page_height_ - rod_height_ - last_line_.bottom_padding_ - spring_len_)
23 / max (0.1, inverse_spring_k_);
27 Page_spacing::resize (Real new_height)
29 page_height_ = new_height;
34 Page_spacing::append_system (const Line_details &line)
36 rod_height_ += last_line_.padding_;
38 rod_height_ += line.extent_.length ();
39 spring_len_ += line.space_;
40 inverse_spring_k_ += line.inverse_hooke_;
48 Page_spacing::prepend_system (const Line_details &line)
51 rod_height_ += line.padding_;
55 rod_height_ += line.extent_.length ();
56 spring_len_ += line.space_;
57 inverse_spring_k_ += line.inverse_hooke_;
63 Page_spacing::clear ()
65 force_ = rod_height_ = spring_len_ = 0;
66 inverse_spring_k_ = 0;
70 Page_spacer::Page_spacer (vector<Line_details> const &lines, vsize first_page_num, Page_breaking const *breaker)
73 first_page_num_ = first_page_num;
76 ragged_ = breaker->ragged ();
77 ragged_last_ = breaker->is_last () && breaker->ragged_last ();
81 Page_spacer::solve (vsize page_count)
83 if (page_count > max_page_count_)
86 Page_spacing_result ret;
87 ret.force_.resize (page_count);
88 ret.systems_per_page_.resize (page_count);
90 vsize system = lines_.size () - 1;
91 vsize tack_onto_the_end = 0;
93 if (isinf (state_.at (system, page_count-1).demerits_))
95 programming_error ("tried to space systems on a bad number of pages");
96 /* Usually, this means that we tried to cram too many systems into
97 to few pages. To avoid crashing, we look for the largest number of
98 systems that we can fit properly onto the right number of pages.
99 All the systems that don't fit get tacked onto the last page.
102 for (i = system; isinf (state_.at (i, page_count-1).demerits_) && i; i--)
107 tack_onto_the_end = system - i;
111 return Page_spacing_result (); /* couldn't salvage it -- probably going to crash */
114 ret.penalty_ = state_.at (system, page_count-1).penalty_
115 + lines_.back ().page_penalty_ + lines_.back ().turn_penalty_;
118 for (vsize p = page_count; p--;)
120 assert (system != VPOS);
122 Page_spacing_node const &ps = state_.at (system, p);
123 ret.force_[p] = ps.force_;
124 ret.demerits_ += ps.force_ * ps.force_;
126 ret.systems_per_page_[p] = system + 1;
128 ret.systems_per_page_[p] = system - ps.prev_ + tack_onto_the_end;
131 ret.demerits_ += ret.penalty_;
136 Page_spacer::resize (vsize page_count)
138 assert (page_count > 0);
140 if (max_page_count_ >= page_count)
143 state_.resize (lines_.size (), page_count, Page_spacing_node ());
144 for (vsize page = max_page_count_; page < page_count; page++)
145 for (vsize line = page; line < lines_.size (); line++)
146 if (!calc_subproblem (page, line))
149 max_page_count_ = page_count;
153 Page_spacer::calc_subproblem (vsize page, vsize line)
155 bool last = line == lines_.size () - 1;
156 Page_spacing space (breaker_->page_height (page + first_page_num_, last));
157 Page_spacing_node &cur = state_.at (line, page);
158 bool ragged = ragged_ || (ragged_last_ && last);
160 for (vsize page_start = line+1; page_start > page && page_start--;)
162 Page_spacing_node const *prev = page > 0 ? &state_.at (page_start-1, page-1) : 0;
164 space.prepend_system (lines_[page_start]);
165 if (page_start < line && (isinf (space.force_) || (space.force_ < 0 && ragged)))
168 if (page > 0 || page_start == 0)
170 if (line == lines_.size () - 1 && ragged_last_ && space.force_ > 0)
173 /* we may have to deal with single lines that are taller than a page */
174 if (isinf (space.force_) && page_start == line)
175 space.force_ = -200000;
177 Real dem = fabs (space.force_) + (prev ? prev->demerits_ : 0);
180 penalty = lines_[page_start-1].page_penalty_
181 + (page % 2 == 0) ? lines_[page_start-1].turn_penalty_ : 0;
184 if (dem < cur.demerits_ || page_start == line)
187 cur.force_ = space.force_;
188 cur.penalty_ = penalty + (prev ? prev->penalty_ : 0);
189 cur.prev_ = page_start - 1;
194 && lines_[page_start-1].page_permission_ == ly_symbol2scm ("force"))
197 return !isinf (cur.demerits_);