2 notehead.cc -- implement Note_head
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2004 Han-Wen Nienhuys <hanwen@cs.uu.nl>
11 #include "staff-symbol.hh"
14 #include "note-head.hh"
16 #include "font-interface.hh"
19 #include "rhythmic-head.hh"
20 #include "staff-symbol-referencer.hh"
22 #include "output-def.hh"
25 Note_head contains the code for printing note heads.
29 It also contains the ledger lines, for historical reasons. Ledger
30 lines are somewhat of a PITA. In some cases, they take up no space, in
31 some cases they don't:
35 - when ledgered notes are juxtaposed: there should be some white
36 space between the ledger lines.
38 - when accidentals are near: the accidentals should not be on the
41 [both tips by Heinz Stolba from Universal Edition].
43 DO NOT take space into account:
45 - for basically everything else, e.g. swapping ledgered notes on
46 clustered chords, spacing between ledgered and unledgered notes.
48 TODO: fix this. It is not feasible to have a special grob for
49 ledgers, since you basically don't know if there will be ledgers,
50 unless you know at interpretation phase already 1. the Y-position,
51 2. the number of staff lines. It's not yet specced when both pieces
52 of information are there, so for now, it is probably better to build
53 special support for ledgers into the accidental and separation-item
56 (Besides a separate ledger seems overkill. For what else would
62 TODO: ledger lines are also a property of the staff. Maybe move them
66 Note_head::brew_ledger_lines (Grob *me,
73 Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
74 Real inter_f = Staff_symbol_referencer::staff_space (me)/2;
75 int line_count = ((abs (pos) < interspaces)
77 : (abs (pos) - interspaces) / 2);
81 Real ledgerlinethickness =
82 Staff_symbol::get_ledger_line_thickness (staff);
83 Real blotdiameter = ledgerlinethickness;
85 Interval (-0.5*(ledgerlinethickness),
86 +0.5*(ledgerlinethickness));
87 Stencil proto_ledger_line =
88 Lookup::round_filled_box (Box (x_extent, y_extent), blotdiameter);
90 x_extent[LEFT] += left_shorten;
91 Stencil proto_first_line =
92 Lookup::round_filled_box (Box (x_extent, y_extent), blotdiameter);
96 proto_ledger_line.set_empty (true);
97 proto_first_line.set_empty (true);
100 Direction dir = (Direction)sign (pos);
101 Real offs = (Staff_symbol_referencer::on_staffline (me, pos))
105 for (int i = 0; i < line_count; i++)
107 Stencil ledger_line ((i == 0)
111 ledger_line.translate_axis (-dir * inter_f * i * 2 + offs, Y_AXIS);
112 stencil.add_stencil (ledger_line);
120 internal_print (Grob *me, bool with_ledgers)
122 SCM style = me->get_property ("style");
123 if (!ly_c_symbol_p (style))
128 SCM log = scm_int2num (Note_head::get_balltype (me));
129 SCM proc = me->get_property ("glyph-name-procedure");
130 SCM scm_font_char = scm_call_2 (proc, log, style);
131 String font_char = "noteheads-" + ly_scm2string (scm_font_char);
133 Font_metric * fm = Font_interface::get_default_font (me);
134 Stencil out = fm->find_by_name (font_char);
137 me->warning (_f ("note head `%s' not found", font_char.to_str0 ()));
141 int interspaces = Staff_symbol_referencer::line_count (me)-1;
142 int pos = Staff_symbol_referencer::get_rounded_position (me);
143 if (with_ledgers && interspaces >= 0
144 && abs (pos) - interspaces > 1)
146 Interval ledger_size = out.extent (X_AXIS);
147 ledger_size.widen ( ledger_size.length ()/4);
149 Real left_shorten =0.0;
150 if (Grob * g = unsmob_grob (me->get_property ("accidental-grob")))
153 make a little room for accidentals.
155 TODO: this will look silly if a chord has ledger lines,
156 and only the bottom note has an accidental.
159 Grob *common = g->common_refpoint (me, X_AXIS);
161 linear_combination (Drul_array<Real> (me->extent (common, X_AXIS)[LEFT],
162 g->extent (common, X_AXIS)[RIGHT]),
166 left_shorten = (-ledger_size[LEFT] + d) >? 0 ;
169 TODO: shorten 2 ledger lines for the case natural +
174 out.add_stencil (Note_head::brew_ledger_lines (me, pos, interspaces,
185 MAKE_SCHEME_CALLBACK (Note_head,print,1);
187 Note_head::print (SCM smob)
189 Grob *me = unsmob_grob (smob);
192 ledgers don't take space. See top of file.
194 return internal_print (me, true).smobbed_copy ();
198 Compute the width the head without ledgers.
200 -- there used to be some code from the time that ledgers
201 did take space. Nowadays, we can simply take the standard extent.
204 Note_head::head_extent (Grob *me, Axis a)
206 SCM brewer = me->get_property ("print-function");
207 if (brewer == Note_head::print_proc)
209 Stencil mol = internal_print (me, false);
211 if (!mol.is_empty ())
212 return mol.extent (a);
216 Stencil * mol = me->get_stencil ();
218 return mol->extent (a) ;
221 return Interval (0,0);
225 This is necessary to prevent a cyclic dependency: the appearance of
226 the ledgers depends on positioning, so the Grob::get_stencil () can
227 not be used for determining the note head extent.
230 MAKE_SCHEME_CALLBACK (Note_head,extent,2);
232 Note_head::extent (SCM smob, SCM axis)
234 Grob *me = unsmob_grob (smob);
236 return ly_interval2scm (head_extent (me, (Axis) ly_scm2int (axis)));
239 MAKE_SCHEME_CALLBACK (Note_head,brew_ez_stencil,1);
241 Note_head::brew_ez_stencil (SCM smob)
243 Grob *me = unsmob_grob (smob);
244 int l = Note_head::get_balltype (me);
248 SCM cause = me->get_property ("cause");
249 SCM spitch = unsmob_music (cause)->get_property ("pitch");
250 Pitch* pit = unsmob_pitch (spitch);
252 SCM idx = scm_int2num (pit->get_notename ());
253 SCM names = me->get_property ("note-names");
254 SCM charstr = SCM_EOL;
255 if (ly_c_vector_p (names))
256 charstr = scm_vector_ref (names, idx);
260 s[0] = (pit->get_notename () + 2)%7 + 'a';
261 s[0] = toupper (s[0]);
262 charstr = scm_makfrom0str (s);
265 SCM at = scm_list_n (ly_symbol2scm ("ez-ball"),
270 Box bx (Interval (0, 1.0), Interval (-0.5, 0.5));
273 int pos = Staff_symbol_referencer::get_rounded_position (me);
274 int interspaces = Staff_symbol_referencer::line_count (me)-1;
275 if (abs (pos) - interspaces > 1)
277 Interval hd = m.extent (X_AXIS);
278 hd.widen ( hd.length ()/4);
279 m.add_stencil (brew_ledger_lines (me, pos, interspaces, hd, 0, false));
282 return m.smobbed_copy ();
287 Note_head::stem_attachment_coordinate (Grob *me, Axis a)
289 SCM brewer = me->get_property ("print-function");
290 Font_metric * fm = Font_interface::get_default_font (me);
292 if (brewer == Note_head::print_proc)
294 SCM style = me->get_property ("style");
295 if (!ly_c_symbol_p (style))
300 SCM log = scm_int2num (Note_head::get_balltype (me));
301 SCM proc = me->get_property ("glyph-name-procedure");
302 SCM scm_font_char = scm_call_2 (proc, log, style);
303 String font_char = "noteheads-" + ly_scm2string (scm_font_char);
305 int k = fm->name_to_index (font_char) ;
309 Box b = fm->get_indexed_char (k);
310 Offset wxwy = fm->get_indexed_wxwy (k);
313 return 2 * (wxwy[a] - v.center ()) / v.length ();
320 SCM v = me->get_property ("stem-attachment-function");
321 if (!ly_c_procedure_p (v))
324 SCM result = scm_call_2 (v, me->self_scm (), scm_int2num (a));
325 if (!ly_c_pair_p (result))
328 result = (a == X_AXIS) ? ly_car (result) : ly_cdr (result);
330 return robust_scm2double (result,0);
334 Note_head::get_balltype (Grob*me)
336 SCM s = me->get_property ("duration-log");
337 return ly_c_number_p (s) ? ly_scm2int (s) <? 2 : 0;
340 ADD_INTERFACE (Note_head,"note-head-interface",
342 "note-names glyph-name-procedure accidental-grob style stem-attachment-function");