2 notehead.cc -- implement Note_head
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2003 Han-Wen Nienhuys <hanwen@cs.uu.nl>
13 #include "note-head.hh"
15 #include "font-interface.hh"
16 #include "molecule.hh"
18 #include "rhythmic-head.hh"
19 #include "staff-symbol-referencer.hh"
21 #include "paper-def.hh"
24 Note_head contains the code for printing note heads.
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:
34 - when ledgered notes are juxtaposed: there should be some white
35 space between the ledger lines.
37 - when accidentals are near: the accidentals should not be on the
40 [both tips by Heinz Stolba from Universal Edition].
42 DO NOT take space into account:
44 - for basically everything else, e.g. swapping ledgered notes on
45 clustered chords, spacing between ledgered and unledgered notes.
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
55 (Besides a separate ledger seems overkill. For what else would
61 TODO: ledger lines are also a property of the staff. Maybe move them
65 Note_head::brew_ledger_lines (Grob *me,
72 Real inter_f = Staff_symbol_referencer::staff_space (me)/2;
73 int line_count = (abs (pos) < interspaces)
75 : (abs (pos) - interspaces) / 2;
76 Molecule molecule = Molecule();
81 Real ledgerlinethickness =
82 (me->get_paper ()->get_realvar (ly_symbol2scm ("ledgerlinethickness")));
83 Real blotdiameter = ledgerlinethickness;
84 // (me->get_paper ()->get_realvar (ly_symbol2scm ("blotdiameter")));
86 Interval (-0.5*(ledgerlinethickness),
87 +0.5*(ledgerlinethickness));
88 Molecule proto_ledger_line =
89 Lookup::round_filled_box (Box (x_extent, y_extent), blotdiameter);
91 x_extent[LEFT] += left_shorten;
92 Molecule proto_first_line =
93 Lookup::round_filled_box (Box (x_extent, y_extent), blotdiameter);
97 proto_ledger_line.set_empty (true);
98 proto_first_line.set_empty (true);
101 Direction dir = (Direction)sign (pos);
102 Real offs = (Staff_symbol_referencer::on_staffline (me, pos))
106 for (int i = 0; i < line_count; i++)
108 Molecule ledger_line ((i == 0)
112 ledger_line.translate_axis (-dir * inter_f * i * 2 + offs, Y_AXIS);
113 molecule.add_molecule (ledger_line);
121 internal_brew_molecule (Grob *me, bool with_ledgers)
123 SCM style = me->get_grob_property ("style");
124 if (!gh_symbol_p (style))
129 SCM log = gh_int2scm (Note_head::get_balltype (me));
130 SCM proc = me->get_grob_property ("glyph-name-procedure");
131 SCM scm_font_char = scm_call_2 (proc, log, style);
132 String font_char = "noteheads-" + ly_scm2string (scm_font_char);
134 Font_metric * fm = Font_interface::get_default_font (me);
135 Molecule out = fm->find_by_name (font_char);
138 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 = (int)rint (Staff_symbol_referencer::get_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_grob_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 (me->extent (common, X_AXIS)[LEFT]
162 +g->extent (common, X_AXIS)[RIGHT]) /2;
164 left_shorten = (-ledger_size[LEFT] + d) >? 0 ;
167 TODO: shorten 2 ledger lines for the case natural +
172 out.add_molecule (Note_head::brew_ledger_lines (me, pos, interspaces,
181 MAKE_SCHEME_CALLBACK (Note_head,brew_molecule,1);
183 Note_head::brew_molecule (SCM smob)
185 Grob *me = unsmob_grob (smob);
188 ledgers don't take space. See top of file.
190 return internal_brew_molecule (me, true).smobbed_copy ();
194 Compute the width the head without ledgers.
196 -- there used to be some code from the time that ledgers
197 did take space. Nowadays, we can simply take the standard extent.
200 Note_head::head_extent (Grob *me, Axis a)
202 SCM brewer = me->get_grob_property ("molecule-callback");
203 if (brewer == Note_head::brew_molecule_proc)
205 Molecule mol = internal_brew_molecule (me, false);
207 if (!mol.is_empty ())
208 return mol.extent (a);
212 Molecule * mol = me->get_molecule ();
214 return mol->extent (a) ;
217 return Interval (0,0);
221 This is necessary to prevent a cyclic dependency: the appearance of
222 the ledgers depends on positioning, so the Grob::get_molecule() can
223 not be used for determining the note head extent.
226 MAKE_SCHEME_CALLBACK (Note_head,extent,2);
228 Note_head::extent (SCM smob, SCM axis)
230 Grob *me = unsmob_grob (smob);
232 return ly_interval2scm (head_extent (me, (Axis) gh_scm2int (axis)));
235 MAKE_SCHEME_CALLBACK (Note_head,brew_ez_molecule,1);
237 Note_head::brew_ez_molecule (SCM smob)
239 Grob *me = unsmob_grob (smob);
240 int l = Note_head::get_balltype (me);
244 SCM cause = me->get_grob_property ("cause");
245 SCM spitch = unsmob_music (cause)->get_mus_property ("pitch");
246 Pitch* pit = unsmob_pitch (spitch);
249 s[0] = (pit->get_notename () + 2)%7 + 'a';
250 s[0] = toupper (s[0]);
252 SCM charstr = scm_makfrom0str (s);
254 SCM at = scm_list_n (ly_symbol2scm ("ez-ball"),
259 Box bx (Interval (0, 1.0), Interval (-0.5, 0.5));
262 int pos = (int)rint (Staff_symbol_referencer::get_position (me));
263 int interspaces = Staff_symbol_referencer::line_count (me)-1;
264 if (abs (pos) - interspaces > 1)
266 Interval hd = m.extent (X_AXIS);
267 hd.widen ( hd.length ()/4);
268 m.add_molecule (brew_ledger_lines (me, pos, interspaces, hd, 0, false));
271 return m.smobbed_copy ();
276 Note_head::stem_attachment_coordinate (Grob *me, Axis a)
278 SCM brewer = me->get_grob_property ("molecule-callback");
279 Font_metric * fm = Font_interface::get_default_font (me);
281 if (brewer == Note_head::brew_molecule_proc)
283 SCM style = me->get_grob_property ("style");
284 if (!gh_symbol_p (style))
289 SCM log = gh_int2scm (Note_head::get_balltype (me));
290 SCM proc = me->get_grob_property ("glyph-name-procedure");
291 SCM scm_font_char = scm_call_2 (proc, log, style);
292 String font_char = "noteheads-" + ly_scm2string (scm_font_char);
294 int k = fm->name_to_index (font_char) ;
298 Box b = fm->get_indexed_char (k);
299 Offset wxwy = fm->get_indexed_wxwy (k);
302 return 2 * (wxwy[a] - v.center()) / v.length ();
309 SCM v = me->get_grob_property ("stem-attachment-function");
310 if (!gh_procedure_p (v))
313 SCM result = scm_call_2 (v, me->self_scm(), gh_int2scm (a));
314 if (!gh_pair_p (result))
317 result = (a == X_AXIS) ? ly_car (result) : ly_cdr (result);
319 return robust_scm2double (result,0);
323 Note_head::get_balltype (Grob*me)
325 SCM s = me->get_grob_property ("duration-log");
326 return gh_number_p (s) ? gh_scm2int (s) <? 2 : 0;
329 ADD_INTERFACE (Note_head,"note-head-interface",
331 "glyph-name-procedure accidental-grob style stem-attachment-function");