*** empty log message ***
[lilypond.git] / lily / ambitus.cc
blob9e5b145557a9a0f2472f0a56b7bcac37ce356013
1 /*
2 ambitus.cc -- implement Ambitus
4 source file of the GNU LilyPond music typesetter
6 (c) 2002--2004 Juergen Reuter <reuter@ipd.uka.de>
7 */
9 #include "staff-symbol-referencer.hh"
10 #include "pitch.hh"
11 #include "ambitus.hh"
12 #include "stencil.hh"
13 #include "note-head.hh"
14 #include "item.hh"
15 #include "font-interface.hh"
16 #include "paper-def.hh"
17 #include "lookup.hh"
20 UGH UGH UGH
22 This does 3 things at one:
24 - acc positioning
25 - drawing accidentals
26 - drawing note heads
28 It confuses interpretation & formatting.
30 UGH.
31 --hwn.
35 * TODO: note-head collision handling
37 * TODO: accidentals collision handling
39 * TODO: alternative representation: adding the ambitus as text script
40 * to the instrument name (e.g. "Soprano (c^1 - f^2)").
42 * FIXME: Accidentals are too close at the note heads (it seems that
43 * the extent of the ledger lines is ignored).
45 * TODO: If (depending on breakAlignOrder) ambitus is put behind
46 * key-signature, then do not repeat accidentals that already appear
47 * in the key signature.
49 * FIXME: A staff containing more than a single context will result in
50 * multiple ambitus grobs per staff. This is basically ok, but there is
51 * currently no proper collision handling for this case.
53 * TODO: make ignore_octave and force_accidental of function
54 * number_accidentals accessible via grob properties.
57 /**
58 Given a pitch and a key_signature, decide what accidentals to show.
60 Possible return values:
62 0: do not show any accidental
63 1: show pitch->alteration_ only
64 2: show pitch->get_alteration, preceded by a natural sign
66 UGH: code duplication! See accidental-engraver.
69 static int
70 number_accidentals (SCM key_signature, Pitch *pitch,
71 bool ignore_octave_b, bool force_accidental)
73 int notename = pitch->get_notename ();
74 int octave = pitch->get_octave ();
75 int alteration = pitch->get_alteration ();
77 if (force_accidental) // ignore key signature
78 return 1;
81 #if DEBUG_AMBITUS
82 scm_display (key_signature, scm_current_output_port ());
83 #endif
85 SCM prev;
86 if (ignore_octave_b)
87 prev = ly_assoc_cdr (scm_int2num (notename), key_signature);
88 else
89 prev = scm_assoc (gh_cons (scm_int2num (octave), scm_int2num (notename)),
90 key_signature);
92 /* should really be true unless prev == SCM_BOOL_F */
93 if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
95 prev = gh_cons (ly_car (prev), ly_cadr (prev));
98 /* If an accidental was not found */
99 if (prev == SCM_BOOL_F)
100 prev = scm_assoc (scm_int2num (notename), key_signature);
102 SCM prev_acc = (prev == SCM_BOOL_F) ? scm_int2num (0) : ly_cdr (prev);
103 int sig_alteration = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
105 if (alteration == sig_alteration) // no accidental at all needed
106 return 0;
108 if ((alteration == 0) && (sig_alteration != 0)) // need ordinary natural
109 return 2;
111 if (sig_alteration == 0) // use pitch's alteration
112 return 1;
114 return 2;
117 void
118 add_accidentals (Item *me, Stencil *head, int num_acc,
119 Pitch *pitch, String accidentals_style, Real yoffs)
121 if (!num_acc)
122 return;
123 if (pitch->get_alteration ())
125 Stencil accidental (Font_interface::get_default_font (me)->
126 find_by_name (String ("accidentals-") +
127 accidentals_style +
128 to_string (pitch->get_alteration ())));
129 accidental.translate_axis (yoffs, Y_AXIS);
130 head->add_at_edge (X_AXIS, LEFT, accidental, 0.1, 0);
132 if (num_acc == 2)
134 Stencil natural (Font_interface::get_default_font (me)->
135 find_by_name (String ("accidentals-") +
136 accidentals_style +
137 to_string ("0")));
138 natural.translate_axis (yoffs, Y_AXIS);
139 head->add_at_edge (X_AXIS, LEFT, natural, 0.1, 0);
143 MAKE_SCHEME_CALLBACK (Ambitus,print,1);
145 Ambitus::print (SCM smob)
147 Item *me = (Item *)unsmob_grob (smob);
148 Stencil stencil = Stencil ();
150 SCM scm_note_head_style = me->get_property ("note-head-style");
151 String note_head_style;
152 if (gh_symbol_p (scm_note_head_style))
154 String note_head_style =
155 ly_symbol2string (scm_note_head_style);
157 else
159 note_head_style = String ("noteheads-2");
161 if (Font_interface::get_default_font (me)->find_by_name (note_head_style).is_empty ())
163 String message = "Ambitus: no such note head: `" + note_head_style + "'";
164 me->warning (_ (message.to_str0 ()));
165 return SCM_EOL;
169 FIXME: Use positions.
171 int p_min, p_max;
172 Pitch *pitch_min = unsmob_pitch (me->get_property ("pitch-min"));
173 if (!pitch_min)
175 me->programming_error ("Ambitus: pitch_min undefined; assuming 0");
176 p_min = 0;
178 else
180 p_min = pitch_min->steps ();
182 Pitch *pitch_max = unsmob_pitch (me->get_property ("pitch-max"));
183 if (!pitch_max)
185 me->programming_error ("Ambitus: pitch_max undefined; assuming 0");
186 p_max = 0;
188 else
190 p_max = pitch_max->steps ();
192 if (p_min > p_max)
194 me->programming_error ("Ambitus: reverse range");
197 SCM c0 = me->get_property ("c0-position");
198 if (gh_number_p (c0))
200 p_min += gh_scm2int (c0);
201 p_max += gh_scm2int (c0);
204 // create heads
205 Stencil head_min =
206 Font_interface::get_default_font (me)->find_by_name (note_head_style);
207 head_min.translate_axis (0.5*p_min, Y_AXIS);
208 Stencil head_max =
209 Font_interface::get_default_font (me)->find_by_name (note_head_style);
210 head_max.translate_axis (0.5*p_max, Y_AXIS);
212 // join heads
213 if (to_boolean (me->get_property ("join-heads")) &&
214 ((p_max - p_min) >= 3))
216 Real linethickness = me->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
217 Real blotdiameter = me->get_paper ()->get_realvar (ly_symbol2scm ("blotdiameter"));
218 Interval x_extent = 0.5 * Interval (-linethickness, +linethickness);
219 Interval y_extent = 0.5 * Interval (p_min + 1.35, p_max - 1.35);
220 Box line_box (x_extent, y_extent);
221 Stencil line = Lookup::round_filled_box (line_box, blotdiameter);
222 line.translate_axis (0.5 * head_min.extent (X_AXIS).length (), X_AXIS);
223 stencil.add_stencil (line);
226 // add ledger lines
227 Interval hd = head_min.extent (X_AXIS);
228 Real left_ledger_protusion = hd.length () / 4;
229 Real right_ledger_protusion = left_ledger_protusion;
230 Interval l_extents = Interval (hd[LEFT] - left_ledger_protusion,
231 hd[RIGHT] + right_ledger_protusion);
232 Stencil ledger_lines;
233 int interspaces = Staff_symbol_referencer::line_count (me) - 1;
234 ledger_lines =
235 Note_head::brew_ledger_lines (me, p_min, interspaces, l_extents, 0,true);
236 ledger_lines.translate_axis (0.5 * p_min, Y_AXIS);
237 stencil.add_stencil (ledger_lines);
238 ledger_lines =
239 Note_head::brew_ledger_lines (me, p_max, interspaces, l_extents, 0, true);
240 ledger_lines.translate_axis (0.5 * p_max, Y_AXIS);
241 stencil.add_stencil (ledger_lines);
243 // add accidentals
244 SCM key_signature = me->get_property ("key-signature");
245 SCM scm_accidentals_style = me->get_property ("accidentals-style");
246 String accidentals_style;
247 if (gh_symbol_p (scm_accidentals_style))
249 accidentals_style =
250 ly_symbol2string (scm_accidentals_style);
252 else
254 accidentals_style = String ("");
257 int num_acc;
258 num_acc = number_accidentals (key_signature, pitch_min, true, false);
259 add_accidentals (me, &head_min, num_acc, pitch_min,
260 accidentals_style, 0.5 * p_min);
261 num_acc = number_accidentals (key_signature, pitch_max, true, false);
262 add_accidentals (me, &head_max, num_acc, pitch_max,
263 accidentals_style, 0.5 * p_max);
265 // add heads
266 stencil.add_stencil (head_min);
267 stencil.add_stencil (head_max);
269 return stencil.smobbed_copy ();
272 ADD_INTERFACE (Ambitus, "ambitus-interface",
273 "An object that represents the pitch range of a voice.",
274 "c0-position pitch-min pitch-max accidentals note-head-style accidentals-style join-heads");