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