]> git.donarmstrong.com Git - lilypond.git/blob - lily/note-head.cc
''
[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 "debug.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->paper_l ()->get_var ("ledgerlinethickness"));
77       Real blotdiameter = ledgerlinethickness;
78       //        (me->paper_l ()->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 #if 1
85       Molecule proto_ledger_line =
86         Lookup::roundfilledbox (ledger_line, blotdiameter);
87 #else
88       Molecule proto_ledger_line = // if you like it the old way
89         Lookup::filledbox (ledger_line);
90 #endif
91       
92       if (!take_space)
93         proto_ledger_line.set_empty (true);
94
95       Direction dir = (Direction)sign (pos);
96       Real offs = (Staff_symbol_referencer::on_staffline (me, pos))
97         ? 0.0
98         : -dir * inter_f;
99       for (int i = 0; i < lines_i; i++)
100         {
101           Molecule ledger_line (proto_ledger_line);
102           ledger_line.translate_axis (-dir * inter_f * i * 2 + offs, Y_AXIS);
103           molecule.add_molecule (ledger_line);
104         }
105     }
106
107   return molecule;
108 }
109
110 Molecule
111 internal_brew_molecule (Grob *me,  bool ledger_take_space)
112 {
113   SCM style  = me->get_grob_property ("style");
114   if (!gh_symbol_p (style))
115     {
116       return Molecule();
117     }
118
119   /*
120     ugh: use gh_call () / scm_apply ().
121
122     UGH: use grob-property.
123   */
124   SCM log = gh_int2scm (Rhythmic_head::balltype_i (me));
125   SCM exp = scm_list_n (ly_symbol2scm ("find-notehead-symbol"), log,
126                         ly_quote_scm (style),
127                         SCM_UNDEFINED);
128   String name = "noteheads-" + ly_scm2string (scm_primitive_eval (exp));
129   Molecule out = Font_interface::get_default_font (me)->find_by_name (name);
130
131   int interspaces = Staff_symbol_referencer::line_count (me)-1;
132   int pos = (int)rint (Staff_symbol_referencer::position_f (me));
133   if (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   return me->get_molecule()->extent (a);
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 = Rhythmic_head::balltype_i (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->notename_i_ + 2)%7 + 'a';
204   s[0] = toupper (s[0]);
205   
206   SCM charstr = ly_str02scm (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::position_f (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 result = gh_apply (v, scm_list_n (st, SCM_UNDEFINED));
240
241   if (!gh_pair_p (result))
242     return 0.0;
243
244   result = (a == X_AXIS) ? ly_car (result) : ly_cdr (result);
245   
246   return gh_number_p (result) ?  gh_scm2double (result) : 0.0;
247 }
248
249 ADD_INTERFACE (Note_head,"note-head-interface",
250   "Note head",
251   "accidental-grob style stem-attachment-function");