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