]> git.donarmstrong.com Git - lilypond.git/blob - lily/note-head.cc
* lily/midi-stream.cc (operator <<): rewrite.
[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 line_count = (abs (pos) < interspaces)
73     ? 0
74     : (abs (pos) - interspaces) / 2;
75   Molecule molecule = Molecule();
76
77   if (line_count)
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 < line_count; 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 (interspaces >= 0
133       && abs (pos) - interspaces > 1)
134     {
135       Interval hd = out.extent (X_AXIS);
136       Real left_ledger_protusion = hd.length ()/4;
137       Real right_ledger_protusion = left_ledger_protusion;
138
139       if (unsmob_grob(me->get_grob_property ("accidental-grob")))
140         {
141           /*
142             make a little room for accidentals.
143           
144             TODO: this will look silly if a chord has ledger lines,
145             and only the bottom note has an accidental.
146           */
147           
148           left_ledger_protusion *= 0.66;
149           right_ledger_protusion *= 0.9; 
150         }
151
152       Interval l_extents = Interval (hd[LEFT] - left_ledger_protusion,
153                                      hd[RIGHT] + right_ledger_protusion);
154       out.add_molecule (Note_head::brew_ledger_lines (me, pos, interspaces,
155                                                       l_extents,
156                                                       ledger_take_space));
157     }
158   return out;
159 }
160
161
162 MAKE_SCHEME_CALLBACK (Note_head,brew_molecule,1);
163 SCM
164 Note_head::brew_molecule (SCM smob)  
165 {
166   Grob *me = unsmob_grob (smob);
167
168   /*
169     ledgers don't take space. See top of file.
170    */
171   return internal_brew_molecule (me, false).smobbed_copy ();
172 }
173
174 /*
175   Compute the width the head without ledgers.
176
177   -- there used to be some code from the time that ledgers
178   did take space. Nowadays, we can simply take the standard extent.
179  */
180 Interval
181 Note_head::head_extent (Grob *me, Axis a)
182 {
183   Molecule * mol = me->get_molecule();
184   return mol ? mol ->extent (a) : Interval(0,0);
185 }
186
187
188
189 MAKE_SCHEME_CALLBACK (Note_head,brew_ez_molecule,1);
190
191 SCM
192 Note_head::brew_ez_molecule (SCM smob)
193 {
194   Grob *me = unsmob_grob (smob);
195   int l = Note_head::get_balltype (me);
196
197   int b = (l >= 2);
198
199   SCM cause = me->get_grob_property ("cause");
200   SCM spitch = unsmob_music (cause)->get_mus_property ("pitch");
201   Pitch* pit =  unsmob_pitch (spitch);
202
203   char s[2] = "a";
204   s[0] = (pit->get_notename () + 2)%7 + 'a';
205   s[0] = toupper (s[0]);
206   
207   SCM charstr = scm_makfrom0str (s);
208   
209   SCM at = scm_list_n (ly_symbol2scm ("ez-ball"),
210                        charstr,
211                        gh_int2scm (b),
212                        gh_int2scm (1-b),
213                        SCM_UNDEFINED);
214   Box bx (Interval (0, 1.0), Interval (-0.5, 0.5));
215   Molecule m (bx, at);
216
217   int pos = (int)rint (Staff_symbol_referencer::get_position (me));
218   int interspaces = Staff_symbol_referencer::line_count (me)-1;
219   if (abs (pos) - interspaces > 1)
220     {
221       Interval hd = m.extent (X_AXIS);
222       Real hw = hd.length ()/4;
223       Interval extent = Interval (hd[LEFT] - hw, hd[RIGHT] + hw);
224       m.add_molecule (brew_ledger_lines (me, pos, interspaces, extent, false));
225     }
226
227   return m.smobbed_copy ();
228 }
229
230
231 Real
232 Note_head::stem_attachment_coordinate (Grob *me, Axis a)
233 {
234   SCM v = me->get_grob_property ("stem-attachment-function");
235
236   if (!gh_procedure_p (v))
237     return 0.0;
238
239   SCM st = me->get_grob_property ("style");
240   SCM log = gh_int2scm (get_balltype (me));
241   SCM result = gh_apply (v, scm_list_n (st, log, SCM_UNDEFINED));
242
243   if (!gh_pair_p (result))
244     return 0.0;
245
246   result = (a == X_AXIS) ? ly_car (result) : ly_cdr (result);
247   
248   return gh_number_p (result) ?  gh_scm2double (result) : 0.0;
249 }
250
251 int
252 Note_head::get_balltype (Grob*me) 
253 {
254   SCM s = me->get_grob_property ("duration-log");
255   return gh_number_p (s) ? gh_scm2int (s) <? 2 : 0;
256 }
257
258 ADD_INTERFACE (Note_head,"note-head-interface",
259   "Note head",
260   "glyph-name-procedure accidental-grob style stem-attachment-function");
261