2 vaticana-ligature-engraver.cc -- implement Vaticana_ligature_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 2003 Juergen Reuter <reuter@ipd.uka.de>
9 #include "gregorian-ligature-engraver.hh"
10 #include "gregorian-ligature.hh"
11 #include "vaticana-ligature.hh"
14 #include "staff-symbol-referencer.hh"
15 #include "font-interface.hh"
17 #include "paper-def.hh"
18 #include "paper-column.hh"
21 * This class implements the notation specific aspects of Vaticana
22 * style ligatures for Gregorian chant notation.
24 class Vaticana_ligature_engraver : public Gregorian_ligature_engraver
28 Real finish_primitive (Item *first_primitive,
38 TRANSLATOR_DECLARATIONS(Vaticana_ligature_engraver);
41 virtual Spanner *create_ligature_spanner ();
42 virtual void transform_heads (Spanner *ligature,
43 Array<Grob_info> primitives);
46 Vaticana_ligature_engraver::Vaticana_ligature_engraver ()
51 Vaticana_ligature_engraver::create_ligature_spanner ()
53 return new Spanner (get_property ("VaticanaLigature"));
57 Vaticana_ligature_engraver::finish_primitive (Item *first_primitive,
66 Real next_distance = distance;
69 // determine width of previous head and x-offset
73 is_stacked = context_info & PES_UPPER;
74 if (context_info & FLEXA_LEFT)
76 if (!String::compare (glyph_name, "vaticana_cephalicus") &&
77 !(context_info & PES_LOWER))
79 if (context_info & AUCTUM)
84 * This head is stacked upon another one; hence, it does not
85 * contribute to the total width of the ligature, hence its
86 * width is assumed to be 0.0. Moreover, it is shifted to
87 * the left by its width such that the right side of this
88 * and the other head are horizontally aligned.
91 x_offset = join_thickness -
92 Font_interface::get_default_font (primitive)->
93 find_by_name ("noteheads-" + glyph_name).extent (X_AXIS).length ();
95 else if (!String::compare (glyph_name, "flexa") ||
96 !String::compare (glyph_name, ""))
99 * This head represents either half of a flexa shape.
100 * Hence, it is assigned half the width of this shape.
102 head_width = 0.5 * flexa_width;
105 else // retrieve width from corresponding font
108 Font_interface::get_default_font (primitive)->
109 find_by_name ("noteheads-" + glyph_name).extent (X_AXIS).length ();
114 * Save the head's final shape and x-offset.
116 primitive->set_grob_property ("glyph-name",
117 scm_makfrom0str (glyph_name.to_str0 ()));
118 primitive->set_grob_property ("x-offset",
119 gh_double2scm (x_offset));
122 * If the head is the 2nd head of a pes or flexa (but not a
123 * flexa shape), mark this head to be joined with the left-side
124 * neighbour head (i.e. the previous head) by a vertical beam.
126 if ((context_info & PES_UPPER) ||
127 ((context_info & FLEXA_RIGHT) &&
128 !(context_info & PES_LOWER)))
130 primitive->set_grob_property ("join-left",
131 gh_int2scm (pitch_delta));
134 * Create a small overlap of adjacent heads so that the join
135 * can be drawn perfectly between them.
137 next_distance -= join_thickness;
139 else if (!String::compare (glyph_name, ""))
142 * 2nd (virtual) head of flexa shape: join tightly with 1st
143 * head, i.e. do *not* add additional space, such that next
144 * head will not be off from the flexa shape.
147 else if (context_info & AFTER_VIRGA)
150 * Make a small space after a virga.
152 next_distance += 2 * join_thickness;
154 else if (pitch_delta == 0)
157 * Make a small space between two adjacent notes with the
160 next_distance += 2 * join_thickness;
164 * Horizontally line-up this head to form a ligature.
166 get_set_column (primitive, first_primitive->get_column ());
167 primitive->translate_axis (next_distance, X_AXIS);
168 next_distance += head_width;
172 return next_distance;
176 Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
177 Array<Grob_info> primitives)
180 SCM flexa_width_scm = ligature->get_grob_property ("flexa-width");
181 if (flexa_width_scm != SCM_EOL)
183 flexa_width = gh_scm2double (flexa_width_scm);
187 programming_error ("Vaticana_ligature_engraver:"
188 "flexa-width undefined; assuming 2.0 staff space");
190 2.0 * Staff_symbol_referencer::staff_space (ligature);
194 SCM join_thickness_scm = ligature->get_grob_property ("thickness");
195 if (join_thickness_scm != SCM_EOL)
197 join_thickness = gh_scm2double (join_thickness_scm);
201 programming_error ("Vaticana_ligature_engraver:"
202 "thickness undefined; assuming 1.4 linethickness");
203 join_thickness = 1.4;
205 join_thickness *= ligature->get_paper ()->get_var ("linethickness");
207 Item *first_primitive = 0;
208 Item *prev_primitive = 0;
209 int prev_context_info = 0;
211 int prev_pitch_delta = 0;
212 String prev_glyph_name = "";
213 Real prev_distance = 0.0;
214 for (int i = 0; i < primitives.size(); i++) {
215 Item *primitive = dynamic_cast<Item*> (primitives[i].grob_);
216 Music *music_cause = primitives[i].music_cause ();
217 int context_info = gh_scm2int (primitive->get_grob_property ("context-info"));
218 int pitch = unsmob_pitch (music_cause->get_mus_property ("pitch"))->steps ();
220 if (!first_primitive)
221 first_primitive = primitive;
222 int prefix_set = gh_scm2int (primitive->get_grob_property ("prefix-set"));
225 * Now determine which head to typeset (this is context sensitive
226 * information, since it depends on neighbouring heads; therefore,
227 * this decision must be made here in the engraver rather than in
230 if (prefix_set & VIRGA)
231 glyph_name = "vaticana_virga";
232 else if (prefix_set & QUILISMA)
233 glyph_name = "vaticana_quilisma";
234 else if (prefix_set & ORISCUS)
235 glyph_name = "solesmes_oriscus";
236 else if (prefix_set & STROPHA)
237 if (prefix_set & AUCTUM)
238 glyph_name = "solesmes_stropha_aucta";
239 else glyph_name = "solesmes_stropha";
240 else if (prefix_set & SEMIVOCALIS)
241 if (pitch > prev_pitch)
242 glyph_name = "vaticana_epiphonus";
243 else glyph_name = "vaticana_cephalicus";
244 else if (prefix_set & INCLINATUM)
245 if (prefix_set & AUCTUM)
246 glyph_name = "solesmes_incl_auctum";
247 else if (prefix_set & DEMINUTUM)
248 glyph_name = "solesmes_incl_parvum";
250 glyph_name = "vaticana_inclinatum";
251 else if (prefix_set & (CAVUM | LINEA))
252 if ((prefix_set & CAVUM) && (prefix_set & LINEA))
253 glyph_name = "vaticana_linea_punctum_cavum";
254 else if (prefix_set & CAVUM)
255 glyph_name = "vaticana_punctum_cavum";
257 glyph_name = "vaticana_linea_punctum";
258 else if (prefix_set & AUCTUM)
259 if (prefix_set & ASCENDENS)
260 glyph_name = "solesmes_auct_asc";
262 glyph_name = "solesmes_auct_desc";
263 else if (prefix_set & DEMINUTUM)
264 glyph_name = "vaticana_plica";
265 else if ((prefix_set & PES_OR_FLEXA) &&
266 (context_info & PES_LOWER) &&
267 (context_info & FLEXA_RIGHT))
268 glyph_name = ""; // second head of flexa shape
269 else if (context_info & PES_UPPER)
270 if (pitch - prev_pitch > 1)
271 glyph_name = "vaticana_upes";
273 glyph_name = "vaticana_vupes";
275 glyph_name = "vaticana_punctum";
278 * May need updating previous head, depending on the current head.
280 if (prefix_set & PES_OR_FLEXA)
281 if ((context_info & PES_LOWER) &&
282 (context_info & FLEXA_RIGHT)) // flexa shape
284 prev_glyph_name = "flexa";
285 prev_primitive->set_grob_property ("flexa-height",
286 gh_int2scm (pitch - prev_pitch));
287 prev_primitive->set_grob_property ("flexa-width",
288 gh_double2scm (flexa_width));
290 !(prev_context_info & PES_UPPER) &&
291 !(prev_context_info & FLEXA_RIGHT);
292 prev_primitive->set_grob_property ("add-stem",
293 gh_bool2scm (add_stem));
295 else if (context_info & PES_UPPER)
297 if (!String::compare (prev_glyph_name, "vaticana_punctum"))
298 prev_glyph_name = "vaticana_lpes";
302 if (!String::compare (prev_glyph_name, "vaticana_punctum"))
303 prev_glyph_name = "vaticana_rvirga";
307 * In the backend, flexa shapes and joins need to know about
308 * thickness. Hence, for simplicity, let's distribute the
309 * ligature grob's value for thickness to each ligature head (even
310 * if not all of them need to know).
312 primitive->set_grob_property ("thickness", gh_double2scm (join_thickness));
315 * The head of the current iteration still may change during the
316 * next iteration due to the context sensitiveness of the
317 * transformation. However, the head of the previous iteration is
318 * now fully attributed and can be prepared for the backend.
322 * Finish head of previous iteration for backend.
325 finish_primitive (first_primitive, prev_primitive,
326 prev_context_info, prev_glyph_name, prev_pitch_delta,
327 flexa_width, join_thickness, prev_distance);
329 prev_primitive = primitive;
330 prev_context_info = context_info;
331 prev_pitch_delta = pitch - prev_pitch;
333 prev_glyph_name = glyph_name;
337 * Finish head of last iteration for backend.
340 finish_primitive (first_primitive, prev_primitive,
341 prev_context_info, prev_glyph_name, prev_pitch_delta,
342 flexa_width, join_thickness, prev_distance);
344 /* TODO: make this cfg'able via SCM */
345 Real padding = join_thickness;
347 /* horizontal padding space after ligature */
348 prev_distance += padding;
350 #if 0 // experimental code to collapse spacing after ligature
351 /* TODO: set to max(old/new spacing-increment), since other
352 voices/staves also may want to set this property. */
353 Paper_column *paper_column = first_primitive->get_column();
354 paper_column->warning (_f ("Vaticana_ligature_engraver: "
355 "setting `spacing-increment = %f': ptr=%ul",
356 prev_distance, paper_column));
358 set_grob_property("forced-spacing", gh_double2scm (prev_distance));
363 ENTER_DESCRIPTION (Vaticana_ligature_engraver,
364 /* descr */ "Handles ligatures by glueing special ligature heads together.",
365 /* creats*/ "VaticanaLigature",
366 /* accepts */ "ligature-event abort-event",
367 /* acks */ "note-head-interface rest-interface",