]> git.donarmstrong.com Git - lilypond.git/blob - lily/note-head.cc
cleanup. Separate into internal
[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 "molecule.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 Molecule
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   Molecule molecule = Molecule();
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       Molecule proto_ledger_line =
90         Lookup::round_filled_box (Box (x_extent, y_extent), blotdiameter);
91
92       x_extent[LEFT] += left_shorten;
93       Molecule 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           Molecule 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           molecule.add_molecule (ledger_line);
115         }
116     }
117
118   return molecule;
119 }
120
121 Molecule
122 internal_print (Grob *me, bool with_ledgers)
123 {
124   SCM style  = me->get_grob_property ("style");
125   if (!gh_symbol_p (style))
126     {
127       return Molecule ();
128     }
129
130   SCM log = gh_int2scm (Note_head::get_balltype (me));
131   SCM proc = me->get_grob_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   Molecule 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 = (int)rint (Staff_symbol_referencer::get_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_grob_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             (me->extent (common, X_AXIS)[LEFT]
163              +g->extent (common, X_AXIS)[RIGHT]) /2;
164
165           left_shorten =  (-ledger_size[LEFT] + d) >?  0 ;
166
167           /*
168             TODO: shorten 2 ledger lines for the case natural +
169             downstem.
170            */
171         }
172
173       out.add_molecule (Note_head::brew_ledger_lines (me, pos, interspaces,
174                                                       ledger_size,
175                                                       left_shorten,
176                                                       false));
177     }
178   return out;
179 }
180
181
182 MAKE_SCHEME_CALLBACK (Note_head,print,1);
183 SCM
184 Note_head::print (SCM smob)  
185 {
186   Grob *me = unsmob_grob (smob);
187
188   /*
189     ledgers don't take space. See top of file.
190    */
191   return internal_print (me, true).smobbed_copy ();
192 }
193
194 /*
195   Compute the width the head without ledgers.
196
197   -- there used to be some code from the time that ledgers
198   did take space. Nowadays, we can simply take the standard extent.
199  */
200 Interval
201 Note_head::head_extent (Grob *me, Axis a)
202 {
203   SCM brewer = me->get_grob_property ("print-function");
204   if (brewer == Note_head::print_proc)
205     {
206       Molecule mol = internal_print (me, false);
207   
208       if (!mol.is_empty ())
209         return mol.extent (a);
210     }
211   else
212     {
213       Molecule * mol = me->get_molecule ();
214       if (mol)
215         return  mol->extent (a) ;
216     }
217   
218   return Interval (0,0);
219 }
220
221 /*
222   This is necessary to prevent a cyclic dependency: the appearance of
223   the ledgers depends on positioning, so the Grob::get_molecule() can
224   not be used for determining the note head extent.
225   
226  */ 
227 MAKE_SCHEME_CALLBACK (Note_head,extent,2);
228 SCM
229 Note_head::extent (SCM smob, SCM axis)  
230 {
231   Grob *me = unsmob_grob (smob);
232
233   return ly_interval2scm (head_extent (me, (Axis) gh_scm2int (axis)));
234 }
235
236 MAKE_SCHEME_CALLBACK (Note_head,brew_ez_molecule,1);
237 SCM
238 Note_head::brew_ez_molecule (SCM smob)
239 {
240   Grob *me = unsmob_grob (smob);
241   int l = Note_head::get_balltype (me);
242
243   int b = (l >= 2);
244
245   SCM cause = me->get_grob_property ("cause");
246   SCM spitch = unsmob_music (cause)->get_mus_property ("pitch");
247   Pitch* pit =  unsmob_pitch (spitch);
248
249   char s[2] = "a";
250   s[0] = (pit->get_notename () + 2)%7 + 'a';
251   s[0] = toupper (s[0]);
252   
253   SCM charstr = scm_makfrom0str (s);
254   
255   SCM at = scm_list_n (ly_symbol2scm ("ez-ball"),
256                        charstr,
257                        gh_int2scm (b),
258                        gh_int2scm (1-b),
259                        SCM_UNDEFINED);
260   Box bx (Interval (0, 1.0), Interval (-0.5, 0.5));
261   Molecule m (bx, at);
262
263   int pos = (int)rint (Staff_symbol_referencer::get_position (me));
264   int interspaces = Staff_symbol_referencer::line_count (me)-1;
265   if (abs (pos) - interspaces > 1)
266     {
267       Interval hd = m.extent (X_AXIS);
268       hd.widen ( hd.length ()/4);
269       m.add_molecule (brew_ledger_lines (me, pos, interspaces, hd, 0, false));
270     }
271
272   return m.smobbed_copy ();
273 }
274
275
276 Real
277 Note_head::stem_attachment_coordinate (Grob *me, Axis a)
278 {
279   SCM brewer = me->get_grob_property ("print-function");
280   Font_metric * fm  = Font_interface::get_default_font (me);
281   
282   if (brewer == Note_head::print_proc)
283     {
284       SCM style  = me->get_grob_property ("style");
285       if (!gh_symbol_p (style))
286         {
287           return 0.0;
288         }
289       
290       SCM log = gh_int2scm (Note_head::get_balltype (me));
291       SCM proc = me->get_grob_property ("glyph-name-procedure");
292       SCM scm_font_char = scm_call_2 (proc, log, style);
293       String font_char = "noteheads-" + ly_scm2string (scm_font_char);
294
295       int k = fm->name_to_index (font_char) ;
296
297       if (k >= 0)
298         {
299           Box b = fm->get_indexed_char (k);
300           Offset wxwy = fm->get_indexed_wxwy (k);
301           Interval v = b[a];
302           if (!v.is_empty ())
303             return 2 * (wxwy[a] - v.center()) / v.length ();
304         }
305     }
306   
307   /*
308     Fallback
309    */
310   SCM v = me->get_grob_property ("stem-attachment-function");
311   if (!gh_procedure_p (v))
312     return 0.0;
313   
314   SCM result = scm_call_2 (v, me->self_scm(), gh_int2scm (a));
315   if (!gh_pair_p (result))
316     return 0.0;
317
318   result = (a == X_AXIS) ? ly_car (result) : ly_cdr (result);
319   
320   return robust_scm2double (result,0);
321 }
322
323 int
324 Note_head::get_balltype (Grob*me) 
325 {
326   SCM s = me->get_grob_property ("duration-log");
327   return gh_number_p (s) ? gh_scm2int (s) <? 2 : 0;
328 }
329
330 ADD_INTERFACE (Note_head,"note-head-interface",
331   "Note head",
332   "glyph-name-procedure accidental-grob style stem-attachment-function");
333