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