Fixes Issue 1504, allowing feather beam line breaking.
[lilypond/patrick.git] / lily / page-turn-engraver.cc
blob5d08a24b91b3116c03b0a8963ce9dc104eb4ae4e
1 /*
2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2006--2011 Joe Neeman <joeneeman@gmail.com>
6 LilyPond is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 LilyPond is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
20 #include "engraver.hh"
22 #include "context.hh"
23 #include "duration.hh"
24 #include "grob.hh"
25 #include "international.hh"
26 #include "paper-column.hh"
27 #include "stream-event.hh"
28 #include "warn.hh"
30 #include "translator.icc"
32 class Page_turn_event {
33 public:
34 SCM permission_;
35 Real penalty_;
36 Interval_t<Rational> duration_;
38 Page_turn_event (Rational start, Rational end, SCM perm, Real pen)
40 duration_[LEFT] = start;
41 duration_[RIGHT] = end;
42 permission_ = perm;
43 penalty_ = pen;
46 /* Suppose we have decided on a possible page turn, only to change
47 out mind later (for example, if there is a volta repeat and it
48 would be difficult to turn the page back). Then we need to
49 re-penalize a region of the piece. Depending on how the events
50 intersect, we may have to split it into as many as 3 pieces.
52 vector<Page_turn_event> penalize (Page_turn_event const &penalty)
54 Interval_t<Rational> intersect = intersection (duration_, penalty.duration_);
55 vector<Page_turn_event> ret;
57 if (intersect.is_empty ())
59 ret.push_back (*this);
60 return ret;
63 Real new_pen = max (penalty_, penalty.penalty_);
65 if (duration_[LEFT] < penalty.duration_[LEFT])
66 ret.push_back (Page_turn_event (duration_[LEFT], penalty.duration_[LEFT], permission_, penalty_));
68 if (penalty.permission_ != SCM_EOL)
69 ret.push_back (Page_turn_event (intersect[LEFT], intersect[RIGHT], permission_, new_pen));
71 if (penalty.duration_[RIGHT] < duration_[RIGHT])
72 ret.push_back (Page_turn_event (penalty.duration_[RIGHT], duration_[RIGHT], permission_, penalty_));
74 return ret;
78 class Page_turn_engraver : public Engraver
80 Moment rest_begin_;
81 Moment repeat_begin_;
82 Moment note_end_;
83 Rational repeat_begin_rest_length_;
85 vector<Page_turn_event> forced_breaks_;
86 vector<Page_turn_event> automatic_breaks_;
87 vector<Page_turn_event> repeat_penalties_;
89 /* the next 3 are in sync (ie. same number of elements, etc.) */
90 vector<Rational> breakable_moments_;
91 vector<Grob*> breakable_columns_;
92 vector<bool> special_barlines_;
94 SCM max_permission (SCM perm1, SCM perm2);
95 Real penalty (Rational rest_len);
96 Grob *breakable_column (Page_turn_event const &);
98 protected:
99 DECLARE_TRANSLATOR_LISTENER (break);
100 DECLARE_ACKNOWLEDGER (note_head);
102 public:
103 TRANSLATOR_DECLARATIONS (Page_turn_engraver);
104 void stop_translation_timestep ();
105 void start_translation_timestep ();
106 void finalize ();
109 Page_turn_engraver::Page_turn_engraver ()
111 repeat_begin_ = Moment (-1);
112 repeat_begin_rest_length_ = 0;
113 rest_begin_ = 0;
114 note_end_ = 0;
117 Grob*
118 Page_turn_engraver::breakable_column (Page_turn_event const &brk)
120 vsize start = lower_bound (breakable_moments_, brk.duration_[LEFT], less<Rational> ());
121 vsize end = upper_bound (breakable_moments_, brk.duration_[RIGHT], less<Rational> ());
123 if (start == breakable_moments_.size ())
124 return NULL;
125 if (end == 0)
126 return NULL;
127 end--;
129 for (vsize i = end + 1; i-- > start;)
130 if (special_barlines_[i])
131 return breakable_columns_[i];
133 return breakable_columns_[end];
136 Real
137 Page_turn_engraver::penalty (Rational rest_len)
139 Rational min_turn = robust_scm2moment (get_property ("minimumPageTurnLength"), Moment (1)).main_part_;
141 return (rest_len < min_turn) ? infinity_f : 0;
144 void
145 Page_turn_engraver::acknowledge_note_head (Grob_info gi)
147 Stream_event *cause = gi.event_cause ();
149 Duration *dur_ptr = cause
150 ? unsmob_duration (cause->get_property ("duration"))
151 : 0;
153 if (!dur_ptr)
154 return;
156 if (rest_begin_ < now_mom ())
158 Real pen = penalty ((now_mom () - rest_begin_).main_part_);
159 if (!isinf (pen))
160 automatic_breaks_.push_back (Page_turn_event (rest_begin_.main_part_,
161 now_mom ().main_part_,
162 ly_symbol2scm ("allow"), 0));
165 if (rest_begin_ <= repeat_begin_)
166 repeat_begin_rest_length_ = (now_mom () - repeat_begin_).main_part_;
167 note_end_ = now_mom () + dur_ptr->get_length ();
170 IMPLEMENT_TRANSLATOR_LISTENER (Page_turn_engraver, break);
171 void
172 Page_turn_engraver::listen_break (Stream_event *ev)
174 string name = ly_scm2string (scm_symbol_to_string (ev->get_property ("class")));
176 if (name == "page-turn-event")
178 SCM permission = ev->get_property ("break-permission");
179 Real penalty = robust_scm2double (ev->get_property ("break-penalty"), 0);
180 Rational now = now_mom ().main_part_;
182 forced_breaks_.push_back (Page_turn_event (now, now, permission, penalty));
186 void
187 Page_turn_engraver::start_translation_timestep ()
189 /* What we want to do is to build a list of all the
190 breakable paper columns. In general, paper-columns won't be marked as
191 such until the Paper_column_engraver has done stop_translation_timestep.
193 Therefore, we just grab /all/ paper columns (in the
194 stop_translation_timestep, since they're not created here yet)
195 and remove the non-breakable ones at the beginning of the following
196 timestep.
199 if (breakable_columns_.size () && !Paper_column::is_breakable (breakable_columns_.back ()))
201 breakable_columns_.pop_back ();
202 breakable_moments_.pop_back ();
203 special_barlines_.pop_back ();
207 void
208 Page_turn_engraver::stop_translation_timestep ()
210 Grob *pc = unsmob_grob (get_property ("currentCommandColumn"));
212 if (pc)
214 breakable_columns_.push_back (pc);
215 breakable_moments_.push_back (now_mom ().main_part_);
217 SCM bar_scm = get_property ("whichBar");
218 string bar = robust_scm2string (bar_scm, "");
220 special_barlines_.push_back (bar != "" && bar != "|");
223 /* C&P from Repeat_acknowledge_engraver */
224 SCM cs = get_property ("repeatCommands");
225 bool start = false;
226 bool end = false;
228 for (; scm_is_pair (cs); cs = scm_cdr (cs))
230 SCM command = scm_car (cs);
231 if (command == ly_symbol2scm ("start-repeat"))
232 start = true;
233 else if (command == ly_symbol2scm ("end-repeat"))
234 end = true;
237 if (end && repeat_begin_.main_part_ >= Moment (0))
239 Rational now = now_mom ().main_part_;
240 Real pen = penalty ((now_mom () - rest_begin_).main_part_ + repeat_begin_rest_length_);
241 Moment *m = unsmob_moment (get_property ("minimumRepeatLengthForPageTurn"));
242 if (m && *m > (now_mom () - repeat_begin_))
243 pen = infinity_f;
245 if (pen == infinity_f)
246 repeat_penalties_.push_back (Page_turn_event (repeat_begin_.main_part_, now, SCM_EOL, -infinity_f));
247 else
248 repeat_penalties_.push_back (Page_turn_event (repeat_begin_.main_part_, now, ly_symbol2scm ("allow"), pen));
250 repeat_begin_ = Moment (-1);
253 if (start)
255 repeat_begin_ = now_mom ();
256 repeat_begin_rest_length_ = 0;
258 rest_begin_ = note_end_;
261 /* return the most permissive symbol (where force is the most permissive and
262 forbid is the least
265 Page_turn_engraver::max_permission (SCM perm1, SCM perm2)
267 if (perm1 == SCM_EOL)
268 return perm2;
269 if (perm1 == ly_symbol2scm ("allow") && perm2 == ly_symbol2scm ("force"))
270 return perm2;
271 return perm1;
274 void
275 Page_turn_engraver::finalize ()
277 vsize rep_index = 0;
278 vector<Page_turn_event> auto_breaks;
280 /* filter the automatic breaks through the repeat penalties */
281 for (vsize i = 0; i < automatic_breaks_.size (); i++)
283 Page_turn_event &brk = automatic_breaks_[i];
285 /* find the next applicable repeat penalty */
286 for (;
287 rep_index < repeat_penalties_.size ()
288 && repeat_penalties_[rep_index].duration_[RIGHT] <= brk.duration_[LEFT];
289 rep_index++)
292 if (rep_index >= repeat_penalties_.size ()
293 || brk.duration_[RIGHT] <= repeat_penalties_[rep_index].duration_[LEFT])
294 auto_breaks.push_back (brk);
295 else
297 vector<Page_turn_event> split = brk.penalize (repeat_penalties_[rep_index]);
299 /* it's possible that the last of my newly-split events overlaps the next repeat_penalty,
300 in which case we need to refilter that event */
301 if (rep_index + 1 < repeat_penalties_.size ()
302 && split.size ()
303 && split.back ().duration_[RIGHT] > repeat_penalties_[rep_index+1].duration_[LEFT])
305 automatic_breaks_[i] = split.back ();
306 split.pop_back ();
307 i--;
309 auto_breaks.insert (auto_breaks.end (), split.begin (), split.end ());
313 /* apply the automatic breaks */
314 for (vsize i = 0; i < auto_breaks.size (); i++)
316 Page_turn_event const &brk = auto_breaks[i];
317 Grob *pc = breakable_column (auto_breaks[i]);
318 if (pc)
320 SCM perm = max_permission (pc->get_property ("page-turn-permission"), brk.permission_);
321 Real pen = min (robust_scm2double (pc->get_property ("page-turn-penalty"), infinity_f), brk.penalty_);
322 pc->set_property ("page-turn-permission", perm);
323 pc->set_property ("page-turn-penalty", scm_from_double (pen));
327 /* unless a manual break overrides it, allow a page turn at the end of the piece */
328 breakable_columns_.back ()->set_property ("page-turn-permission", ly_symbol2scm ("allow"));
330 /* apply the manual breaks */
331 for (vsize i = 0; i < forced_breaks_.size (); i++)
333 Page_turn_event const &brk = forced_breaks_[i];
334 Grob *pc = breakable_column (forced_breaks_[i]);
335 if (pc)
337 pc->set_property ("page-turn-permission", brk.permission_);
338 pc->set_property ("page-turn-penalty", scm_from_double (brk.penalty_));
343 ADD_ACKNOWLEDGER (Page_turn_engraver, note_head);
345 ADD_TRANSLATOR (Page_turn_engraver,
346 /* doc */
347 "Decide where page turns are allowed to go.",
349 /* create */
352 /* read */
353 "minimumPageTurnLength "
354 "minimumRepeatLengthForPageTurn ",
356 /* write */