]> git.donarmstrong.com Git - lilypond.git/blob - lily/rest.cc
Issue 3307: Rest positions incorrect with non-standard line count
[lilypond.git] / lily / rest.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--2012 Han-Wen Nienhuys <hanwen@xs4all.nl>
5
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.
10
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.
15
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/>.
18 */
19
20 #include "rest.hh"
21
22 #include "directional-element-interface.hh"
23 #include "dots.hh"
24 #include "font-interface.hh"
25 #include "international.hh"
26 #include "output-def.hh"
27 #include "paper-score.hh"
28 #include "staff-symbol-referencer.hh"
29 #include "staff-symbol.hh"
30 #include "stencil.hh"
31 #include "grob.hh"
32
33 // -> offset callback
34 MAKE_SCHEME_CALLBACK (Rest, y_offset_callback, 1);
35 SCM
36 Rest::y_offset_callback (SCM smob)
37 {
38   Grob *me = unsmob_grob (smob);
39   int duration_log = scm_to_int (me->get_property ("duration-log"));
40   Real ss = Staff_symbol_referencer::staff_space (me);
41
42   return scm_from_double (ss * 0.5 * Rest::staff_position_internal (me, duration_log, get_grob_direction (me)));
43 }
44
45 Real
46 Rest::staff_position_internal (Grob *me, int duration_log, int dir)
47 {
48   if (!me)
49     return 0;
50
51   bool position_override = scm_is_number (me->get_property ("staff-position"));
52   Real pos;
53
54   if (position_override)
55     {
56       pos
57         = robust_scm2double (me->get_property ("staff-position"), 0);
58
59       /*
60         semibreve rests are positioned one staff line off
61       */
62       if (duration_log == 0)
63         return pos + 2;
64
65       /*
66         trust the client on good positioning;
67         would be tempting to adjust position of rests longer than a quarter
68         to be properly aligned to staff lines,
69         but custom rest shapes may not need that sort of care.
70       */
71
72       return pos;
73     }
74
75   pos = 4 * dir;
76
77   if (duration_log > 1)
78     /* Only half notes or longer want alignment with staff lines */
79     return pos;
80       
81   /*
82     We need a staff symbol for actually aligning anything
83   */
84   Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
85   if (!staff)
86     return pos;
87
88   std::vector<Real> linepos = Staff_symbol::line_positions (staff);
89
90   if (linepos.empty ())
91     return pos;
92       
93   std::sort (linepos.begin (), linepos.end ());
94           
95   if (duration_log == 0)
96     {
97       /*
98         lower voice semibreve rests generally hang a line lower
99       */
100
101       if (dir < 0)
102         pos -= 2;
103
104       /*
105         make a semibreve rest hang from the next available line,
106         except when there is none.
107       */
108       
109       std::vector<Real>::const_iterator it
110         = std::upper_bound (linepos.begin (), linepos.end (), pos);
111       if (it != linepos.end ())
112         pos = *it;
113       else
114         pos = linepos.back ();
115     }
116   else
117     {
118       std::vector<Real>::const_iterator it
119         = std::upper_bound (linepos.begin (), linepos.end (), pos);
120       if (it != linepos.begin ())
121         --it;
122       pos = *it;
123     }
124
125   /* Finished for neutral position */
126   if (!dir)
127     return pos;
128
129   /* If we have a voiced position, make sure that it's on the
130      proper side of neutral before using it.  If it isn't, we fall
131      back to a constant offset from neutral position.
132   */
133
134   Real neutral = staff_position_internal (me, duration_log, 0);
135
136   if (dir * (pos - neutral) > 0)
137     return pos;
138
139   return neutral + 4 * dir;
140 }
141
142 /* A rest might lie under a beam, in which case it should be cross-staff if
143    the beam is cross-staff because the rest's position depends on the
144    formatting of the beam. */
145 MAKE_SCHEME_CALLBACK (Rest, calc_cross_staff, 1);
146 SCM
147 Rest::calc_cross_staff (SCM smob)
148 {
149   Grob *me = unsmob_grob (smob);
150   Grob *stem = unsmob_grob (me->get_object ("stem"));
151
152   if (!stem)
153     return SCM_BOOL_F;
154
155   return stem->get_property ("cross-staff");
156 }
157
158 /*
159   make this function easily usable in C++
160 */
161 string
162 Rest::glyph_name (Grob *me, int durlog, string style, bool try_ledgers)
163 {
164   bool is_ledgered = false;
165   if (try_ledgers && (durlog == -1 || durlog == 0 || durlog == 1))
166     {
167       int const pos = int (Staff_symbol_referencer::get_position (me));
168
169       /*
170         half rests need ledger if not lying on a staff line,
171         whole rests need ledger if not hanging from a staff line,
172         breve rests need ledger if neither lying on nor hanging from a staff line
173       */
174       if (-1 <= durlog && durlog <= 1)
175         is_ledgered = !Staff_symbol_referencer::on_staff_line (me, pos)
176                       && !(durlog == -1
177                            && Staff_symbol_referencer::on_staff_line (me, pos + 2));
178     }
179
180   string actual_style (style.c_str ());
181
182   if ((style == "mensural") || (style == "neomensural"))
183     {
184
185       /*
186         FIXME: Currently, ancient font does not provide ledgered rests;
187         hence the "o" suffix in the glyph name is bogus.  But do we need
188         ledgered rests at all now that we can draw ledger lines with
189         variable width, length and blotdiameter? -- jr
190       */
191       is_ledgered = 0;
192
193       /*
194         There are no 32th/64th/128th mensural/neomensural rests.  In
195         these cases, revert back to default style.
196       */
197       if (durlog > 4)
198         actual_style = "";
199     }
200
201   if ((style == "classical") && (durlog != 2))
202     {
203       /*
204         classical style: revert back to default style for any rest other
205         than quarter rest
206       */
207       actual_style = "";
208     }
209
210   if (style == "default")
211     {
212       /*
213         Some parts of lily still prefer style "default" over "".
214         Correct this here. -- jr
215       */
216       actual_style = "";
217     }
218
219   return ("rests." + to_string (durlog) + (is_ledgered ? "o" : "")
220           + actual_style);
221 }
222
223 MAKE_SCHEME_CALLBACK (Rest, print, 1);
224 SCM
225 Rest::brew_internal_stencil (Grob *me, bool ledgered)
226 {
227   SCM durlog_scm = me->get_property ("duration-log");
228   if (!scm_is_number (durlog_scm))
229     return Stencil ().smobbed_copy ();
230
231   int durlog = scm_to_int (durlog_scm);
232
233   string style = robust_symbol2string (me->get_property ("style"), "default");
234
235   Font_metric *fm = Font_interface::get_default_font (me);
236   string font_char = glyph_name (me, durlog, style, ledgered);
237   Stencil out = fm->find_by_name (font_char);
238   if (out.is_empty ())
239     me->warning (_f ("rest `%s' not found", font_char.c_str ()));
240
241   return out.smobbed_copy ();
242 }
243
244 /**
245    translate the rest vertically by amount DY, but only if
246    it doesn't have staff-position set.
247 */
248 void
249 Rest::translate (Grob *me, int dy)
250 {
251   if (!scm_is_number (me->get_property ("staff-position")))
252     {
253       me->translate_axis (dy * Staff_symbol_referencer::staff_space (me) / 2.0, Y_AXIS);
254       Grob *p = me->get_parent (Y_AXIS);
255       p->flush_extent_cache (Y_AXIS);
256     }
257 }
258
259 SCM
260 Rest::print (SCM smob)
261 {
262   return brew_internal_stencil (unsmob_grob (smob), true);
263 }
264
265 MAKE_SCHEME_CALLBACK (Rest, width, 1);
266 /*
267   We need the callback. The real stencil has ledgers depending on
268   Y-position. The Y-position is known only after line breaking.  */
269 SCM
270 Rest::width (SCM smob)
271 {
272   return generic_extent_callback (unsmob_grob (smob), X_AXIS);
273 }
274
275 MAKE_SCHEME_CALLBACK (Rest, height, 1);
276 SCM
277 Rest::height (SCM smob)
278 {
279   return generic_extent_callback (unsmob_grob (smob), Y_AXIS);
280 }
281
282 /*
283   We need the callback. The real stencil has ledgers depending on
284   Y-position. The Y-position is known only after line breaking.  */
285 SCM
286 Rest::generic_extent_callback (Grob *me, Axis a)
287 {
288   /*
289     Don't want ledgers: ledgers depend on Y position, which depends on
290     rest collision, which depends on stem size which depends on beam
291     slop of opposite note column.
292
293     consequence: we get too small extents and potential collisions
294     with ledgered rests.
295   */
296   SCM m = brew_internal_stencil (me, a != X_AXIS);
297   return ly_interval2scm (unsmob_stencil (m)->extent (a));
298 }
299
300 MAKE_SCHEME_CALLBACK (Rest, pure_height, 3);
301 SCM
302 Rest::pure_height (SCM smob,
303                    SCM /* start */,
304                    SCM /* end */)
305 {
306   Grob *me = unsmob_grob (smob);
307   SCM m = brew_internal_stencil (me, false);
308   return ly_interval2scm (unsmob_stencil (m)->extent (Y_AXIS));
309 }
310
311 ADD_INTERFACE (Rest,
312                "A rest symbol.  The property @code{style} can be"
313                " @code{default}, @code{mensural}, @code{neomensural} or"
314                " @code{classical}.",
315
316                /* properties */
317                "direction "
318                "minimum-distance "
319                "style "
320               );