]> git.donarmstrong.com Git - lilypond.git/blob - lily/note-head.cc
release: 1.5.48
[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 - blotdiameter),
81                   +0.5*(ledgerlinethickness - blotdiameter));
82       Box ledger_line (x_extent, y_extent);
83
84       // FIXME: Currently need blotdiameter factor 2.0 to compensate
85       // for error somewhere else.  (Maybe draw_box confuses radius
86       // and diameter?)
87 #if 1
88        Molecule proto_ledger_line =
89          Lookup::roundfilledbox (ledger_line, ledgerlinethickness );
90 #else
91       Molecule proto_ledger_line = // if you like it the old way
92         Lookup::filledbox (ledger_line);
93 #endif
94       
95       if (!take_space)
96         proto_ledger_line.set_empty (true);
97
98       Direction dir = (Direction)sign (pos);
99       Real offs = (Staff_symbol_referencer::on_staffline (me, pos))
100         ? 0.0
101         : -dir * inter_f;
102       for (int i = 0; i < lines_i; i++)
103         {
104           Molecule ledger_line (proto_ledger_line);
105           ledger_line.translate_axis (-dir * inter_f * i * 2 + offs, Y_AXIS);
106           molecule.add_molecule (ledger_line);
107         }
108     }
109
110   return molecule;
111 }
112
113 Molecule
114 internal_brew_molecule (Grob *me,  bool ledger_take_space)
115 {
116   SCM style  = me->get_grob_property ("style");
117   if (!gh_symbol_p (style))
118     {
119       return Molecule();
120     }
121
122   /*
123     ugh: use gh_call () / scm_apply ().
124
125     UGH: use grob-property.
126   */
127   SCM log = gh_int2scm (Rhythmic_head::balltype_i (me));
128   SCM exp = scm_list_n (ly_symbol2scm ("find-notehead-symbol"), log,
129                         ly_quote_scm (style),
130                         SCM_UNDEFINED);
131   String name = "noteheads-" + ly_scm2string (scm_primitive_eval (exp));
132   Molecule out = Font_interface::get_default_font (me)->find_by_name (name);
133
134   int interspaces = Staff_symbol_referencer::line_count (me)-1;
135   int pos = (int)rint (Staff_symbol_referencer::position_f (me));
136   if (abs (pos) - interspaces > 1)
137     {
138       Interval hd = out.extent (X_AXIS);
139       Real left_ledger_protusion = hd.length ()/4;
140       Real right_ledger_protusion = left_ledger_protusion;
141
142       if (unsmob_grob(me->get_grob_property ("accidentals-grob")))
143         {
144           /*
145             make a little room for accidentals.
146           
147             TODO: this will look silly if a chord has ledger lines,
148             and only the bottom note has an accidental.
149           */
150           
151           left_ledger_protusion *= 0.66;
152           right_ledger_protusion *= 0.9; 
153         }
154
155       Interval l_extents = Interval (hd[LEFT] - left_ledger_protusion,
156                                      hd[RIGHT] + right_ledger_protusion);
157       out.add_molecule (Note_head::brew_ledger_lines (me, pos, interspaces,
158                                                       l_extents,
159                                                       ledger_take_space));
160     }
161   return out;
162 }
163
164
165 MAKE_SCHEME_CALLBACK (Note_head,brew_molecule,1);
166 SCM
167 Note_head::brew_molecule (SCM smob)  
168 {
169   Grob *me = unsmob_grob (smob);
170
171   /*
172     ledgers don't take space. See top of file.
173    */
174   return internal_brew_molecule (me, false).smobbed_copy ();
175 }
176
177 /*
178   Compute the width the head without ledgers.
179  */
180 Interval
181 Note_head::head_extent (Grob *me, Axis a)
182 {
183   return internal_brew_molecule (me, false).extent (a);
184 }
185
186 bool
187 Note_head::has_interface (Grob*m)
188 {
189   return m&& m->has_interface (ly_symbol2scm ("note-head-interface"));
190 }
191
192
193 MAKE_SCHEME_CALLBACK (Note_head,brew_ez_molecule,1);
194
195 SCM
196 Note_head::brew_ez_molecule (SCM smob)
197 {
198   Grob *me = unsmob_grob (smob);
199   int l = Rhythmic_head::balltype_i (me);
200
201   int b = (l >= 2);
202
203   SCM cause = me->get_grob_property ("cause");
204   SCM spitch = unsmob_music (cause)->get_mus_property ("pitch");
205   Pitch* pit =  unsmob_pitch (spitch);
206
207   char s[2] = "a";
208   s[0] = (pit->notename_i_ + 2)%7 + 'a';
209   s[0] = toupper (s[0]);
210   
211   SCM charstr = ly_str02scm (s);
212   
213   SCM at = scm_list_n (ly_symbol2scm ("ez-ball"),
214                        charstr,
215                        gh_int2scm (b),
216                        gh_int2scm (1-b),
217                        SCM_UNDEFINED);
218   Box bx (Interval (0, 1.0), Interval (-0.5, 0.5));
219   Molecule m (bx, at);
220
221   int pos = (int)rint (Staff_symbol_referencer::position_f (me));
222   int interspaces = Staff_symbol_referencer::line_count (me)-1;
223   if (abs (pos) - interspaces > 1)
224     {
225       Interval hd = m.extent (X_AXIS);
226       Real hw = hd.length ()/4;
227       Interval extent = Interval (hd[LEFT] - hw, hd[RIGHT] + hw);
228       m.add_molecule (brew_ledger_lines (me, pos, interspaces, extent, false));
229     }
230
231   return m.smobbed_copy ();
232 }
233
234
235 Real
236 Note_head::stem_attachment_coordinate (Grob *me, Axis a)
237 {
238   SCM v = me->get_grob_property ("stem-attachment-function");
239
240   if (!gh_procedure_p (v))
241     return 0.0;
242
243   SCM st = me->get_grob_property ("style");
244   SCM result = gh_apply (v, scm_list_n (st, SCM_UNDEFINED));
245
246   if (!gh_pair_p (result))
247     return 0.0;
248
249   result = (a == X_AXIS) ? ly_car (result) : ly_cdr (result);
250   
251   return gh_number_p (result) ?  gh_scm2double (result) : 0.0;
252 }
253
254 ADD_INTERFACE (Note_head,"note-head-interface",
255   "Note head",
256   "accidentals-grob style stem-attachment-function");