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
87 if (context_info & AFTER_DEMINUTUM)
90 // auctum head is never stacked upon preceding note
91 if (prefix_set & AUCTUM)
94 // virga is never stacked upon preceding note
95 if (prefix_set & VIRGA)
98 // oriscus is never stacked upon preceding note
99 if (prefix_set & ORISCUS)
102 if ((prefix_set & DEMINUTUM) &&
103 !(prefix_set & INCLINATUM) &&
104 (context_info & FLEXA_RIGHT))
105 is_stacked = true; // semivocalis head of deminutus form
110 * This head is stacked upon the previous one; hence, it
111 * does not contribute to the total width of the ligature,
112 * and its width is assumed to be 0.0. Moreover, it is
113 * shifted to the left by its width such that the right side
114 * of this and the other head are horizontally aligned.
117 x_offset = join_thickness -
118 Font_interface::get_default_font (primitive)->
119 find_by_name ("noteheads-" + glyph_name).extent (X_AXIS).length ();
121 else if (!String::compare (glyph_name, "flexa") ||
122 !String::compare (glyph_name, ""))
125 * This head represents either half of a flexa shape.
126 * Hence, it is assigned half the width of this shape.
128 head_width = 0.5 * flexa_width;
131 else // retrieve width from corresponding font
134 Font_interface::get_default_font (primitive)->
135 find_by_name ("noteheads-" + glyph_name).extent (X_AXIS).length ();
140 * Save the head's final shape and x-offset.
142 primitive->set_grob_property ("glyph-name",
143 scm_makfrom0str (glyph_name.to_str0 ()));
144 primitive->set_grob_property ("x-offset",
145 gh_double2scm (x_offset));
148 * If the head is the 2nd head of a pes or flexa (but not a
149 * flexa shape), mark this head to be joined with the left-side
150 * neighbour head (i.e. the previous head) by a vertical beam.
152 if ((context_info & PES_UPPER) ||
153 ((context_info & FLEXA_RIGHT) &&
154 !(context_info & PES_LOWER)))
156 primitive->set_grob_property ("join-left",
157 gh_int2scm (pitch_delta));
160 * Create a small overlap of adjacent heads so that the join
161 * can be drawn perfectly between them.
163 next_distance -= join_thickness;
165 else if (!String::compare (glyph_name, ""))
168 * 2nd (virtual) head of flexa shape: join tightly with 1st
169 * head, i.e. do *not* add additional space, such that next
170 * head will not be off from the flexa shape.
173 else if (context_info & AFTER_VIRGA)
176 * Make a small space after a virga.
178 next_distance += 2 * join_thickness;
180 else if (pitch_delta == 0)
183 * Make a small space between two adjacent notes with the
186 next_distance += 2 * join_thickness;
190 * Horizontally line-up this head to form a ligature.
192 get_set_column (primitive, first_primitive->get_column ());
193 primitive->translate_axis (next_distance, X_AXIS);
194 next_distance += head_width;
198 return next_distance;
202 Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
203 Array<Grob_info> primitives)
206 SCM flexa_width_scm = ligature->get_grob_property ("flexa-width");
207 if (flexa_width_scm != SCM_EOL)
209 flexa_width = gh_scm2double (flexa_width_scm);
213 programming_error ("Vaticana_ligature_engraver:"
214 "flexa-width undefined; assuming 2.0 staff space");
216 2.0 * Staff_symbol_referencer::staff_space (ligature);
220 SCM join_thickness_scm = ligature->get_grob_property ("thickness");
221 if (join_thickness_scm != SCM_EOL)
223 join_thickness = gh_scm2double (join_thickness_scm);
227 programming_error ("Vaticana_ligature_engraver:"
228 "thickness undefined; assuming 1.4 linethickness");
229 join_thickness = 1.4;
231 join_thickness *= ligature->get_paper ()->get_var ("linethickness");
233 Item *first_primitive = 0;
234 Item *prev_primitive = 0;
235 int prev_prefix_set = 0;
236 int prev_context_info = 0;
238 int prev_pitch_delta = 0;
239 String prev_glyph_name = "";
240 Real prev_distance = 0.0;
241 for (int i = 0; i < primitives.size(); i++) {
242 Item *primitive = dynamic_cast<Item*> (primitives[i].grob_);
243 Music *music_cause = primitives[i].music_cause ();
244 int context_info = gh_scm2int (primitive->get_grob_property ("context-info"));
245 int pitch = unsmob_pitch (music_cause->get_mus_property ("pitch"))->steps ();
247 if (!first_primitive)
248 first_primitive = primitive;
249 int prefix_set = gh_scm2int (primitive->get_grob_property ("prefix-set"));
252 * Now determine which head to typeset (this is context sensitive
253 * information, since it depends on neighbouring heads; therefore,
254 * this decision must be made here in the engraver rather than in
257 if (prefix_set & VIRGA)
258 glyph_name = "vaticana_virga";
259 else if (prefix_set & QUILISMA)
260 glyph_name = "vaticana_quilisma";
261 else if (prefix_set & ORISCUS)
262 glyph_name = "solesmes_oriscus";
263 else if (prefix_set & STROPHA)
264 if (prefix_set & AUCTUM)
265 glyph_name = "solesmes_stropha_aucta";
266 else glyph_name = "solesmes_stropha";
267 else if (prefix_set & INCLINATUM)
268 if (prefix_set & AUCTUM)
269 glyph_name = "solesmes_incl_auctum";
270 else if (prefix_set & DEMINUTUM)
271 glyph_name = "solesmes_incl_parvum";
273 glyph_name = "vaticana_inclinatum";
274 else if (prefix_set & DEMINUTUM)
275 if (primitive == first_primitive)
278 glyph_name = "vaticana_reverse_plica";
280 else if (pitch > prev_pitch)
283 if (!(prev_context_info & FLEXA_RIGHT))
285 prev_glyph_name = "vaticana_epiphonus";
287 glyph_name = "vaticana_plica";
289 else // (pitch <= prev_pitch)
292 if (!(prev_context_info & FLEXA_RIGHT))
294 if (prev_primitive == first_primitive)
296 prev_glyph_name = "vaticana_cephalicus";
300 prev_glyph_name = "vaticana_inner_cephalicus";
303 glyph_name = "vaticana_reverse_plica";
305 else if (prefix_set & (CAVUM | LINEA))
306 if ((prefix_set & CAVUM) && (prefix_set & LINEA))
307 glyph_name = "vaticana_linea_punctum_cavum";
308 else if (prefix_set & CAVUM)
309 glyph_name = "vaticana_punctum_cavum";
311 glyph_name = "vaticana_linea_punctum";
312 else if (prefix_set & AUCTUM)
313 if (prefix_set & ASCENDENS)
314 glyph_name = "solesmes_auct_asc";
316 glyph_name = "solesmes_auct_desc";
317 else if ((prefix_set & PES_OR_FLEXA) &&
318 (context_info & PES_LOWER) &&
319 (context_info & FLEXA_RIGHT))
320 glyph_name = ""; // second head of flexa shape
321 else if (context_info & PES_UPPER)
322 if (pitch - prev_pitch > 1)
323 glyph_name = "vaticana_upes";
325 glyph_name = "vaticana_vupes";
327 glyph_name = "vaticana_punctum";
330 * May need updating previous head, depending on the current head.
332 if (prefix_set & PES_OR_FLEXA)
333 if ((context_info & PES_LOWER) &&
334 (context_info & FLEXA_RIGHT)) // flexa shape
336 prev_glyph_name = "flexa";
337 prev_primitive->set_grob_property ("flexa-height",
338 gh_int2scm (pitch - prev_pitch));
339 prev_primitive->set_grob_property ("flexa-width",
340 gh_double2scm (flexa_width));
342 !(prev_context_info & PES_UPPER) &&
343 !(prev_context_info & FLEXA_RIGHT);
344 prev_primitive->set_grob_property ("add-stem",
345 gh_bool2scm (add_stem));
347 else if (context_info & PES_UPPER)
349 if (!String::compare (prev_glyph_name, "vaticana_punctum"))
350 prev_glyph_name = "vaticana_lpes";
354 if (!String::compare (prev_glyph_name, "vaticana_punctum"))
355 prev_glyph_name = "vaticana_rvirga";
359 * In the backend, flexa shapes and joins need to know about
360 * thickness. Hence, for simplicity, let's distribute the
361 * ligature grob's value for thickness to each ligature head (even
362 * if not all of them need to know).
364 primitive->set_grob_property ("thickness", gh_double2scm (join_thickness));
367 * The head of the current iteration still may change during the
368 * next iteration due to the context sensitiveness of the
369 * transformation. However, the head of the previous iteration is
370 * now fully attributed and can be prepared for the backend.
374 * Finish head of previous iteration for backend.
377 finish_primitive (first_primitive, prev_primitive, prev_prefix_set,
378 prev_context_info, prev_glyph_name, prev_pitch_delta,
379 flexa_width, join_thickness, prev_distance);
381 prev_primitive = primitive;
382 prev_prefix_set = prefix_set;
383 prev_context_info = context_info;
384 prev_pitch_delta = pitch - prev_pitch;
386 prev_glyph_name = glyph_name;
390 * Finish head of last iteration for backend.
393 finish_primitive (first_primitive, prev_primitive, prev_prefix_set,
394 prev_context_info, prev_glyph_name, prev_pitch_delta,
395 flexa_width, join_thickness, prev_distance);
397 /* TODO: make this cfg'able via SCM */
398 Real padding = join_thickness;
400 /* horizontal padding space after ligature */
401 prev_distance += padding;
403 #if 0 // experimental code to collapse spacing after ligature
404 /* TODO: set to max(old/new spacing-increment), since other
405 voices/staves also may want to set this property. */
406 Paper_column *paper_column = first_primitive->get_column();
407 paper_column->warning (_f ("Vaticana_ligature_engraver: "
408 "setting `spacing-increment = %f': ptr=%ul",
409 prev_distance, paper_column));
411 set_grob_property("forced-spacing", gh_double2scm (prev_distance));
416 ENTER_DESCRIPTION (Vaticana_ligature_engraver,
417 /* descr */ "Handles ligatures by glueing special ligature heads together.",
418 /* creats*/ "VaticanaLigature",
419 /* accepts */ "ligature-event abort-event",
420 /* acks */ "note-head-interface rest-interface",