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
94 inline int get_context_info (Item *primitive)
96 SCM context_info_scm = primitive->get_grob_property ("context-info");
97 if (context_info_scm != SCM_EOL)
99 return gh_scm2int (context_info_scm);
103 primitive->programming_error ("Vaticana_ligature:"
104 "context-info undefined -> "
111 Vaticana_ligature_engraver::align_heads (Array<Grob_info> primitives,
115 if (!primitives.size ())
117 programming_error ("Vaticana_ligature: "
118 "empty ligature [ignored]");
122 Item *first_primitive = dynamic_cast<Item*> (primitives[0].grob_);
123 Real ligature_width = 0.0;
126 * Amount of extra space two put between some particular
127 * configurations of adjacent heads.
129 * TODO: make this a property of primtive grobs.
131 Real extra_space = 2.0 * join_thickness;
133 Item *prev_primitive, *primitive, *next_primitive;
134 int prev_context_info, context_info, next_context_info;
139 next_primitive = first_primitive;
140 if ((next_context_info = get_context_info (next_primitive)) < 0)
145 for (int i = 0; i < primitives.size (); i++)
147 prev_primitive = primitive;
148 prev_context_info = context_info;
149 context_info = next_context_info;
150 primitive = next_primitive;
152 if (i+1 < primitives.size ())
154 next_primitive = dynamic_cast<Item*> (primitives[i+1].grob_);
155 if ((next_context_info = get_context_info (next_primitive)) < 0)
163 next_context_info = 0;
167 * Get glyph_name, delta_pitch and context_info for this head.
170 SCM glyph_name_scm = primitive->get_grob_property ("glyph-name");
171 if (glyph_name_scm == SCM_EOL)
173 primitive->programming_error ("Vaticana_ligature:"
174 "undefined glyph-name -> "
178 String glyph_name = ly_scm2string (glyph_name_scm);
181 SCM delta_pitch_scm = primitive->get_grob_property ("delta-pitch");
182 if (delta_pitch_scm != SCM_EOL)
184 delta_pitch = gh_scm2int (delta_pitch_scm);
188 primitive->programming_error ("Vaticana_ligature:"
189 "delta-pitch undefined -> "
195 * Now determine width and x-offset of head.
201 if (context_info & STACKED_HEAD)
204 * This head is stacked upon the previous one; hence, it
205 * does not contribute to the total width of the ligature,
206 * and its width is assumed to be 0.0. Moreover, it is
207 * shifted to the left by its width such that the right side
208 * of this and the other head are horizontally aligned.
211 x_offset = join_thickness -
212 Font_interface::get_default_font (primitive)->
213 find_by_name ("noteheads-" + glyph_name).extent (X_AXIS).length ();
215 else if (!String::compare (glyph_name, "flexa") ||
216 !String::compare (glyph_name, ""))
219 * This head represents either half of a flexa shape.
220 * Hence, it is assigned half the width of this shape.
222 head_width = 0.5 * flexa_width;
225 else // retrieve width from corresponding font
228 Font_interface::get_default_font (primitive)->
229 find_by_name ("noteheads-" + glyph_name).extent (X_AXIS).length ();
234 * Save the head's final x-offset.
236 primitive->set_grob_property ("x-offset",
237 gh_double2scm (x_offset));
240 * If the head is the 2nd head of a pes or flexa (but not a
241 * flexa shape), mark this head to be joined with the left-side
242 * neighbour head (i.e. the previous head) by a vertical beam.
244 if ((context_info & PES_UPPER) ||
245 ((context_info & FLEXA_RIGHT) &&
246 !(context_info & PES_LOWER)))
248 primitive->set_grob_property ("join-left", gh_bool2scm(true));
251 * Create a small overlap of adjacent heads so that the join
252 * can be drawn perfectly between them.
254 ligature_width -= join_thickness;
256 else if (!String::compare (glyph_name, ""))
259 * This is the 2nd (virtual) head of flexa shape. Join it
260 * tightly with 1st head, i.e. do *not* add additional
261 * space, such that next head will not be off from the flexa
266 /* Sometimes, extra space is needed, e.g. to avoid clashing with
267 the appendix of an adjacent notehead or with an adjacent
268 notehead itself if it has the same pitch. */
270 if (context_info & AFTER_VIRGA)
273 * After a virga, make a an additional small space such that
274 * the appendix on the right side of the head does not touch
275 * the following head.
277 ligature_width += extra_space;
279 else if ((context_info & FLEXA_LEFT) &&
280 !(prev_context_info & PES_LOWER))
283 * Before a flexa (but not within a torculus), make a an
284 * additional small space such that the appendix on the left
285 * side of the flexa does not touch the this head.
287 ligature_width += extra_space;
289 else if (delta_pitch == 0)
292 * If there are two adjacent noteheads with the same pitch,
293 * add additional small space between them, such that they
294 * do not touch each other.
296 ligature_width += extra_space;
300 * Horizontally line-up this head to form a ligature.
302 get_set_column (primitive, first_primitive->get_column ());
303 primitive->translate_axis (ligature_width, X_AXIS);
304 ligature_width += head_width;
309 * Add extra horizontal padding space after ligature, such that
310 * neighbouring ligatures do not touch each other.
312 ligature_width += extra_space;
314 return ligature_width;
318 Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
319 Array<Grob_info> primitives)
322 SCM flexa_width_scm = ligature->get_grob_property ("flexa-width");
323 if (flexa_width_scm != SCM_EOL)
325 flexa_width = gh_scm2double (flexa_width_scm);
329 ligature->programming_error ("Vaticana_ligature_engraver:"
330 "flexa-width undefined; "
331 "assuming 2.0 staff space");
333 2.0 * Staff_symbol_referencer::staff_space (ligature);
337 SCM join_thickness_scm = ligature->get_grob_property ("thickness");
338 if (join_thickness_scm != SCM_EOL)
340 join_thickness = gh_scm2double (join_thickness_scm);
344 ligature->programming_error ("Vaticana_ligature_engraver:"
345 "thickness undefined; "
346 "assuming 1.4 linethickness");
347 join_thickness = 1.4;
349 join_thickness *= ligature->get_paper ()->get_realvar (ly_symbol2scm ("linethickness"));
351 Item *prev_primitive = 0;
352 int prev_prefix_set = 0;
353 int prev_context_info = 0;
355 String prev_glyph_name = "";
356 for (int i = 0; i < primitives.size(); i++) {
357 Item *primitive = dynamic_cast<Item*> (primitives[i].grob_);
358 Music *music_cause = primitives[i].music_cause ();
360 /* compute interval between previous and current primitive */
362 unsmob_pitch (music_cause->get_mus_property ("pitch"))->steps ();
370 delta_pitch = pitch - prev_pitch;
373 /* retrieve & complete prefix_set and context_info */
375 gh_scm2int (primitive->get_grob_property ("prefix-set"));
377 gh_scm2int (primitive->get_grob_property ("context-info"));
378 if (is_stacked_head (prefix_set, context_info))
380 context_info |= STACKED_HEAD;
381 primitive->set_grob_property ("context-info",
382 gh_int2scm (context_info));
386 * Now determine which head to typeset (this is context sensitive
387 * information, since it depends on neighbouring heads; therefore,
388 * this decision must be made here in the engraver rather than in
392 if (prefix_set & VIRGA)
393 glyph_name = "vaticana_virga";
394 else if (prefix_set & QUILISMA)
395 glyph_name = "vaticana_quilisma";
396 else if (prefix_set & ORISCUS)
397 glyph_name = "solesmes_oriscus";
398 else if (prefix_set & STROPHA)
399 if (prefix_set & AUCTUM)
400 glyph_name = "solesmes_stropha_aucta";
401 else glyph_name = "solesmes_stropha";
402 else if (prefix_set & INCLINATUM)
403 if (prefix_set & AUCTUM)
404 glyph_name = "solesmes_incl_auctum";
405 else if (prefix_set & DEMINUTUM)
406 glyph_name = "solesmes_incl_parvum";
408 glyph_name = "vaticana_inclinatum";
409 else if (prefix_set & DEMINUTUM)
413 glyph_name = "vaticana_reverse_plica";
415 else if (delta_pitch > 0)
418 if (!(prev_context_info & FLEXA_RIGHT))
420 prev_glyph_name = "vaticana_epiphonus";
422 glyph_name = "vaticana_plica";
424 else // (delta_pitch <= 0)
427 if (!(prev_context_info & FLEXA_RIGHT))
431 prev_glyph_name = "vaticana_inner_cephalicus";
435 prev_glyph_name = "vaticana_cephalicus";
438 glyph_name = "vaticana_reverse_plica";
440 else if (prefix_set & (CAVUM | LINEA))
441 if ((prefix_set & CAVUM) && (prefix_set & LINEA))
442 glyph_name = "vaticana_linea_punctum_cavum";
443 else if (prefix_set & CAVUM)
444 glyph_name = "vaticana_punctum_cavum";
446 glyph_name = "vaticana_linea_punctum";
447 else if (prefix_set & AUCTUM)
448 if (prefix_set & ASCENDENS)
449 glyph_name = "solesmes_auct_asc";
451 glyph_name = "solesmes_auct_desc";
452 else if ((prefix_set & PES_OR_FLEXA) &&
453 (context_info & PES_LOWER) &&
454 (context_info & FLEXA_RIGHT))
455 glyph_name = ""; // second head of flexa shape
456 else if ((context_info & STACKED_HEAD) &&
457 (context_info & PES_UPPER))
459 glyph_name = "vaticana_upes";
461 glyph_name = "vaticana_vupes";
462 else if ((context_info & FLEXA_LEFT) &&
463 !(prefix_set && PES_OR_FLEXA))
464 glyph_name = "vaticana_rvirga";
466 glyph_name = "vaticana_punctum";
469 * If the head for the current primitive represents the right head
470 * of a flexa or the upper head of a pes, then this may affect the
471 * shape of the previous head.
473 if (prefix_set & PES_OR_FLEXA)
475 if ((context_info & FLEXA_RIGHT) && (context_info & PES_LOWER))
477 // join the two flexa heads into a single curved flexa shape
478 prev_glyph_name = "flexa";
479 prev_primitive->set_grob_property ("flexa-height",
480 gh_int2scm (delta_pitch));
481 prev_primitive->set_grob_property ("flexa-width",
482 gh_double2scm (flexa_width));
483 bool add_stem = !(prev_prefix_set && PES_OR_FLEXA);
484 prev_primitive->set_grob_property ("add-stem",
485 gh_bool2scm (add_stem));
487 if ((context_info & PES_UPPER) && (context_info & STACKED_HEAD))
489 if (!String::compare (prev_glyph_name, "vaticana_punctum"))
490 prev_glyph_name = "vaticana_lpes";
495 prev_primitive->set_grob_property ("glyph-name",
496 scm_makfrom0str (prev_glyph_name.to_str0 ()));
498 primitive->set_grob_property ("delta-pitch",
499 gh_int2scm (delta_pitch));
502 * In the backend, flexa shapes and joins need to know about
503 * thickness. Hence, for simplicity, let's distribute the
504 * ligature grob's value for thickness to each ligature head (even
505 * if not all of them need to know).
507 primitive->set_grob_property ("thickness", gh_double2scm (join_thickness));
509 prev_primitive = primitive;
510 prev_prefix_set = prefix_set;
511 prev_context_info = context_info;
513 prev_glyph_name = glyph_name;
516 prev_primitive->set_grob_property ("glyph-name",
517 scm_makfrom0str (prev_glyph_name.to_str0 ()));
520 Real ligature_width =
523 align_heads (primitives, flexa_width, join_thickness);
525 #if 0 // experimental code to collapse spacing after ligature
526 /* TODO: set to max(old/new spacing-increment), since other
527 voices/staves also may want to set this property. */
528 Item *first_primitive = dynamic_cast<Item*> (primitives[0].grob_);
529 Paper_column *paper_column = first_primitive->get_column();
530 paper_column->warning (_f ("Vaticana_ligature_engraver: "
531 "setting `spacing-increment = %f': ptr=%ul",
532 ligature_width, paper_column));
534 set_grob_property("forced-spacing", gh_double2scm (ligature_width));
539 ENTER_DESCRIPTION (Vaticana_ligature_engraver,
540 /* descr */ "Handles ligatures by glueing special ligature heads together.",
541 /* creats*/ "VaticanaLigature",
542 /* accepts */ "ligature-event abort-event",
543 /* acks */ "note-head-interface rest-interface",