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