* input/regression/ambitus.ly: move file.
[lilypond.git] / lily / ambitus.cc
blob2a3a29f23b0f4e8cbd011fa17a07ede79d1c76e8
1 /*
2 ambitus.cc -- implement Ambitus
4 source file of the GNU LilyPond music typesetter
6 (c) 2002--2003 Juergen Reuter <reuter@ipd.uka.de>
7 */
9 #include "staff-symbol-referencer.hh"
10 #include "pitch.hh"
11 #include "ambitus.hh"
12 #include "molecule.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 * TODO: note-head collision handling
22 * TODO: accidentals collision handling
24 * TODO: alternative representation: adding the ambitus as text script
25 * to the instrument name (e.g. "Soprano (c^1 - f^2)").
27 * FIXME: Accidentals are too close at the note heads (it seems that
28 * the extent of the ledger lines is ignored).
30 * TODO: If (depending on breakAlignOrder) ambitus is put behind
31 * key-signature, then do not repeat accidentals that already appear
32 * in the key signature.
34 * FIXME: A staff containing more than a single context will result in
35 * multiple ambitus grobs per staff. This is basically ok, but there is
36 * currently no proper collision handling for this case.
38 * TODO: make ignore_octave and force_accidental of function
39 * number_accidentals accessible via grob properties.
42 /**
43 Given a pitch and a key_signature, decide what accidentals to show.
45 Possible return values:
47 0: do not show any accidental
48 1: show pitch->alteration_ only
49 2: show pitch->get_alteration, preceded by a natural sign
51 UGH: code duplication! See accidental-engraver.
54 static int
55 number_accidentals (SCM key_signature, Pitch *pitch,
56 bool ignore_octave_b, bool force_accidental)
58 int notename = pitch->get_notename ();
59 int octave = pitch->get_octave();
60 int alteration = pitch->get_alteration();
62 if (force_accidental) // ignore key signature
63 return 1;
66 #if DEBUG_AMBITUS
67 scm_display (key_signature, scm_current_output_port ());
68 #endif
70 SCM prev;
71 if (ignore_octave_b)
72 prev = ly_assoc_cdr (scm_int2num (notename), key_signature);
73 else
74 prev = gh_assoc (gh_cons (scm_int2num (octave), scm_int2num (notename)),
75 key_signature);
77 /* should really be true unless prev == SCM_BOOL_F */
78 if (gh_pair_p (prev) && gh_pair_p (ly_cdr (prev)))
80 prev = gh_cons (ly_car (prev), ly_cadr (prev));
83 /* If an accidental was not found */
84 if (prev == SCM_BOOL_F)
85 prev = gh_assoc (scm_int2num (notename), key_signature);
87 SCM prev_acc = (prev == SCM_BOOL_F) ? scm_int2num (0) : ly_cdr (prev);
88 int sig_alteration = gh_number_p (prev_acc) ? gh_scm2int (prev_acc) : 0;
90 if (alteration == sig_alteration) // no accidental at all needed
91 return 0;
93 if ((alteration == 0) && (sig_alteration != 0)) // need ordinary natural
94 return 2;
96 if (sig_alteration == 0) // use pitch's alteration
97 return 1;
99 return 2;
102 void
103 add_accidentals (Item *me, Molecule *head, int num_acc,
104 Pitch *pitch, String accidentals_style, Real yoffs)
106 if (!num_acc)
107 return;
108 if (pitch->get_alteration())
110 Molecule accidental (Font_interface::get_default_font (me)->
111 find_by_name (String ("accidentals-") +
112 accidentals_style +
113 to_string (pitch->get_alteration())));
114 accidental.translate_axis (yoffs, Y_AXIS);
115 head->add_at_edge (X_AXIS, LEFT, accidental, 0.1, 0);
117 if (num_acc == 2)
119 Molecule natural (Font_interface::get_default_font (me)->
120 find_by_name (String ("accidentals-") +
121 accidentals_style +
122 to_string ("0")));
123 natural.translate_axis (yoffs, Y_AXIS);
124 head->add_at_edge (X_AXIS, LEFT, natural, 0.1, 0);
128 MAKE_SCHEME_CALLBACK (Ambitus,brew_molecule,1);
130 Ambitus::brew_molecule (SCM smob)
132 Item *me = (Item *)unsmob_grob (smob);
133 Molecule molecule = Molecule ();
135 SCM scm_note_head_style = me->get_grob_property ("note-head-style");
136 String note_head_style;
137 if (gh_symbol_p (scm_note_head_style))
139 String note_head_style =
140 ly_symbol2string (scm_note_head_style);
142 else
144 note_head_style = String ("noteheads-2");
146 if (Font_interface::get_default_font (me)->find_by_name (note_head_style).empty_b ())
148 String message = "Ambitus: no such note head: `" + note_head_style + "'";
149 me->warning (_ (message.to_str0 ()));
150 return SCM_EOL;
154 FIXME: Use positions.
156 int p_min, p_max;
157 Pitch *pitch_min = unsmob_pitch (me->get_grob_property ("pitch-min"));
158 if (!pitch_min)
160 me->programming_error("Ambitus: pitch_min undefined; assuming 0");
161 p_min = 0;
163 else
165 p_min = pitch_min->steps ();
167 Pitch *pitch_max = unsmob_pitch (me->get_grob_property ("pitch-max"));
168 if (!pitch_max)
170 me->programming_error("Ambitus: pitch_max undefined; assuming 0");
171 p_max = 0;
173 else
175 p_max = pitch_max->steps ();
177 if (p_min > p_max)
179 me->programming_error ("Ambitus: reverse range");
182 SCM c0 = me->get_grob_property ("c0-position");
183 if (gh_number_p (c0))
185 p_min += gh_scm2int (c0);
186 p_max += gh_scm2int (c0);
189 // create heads
190 Molecule head_min =
191 Font_interface::get_default_font (me)->find_by_name (note_head_style);
192 head_min.translate_axis (0.5*p_min, Y_AXIS);
193 Molecule head_max =
194 Font_interface::get_default_font (me)->find_by_name (note_head_style);
195 head_max.translate_axis (0.5*p_max, Y_AXIS);
197 // join heads
198 if (to_boolean (me->get_grob_property ("join-heads")) &&
199 ((p_max - p_min) >= 3))
201 Real linethickness = me->get_paper ()->get_var ("linethickness");
202 Real blotdiameter = me->get_paper ()->get_var ("blotdiameter");
203 Interval x_extent = 0.5 * Interval (-linethickness, +linethickness);
204 Interval y_extent = 0.5 * Interval (p_min + 1.35, p_max - 1.35);
205 Box line_box (x_extent, y_extent);
206 Molecule line = Lookup::roundfilledbox (line_box, blotdiameter);
207 line.translate_axis (0.5 * head_min.extent (X_AXIS).length (), X_AXIS);
208 molecule.add_molecule (line);
211 // add ledger lines
212 Interval hd = head_min.extent (X_AXIS);
213 Real left_ledger_protusion = hd.length () / 4;
214 Real right_ledger_protusion = left_ledger_protusion;
215 Interval l_extents = Interval (hd[LEFT] - left_ledger_protusion,
216 hd[RIGHT] + right_ledger_protusion);
217 Molecule ledger_lines;
218 int interspaces = Staff_symbol_referencer::line_count (me) - 1;
219 ledger_lines =
220 Note_head::brew_ledger_lines (me, p_min, interspaces, l_extents, true);
221 ledger_lines.translate_axis (0.5 * p_min, Y_AXIS);
222 molecule.add_molecule (ledger_lines);
223 ledger_lines =
224 Note_head::brew_ledger_lines (me, p_max, interspaces, l_extents, true);
225 ledger_lines.translate_axis (0.5 * p_max, Y_AXIS);
226 molecule.add_molecule (ledger_lines);
228 // add accidentals
229 SCM key_signature = me->get_grob_property ("key-signature");
230 SCM scm_accidentals_style = me->get_grob_property ("accidentals-style");
231 String accidentals_style;
232 if (gh_symbol_p (scm_accidentals_style))
234 accidentals_style =
235 ly_symbol2string (scm_accidentals_style);
237 else
239 accidentals_style = String ("");
241 int num_acc;
242 num_acc = number_accidentals (key_signature, pitch_min, true, false);
243 add_accidentals (me, &head_min, num_acc, pitch_min,
244 accidentals_style, 0.5 * p_min);
245 num_acc = number_accidentals (key_signature, pitch_max, true, false);
246 add_accidentals (me, &head_max, num_acc, pitch_max,
247 accidentals_style, 0.5 * p_max);
249 // add heads
250 molecule.add_molecule (head_min);
251 molecule.add_molecule (head_max);
253 return molecule.smobbed_copy ();
256 ADD_INTERFACE (Ambitus, "ambitus-interface",
257 "An ambitus represents the pitch range of a voice.",
258 "c0-position pitch-min pitch-max accidentals note-head-style join-heads");