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,
39 TRANSLATOR_DECLARATIONS(Vaticana_ligature_engraver);
42 virtual Spanner *create_ligature_spanner ();
43 virtual void transform_heads (Spanner *ligature,
44 Array<Grob_info> primitives);
47 Vaticana_ligature_engraver::Vaticana_ligature_engraver ()
52 Vaticana_ligature_engraver::create_ligature_spanner ()
54 return new Spanner (get_property ("VaticanaLigature"));
58 Vaticana_ligature_engraver::finish_primitive (Item *first_primitive,
68 Real next_distance = distance;
70 { // determine width of previous head and x-offset
75 // upper head of pes is stacked upon lower head of pes ...
76 is_stacked = context_info & PES_UPPER;
78 // ... unless this note starts a flexa
79 if (context_info & FLEXA_LEFT)
83 if (context_info & PES_LOWER)
86 // ... or the previous note is a semivocalis or inclinatum
88 if (prev_prefix_set & DEMINUTUM)
92 // auctum head is never stacked upon preceding note
93 if (prefix_set & AUCTUM)
96 // virga is never stacked upon preceding note
97 if (prefix_set & VIRGA)
100 // oriscus is never stacked upon preceding note
101 if (prefix_set & ORISCUS)
104 if ((prefix_set & DEMINUTUM) &&
105 !(prefix_set & INCLINATUM) &&
106 (context_info & FLEXA_RIGHT))
107 is_stacked = true; // semivocalis head of deminutus form
112 * This head is stacked upon the previous one; hence, it
113 * does not contribute to the total width of the ligature,
114 * and its width is assumed to be 0.0. Moreover, it is
115 * shifted to the left by its width such that the right side
116 * of this and the other head are horizontally aligned.
119 x_offset = join_thickness -
120 Font_interface::get_default_font (primitive)->
121 find_by_name ("noteheads-" + glyph_name).extent (X_AXIS).length ();
123 else if (!String::compare (glyph_name, "flexa") ||
124 !String::compare (glyph_name, ""))
127 * This head represents either half of a flexa shape.
128 * Hence, it is assigned half the width of this shape.
130 head_width = 0.5 * flexa_width;
133 else // retrieve width from corresponding font
136 Font_interface::get_default_font (primitive)->
137 find_by_name ("noteheads-" + glyph_name).extent (X_AXIS).length ();
142 * Save the head's final shape and x-offset.
144 primitive->set_grob_property ("glyph-name",
145 scm_makfrom0str (glyph_name.to_str0 ()));
146 primitive->set_grob_property ("x-offset",
147 gh_double2scm (x_offset));
150 * If the head is the 2nd head of a pes or flexa (but not a
151 * flexa shape), mark this head to be joined with the left-side
152 * neighbour head (i.e. the previous head) by a vertical beam.
154 if ((context_info & PES_UPPER) ||
155 ((context_info & FLEXA_RIGHT) &&
156 !(context_info & PES_LOWER)))
158 primitive->set_grob_property ("join-left",
159 gh_int2scm (pitch_delta));
162 * Create a small overlap of adjacent heads so that the join
163 * can be drawn perfectly between them.
165 next_distance -= join_thickness;
167 else if (!String::compare (glyph_name, ""))
170 * 2nd (virtual) head of flexa shape: join tightly with 1st
171 * head, i.e. do *not* add additional space, such that next
172 * head will not be off from the flexa shape.
175 else if (context_info & AFTER_VIRGA)
178 * Make a small space after a virga.
180 next_distance += 2 * join_thickness;
182 else if (pitch_delta == 0)
185 * Make a small space between two adjacent notes with the
188 next_distance += 2 * join_thickness;
192 * Horizontally line-up this head to form a ligature.
194 get_set_column (primitive, first_primitive->get_column ());
195 primitive->translate_axis (next_distance, X_AXIS);
196 next_distance += head_width;
200 return next_distance;
204 Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
205 Array<Grob_info> primitives)
208 SCM flexa_width_scm = ligature->get_grob_property ("flexa-width");
209 if (flexa_width_scm != SCM_EOL)
211 flexa_width = gh_scm2double (flexa_width_scm);
215 programming_error ("Vaticana_ligature_engraver:"
216 "flexa-width undefined; assuming 2.0 staff space");
218 2.0 * Staff_symbol_referencer::staff_space (ligature);
222 SCM join_thickness_scm = ligature->get_grob_property ("thickness");
223 if (join_thickness_scm != SCM_EOL)
225 join_thickness = gh_scm2double (join_thickness_scm);
229 programming_error ("Vaticana_ligature_engraver:"
230 "thickness undefined; assuming 1.4 linethickness");
231 join_thickness = 1.4;
233 join_thickness *= ligature->get_paper ()->get_var ("linethickness");
235 Item *first_primitive = 0;
236 Item *prev_primitive = 0;
237 int prev_prefix_set = 0;
238 int prev_context_info = 0;
240 int prev_pitch_delta = 0;
241 String prev_glyph_name = "";
242 Real prev_distance = 0.0;
243 for (int i = 0; i < primitives.size(); i++) {
244 Item *primitive = dynamic_cast<Item*> (primitives[i].grob_);
245 Music *music_cause = primitives[i].music_cause ();
246 int context_info = gh_scm2int (primitive->get_grob_property ("context-info"));
247 int pitch = unsmob_pitch (music_cause->get_mus_property ("pitch"))->steps ();
249 if (!first_primitive)
250 first_primitive = primitive;
251 int prefix_set = gh_scm2int (primitive->get_grob_property ("prefix-set"));
254 * Now determine which head to typeset (this is context sensitive
255 * information, since it depends on neighbouring heads; therefore,
256 * this decision must be made here in the engraver rather than in
259 if (prefix_set & VIRGA)
260 glyph_name = "vaticana_virga";
261 else if (prefix_set & QUILISMA)
262 glyph_name = "vaticana_quilisma";
263 else if (prefix_set & ORISCUS)
264 glyph_name = "solesmes_oriscus";
265 else if (prefix_set & STROPHA)
266 if (prefix_set & AUCTUM)
267 glyph_name = "solesmes_stropha_aucta";
268 else glyph_name = "solesmes_stropha";
269 else if (prefix_set & INCLINATUM)
270 if (prefix_set & AUCTUM)
271 glyph_name = "solesmes_incl_auctum";
272 else if (prefix_set & DEMINUTUM)
273 glyph_name = "solesmes_incl_parvum";
275 glyph_name = "vaticana_inclinatum";
276 else if (prefix_set & DEMINUTUM)
277 if (primitive == first_primitive)
280 glyph_name = "vaticana_reverse_plica";
282 else if (pitch > prev_pitch)
285 if (!(prev_context_info & FLEXA_RIGHT))
287 prev_glyph_name = "vaticana_epiphonus";
289 glyph_name = "vaticana_plica";
291 else // (pitch <= prev_pitch)
294 if (!(prev_context_info & FLEXA_RIGHT))
296 if (prev_primitive == first_primitive)
298 prev_glyph_name = "vaticana_cephalicus";
302 prev_glyph_name = "vaticana_inner_cephalicus";
305 glyph_name = "vaticana_reverse_plica";
307 else if (prefix_set & (CAVUM | LINEA))
308 if ((prefix_set & CAVUM) && (prefix_set & LINEA))
309 glyph_name = "vaticana_linea_punctum_cavum";
310 else if (prefix_set & CAVUM)
311 glyph_name = "vaticana_punctum_cavum";
313 glyph_name = "vaticana_linea_punctum";
314 else if (prefix_set & AUCTUM)
315 if (prefix_set & ASCENDENS)
316 glyph_name = "solesmes_auct_asc";
318 glyph_name = "solesmes_auct_desc";
319 else if ((prefix_set & PES_OR_FLEXA) &&
320 (context_info & PES_LOWER) &&
321 (context_info & FLEXA_RIGHT))
322 glyph_name = ""; // second head of flexa shape
323 else if (context_info & PES_UPPER)
324 if (pitch - prev_pitch > 1)
325 glyph_name = "vaticana_upes";
327 glyph_name = "vaticana_vupes";
329 glyph_name = "vaticana_punctum";
332 * May need updating previous head, depending on the current head.
334 if (prefix_set & PES_OR_FLEXA)
335 if ((context_info & PES_LOWER) &&
336 (context_info & FLEXA_RIGHT)) // flexa shape
338 prev_glyph_name = "flexa";
339 prev_primitive->set_grob_property ("flexa-height",
340 gh_int2scm (pitch - prev_pitch));
341 prev_primitive->set_grob_property ("flexa-width",
342 gh_double2scm (flexa_width));
344 !(prev_context_info & PES_UPPER) &&
345 !(prev_context_info & FLEXA_RIGHT);
346 prev_primitive->set_grob_property ("add-stem",
347 gh_bool2scm (add_stem));
349 else if (context_info & PES_UPPER)
351 if (!String::compare (prev_glyph_name, "vaticana_punctum"))
352 prev_glyph_name = "vaticana_lpes";
356 if (!String::compare (prev_glyph_name, "vaticana_punctum"))
357 prev_glyph_name = "vaticana_rvirga";
361 * In the backend, flexa shapes and joins need to know about
362 * thickness. Hence, for simplicity, let's distribute the
363 * ligature grob's value for thickness to each ligature head (even
364 * if not all of them need to know).
366 primitive->set_grob_property ("thickness", gh_double2scm (join_thickness));
369 * The head of the current iteration still may change during the
370 * next iteration due to the context sensitiveness of the
371 * transformation. However, the head of the previous iteration is
372 * now fully attributed and can be prepared for the backend.
376 * Finish head of previous iteration for backend.
379 finish_primitive (first_primitive, prev_primitive, prev_prefix_set,
380 prev_context_info, prev_glyph_name, prev_pitch_delta,
381 flexa_width, join_thickness, prev_distance);
383 prev_primitive = primitive;
384 prev_prefix_set = prefix_set;
385 prev_context_info = context_info;
386 prev_pitch_delta = pitch - prev_pitch;
388 prev_glyph_name = glyph_name;
392 * Finish head of last iteration for backend.
395 finish_primitive (first_primitive, prev_primitive, prev_prefix_set,
396 prev_context_info, prev_glyph_name, prev_pitch_delta,
397 flexa_width, join_thickness, prev_distance);
399 /* TODO: make this cfg'able via SCM */
400 Real padding = join_thickness;
402 /* horizontal padding space after ligature */
403 prev_distance += padding;
405 #if 0 // experimental code to collapse spacing after ligature
406 /* TODO: set to max(old/new spacing-increment), since other
407 voices/staves also may want to set this property. */
408 Paper_column *paper_column = first_primitive->get_column();
409 paper_column->warning (_f ("Vaticana_ligature_engraver: "
410 "setting `spacing-increment = %f': ptr=%ul",
411 prev_distance, paper_column));
413 set_grob_property("forced-spacing", gh_double2scm (prev_distance));
418 ENTER_DESCRIPTION (Vaticana_ligature_engraver,
419 /* descr */ "Handles ligatures by glueing special ligature heads together.",
420 /* creats*/ "VaticanaLigature",
421 /* accepts */ "ligature-event abort-event",
422 /* acks */ "note-head-interface rest-interface",