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 "paper-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 ()));
140 int interspaces = Staff_symbol_referencer::line_count (me)-1;
141 int pos = Staff_symbol_referencer::get_rounded_position (me);
142 if (with_ledgers && interspaces >= 0
143 && abs (pos) - interspaces > 1)
145 Interval ledger_size = out.extent (X_AXIS);
146 ledger_size.widen ( ledger_size.length ()/4);
148 Real left_shorten =0.0;
149 if (Grob * g = unsmob_grob (me->get_property ("accidental-grob")))
152 make a little room for accidentals.
154 TODO: this will look silly if a chord has ledger lines,
155 and only the bottom note has an accidental.
158 Grob *common = g->common_refpoint (me, X_AXIS);
160 linear_combination (Drul_array<Real> (me->extent (common, X_AXIS)[LEFT],
161 g->extent (common, X_AXIS)[RIGHT]),
165 left_shorten = (-ledger_size[LEFT] + d) >? 0 ;
168 TODO: shorten 2 ledger lines for the case natural +
173 out.add_stencil (Note_head::brew_ledger_lines (me, pos, interspaces,
182 MAKE_SCHEME_CALLBACK (Note_head,print,1);
184 Note_head::print (SCM smob)
186 Grob *me = unsmob_grob (smob);
189 ledgers don't take space. See top of file.
191 return internal_print (me, true).smobbed_copy ();
195 Compute the width the head without ledgers.
197 -- there used to be some code from the time that ledgers
198 did take space. Nowadays, we can simply take the standard extent.
201 Note_head::head_extent (Grob *me, Axis a)
203 SCM brewer = me->get_property ("print-function");
204 if (brewer == Note_head::print_proc)
206 Stencil mol = internal_print (me, false);
208 if (!mol.is_empty ())
209 return mol.extent (a);
213 Stencil * mol = me->get_stencil ();
215 return mol->extent (a) ;
218 return Interval (0,0);
222 This is necessary to prevent a cyclic dependency: the appearance of
223 the ledgers depends on positioning, so the Grob::get_stencil () can
224 not be used for determining the note head extent.
227 MAKE_SCHEME_CALLBACK (Note_head,extent,2);
229 Note_head::extent (SCM smob, SCM axis)
231 Grob *me = unsmob_grob (smob);
233 return ly_interval2scm (head_extent (me, (Axis) ly_scm2int (axis)));
236 MAKE_SCHEME_CALLBACK (Note_head,brew_ez_stencil,1);
238 Note_head::brew_ez_stencil (SCM smob)
240 Grob *me = unsmob_grob (smob);
241 int l = Note_head::get_balltype (me);
245 SCM cause = me->get_property ("cause");
246 SCM spitch = unsmob_music (cause)->get_property ("pitch");
247 Pitch* pit = unsmob_pitch (spitch);
249 SCM idx = scm_int2num (pit->get_notename ());
250 SCM names = me->get_property ("note-names");
251 SCM charstr = SCM_EOL;
252 if (ly_c_vector_p (names))
253 charstr = scm_vector_ref (names, idx);
257 s[0] = (pit->get_notename () + 2)%7 + 'a';
258 s[0] = toupper (s[0]);
259 charstr = scm_makfrom0str (s);
262 SCM at = scm_list_n (ly_symbol2scm ("ez-ball"),
267 Box bx (Interval (0, 1.0), Interval (-0.5, 0.5));
270 int pos = Staff_symbol_referencer::get_rounded_position (me);
271 int interspaces = Staff_symbol_referencer::line_count (me)-1;
272 if (abs (pos) - interspaces > 1)
274 Interval hd = m.extent (X_AXIS);
275 hd.widen ( hd.length ()/4);
276 m.add_stencil (brew_ledger_lines (me, pos, interspaces, hd, 0, false));
279 return m.smobbed_copy ();
284 Note_head::stem_attachment_coordinate (Grob *me, Axis a)
286 SCM brewer = me->get_property ("print-function");
287 Font_metric * fm = Font_interface::get_default_font (me);
289 if (brewer == Note_head::print_proc)
291 SCM style = me->get_property ("style");
292 if (!ly_c_symbol_p (style))
297 SCM log = scm_int2num (Note_head::get_balltype (me));
298 SCM proc = me->get_property ("glyph-name-procedure");
299 SCM scm_font_char = scm_call_2 (proc, log, style);
300 String font_char = "noteheads-" + ly_scm2string (scm_font_char);
302 int k = fm->name_to_index (font_char) ;
306 Box b = fm->get_indexed_char (k);
307 Offset wxwy = fm->get_indexed_wxwy (k);
310 return 2 * (wxwy[a] - v.center ()) / v.length ();
317 SCM v = me->get_property ("stem-attachment-function");
318 if (!ly_c_procedure_p (v))
321 SCM result = scm_call_2 (v, me->self_scm (), scm_int2num (a));
322 if (!ly_c_pair_p (result))
325 result = (a == X_AXIS) ? ly_car (result) : ly_cdr (result);
327 return robust_scm2double (result,0);
331 Note_head::get_balltype (Grob*me)
333 SCM s = me->get_property ("duration-log");
334 return ly_c_number_p (s) ? ly_scm2int (s) <? 2 : 0;
337 ADD_INTERFACE (Note_head,"note-head-interface",
339 "note-names glyph-name-procedure accidental-grob style stem-attachment-function");