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 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)
25 force_ = (height - rod_height_ - last_line_.bottom_padding_ - spring_len_)
26 / max (0.1, inverse_spring_k_);
30 Page_spacing::resize (Real new_height)
32 page_height_ = new_height;
37 Page_spacing::append_system (const Line_details &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_;
54 Page_spacing::prepend_system (const Line_details &line)
57 rod_height_ += line.padding_;
61 rod_height_ += line.extent_.length ();
62 spring_len_ += line.space_;
63 inverse_spring_k_ += line.inverse_hooke_;
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)
81 first_page_num_ = first_page_num;
84 ragged_ = breaker->ragged ();
85 ragged_last_ = breaker->is_last () && breaker->ragged_last ();
89 Page_spacer::solve (vsize page_count)
91 if (page_count > max_page_count_)
94 Page_spacing_result ret;
95 ret.force_.resize (page_count);
96 ret.systems_per_page_.resize (page_count);
98 vsize system = lines_.size () - 1;
99 vsize tack_onto_the_end = 0;
101 if (isinf (state_.at (system, page_count-1).demerits_))
103 programming_error ("tried to space systems on a bad number of pages");
104 /* Usually, this means that we tried to cram too many systems into
105 to few pages. To avoid crashing, we look for the largest number of
106 systems that we can fit properly onto the right number of pages.
107 All the systems that don't fit get tacked onto the last page.
110 for (i = system; isinf (state_.at (i, page_count-1).demerits_) && i; i--)
115 tack_onto_the_end = system - i;
119 return Page_spacing_result (); /* couldn't salvage it -- probably going to crash */
122 ret.penalty_ = state_.at (system, page_count-1).penalty_
123 + lines_.back ().page_penalty_ + lines_.back ().turn_penalty_;
126 for (vsize p = page_count; p--;)
128 assert (system != VPOS);
130 Page_spacing_node const &ps = state_.at (system, p);
131 ret.force_[p] = ps.force_;
132 ret.demerits_ += ps.force_ * ps.force_;
134 ret.systems_per_page_[p] = system + 1;
136 ret.systems_per_page_[p] = system - ps.prev_ + tack_onto_the_end;
139 ret.demerits_ += ret.penalty_;
144 Page_spacer::resize (vsize page_count)
146 assert (page_count > 0);
148 if (max_page_count_ >= page_count)
151 state_.resize (lines_.size (), page_count, Page_spacing_node ());
152 for (vsize page = max_page_count_; page < page_count; page++)
153 for (vsize line = page; line < lines_.size (); line++)
154 if (!calc_subproblem (page, line))
157 max_page_count_ = page_count;
161 Page_spacer::calc_subproblem (vsize page, vsize line)
163 bool last = line == lines_.size () - 1;
164 Page_spacing space (breaker_->page_height (page + first_page_num_, last),
165 breaker_->page_top_space ());
166 Page_spacing_node &cur = state_.at (line, page);
167 bool ragged = ragged_ || (ragged_last_ && last);
169 for (vsize page_start = line+1; page_start > page && page_start--;)
171 Page_spacing_node const *prev = page > 0 ? &state_.at (page_start-1, page-1) : 0;
173 space.prepend_system (lines_[page_start]);
174 if (page_start < line && (isinf (space.force_) || (space.force_ < 0 && ragged)))
177 if (page > 0 || page_start == 0)
179 if (line == lines_.size () - 1 && ragged_last_ && space.force_ > 0)
182 /* we may have to deal with single lines that are taller than a page */
183 if (isinf (space.force_) && page_start == line)
184 space.force_ = -200000;
186 Real dem = fabs (space.force_) + (prev ? prev->demerits_ : 0);
189 penalty = lines_[page_start-1].page_penalty_
190 + (page % 2 == 0) ? lines_[page_start-1].turn_penalty_ : 0;
193 if (dem < cur.demerits_ || page_start == line)
196 cur.force_ = space.force_;
197 cur.penalty_ = penalty + (prev ? prev->penalty_ : 0);
198 cur.prev_ = page_start - 1;
203 && lines_[page_start-1].page_permission_ == ly_symbol2scm ("force"))
206 return !isinf (cur.demerits_);