]> git.donarmstrong.com Git - lilypond.git/blob - lily/ambitus.cc
* lily/book.cc (to_stencil): New method.
[lilypond.git] / lily / ambitus.cc
1 /*
2   ambitus.cc -- implement Ambitus
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2002--2004 Juergen Reuter <reuter@ipd.uka.de>
7 */
8
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"
18
19 /*
20   UGH UGH UGH
21
22   This does 3 things at one:
23
24   - acc positioning
25   - drawing accidentals
26   - drawing note heads
27
28   It confuses interpretation & formatting.
29
30   UGH.
31   --hwn.
32  */
33
34 /*
35  * TODO: note-head collision handling
36  *
37  * TODO: accidentals collision handling
38  *
39  * TODO: alternative representation: adding the ambitus as text script
40  * to the instrument name (e.g. "Soprano (c^1 - f^2)").
41  *
42  * FIXME: Accidentals are too close at the note heads (it seems that
43  * the extent of the ledger lines is ignored).
44  *
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.
48  *
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.
52  *
53  * TODO: make ignore_octave and force_accidental of function
54  * number_accidentals accessible via grob properties.
55  */
56
57 /**
58   Given a pitch and a key_signature, decide what accidentals to show.
59  
60   Possible return values:
61  
62   0: do not show any accidental
63   1: show pitch->alteration_ only
64   2: show pitch->get_alteration, preceded by a natural sign
65
66   UGH: code duplication! See accidental-engraver.
67  
68  */
69 static int
70 number_accidentals (SCM key_signature, Pitch *pitch,
71                     bool ignore_octave_b, bool force_accidental)
72 {
73   int notename = pitch->get_notename ();
74   int octave = pitch->get_octave ();
75   int alteration = pitch->get_alteration ();
76
77   if (force_accidental) // ignore key signature
78     return 1;
79
80   
81 #if DEBUG_AMBITUS
82   scm_display (key_signature, scm_current_output_port ());
83 #endif
84
85   SCM prev;
86   if (ignore_octave_b)
87     prev = ly_assoc_cdr (scm_int2num (notename), key_signature);
88   else
89     prev = scm_assoc (scm_cons (scm_int2num (octave), scm_int2num (notename)),
90                      key_signature);
91
92   /* should really be true unless prev == SCM_BOOL_F */
93   if (ly_c_pair_p (prev) && ly_c_pair_p (ly_cdr (prev)))
94     {
95       prev = scm_cons (ly_car (prev), ly_cadr (prev));
96     }
97
98   /* If an accidental was not found */
99   if (prev == SCM_BOOL_F)
100     prev = scm_assoc (scm_int2num (notename), key_signature);
101
102   SCM prev_acc = (prev == SCM_BOOL_F) ? scm_int2num (0) : ly_cdr (prev);
103   int sig_alteration = ly_c_number_p (prev_acc) ? ly_scm2int (prev_acc) : 0;
104
105   if (alteration == sig_alteration) // no accidental at all needed
106     return 0;
107
108   if ((alteration == 0) && (sig_alteration != 0)) // need ordinary natural
109     return 2;
110
111   if (sig_alteration == 0) // use pitch's alteration
112     return 1;
113
114   return 2;
115 }
116
117 void
118 add_accidentals (Item *me, Stencil *head, int num_acc,
119                  Pitch *pitch, String accidentals_style, Real yoffs)
120 {
121   if (!num_acc)
122     return;
123   if (pitch->get_alteration ())
124     {
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);
131     }
132   if (num_acc == 2)
133     {
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);
140     }
141 }
142
143 MAKE_SCHEME_CALLBACK (Ambitus,print,1);
144 SCM
145 Ambitus::print (SCM smob)
146 {
147   Item *me = (Item*) unsmob_grob (smob);
148   Stencil stencil;
149
150   SCM scm_note_head_style = me->get_property ("note-head-style");
151   String note_head_style;
152   if (ly_c_symbol_p (scm_note_head_style))
153     {
154       String note_head_style =
155         ly_symbol2string (scm_note_head_style);
156     }
157   else
158     {
159       note_head_style = String ("noteheads-2");
160     }
161   if (Font_interface::get_default_font (me)->find_by_name (note_head_style).is_empty ())
162     {
163       String message = "Ambitus: no such note head: `" + note_head_style + "'";
164       me->warning (_ (message.to_str0 ()));
165       return SCM_EOL;
166     }
167
168   /*
169     FIXME: Use positions. 
170    */
171   int p_min, p_max;
172   Pitch *pitch_min = unsmob_pitch (me->get_property ("pitch-min"));
173   if (!pitch_min)
174     {
175       me->programming_error ("Ambitus: pitch_min undefined; assuming 0");
176       p_min = 0;
177     }
178   else
179     {
180       p_min = pitch_min->steps ();
181     }
182   Pitch *pitch_max = unsmob_pitch (me->get_property ("pitch-max"));
183   if (!pitch_max)
184     {
185       me->programming_error ("Ambitus: pitch_max undefined; assuming 0");
186       p_max = 0;
187     }
188   else
189     {
190       p_max = pitch_max->steps ();
191     }
192   if (p_min > p_max)
193     {
194       me->programming_error ("Ambitus: reverse range");
195     }
196
197   SCM c0 = me->get_property ("c0-position");
198   if (ly_c_number_p (c0))
199     {
200       p_min += ly_scm2int (c0);
201       p_max += ly_scm2int (c0);
202     }
203
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);
211
212   // join heads
213   if (to_boolean (me->get_property ("join-heads")) &&
214       ((p_max - p_min) >= 3))
215     {
216       Real linethickness = me->get_paper ()->get_dimension (ly_symbol2scm ("linethickness"));
217       Real blotdiameter = me->get_paper ()->get_dimension (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);
224     }
225
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);
242
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 (ly_c_symbol_p (scm_accidentals_style))
248     {
249       accidentals_style =
250         ly_symbol2string (scm_accidentals_style);
251     }
252   else
253     {
254       accidentals_style = String ("");
255     }
256   
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);
264
265   // add heads
266   stencil.add_stencil (head_min);
267   stencil.add_stencil (head_max);
268
269   return stencil.smobbed_copy ();
270 }
271
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");