]> git.donarmstrong.com Git - lilypond.git/blob - lily/note-head.cc
* lily/my-lily-lexer.cc (My_lily_lexer): don't crash
[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                               bool take_space)
70 {
71   Real inter_f = Staff_symbol_referencer::staff_space (me)/2;
72   int lines_i = abs (pos) < interspaces
73     ? 0
74     : (abs (pos) - interspaces) / 2;
75   Molecule molecule = Molecule();
76
77   if (lines_i)
78     {
79       Real ledgerlinethickness =
80         (me->get_paper ()->get_realvar (ly_symbol2scm ("ledgerlinethickness")));
81       Real blotdiameter = ledgerlinethickness;
82       //        (me->get_paper ()->get_realvar (ly_symbol2scm ("blotdiameter")));
83       Interval y_extent =
84         Interval (-0.5*(ledgerlinethickness),
85                   +0.5*(ledgerlinethickness));
86       Box ledger_line (x_extent, y_extent);
87
88       Molecule proto_ledger_line =
89         Lookup::round_filled_box (ledger_line, blotdiameter);
90       
91       if (!take_space)
92         proto_ledger_line.set_empty (true);
93
94       Direction dir = (Direction)sign (pos);
95       Real offs = (Staff_symbol_referencer::on_staffline (me, pos))
96         ? 0.0
97         : -dir * inter_f;
98       for (int i = 0; i < lines_i; i++)
99         {
100           Molecule ledger_line (proto_ledger_line);
101           ledger_line.translate_axis (-dir * inter_f * i * 2 + offs, Y_AXIS);
102           molecule.add_molecule (ledger_line);
103         }
104     }
105
106   return molecule;
107 }
108
109 Molecule
110 internal_brew_molecule (Grob *me, bool ledger_take_space)
111 {
112   SCM style  = me->get_grob_property ("style");
113   if (!gh_symbol_p (style))
114     {
115       return Molecule ();
116     }
117
118   SCM log = gh_int2scm (Note_head::get_balltype (me));
119   SCM proc = me->get_grob_property ("glyph-name-procedure");
120   SCM scm_font_char = scm_call_2 (proc, log, style);
121   String font_char = "noteheads-" + ly_scm2string (scm_font_char);
122
123   Font_metric * fm = Font_interface::get_default_font (me);
124   Molecule out = fm->find_by_name (font_char);
125   if (out.empty_b())
126     {
127       me->warning (_f ("note head `%s' not found", font_char.to_str0 ()));
128     }
129
130   int interspaces = Staff_symbol_referencer::line_count (me)-1;
131   int pos = (int)rint (Staff_symbol_referencer::get_position (me));
132   if (abs (pos) - interspaces > 1)
133     {
134       Interval hd = out.extent (X_AXIS);
135       Real left_ledger_protusion = hd.length ()/4;
136       Real right_ledger_protusion = left_ledger_protusion;
137
138       if (unsmob_grob(me->get_grob_property ("accidental-grob")))
139         {
140           /*
141             make a little room for accidentals.
142           
143             TODO: this will look silly if a chord has ledger lines,
144             and only the bottom note has an accidental.
145           */
146           
147           left_ledger_protusion *= 0.66;
148           right_ledger_protusion *= 0.9; 
149         }
150
151       Interval l_extents = Interval (hd[LEFT] - left_ledger_protusion,
152                                      hd[RIGHT] + right_ledger_protusion);
153       out.add_molecule (Note_head::brew_ledger_lines (me, pos, interspaces,
154                                                       l_extents,
155                                                       ledger_take_space));
156     }
157   return out;
158 }
159
160
161 MAKE_SCHEME_CALLBACK (Note_head,brew_molecule,1);
162 SCM
163 Note_head::brew_molecule (SCM smob)  
164 {
165   Grob *me = unsmob_grob (smob);
166
167   /*
168     ledgers don't take space. See top of file.
169    */
170   return internal_brew_molecule (me, false).smobbed_copy ();
171 }
172
173 /*
174   Compute the width the head without ledgers.
175
176   -- there used to be some code from the time that ledgers
177   did take space. Nowadays, we can simply take the standard extent.
178  */
179 Interval
180 Note_head::head_extent (Grob *me, Axis a)
181 {
182   Molecule * mol = me->get_molecule();
183   return mol ? mol ->extent (a) : Interval(0,0);
184 }
185
186
187
188 MAKE_SCHEME_CALLBACK (Note_head,brew_ez_molecule,1);
189
190 SCM
191 Note_head::brew_ez_molecule (SCM smob)
192 {
193   Grob *me = unsmob_grob (smob);
194   int l = Note_head::get_balltype (me);
195
196   int b = (l >= 2);
197
198   SCM cause = me->get_grob_property ("cause");
199   SCM spitch = unsmob_music (cause)->get_mus_property ("pitch");
200   Pitch* pit =  unsmob_pitch (spitch);
201
202   char s[2] = "a";
203   s[0] = (pit->get_notename () + 2)%7 + 'a';
204   s[0] = toupper (s[0]);
205   
206   SCM charstr = scm_makfrom0str (s);
207   
208   SCM at = scm_list_n (ly_symbol2scm ("ez-ball"),
209                        charstr,
210                        gh_int2scm (b),
211                        gh_int2scm (1-b),
212                        SCM_UNDEFINED);
213   Box bx (Interval (0, 1.0), Interval (-0.5, 0.5));
214   Molecule m (bx, at);
215
216   int pos = (int)rint (Staff_symbol_referencer::get_position (me));
217   int interspaces = Staff_symbol_referencer::line_count (me)-1;
218   if (abs (pos) - interspaces > 1)
219     {
220       Interval hd = m.extent (X_AXIS);
221       Real hw = hd.length ()/4;
222       Interval extent = Interval (hd[LEFT] - hw, hd[RIGHT] + hw);
223       m.add_molecule (brew_ledger_lines (me, pos, interspaces, extent, false));
224     }
225
226   return m.smobbed_copy ();
227 }
228
229
230 Real
231 Note_head::stem_attachment_coordinate (Grob *me, Axis a)
232 {
233   SCM v = me->get_grob_property ("stem-attachment-function");
234
235   if (!gh_procedure_p (v))
236     return 0.0;
237
238   SCM st = me->get_grob_property ("style");
239   SCM log = gh_int2scm (get_balltype (me));
240   SCM result = gh_apply (v, scm_list_n (st, log, SCM_UNDEFINED));
241
242   if (!gh_pair_p (result))
243     return 0.0;
244
245   result = (a == X_AXIS) ? ly_car (result) : ly_cdr (result);
246   
247   return gh_number_p (result) ?  gh_scm2double (result) : 0.0;
248 }
249
250 int
251 Note_head::get_balltype (Grob*me) 
252 {
253   SCM s = me->get_grob_property ("duration-log");
254   return gh_number_p (s) ? gh_scm2int (s) <? 2 : 0;
255 }
256
257 ADD_INTERFACE (Note_head,"note-head-interface",
258   "Note head",
259   "glyph-name-procedure accidental-grob style stem-attachment-function");
260