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;
78 Stencil stencil = Stencil ();
83 Real ledgerlinethickness =
84 Staff_symbol::get_ledger_line_thickness (staff);
85 Real blotdiameter = ledgerlinethickness;
87 Interval (-0.5*(ledgerlinethickness),
88 +0.5*(ledgerlinethickness));
89 Stencil proto_ledger_line =
90 Lookup::round_filled_box (Box (x_extent, y_extent), blotdiameter);
92 x_extent[LEFT] += left_shorten;
93 Stencil proto_first_line =
94 Lookup::round_filled_box (Box (x_extent, y_extent), blotdiameter);
98 proto_ledger_line.set_empty (true);
99 proto_first_line.set_empty (true);
102 Direction dir = (Direction)sign (pos);
103 Real offs = (Staff_symbol_referencer::on_staffline (me, pos))
107 for (int i = 0; i < line_count; i++)
109 Stencil ledger_line ((i == 0)
113 ledger_line.translate_axis (-dir * inter_f * i * 2 + offs, Y_AXIS);
114 stencil.add_stencil (ledger_line);
122 internal_print (Grob *me, bool with_ledgers)
124 SCM style = me->get_property ("style");
125 if (!ly_c_symbol_p (style))
130 SCM log = scm_int2num (Note_head::get_balltype (me));
131 SCM proc = me->get_property ("glyph-name-procedure");
132 SCM scm_font_char = scm_call_2 (proc, log, style);
133 String font_char = "noteheads-" + ly_scm2string (scm_font_char);
135 Font_metric * fm = Font_interface::get_default_font (me);
136 Stencil out = fm->find_by_name (font_char);
139 me->warning (_f ("note head `%s' not found", font_char.to_str0 ()));
142 int interspaces = Staff_symbol_referencer::line_count (me)-1;
143 int pos = Staff_symbol_referencer::get_rounded_position (me);
144 if (with_ledgers && interspaces >= 0
145 && abs (pos) - interspaces > 1)
147 Interval ledger_size = out.extent (X_AXIS);
148 ledger_size.widen ( ledger_size.length ()/4);
150 Real left_shorten =0.0;
151 if (Grob * g = unsmob_grob (me->get_property ("accidental-grob")))
154 make a little room for accidentals.
156 TODO: this will look silly if a chord has ledger lines,
157 and only the bottom note has an accidental.
160 Grob *common = g->common_refpoint (me, X_AXIS);
162 linear_combination (Drul_array<Real> (me->extent (common, X_AXIS)[LEFT],
163 g->extent (common, X_AXIS)[RIGHT]),
167 left_shorten = (-ledger_size[LEFT] + d) >? 0 ;
170 TODO: shorten 2 ledger lines for the case natural +
175 out.add_stencil (Note_head::brew_ledger_lines (me, pos, interspaces,
184 MAKE_SCHEME_CALLBACK (Note_head,print,1);
186 Note_head::print (SCM smob)
188 Grob *me = unsmob_grob (smob);
191 ledgers don't take space. See top of file.
193 return internal_print (me, true).smobbed_copy ();
197 Compute the width the head without ledgers.
199 -- there used to be some code from the time that ledgers
200 did take space. Nowadays, we can simply take the standard extent.
203 Note_head::head_extent (Grob *me, Axis a)
205 SCM brewer = me->get_property ("print-function");
206 if (brewer == Note_head::print_proc)
208 Stencil mol = internal_print (me, false);
210 if (!mol.is_empty ())
211 return mol.extent (a);
215 Stencil * mol = me->get_stencil ();
217 return mol->extent (a) ;
220 return Interval (0,0);
224 This is necessary to prevent a cyclic dependency: the appearance of
225 the ledgers depends on positioning, so the Grob::get_stencil () can
226 not be used for determining the note head extent.
229 MAKE_SCHEME_CALLBACK (Note_head,extent,2);
231 Note_head::extent (SCM smob, SCM axis)
233 Grob *me = unsmob_grob (smob);
235 return ly_interval2scm (head_extent (me, (Axis) ly_scm2int (axis)));
238 MAKE_SCHEME_CALLBACK (Note_head,brew_ez_stencil,1);
240 Note_head::brew_ez_stencil (SCM smob)
242 Grob *me = unsmob_grob (smob);
243 int l = Note_head::get_balltype (me);
247 SCM cause = me->get_property ("cause");
248 SCM spitch = unsmob_music (cause)->get_property ("pitch");
249 Pitch* pit = unsmob_pitch (spitch);
251 SCM idx = scm_int2num (pit->get_notename ());
252 SCM names = me->get_property ("note-names");
253 SCM charstr = SCM_EOL;
254 if (ly_c_vector_p (names))
255 charstr = scm_vector_ref (names, idx);
259 s[0] = (pit->get_notename () + 2)%7 + 'a';
260 s[0] = toupper (s[0]);
261 charstr = scm_makfrom0str (s);
264 SCM at = scm_list_n (ly_symbol2scm ("ez-ball"),
269 Box bx (Interval (0, 1.0), Interval (-0.5, 0.5));
272 int pos = Staff_symbol_referencer::get_rounded_position (me);
273 int interspaces = Staff_symbol_referencer::line_count (me)-1;
274 if (abs (pos) - interspaces > 1)
276 Interval hd = m.extent (X_AXIS);
277 hd.widen ( hd.length ()/4);
278 m.add_stencil (brew_ledger_lines (me, pos, interspaces, hd, 0, false));
281 return m.smobbed_copy ();
286 Note_head::stem_attachment_coordinate (Grob *me, Axis a)
288 SCM brewer = me->get_property ("print-function");
289 Font_metric * fm = Font_interface::get_default_font (me);
291 if (brewer == Note_head::print_proc)
293 SCM style = me->get_property ("style");
294 if (!ly_c_symbol_p (style))
299 SCM log = scm_int2num (Note_head::get_balltype (me));
300 SCM proc = me->get_property ("glyph-name-procedure");
301 SCM scm_font_char = scm_call_2 (proc, log, style);
302 String font_char = "noteheads-" + ly_scm2string (scm_font_char);
304 int k = fm->name_to_index (font_char) ;
308 Box b = fm->get_indexed_char (k);
309 Offset wxwy = fm->get_indexed_wxwy (k);
312 return 2 * (wxwy[a] - v.center ()) / v.length ();
319 SCM v = me->get_property ("stem-attachment-function");
320 if (!ly_c_procedure_p (v))
323 SCM result = scm_call_2 (v, me->self_scm (), scm_int2num (a));
324 if (!ly_c_pair_p (result))
327 result = (a == X_AXIS) ? ly_car (result) : ly_cdr (result);
329 return robust_scm2double (result,0);
333 Note_head::get_balltype (Grob*me)
335 SCM s = me->get_property ("duration-log");
336 return is_number (s) ? ly_scm2int (s) <? 2 : 0;
339 ADD_INTERFACE (Note_head,"note-head-interface",
341 "note-names glyph-name-procedure accidental-grob style stem-attachment-function");