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 bool is_stacked_head (int prefix_set,
30 Real align_heads (Array<Grob_info> primitives,
35 TRANSLATOR_DECLARATIONS(Vaticana_ligature_engraver);
38 virtual Spanner *create_ligature_spanner ();
39 virtual void transform_heads (Spanner *ligature,
40 Array<Grob_info> primitives);
43 Vaticana_ligature_engraver::Vaticana_ligature_engraver ()
48 Vaticana_ligature_engraver::create_ligature_spanner ()
50 return new Spanner (get_property ("VaticanaLigature"));
54 Vaticana_ligature_engraver::is_stacked_head (int prefix_set,
59 // upper head of pes is stacked upon lower head of pes ...
60 is_stacked_b = context_info & PES_UPPER;
62 // ... unless this note starts a flexa
63 if (context_info & FLEXA_LEFT)
67 if (context_info & PES_LOWER)
70 // ... or the previous note is a semivocalis or inclinatum
71 if (context_info & AFTER_DEMINUTUM)
74 // auctum head is never stacked upon preceding note
75 if (prefix_set & AUCTUM)
78 // virga is never stacked upon preceding note
79 if (prefix_set & VIRGA)
82 // oriscus is never stacked upon preceding note
83 if (prefix_set & ORISCUS)
86 if ((prefix_set & DEMINUTUM) &&
87 !(prefix_set & INCLINATUM) &&
88 (context_info & FLEXA_RIGHT))
89 is_stacked_b = true; // semivocalis head of deminutus form
95 Vaticana_ligature_engraver::align_heads (Array<Grob_info> primitives,
99 Item *first_primitive = dynamic_cast<Item*> (primitives[0].grob_);
100 Real ligature_width = 0.0;
103 * Amount of extra space two put between some particular
104 * configurations of adjacent heads.
106 * TODO: make this a property of primtive grobs.
108 Real extra_space = 2.0 * join_thickness;
110 for (int i = 0; i < primitives.size(); i++)
112 Item *primitive = dynamic_cast<Item*> (primitives[i].grob_);
115 * Get glyph_name, delta_pitch and context_info for this head.
118 SCM glyph_name_scm = primitive->get_grob_property ("glyph-name");
119 if (glyph_name_scm == SCM_EOL)
121 primitive->programming_error ("Vaticana_ligature:"
122 "undefined glyph-name -> "
126 String glyph_name = ly_scm2string (glyph_name_scm);
129 SCM delta_pitch_scm = primitive->get_grob_property ("delta-pitch");
130 if (delta_pitch_scm != SCM_EOL)
132 delta_pitch = gh_scm2int (delta_pitch_scm);
136 primitive->programming_error ("Vaticana_ligature:"
137 "delta-pitch undefined -> "
143 SCM context_info_scm = primitive->get_grob_property ("context-info");
144 if (context_info_scm != SCM_EOL)
146 context_info = gh_scm2int (context_info_scm);
150 primitive->programming_error ("Vaticana_ligature:"
151 "context-info undefined -> "
157 * Now determine width and x-offset of head.
163 if (context_info & STACKED_HEAD)
166 * This head is stacked upon the previous one; hence, it
167 * does not contribute to the total width of the ligature,
168 * and its width is assumed to be 0.0. Moreover, it is
169 * shifted to the left by its width such that the right side
170 * of this and the other head are horizontally aligned.
173 x_offset = join_thickness -
174 Font_interface::get_default_font (primitive)->
175 find_by_name ("noteheads-" + glyph_name).extent (X_AXIS).length ();
177 else if (!String::compare (glyph_name, "flexa") ||
178 !String::compare (glyph_name, ""))
181 * This head represents either half of a flexa shape.
182 * Hence, it is assigned half the width of this shape.
184 head_width = 0.5 * flexa_width;
187 else // retrieve width from corresponding font
190 Font_interface::get_default_font (primitive)->
191 find_by_name ("noteheads-" + glyph_name).extent (X_AXIS).length ();
196 * Save the head's final x-offset.
198 primitive->set_grob_property ("x-offset",
199 gh_double2scm (x_offset));
202 * If the head is the 2nd head of a pes or flexa (but not a
203 * flexa shape), mark this head to be joined with the left-side
204 * neighbour head (i.e. the previous head) by a vertical beam.
206 if ((context_info & PES_UPPER) ||
207 ((context_info & FLEXA_RIGHT) &&
208 !(context_info & PES_LOWER)))
210 primitive->set_grob_property ("join-left", gh_bool2scm(true));
213 * Create a small overlap of adjacent heads so that the join
214 * can be drawn perfectly between them.
216 ligature_width -= join_thickness;
218 else if (!String::compare (glyph_name, ""))
221 * This is the 2nd (virtual) head of flexa shape. Join it
222 * tightly with 1st head, i.e. do *not* add additional
223 * space, such that next head will not be off from the flexa
227 else if (context_info & AFTER_VIRGA)
230 * After a virga, make a an additional small space such that
231 * the appendix on the right side of the head does not touch
232 * the following head.
234 ligature_width += extra_space;
236 else if (delta_pitch == 0)
239 * If there are two adjacent noteheads with the same pitch,
240 * add additional small space between them, such that they
241 * do not touch each other.
243 ligature_width += extra_space;
247 * Horizontally line-up this head to form a ligature.
249 get_set_column (primitive, first_primitive->get_column ());
250 primitive->translate_axis (ligature_width, X_AXIS);
251 ligature_width += head_width;
256 * Add extra horizontal padding space after ligature, such that
257 * neighbouring ligatures do not touch each other.
259 ligature_width += extra_space;
261 return ligature_width;
265 Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
266 Array<Grob_info> primitives)
269 SCM flexa_width_scm = ligature->get_grob_property ("flexa-width");
270 if (flexa_width_scm != SCM_EOL)
272 flexa_width = gh_scm2double (flexa_width_scm);
276 ligature->programming_error ("Vaticana_ligature_engraver:"
277 "flexa-width undefined; "
278 "assuming 2.0 staff space");
280 2.0 * Staff_symbol_referencer::staff_space (ligature);
284 SCM join_thickness_scm = ligature->get_grob_property ("thickness");
285 if (join_thickness_scm != SCM_EOL)
287 join_thickness = gh_scm2double (join_thickness_scm);
291 ligature->programming_error ("Vaticana_ligature_engraver:"
292 "thickness undefined; "
293 "assuming 1.4 linethickness");
294 join_thickness = 1.4;
296 join_thickness *= ligature->get_paper ()->get_var ("linethickness");
298 Item *prev_primitive = 0;
299 int prev_prefix_set = 0;
300 int prev_context_info = 0;
302 String prev_glyph_name = "";
303 for (int i = 0; i < primitives.size(); i++) {
304 Item *primitive = dynamic_cast<Item*> (primitives[i].grob_);
305 Music *music_cause = primitives[i].music_cause ();
307 /* compute interval between previous and current primitive */
309 unsmob_pitch (music_cause->get_mus_property ("pitch"))->steps ();
317 delta_pitch = pitch - prev_pitch;
320 /* retrieve & complete prefix_set and context_info */
322 gh_scm2int (primitive->get_grob_property ("prefix-set"));
324 gh_scm2int (primitive->get_grob_property ("context-info"));
325 if (is_stacked_head (prefix_set, context_info))
327 context_info |= STACKED_HEAD;
328 primitive->set_grob_property ("context-info",
329 gh_int2scm (context_info));
333 * Now determine which head to typeset (this is context sensitive
334 * information, since it depends on neighbouring heads; therefore,
335 * this decision must be made here in the engraver rather than in
339 if (prefix_set & VIRGA)
340 glyph_name = "vaticana_virga";
341 else if (prefix_set & QUILISMA)
342 glyph_name = "vaticana_quilisma";
343 else if (prefix_set & ORISCUS)
344 glyph_name = "solesmes_oriscus";
345 else if (prefix_set & STROPHA)
346 if (prefix_set & AUCTUM)
347 glyph_name = "solesmes_stropha_aucta";
348 else glyph_name = "solesmes_stropha";
349 else if (prefix_set & INCLINATUM)
350 if (prefix_set & AUCTUM)
351 glyph_name = "solesmes_incl_auctum";
352 else if (prefix_set & DEMINUTUM)
353 glyph_name = "solesmes_incl_parvum";
355 glyph_name = "vaticana_inclinatum";
356 else if (prefix_set & DEMINUTUM)
360 glyph_name = "vaticana_reverse_plica";
362 else if (delta_pitch > 0)
365 if (!(prev_context_info & FLEXA_RIGHT))
367 prev_glyph_name = "vaticana_epiphonus";
369 glyph_name = "vaticana_plica";
371 else // (delta_pitch <= 0)
374 if (!(prev_context_info & FLEXA_RIGHT))
378 prev_glyph_name = "vaticana_inner_cephalicus";
382 prev_glyph_name = "vaticana_cephalicus";
385 glyph_name = "vaticana_reverse_plica";
387 else if (prefix_set & (CAVUM | LINEA))
388 if ((prefix_set & CAVUM) && (prefix_set & LINEA))
389 glyph_name = "vaticana_linea_punctum_cavum";
390 else if (prefix_set & CAVUM)
391 glyph_name = "vaticana_punctum_cavum";
393 glyph_name = "vaticana_linea_punctum";
394 else if (prefix_set & AUCTUM)
395 if (prefix_set & ASCENDENS)
396 glyph_name = "solesmes_auct_asc";
398 glyph_name = "solesmes_auct_desc";
399 else if ((prefix_set & PES_OR_FLEXA) &&
400 (context_info & PES_LOWER) &&
401 (context_info & FLEXA_RIGHT))
402 glyph_name = ""; // second head of flexa shape
403 else if ((context_info & STACKED_HEAD) &&
404 (context_info & PES_UPPER))
406 glyph_name = "vaticana_upes";
408 glyph_name = "vaticana_vupes";
409 else if ((context_info & FLEXA_LEFT) &&
410 !(prefix_set && PES_OR_FLEXA))
411 glyph_name = "vaticana_rvirga";
413 glyph_name = "vaticana_punctum";
416 * If the head for the current primitive represents the right head
417 * of a flexa or the upper head of a pes, then this may affect the
418 * shape of the previous head.
420 if (prefix_set & PES_OR_FLEXA)
422 if ((context_info & FLEXA_RIGHT) && (context_info & PES_LOWER))
424 // join the two flexa heads into a single curved flexa shape
425 prev_glyph_name = "flexa";
426 prev_primitive->set_grob_property ("flexa-height",
427 gh_int2scm (delta_pitch));
428 prev_primitive->set_grob_property ("flexa-width",
429 gh_double2scm (flexa_width));
430 bool add_stem = !(prev_prefix_set && PES_OR_FLEXA);
431 prev_primitive->set_grob_property ("add-stem",
432 gh_bool2scm (add_stem));
434 if ((context_info & PES_UPPER) && (context_info & STACKED_HEAD))
436 if (!String::compare (prev_glyph_name, "vaticana_punctum"))
437 prev_glyph_name = "vaticana_lpes";
442 prev_primitive->set_grob_property ("glyph-name",
443 scm_makfrom0str (prev_glyph_name.to_str0 ()));
445 primitive->set_grob_property ("delta-pitch",
446 gh_int2scm (delta_pitch));
449 * In the backend, flexa shapes and joins need to know about
450 * thickness. Hence, for simplicity, let's distribute the
451 * ligature grob's value for thickness to each ligature head (even
452 * if not all of them need to know).
454 primitive->set_grob_property ("thickness", gh_double2scm (join_thickness));
456 prev_primitive = primitive;
457 prev_prefix_set = prefix_set;
458 prev_context_info = context_info;
460 prev_glyph_name = glyph_name;
463 prev_primitive->set_grob_property ("glyph-name",
464 scm_makfrom0str (prev_glyph_name.to_str0 ()));
467 Real ligature_width =
470 align_heads (primitives, flexa_width, join_thickness);
472 #if 0 // experimental code to collapse spacing after ligature
473 /* TODO: set to max(old/new spacing-increment), since other
474 voices/staves also may want to set this property. */
475 Item *first_primitive = dynamic_cast<Item*> (primitives[0].grob_);
476 Paper_column *paper_column = first_primitive->get_column();
477 paper_column->warning (_f ("Vaticana_ligature_engraver: "
478 "setting `spacing-increment = %f': ptr=%ul",
479 ligature_width, paper_column));
481 set_grob_property("forced-spacing", gh_double2scm (ligature_width));
486 ENTER_DESCRIPTION (Vaticana_ligature_engraver,
487 /* descr */ "Handles ligatures by glueing special ligature heads together.",
488 /* creats*/ "VaticanaLigature",
489 /* accepts */ "ligature-event abort-event",
490 /* acks */ "note-head-interface rest-interface",