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,
71 Real inter_f = Staff_symbol_referencer::staff_space (me)/2;
72 int line_count = (abs (pos) < interspaces)
74 : (abs (pos) - interspaces) / 2;
75 Molecule molecule = Molecule();
80 Real ledgerlinethickness =
81 (me->get_paper ()->get_realvar (ly_symbol2scm ("ledgerlinethickness")));
82 Real blotdiameter = ledgerlinethickness;
83 // (me->get_paper ()->get_realvar (ly_symbol2scm ("blotdiameter")));
85 Interval (-0.5*(ledgerlinethickness),
86 +0.5*(ledgerlinethickness));
87 Box ledger_line (x_extent, y_extent);
89 Molecule proto_ledger_line =
90 Lookup::round_filled_box (ledger_line, blotdiameter);
93 proto_ledger_line.set_empty (true);
96 Direction dir = (Direction)sign (pos);
97 Real offs = (Staff_symbol_referencer::on_staffline (me, pos))
100 for (int i = 0; i < line_count; i++)
102 Molecule ledger_line (proto_ledger_line);
103 ledger_line.translate_axis (-dir * inter_f * i * 2 + offs, Y_AXIS);
104 molecule.add_molecule (ledger_line);
112 internal_brew_molecule (Grob *me, bool with_ledgers)
114 SCM style = me->get_grob_property ("style");
115 if (!gh_symbol_p (style))
120 SCM log = gh_int2scm (Note_head::get_balltype (me));
121 SCM proc = me->get_grob_property ("glyph-name-procedure");
122 SCM scm_font_char = scm_call_2 (proc, log, style);
123 String font_char = "noteheads-" + ly_scm2string (scm_font_char);
125 Font_metric * fm = Font_interface::get_default_font (me);
126 Molecule out = fm->find_by_name (font_char);
129 me->warning (_f ("note head `%s' not found", font_char.to_str0 ()));
132 int interspaces = Staff_symbol_referencer::line_count (me)-1;
133 int pos = (int)rint (Staff_symbol_referencer::get_position (me));
134 if (with_ledgers && interspaces >= 0
135 && abs (pos) - interspaces > 1)
137 Interval hd = out.extent (X_AXIS);
138 Real left_ledger_protusion = hd.length ()/4;
139 Real right_ledger_protusion = left_ledger_protusion;
141 if (Grob * g = unsmob_grob(me->get_grob_property ("accidental-grob")))
144 make a little room for accidentals.
146 TODO: this will look silly if a chord has ledger lines,
147 and only the bottom note has an accidental.
150 Grob *common = g->common_refpoint (me, X_AXIS);
152 me->extent (common, X_AXIS)[LEFT]
153 -g->extent (common, X_AXIS)[RIGHT];
155 left_ledger_protusion = left_ledger_protusion <? (d/2);
158 Interval l_extents = Interval (hd[LEFT] - left_ledger_protusion,
159 hd[RIGHT] + right_ledger_protusion);
160 out.add_molecule (Note_head::brew_ledger_lines (me, pos, interspaces,
167 MAKE_SCHEME_CALLBACK (Note_head,brew_molecule,1);
169 Note_head::brew_molecule (SCM smob)
171 Grob *me = unsmob_grob (smob);
174 ledgers don't take space. See top of file.
176 return internal_brew_molecule (me, true).smobbed_copy ();
180 Compute the width the head without ledgers.
182 -- there used to be some code from the time that ledgers
183 did take space. Nowadays, we can simply take the standard extent.
186 Note_head::head_extent (Grob *me, Axis a)
188 SCM brewer = me->get_grob_property ("molecule-callback");
189 if (brewer == Note_head::brew_molecule_proc)
191 Molecule mol = internal_brew_molecule (me, false);
193 if (!mol.is_empty ())
194 return mol.extent (a);
198 Molecule * mol = me->get_molecule ();
200 return mol->extent (a) ;
203 return Interval (0,0);
207 This is necessary to prevent a cyclic dependency: the appearance of
208 the ledgers depends on positioning, so the Grob::get_molecule() can
209 not be used for determining the note head extent.
212 MAKE_SCHEME_CALLBACK (Note_head,extent,2);
214 Note_head::extent (SCM smob, SCM axis)
216 Grob *me = unsmob_grob (smob);
218 return ly_interval2scm (head_extent (me, (Axis) gh_scm2int (axis)));
221 MAKE_SCHEME_CALLBACK (Note_head,brew_ez_molecule,1);
223 Note_head::brew_ez_molecule (SCM smob)
225 Grob *me = unsmob_grob (smob);
226 int l = Note_head::get_balltype (me);
230 SCM cause = me->get_grob_property ("cause");
231 SCM spitch = unsmob_music (cause)->get_mus_property ("pitch");
232 Pitch* pit = unsmob_pitch (spitch);
235 s[0] = (pit->get_notename () + 2)%7 + 'a';
236 s[0] = toupper (s[0]);
238 SCM charstr = scm_makfrom0str (s);
240 SCM at = scm_list_n (ly_symbol2scm ("ez-ball"),
245 Box bx (Interval (0, 1.0), Interval (-0.5, 0.5));
248 int pos = (int)rint (Staff_symbol_referencer::get_position (me));
249 int interspaces = Staff_symbol_referencer::line_count (me)-1;
250 if (abs (pos) - interspaces > 1)
252 Interval hd = m.extent (X_AXIS);
253 Real hw = hd.length ()/4;
254 Interval extent = Interval (hd[LEFT] - hw, hd[RIGHT] + hw);
255 m.add_molecule (brew_ledger_lines (me, pos, interspaces, extent, false));
258 return m.smobbed_copy ();
263 Note_head::stem_attachment_coordinate (Grob *me, Axis a)
265 SCM brewer = me->get_grob_property ("molecule-callback");
266 Font_metric * fm = Font_interface::get_default_font (me);
268 if (brewer == Note_head::brew_molecule_proc)
270 SCM style = me->get_grob_property ("style");
271 if (!gh_symbol_p (style))
276 SCM log = gh_int2scm (Note_head::get_balltype (me));
277 SCM proc = me->get_grob_property ("glyph-name-procedure");
278 SCM scm_font_char = scm_call_2 (proc, log, style);
279 String font_char = "noteheads-" + ly_scm2string (scm_font_char);
281 int k = fm->name_to_index (font_char) ;
285 Box b = fm->get_indexed_char (k);
286 Offset wxwy = fm->get_indexed_wxwy (k);
289 return 2 * (wxwy[a] - v.center()) / v.length ();
296 SCM v = me->get_grob_property ("stem-attachment-function");
297 if (!gh_procedure_p (v))
300 SCM result = scm_call_2 (v, me->self_scm(), gh_int2scm (a));
301 if (!gh_pair_p (result))
304 result = (a == X_AXIS) ? ly_car (result) : ly_cdr (result);
306 return robust_scm2double (result,0);
310 Note_head::get_balltype (Grob*me)
312 SCM s = me->get_grob_property ("duration-log");
313 return gh_number_p (s) ? gh_scm2int (s) <? 2 : 0;
316 ADD_INTERFACE (Note_head,"note-head-interface",
318 "glyph-name-procedure accidental-grob style stem-attachment-function");