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"
17 #include "molecule.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 Molecule molecule = Molecule();
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 Molecule proto_ledger_line =
90 Lookup::round_filled_box (Box (x_extent, y_extent), blotdiameter);
92 x_extent[LEFT] += left_shorten;
93 Molecule 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 Molecule ledger_line ((i == 0)
113 ledger_line.translate_axis (-dir * inter_f * i * 2 + offs, Y_AXIS);
114 molecule.add_molecule (ledger_line);
122 internal_print (Grob *me, bool with_ledgers)
124 SCM style = me->get_grob_property ("style");
125 if (!gh_symbol_p (style))
130 SCM log = gh_int2scm (Note_head::get_balltype (me));
131 SCM proc = me->get_grob_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 Molecule 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 = (int)rint (Staff_symbol_referencer::get_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_grob_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 (me->extent (common, X_AXIS)[LEFT]
163 +g->extent (common, X_AXIS)[RIGHT]) /2;
165 left_shorten = (-ledger_size[LEFT] + d) >? 0 ;
168 TODO: shorten 2 ledger lines for the case natural +
173 out.add_molecule (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_grob_property ("print-function");
204 if (brewer == Note_head::print_proc)
206 Molecule mol = internal_print (me, false);
208 if (!mol.is_empty ())
209 return mol.extent (a);
213 Molecule * mol = me->get_molecule ();
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_molecule() 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) gh_scm2int (axis)));
236 MAKE_SCHEME_CALLBACK (Note_head,brew_ez_molecule,1);
238 Note_head::brew_ez_molecule (SCM smob)
240 Grob *me = unsmob_grob (smob);
241 int l = Note_head::get_balltype (me);
245 SCM cause = me->get_grob_property ("cause");
246 SCM spitch = unsmob_music (cause)->get_mus_property ("pitch");
247 Pitch* pit = unsmob_pitch (spitch);
250 s[0] = (pit->get_notename () + 2)%7 + 'a';
251 s[0] = toupper (s[0]);
253 SCM charstr = scm_makfrom0str (s);
255 SCM at = scm_list_n (ly_symbol2scm ("ez-ball"),
260 Box bx (Interval (0, 1.0), Interval (-0.5, 0.5));
263 int pos = (int)rint (Staff_symbol_referencer::get_position (me));
264 int interspaces = Staff_symbol_referencer::line_count (me)-1;
265 if (abs (pos) - interspaces > 1)
267 Interval hd = m.extent (X_AXIS);
268 hd.widen ( hd.length ()/4);
269 m.add_molecule (brew_ledger_lines (me, pos, interspaces, hd, 0, false));
272 return m.smobbed_copy ();
277 Note_head::stem_attachment_coordinate (Grob *me, Axis a)
279 SCM brewer = me->get_grob_property ("print-function");
280 Font_metric * fm = Font_interface::get_default_font (me);
282 if (brewer == Note_head::print_proc)
284 SCM style = me->get_grob_property ("style");
285 if (!gh_symbol_p (style))
290 SCM log = gh_int2scm (Note_head::get_balltype (me));
291 SCM proc = me->get_grob_property ("glyph-name-procedure");
292 SCM scm_font_char = scm_call_2 (proc, log, style);
293 String font_char = "noteheads-" + ly_scm2string (scm_font_char);
295 int k = fm->name_to_index (font_char) ;
299 Box b = fm->get_indexed_char (k);
300 Offset wxwy = fm->get_indexed_wxwy (k);
303 return 2 * (wxwy[a] - v.center()) / v.length ();
310 SCM v = me->get_grob_property ("stem-attachment-function");
311 if (!gh_procedure_p (v))
314 SCM result = scm_call_2 (v, me->self_scm(), gh_int2scm (a));
315 if (!gh_pair_p (result))
318 result = (a == X_AXIS) ? ly_car (result) : ly_cdr (result);
320 return robust_scm2double (result,0);
324 Note_head::get_balltype (Grob*me)
326 SCM s = me->get_grob_property ("duration-log");
327 return gh_number_p (s) ? gh_scm2int (s) <? 2 : 0;
330 ADD_INTERFACE (Note_head,"note-head-interface",
332 "glyph-name-procedure accidental-grob style stem-attachment-function");