]> git.donarmstrong.com Git - lilypond.git/blob - lily/note-head.cc
* lily/include/lily-guile.hh: many new ly_ functions. Thanks to
[lilypond.git] / lily / note-head.cc
1 /*
2   notehead.cc -- implement Note_head
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8 #include <math.h>
9 #include <ctype.h>
10
11 #include "staff-symbol.hh"
12 #include "misc.hh"
13 #include "dots.hh"
14 #include "note-head.hh"
15 #include "warn.hh"
16 #include "font-interface.hh"
17 #include "stencil.hh"
18 #include "event.hh"
19 #include "rhythmic-head.hh"
20 #include "staff-symbol-referencer.hh"
21 #include "lookup.hh"
22 #include "paper-def.hh"
23
24 /*
25   Note_head contains the code for printing note heads.
26
27   Ledger lines:
28
29   It also contains the ledger lines, for historical reasons.  Ledger
30   lines are somewhat of a PITA. In some cases, they take up no space, in
31   some cases they don't:
32
33   DO take space:
34
35   - when ledgered notes are juxtaposed: there should be some white
36    space between the ledger lines.
37
38   - when accidentals are near: the accidentals should not be on the
39   ledger lines
40
41   [both tips by Heinz Stolba from Universal Edition].
42
43   DO NOT take space into account:
44
45   - for basically everything else, e.g. swapping ledgered notes on
46    clustered chords, spacing between ledgered and unledgered notes.
47   
48   TODO: fix this. It is not feasible to have a special grob for
49   ledgers, since you basically don't know if there will be ledgers,
50   unless you know at interpretation phase already 1. the Y-position,
51   2. the number of staff lines. It's not yet specced when both pieces
52   of information are there, so for now, it is probably better to build
53   special support for ledgers into the accidental and separation-item
54   code.
55
56   (Besides a separate ledger seems overkill. For what else would
57   it be useful?)
58
59 */
60
61 /*
62   TODO: ledger lines are also a property of the staff. Maybe move them
63   to there?
64  */
65 Stencil
66 Note_head::brew_ledger_lines (Grob *me,
67                               int pos,
68                               int interspaces,
69                               Interval x_extent,
70                               Real left_shorten,
71                               bool take_space)
72 {
73   Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
74   Real inter_f = Staff_symbol_referencer::staff_space (me)/2;
75   int line_count = (abs (pos) < interspaces)
76     ? 0
77     : (abs (pos) - interspaces) / 2;
78   Stencil stencil = Stencil ();
79
80
81   if (line_count)
82     {
83       Real ledgerlinethickness =
84         Staff_symbol::get_ledger_line_thickness (staff);
85       Real blotdiameter = ledgerlinethickness;
86       Interval y_extent =
87         Interval (-0.5*(ledgerlinethickness),
88                   +0.5*(ledgerlinethickness));
89       Stencil proto_ledger_line =
90         Lookup::round_filled_box (Box (x_extent, y_extent), blotdiameter);
91
92       x_extent[LEFT] += left_shorten;
93       Stencil proto_first_line =
94         Lookup::round_filled_box (Box (x_extent, y_extent), blotdiameter);
95
96       if (!take_space)
97         {
98           proto_ledger_line.set_empty (true);
99           proto_first_line.set_empty (true);
100         }
101       
102       Direction dir = (Direction)sign (pos);
103       Real offs = (Staff_symbol_referencer::on_staffline (me, pos))
104         ? 0.0
105         : -dir * inter_f;
106       
107       for (int i = 0; i < line_count; i++)
108         {
109           Stencil ledger_line ((i == 0) 
110                                 ? proto_first_line
111                                 : proto_ledger_line
112                                 );
113           ledger_line.translate_axis (-dir * inter_f * i * 2 + offs, Y_AXIS);
114           stencil.add_stencil (ledger_line);
115         }
116     }
117
118   return stencil;
119 }
120
121 Stencil
122 internal_print (Grob *me, bool with_ledgers)
123 {
124   SCM style  = me->get_property ("style");
125   if (!ly_symbol_p (style))
126     {
127       return Stencil ();
128     }
129
130   SCM log = scm_int2num (Note_head::get_balltype (me));
131   SCM proc = me->get_property ("glyph-name-procedure");
132   SCM scm_font_char = scm_call_2 (proc, log, style);
133   String font_char = "noteheads-" + ly_scm2string (scm_font_char);
134
135   Font_metric * fm = Font_interface::get_default_font (me);
136   Stencil out = fm->find_by_name (font_char);
137   if (out.is_empty ())
138     {
139       me->warning (_f ("note head `%s' not found", font_char.to_str0 ()));
140     }
141
142   int interspaces = Staff_symbol_referencer::line_count (me)-1;
143   int pos = Staff_symbol_referencer::get_rounded_position (me);
144   if (with_ledgers && interspaces >= 0
145       && abs (pos) - interspaces > 1)
146     {
147       Interval ledger_size = out.extent (X_AXIS);
148       ledger_size.widen ( ledger_size.length ()/4);
149
150       Real left_shorten =0.0;
151       if (Grob * g = unsmob_grob (me->get_property ("accidental-grob")))
152         {
153           /*
154             make a little room for accidentals.
155           
156             TODO: this will look silly if a chord has ledger lines,
157             and only the bottom note has an accidental.
158           */
159
160           Grob *common = g->common_refpoint (me, X_AXIS);
161           Real d =
162             linear_combination (Drul_array<Real> (me->extent (common, X_AXIS)[LEFT],
163                                                   g->extent (common, X_AXIS)[RIGHT]),
164                                 
165                                 0.5);
166
167           left_shorten =  (-ledger_size[LEFT] + d) >?  0 ;
168
169           /*
170             TODO: shorten 2 ledger lines for the case natural +
171             downstem.
172            */
173         }
174
175       out.add_stencil (Note_head::brew_ledger_lines (me, pos, interspaces,
176                                                       ledger_size,
177                                                       left_shorten,
178                                                       false));
179     }
180   return out;
181 }
182
183
184 MAKE_SCHEME_CALLBACK (Note_head,print,1);
185 SCM
186 Note_head::print (SCM smob)  
187 {
188   Grob *me = unsmob_grob (smob);
189
190   /*
191     ledgers don't take space. See top of file.
192    */
193   return internal_print (me, true).smobbed_copy ();
194 }
195
196 /*
197   Compute the width the head without ledgers.
198
199   -- there used to be some code from the time that ledgers
200   did take space. Nowadays, we can simply take the standard extent.
201  */
202 Interval
203 Note_head::head_extent (Grob *me, Axis a)
204 {
205   SCM brewer = me->get_property ("print-function");
206   if (brewer == Note_head::print_proc)
207     {
208       Stencil mol = internal_print (me, false);
209   
210       if (!mol.is_empty ())
211         return mol.extent (a);
212     }
213   else
214     {
215       Stencil * mol = me->get_stencil ();
216       if (mol)
217         return  mol->extent (a) ;
218     }
219   
220   return Interval (0,0);
221 }
222
223 /*
224   This is necessary to prevent a cyclic dependency: the appearance of
225   the ledgers depends on positioning, so the Grob::get_stencil () can
226   not be used for determining the note head extent.
227   
228  */ 
229 MAKE_SCHEME_CALLBACK (Note_head,extent,2);
230 SCM
231 Note_head::extent (SCM smob, SCM axis)  
232 {
233   Grob *me = unsmob_grob (smob);
234
235   return ly_interval2scm (head_extent (me, (Axis) ly_scm2int (axis)));
236 }
237
238 MAKE_SCHEME_CALLBACK (Note_head,brew_ez_stencil,1);
239 SCM
240 Note_head::brew_ez_stencil (SCM smob)
241 {
242   Grob *me = unsmob_grob (smob);
243   int l = Note_head::get_balltype (me);
244
245   int b = (l >= 2);
246
247   SCM cause = me->get_property ("cause");
248   SCM spitch = unsmob_music (cause)->get_property ("pitch");
249   Pitch* pit =  unsmob_pitch (spitch);
250
251   SCM idx = scm_int2num (pit->get_notename ());
252   SCM names = me->get_property ("note-names");
253   SCM charstr = SCM_EOL;
254   if (ly_vector_p (names))
255     charstr = scm_vector_ref (names, idx);
256   else
257     {
258       char s[2] = "a";
259       s[0] = (pit->get_notename () + 2)%7 + 'a';
260       s[0] = toupper (s[0]);
261       charstr = scm_makfrom0str (s);
262     }
263   
264   SCM at = scm_list_n (ly_symbol2scm ("ez-ball"),
265                        charstr,
266                        scm_int2num (b),
267                        scm_int2num (1-b),
268                        SCM_UNDEFINED);
269   Box bx (Interval (0, 1.0), Interval (-0.5, 0.5));
270   Stencil m (bx, at);
271
272   int pos = Staff_symbol_referencer::get_rounded_position (me);
273   int interspaces = Staff_symbol_referencer::line_count (me)-1;
274   if (abs (pos) - interspaces > 1)
275     {
276       Interval hd = m.extent (X_AXIS);
277       hd.widen ( hd.length ()/4);
278       m.add_stencil (brew_ledger_lines (me, pos, interspaces, hd, 0, false));
279     }
280
281   return m.smobbed_copy ();
282 }
283
284
285 Real
286 Note_head::stem_attachment_coordinate (Grob *me, Axis a)
287 {
288   SCM brewer = me->get_property ("print-function");
289   Font_metric * fm  = Font_interface::get_default_font (me);
290   
291   if (brewer == Note_head::print_proc)
292     {
293       SCM style  = me->get_property ("style");
294       if (!ly_symbol_p (style))
295         {
296           return 0.0;
297         }
298       
299       SCM log = scm_int2num (Note_head::get_balltype (me));
300       SCM proc = me->get_property ("glyph-name-procedure");
301       SCM scm_font_char = scm_call_2 (proc, log, style);
302       String font_char = "noteheads-" + ly_scm2string (scm_font_char);
303
304       int k = fm->name_to_index (font_char) ;
305
306       if (k >= 0)
307         {
308           Box b = fm->get_indexed_char (k);
309           Offset wxwy = fm->get_indexed_wxwy (k);
310           Interval v = b[a];
311           if (!v.is_empty ())
312             return 2 * (wxwy[a] - v.center ()) / v.length ();
313         }
314     }
315   
316   /*
317     Fallback
318    */
319   SCM v = me->get_property ("stem-attachment-function");
320   if (!ly_procedure_p (v))
321     return 0.0;
322   
323   SCM result = scm_call_2 (v, me->self_scm (), scm_int2num (a));
324   if (!ly_pair_p (result))
325     return 0.0;
326
327   result = (a == X_AXIS) ? ly_car (result) : ly_cdr (result);
328   
329   return robust_scm2double (result,0);
330 }
331
332 int
333 Note_head::get_balltype (Grob*me) 
334 {
335   SCM s = me->get_property ("duration-log");
336   return ly_number_p (s) ? ly_scm2int (s) <? 2 : 0;
337 }
338
339 ADD_INTERFACE (Note_head,"note-head-interface",
340   "Note head",
341   "note-names glyph-name-procedure accidental-grob style stem-attachment-function");
342