]> 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 ("accidentals-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 Interval
178 Note_head::head_extent (Grob *me, Axis a)
179 {
180   return internal_brew_molecule (me, false).extent (a);
181 }
182
183 bool
184 Note_head::has_interface (Grob*m)
185 {
186   return m&& m->has_interface (ly_symbol2scm ("note-head-interface"));
187 }
188
189
190 MAKE_SCHEME_CALLBACK (Note_head,brew_ez_molecule,1);
191
192 SCM
193 Note_head::brew_ez_molecule (SCM smob)
194 {
195   Grob *me = unsmob_grob (smob);
196   int l = Rhythmic_head::balltype_i (me);
197
198   int b = (l >= 2);
199
200   SCM cause = me->get_grob_property ("cause");
201   SCM spitch = unsmob_music (cause)->get_mus_property ("pitch");
202   Pitch* pit =  unsmob_pitch (spitch);
203
204   char s[2] = "a";
205   s[0] = (pit->notename_i_ + 2)%7 + 'a';
206   s[0] = toupper (s[0]);
207   
208   SCM charstr = ly_str02scm (s);
209   
210   SCM at = scm_list_n (ly_symbol2scm ("ez-ball"),
211                        charstr,
212                        gh_int2scm (b),
213                        gh_int2scm (1-b),
214                        SCM_UNDEFINED);
215   Box bx (Interval (0, 1.0), Interval (-0.5, 0.5));
216   Molecule m (bx, at);
217
218   int pos = (int)rint (Staff_symbol_referencer::position_f (me));
219   int interspaces = Staff_symbol_referencer::line_count (me)-1;
220   if (abs (pos) - interspaces > 1)
221     {
222       Interval hd = m.extent (X_AXIS);
223       Real hw = hd.length ()/4;
224       Interval extent = Interval (hd[LEFT] - hw, hd[RIGHT] + hw);
225       m.add_molecule (brew_ledger_lines (me, pos, interspaces, extent, false));
226     }
227
228   return m.smobbed_copy ();
229 }
230
231
232 Real
233 Note_head::stem_attachment_coordinate (Grob *me, Axis a)
234 {
235   SCM v = me->get_grob_property ("stem-attachment-function");
236
237   if (!gh_procedure_p (v))
238     return 0.0;
239
240   SCM st = me->get_grob_property ("style");
241   SCM result = gh_apply (v, scm_list_n (st, 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 ADD_INTERFACE (Note_head,"note-head-interface",
252   "Note head",
253   "accidentals-grob style stem-attachment-function");