]> git.donarmstrong.com Git - lilypond.git/blob - lily/rest.cc
3a448546b05f74b034436da5a6df1d905062eb4d
[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, const string &style, bool try_ledgers,
163                   Real offset)
164 {
165   bool is_ledgered = false;
166   if (try_ledgers && (durlog == -1 || durlog == 0 || durlog == 1))
167     {
168       int const pos = int (Staff_symbol_referencer::get_position (me)
169                            + offset);
170       /*
171         half rests need ledger if not lying on a staff line,
172         whole rests need ledger if not hanging from a staff line,
173         breve rests need ledger if neither lying on nor hanging from a staff line
174       */
175       if (-1 <= durlog && durlog <= 1)
176         is_ledgered = !Staff_symbol_referencer::on_staff_line (me, pos)
177                       && !(durlog == -1
178                            && Staff_symbol_referencer::on_staff_line (me, pos + 2));
179     }
180
181   string actual_style (style.c_str ());
182
183   if ((style == "mensural") || (style == "neomensural"))
184     {
185
186       /*
187         FIXME: Currently, ancient font does not provide ledgered rests;
188         hence the "o" suffix in the glyph name is bogus.  But do we need
189         ledgered rests at all now that we can draw ledger lines with
190         variable width, length and blotdiameter? -- jr
191       */
192       is_ledgered = 0;
193
194       /*
195         There are no 32th/64th/128th mensural/neomensural rests.  In
196         these cases, revert back to default style.
197       */
198       if (durlog > 4)
199         actual_style = "";
200     }
201
202   if ((style == "classical") && (durlog != 2))
203     {
204       /*
205         classical style: revert back to default style for any rest other
206         than quarter rest
207       */
208       actual_style = "";
209     }
210
211   if (style == "default")
212     {
213       /*
214         Some parts of lily still prefer style "default" over "".
215         Correct this here. -- jr
216       */
217       actual_style = "";
218     }
219
220   return ("rests." + ::to_string (durlog) + (is_ledgered ? "o" : "")
221           + actual_style);
222 }
223
224 MAKE_SCHEME_CALLBACK (Rest, print, 1);
225 SCM
226 Rest::brew_internal_stencil (Grob *me, bool ledgered)
227 {
228   SCM durlog_scm = me->get_property ("duration-log");
229   if (!scm_is_number (durlog_scm))
230     return Stencil ().smobbed_copy ();
231
232   int durlog = scm_to_int (durlog_scm);
233
234   string style = robust_symbol2string (me->get_property ("style"), "default");
235
236   Font_metric *fm = Font_interface::get_default_font (me);
237   string font_char = glyph_name (me, durlog, style, ledgered, 0.0);
238   Stencil out = fm->find_by_name (font_char);
239   if (out.is_empty ())
240     me->warning (_f ("rest `%s' not found", font_char.c_str ()));
241
242   return out.smobbed_copy ();
243 }
244
245 /**
246    translate the rest vertically by amount DY, but only if
247    it doesn't have staff-position set.
248 */
249 void
250 Rest::translate (Grob *me, int dy)
251 {
252   if (!scm_is_number (me->get_property ("staff-position")))
253     {
254       me->translate_axis (dy * Staff_symbol_referencer::staff_space (me) / 2.0, Y_AXIS);
255       Grob *p = me->get_parent (Y_AXIS);
256       p->flush_extent_cache (Y_AXIS);
257     }
258 }
259
260 SCM
261 Rest::print (SCM smob)
262 {
263   return brew_internal_stencil (unsmob_grob (smob), true);
264 }
265
266 MAKE_SCHEME_CALLBACK (Rest, width, 1);
267 /*
268   We need the callback. The real stencil has ledgers depending on
269   Y-position. The Y-position is known only after line breaking.  */
270 SCM
271 Rest::width (SCM smob)
272 {
273   return generic_extent_callback (unsmob_grob (smob), X_AXIS);
274 }
275
276 MAKE_SCHEME_CALLBACK (Rest, height, 1);
277 SCM
278 Rest::height (SCM smob)
279 {
280   return generic_extent_callback (unsmob_grob (smob), Y_AXIS);
281 }
282
283 /*
284   We need the callback. The real stencil has ledgers depending on
285   Y-position. The Y-position is known only after line breaking.  */
286 SCM
287 Rest::generic_extent_callback (Grob *me, Axis a)
288 {
289   /*
290     Don't want ledgers: ledgers depend on Y position, which depends on
291     rest collision, which depends on stem size which depends on beam
292     slop of opposite note column.
293
294     consequence: we get too small extents and potential collisions
295     with ledgered rests.
296   */
297   SCM m = brew_internal_stencil (me, a != X_AXIS);
298   return ly_interval2scm (unsmob_stencil (m)->extent (a));
299 }
300
301 MAKE_SCHEME_CALLBACK (Rest, pure_height, 3);
302 SCM
303 Rest::pure_height (SCM smob,
304                    SCM /* start */,
305                    SCM /* end */)
306 {
307   Grob *me = unsmob_grob (smob);
308   SCM m = brew_internal_stencil (me, false);
309   return ly_interval2scm (unsmob_stencil (m)->extent (Y_AXIS));
310 }
311
312 ADD_INTERFACE (Rest,
313                "A rest symbol.  The property @code{style} can be"
314                " @code{default}, @code{mensural}, @code{neomensural} or"
315                " @code{classical}.",
316
317                /* properties */
318                "direction "
319                "minimum-distance "
320                "style "
321               );