]> git.donarmstrong.com Git - lilypond.git/blob - lily/note-head.cc
4b2596c298f19376dcf4b35d74e51fa5a44cb3d9
[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 "warn.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->get_paper ()->get_var ("ledgerlinethickness"));
77       Real blotdiameter = ledgerlinethickness;
78       //        (me->get_paper ()->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       Molecule proto_ledger_line =
85         Lookup::roundfilledbox (ledger_line, blotdiameter);
86       
87       if (!take_space)
88         proto_ledger_line.set_empty (true);
89
90       Direction dir = (Direction)sign (pos);
91       Real offs = (Staff_symbol_referencer::on_staffline (me, pos))
92         ? 0.0
93         : -dir * inter_f;
94       for (int i = 0; i < lines_i; i++)
95         {
96           Molecule ledger_line (proto_ledger_line);
97           ledger_line.translate_axis (-dir * inter_f * i * 2 + offs, Y_AXIS);
98           molecule.add_molecule (ledger_line);
99         }
100     }
101
102   return molecule;
103 }
104
105 Molecule
106 internal_brew_molecule (Grob *me,  bool ledger_take_space)
107 {
108   SCM style  = me->get_grob_property ("style");
109   if (!gh_symbol_p (style))
110     {
111       return Molecule();
112     }
113
114   /*
115     ugh: use gh_call () / scm_apply ().
116
117     UGH: use grob-property.
118   */
119   SCM log = gh_int2scm (Note_head::get_balltype (me));
120   SCM exp = scm_list_n (ly_symbol2scm ("find-notehead-symbol"), log,
121                         ly_quote_scm (style),
122                         SCM_UNDEFINED);
123   SCM scm_pair = scm_primitive_eval (exp);
124   SCM scm_font_char = ly_car (scm_pair);
125   SCM scm_font_family = ly_cdr (scm_pair);
126   String font_char = "noteheads-" + ly_scm2string (scm_font_char);
127   String font_family = ly_scm2string (scm_font_family);
128   
129    me->set_grob_property("font-family", ly_symbol2scm (font_family.to_str0 ()));
130    Molecule out =
131      Font_interface::get_default_font (me)->find_by_name (font_char);
132    if (out.empty_b())
133      {
134        warning (_f("Symbol not found, ", font_char.to_str0()));
135     }
136   
137   int interspaces = Staff_symbol_referencer::line_count (me)-1;
138   int pos = (int)rint (Staff_symbol_referencer::get_position (me));
139   if (abs (pos) - interspaces > 1)
140     {
141       Interval hd = out.extent (X_AXIS);
142       Real left_ledger_protusion = hd.length ()/4;
143       Real right_ledger_protusion = left_ledger_protusion;
144
145       if (unsmob_grob(me->get_grob_property ("accidental-grob")))
146         {
147           /*
148             make a little room for accidentals.
149           
150             TODO: this will look silly if a chord has ledger lines,
151             and only the bottom note has an accidental.
152           */
153           
154           left_ledger_protusion *= 0.66;
155           right_ledger_protusion *= 0.9; 
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,
162                                                       ledger_take_space));
163     }
164   return out;
165 }
166
167
168 MAKE_SCHEME_CALLBACK (Note_head,brew_molecule,1);
169 SCM
170 Note_head::brew_molecule (SCM smob)  
171 {
172   Grob *me = unsmob_grob (smob);
173
174   /*
175     ledgers don't take space. See top of file.
176    */
177   return internal_brew_molecule (me, false).smobbed_copy ();
178 }
179
180 /*
181   Compute the width the head without ledgers.
182
183   -- there used to be some code from the time that ledgers
184   did take space. Nowadays, we can simply take the standard extent.
185  */
186 Interval
187 Note_head::head_extent (Grob *me, Axis a)
188 {
189   Molecule * mol = me->get_molecule();
190   return mol ? mol ->extent (a) : Interval(0,0);
191 }
192
193
194
195 MAKE_SCHEME_CALLBACK (Note_head,brew_ez_molecule,1);
196
197 SCM
198 Note_head::brew_ez_molecule (SCM smob)
199 {
200   Grob *me = unsmob_grob (smob);
201   int l = Note_head::get_balltype (me);
202
203   int b = (l >= 2);
204
205   SCM cause = me->get_grob_property ("cause");
206   SCM spitch = unsmob_music (cause)->get_mus_property ("pitch");
207   Pitch* pit =  unsmob_pitch (spitch);
208
209   char s[2] = "a";
210   s[0] = (pit->notename_ + 2)%7 + 'a';
211   s[0] = toupper (s[0]);
212   
213   SCM charstr = scm_makfrom0str (s);
214   
215   SCM at = scm_list_n (ly_symbol2scm ("ez-ball"),
216                        charstr,
217                        gh_int2scm (b),
218                        gh_int2scm (1-b),
219                        SCM_UNDEFINED);
220   Box bx (Interval (0, 1.0), Interval (-0.5, 0.5));
221   Molecule m (bx, at);
222
223   int pos = (int)rint (Staff_symbol_referencer::get_position (me));
224   int interspaces = Staff_symbol_referencer::line_count (me)-1;
225   if (abs (pos) - interspaces > 1)
226     {
227       Interval hd = m.extent (X_AXIS);
228       Real hw = hd.length ()/4;
229       Interval extent = Interval (hd[LEFT] - hw, hd[RIGHT] + hw);
230       m.add_molecule (brew_ledger_lines (me, pos, interspaces, extent, false));
231     }
232
233   return m.smobbed_copy ();
234 }
235
236
237 Real
238 Note_head::stem_attachment_coordinate (Grob *me, Axis a)
239 {
240   SCM v = me->get_grob_property ("stem-attachment-function");
241
242   if (!gh_procedure_p (v))
243     return 0.0;
244
245   SCM st = me->get_grob_property ("style");
246   SCM log = gh_int2scm (get_balltype (me));
247   SCM result = gh_apply (v, scm_list_n (st, log, SCM_UNDEFINED));
248
249   if (!gh_pair_p (result))
250     return 0.0;
251
252   result = (a == X_AXIS) ? ly_car (result) : ly_cdr (result);
253   
254   return gh_number_p (result) ?  gh_scm2double (result) : 0.0;
255 }
256
257
258 int
259 Note_head::get_balltype (Grob*me) 
260 {
261   SCM s = me->get_grob_property ("duration-log");
262   return gh_number_p (s) ? gh_scm2int (s) <? 2 : 0;
263 }
264
265 ADD_INTERFACE (Note_head,"note-head-interface",
266   "Note head",
267   "accidental-grob style stem-attachment-function");
268