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