]> git.donarmstrong.com Git - lilypond.git/blob - lily/note-head.cc
*** empty log message ***
[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   return me->get_molecule()->extent (a);
190 }
191
192
193
194 MAKE_SCHEME_CALLBACK (Note_head,brew_ez_molecule,1);
195
196 SCM
197 Note_head::brew_ez_molecule (SCM smob)
198 {
199   Grob *me = unsmob_grob (smob);
200   int l = Note_head::get_balltype (me);
201
202   int b = (l >= 2);
203
204   SCM cause = me->get_grob_property ("cause");
205   SCM spitch = unsmob_music (cause)->get_mus_property ("pitch");
206   Pitch* pit =  unsmob_pitch (spitch);
207
208   char s[2] = "a";
209   s[0] = (pit->notename_ + 2)%7 + 'a';
210   s[0] = toupper (s[0]);
211   
212   SCM charstr = scm_makfrom0str (s);
213   
214   SCM at = scm_list_n (ly_symbol2scm ("ez-ball"),
215                        charstr,
216                        gh_int2scm (b),
217                        gh_int2scm (1-b),
218                        SCM_UNDEFINED);
219   Box bx (Interval (0, 1.0), Interval (-0.5, 0.5));
220   Molecule m (bx, at);
221
222   int pos = (int)rint (Staff_symbol_referencer::get_position (me));
223   int interspaces = Staff_symbol_referencer::line_count (me)-1;
224   if (abs (pos) - interspaces > 1)
225     {
226       Interval hd = m.extent (X_AXIS);
227       Real hw = hd.length ()/4;
228       Interval extent = Interval (hd[LEFT] - hw, hd[RIGHT] + hw);
229       m.add_molecule (brew_ledger_lines (me, pos, interspaces, extent, false));
230     }
231
232   return m.smobbed_copy ();
233 }
234
235
236 Real
237 Note_head::stem_attachment_coordinate (Grob *me, Axis a)
238 {
239   SCM v = me->get_grob_property ("stem-attachment-function");
240
241   if (!gh_procedure_p (v))
242     return 0.0;
243
244   SCM st = me->get_grob_property ("style");
245   SCM log = gh_int2scm (get_balltype (me));
246   SCM result = gh_apply (v, scm_list_n (st, log, SCM_UNDEFINED));
247
248   if (!gh_pair_p (result))
249     return 0.0;
250
251   result = (a == X_AXIS) ? ly_car (result) : ly_cdr (result);
252   
253   return gh_number_p (result) ?  gh_scm2double (result) : 0.0;
254 }
255
256
257 int
258 Note_head::get_balltype (Grob*me) 
259 {
260   SCM s = me->get_grob_property ("duration-log");
261   return gh_number_p (s) ? gh_scm2int (s) <? 2 : 0;
262 }
263
264 ADD_INTERFACE (Note_head,"note-head-interface",
265   "Note head",
266   "accidental-grob style stem-attachment-function");
267