]> git.donarmstrong.com Git - lilypond.git/blob - lily/note-head.cc
* scm/output-lib.scm (note-head-style->attachment-coordinates):
[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--2003 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 "event.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 /*
61   TODO: ledger lines are also a property of the staff. Maybe move them
62   to there?
63  */
64 Molecule
65 Note_head::brew_ledger_lines (Grob *me,
66                               int pos,
67                               int interspaces,
68                               Interval x_extent,
69                               bool take_space)
70 {
71   Real inter_f = Staff_symbol_referencer::staff_space (me)/2;
72   int line_count = (abs (pos) < interspaces)
73     ? 0
74     : (abs (pos) - interspaces) / 2;
75   Molecule molecule = Molecule();
76
77
78   if (line_count)
79     {
80       Real ledgerlinethickness =
81         (me->get_paper ()->get_realvar (ly_symbol2scm ("ledgerlinethickness")));
82       Real blotdiameter = ledgerlinethickness;
83       //        (me->get_paper ()->get_realvar (ly_symbol2scm ("blotdiameter")));
84       Interval y_extent =
85         Interval (-0.5*(ledgerlinethickness),
86                   +0.5*(ledgerlinethickness));
87       Box ledger_line (x_extent, y_extent);
88
89       Molecule proto_ledger_line =
90         Lookup::round_filled_box (ledger_line, blotdiameter);
91
92       if (!take_space)
93         proto_ledger_line.set_empty (true);
94
95       
96       Direction dir = (Direction)sign (pos);
97       Real offs = (Staff_symbol_referencer::on_staffline (me, pos))
98         ? 0.0
99         : -dir * inter_f;
100       for (int i = 0; i < line_count; i++)
101         {
102           Molecule ledger_line (proto_ledger_line);
103           ledger_line.translate_axis (-dir * inter_f * i * 2 + offs, Y_AXIS);
104           molecule.add_molecule (ledger_line);
105         }
106     }
107
108   return molecule;
109 }
110
111 Molecule
112 internal_brew_molecule (Grob *me, bool with_ledgers)
113 {
114   SCM style  = me->get_grob_property ("style");
115   if (!gh_symbol_p (style))
116     {
117       return Molecule ();
118     }
119
120   SCM log = gh_int2scm (Note_head::get_balltype (me));
121   SCM proc = me->get_grob_property ("glyph-name-procedure");
122   SCM scm_font_char = scm_call_2 (proc, log, style);
123   String font_char = "noteheads-" + ly_scm2string (scm_font_char);
124
125   Font_metric * fm = Font_interface::get_default_font (me);
126   Molecule out = fm->find_by_name (font_char);
127   if (out.empty_b())
128     {
129       me->warning (_f ("note head `%s' not found", font_char.to_str0 ()));
130     }
131
132   int interspaces = Staff_symbol_referencer::line_count (me)-1;
133   int pos = (int)rint (Staff_symbol_referencer::get_position (me));
134   if (with_ledgers && interspaces >= 0
135       && 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 (Grob * g = 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           Grob *common = g->common_refpoint (me, X_AXIS);
151           Real d =
152             me->extent (common, X_AXIS)[LEFT]
153             -g->extent (common, X_AXIS)[RIGHT];
154           
155           left_ledger_protusion = left_ledger_protusion <? (d/2);
156         }
157
158       Interval l_extents = Interval (hd[LEFT] - left_ledger_protusion,
159                                      hd[RIGHT] + right_ledger_protusion);
160       out.add_molecule (Note_head::brew_ledger_lines (me, pos, interspaces,
161                                                       l_extents, false));
162     }
163   return out;
164 }
165
166
167 MAKE_SCHEME_CALLBACK (Note_head,brew_molecule,1);
168 SCM
169 Note_head::brew_molecule (SCM smob)  
170 {
171   Grob *me = unsmob_grob (smob);
172
173   /*
174     ledgers don't take space. See top of file.
175    */
176   return internal_brew_molecule (me, true).smobbed_copy ();
177 }
178
179 /*
180   Compute the width the head without ledgers.
181
182   -- there used to be some code from the time that ledgers
183   did take space. Nowadays, we can simply take the standard extent.
184  */
185 Interval
186 Note_head::head_extent (Grob *me, Axis a)
187 {
188   SCM brewer = me->get_grob_property ("molecule-callback");
189   if (brewer == Note_head::brew_molecule_proc)
190     {
191       Molecule mol = internal_brew_molecule (me, false);
192   
193       if (!mol.empty_b())
194         return mol.extent (a);
195     }
196   else
197     {
198       Molecule * mol = me->get_molecule ();
199       if (mol)
200         return  mol->extent (a) ;
201     }
202   
203   return Interval (0,0);
204 }
205
206 /*
207   This is necessary to prevent a cyclic dependency: the appearance of
208   the ledgers depends on positioning, so the Grob::get_molecule() can
209   not be used for determining the note head extent.
210   
211  */ 
212 MAKE_SCHEME_CALLBACK (Note_head,extent,2);
213 SCM
214 Note_head::extent (SCM smob, SCM axis)  
215 {
216   Grob *me = unsmob_grob (smob);
217
218   return ly_interval2scm (head_extent (me, (Axis) gh_scm2int (axis)));
219 }
220
221 MAKE_SCHEME_CALLBACK (Note_head,brew_ez_molecule,1);
222 SCM
223 Note_head::brew_ez_molecule (SCM smob)
224 {
225   Grob *me = unsmob_grob (smob);
226   int l = Note_head::get_balltype (me);
227
228   int b = (l >= 2);
229
230   SCM cause = me->get_grob_property ("cause");
231   SCM spitch = unsmob_music (cause)->get_mus_property ("pitch");
232   Pitch* pit =  unsmob_pitch (spitch);
233
234   char s[2] = "a";
235   s[0] = (pit->get_notename () + 2)%7 + 'a';
236   s[0] = toupper (s[0]);
237   
238   SCM charstr = scm_makfrom0str (s);
239   
240   SCM at = scm_list_n (ly_symbol2scm ("ez-ball"),
241                        charstr,
242                        gh_int2scm (b),
243                        gh_int2scm (1-b),
244                        SCM_UNDEFINED);
245   Box bx (Interval (0, 1.0), Interval (-0.5, 0.5));
246   Molecule m (bx, at);
247
248   int pos = (int)rint (Staff_symbol_referencer::get_position (me));
249   int interspaces = Staff_symbol_referencer::line_count (me)-1;
250   if (abs (pos) - interspaces > 1)
251     {
252       Interval hd = m.extent (X_AXIS);
253       Real hw = hd.length ()/4;
254       Interval extent = Interval (hd[LEFT] - hw, hd[RIGHT] + hw);
255       m.add_molecule (brew_ledger_lines (me, pos, interspaces, extent, false));
256     }
257
258   return m.smobbed_copy ();
259 }
260
261
262 Real
263 Note_head::stem_attachment_coordinate (Grob *me, Axis a)
264 {
265   SCM brewer = me->get_grob_property ("molecule-callback");
266   Font_metric * fm  = Font_interface::get_default_font (me);
267   
268   if (brewer == Note_head::brew_molecule_proc)
269     {
270       SCM style  = me->get_grob_property ("style");
271       if (!gh_symbol_p (style))
272         {
273           return 0.0;
274         }
275       
276       SCM log = gh_int2scm (Note_head::get_balltype (me));
277       SCM proc = me->get_grob_property ("glyph-name-procedure");
278       SCM scm_font_char = scm_call_2 (proc, log, style);
279       String font_char = "noteheads-" + ly_scm2string (scm_font_char);
280
281       int k = fm->name_to_index (font_char);
282       Box b = fm->get_indexed_char (k);
283       Offset wxwy = fm->get_indexed_wxwy (k);
284       Interval v = b[a];
285       if (!v.empty_b ())
286         return 2 * (wxwy[a] - v.center()) / v.length ();
287     }
288
289   /*
290     Fallback
291    */
292   SCM v = me->get_grob_property ("stem-attachment-function");
293   if (!gh_procedure_p (v))
294     return 0.0;
295   
296   SCM result = scm_call_2 (v, me->self_scm(), gh_int2scm (axis));
297   if (!gh_pair_p (result))
298     return 0.0;
299
300   result = (a == X_AXIS) ? ly_car (result) : ly_cdr (result);
301   
302   return gh_number_p (result) ?  gh_scm2double (result) : 0.0;
303 }
304 int
305 Note_head::get_balltype (Grob*me) 
306 {
307   SCM s = me->get_grob_property ("duration-log");
308   return gh_number_p (s) ? gh_scm2int (s) <? 2 : 0;
309 }
310
311 ADD_INTERFACE (Note_head,"note-head-interface",
312   "Note head",
313   "glyph-name-procedure accidental-grob style stem-attachment-function");
314