2 vaticana-ligature-engraver.cc -- implement Vaticana_ligature_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 2003--2006 Juergen Reuter <reuter@ipd.uka.de>
9 #include "gregorian-ligature-engraver.hh"
11 #include "font-interface.hh"
12 #include "gregorian-ligature.hh"
13 #include "international.hh"
14 #include "output-def.hh"
15 #include "paper-column.hh"
17 #include "staff-symbol-referencer.hh"
18 #include "stream-event.hh"
19 #include "vaticana-ligature.hh"
21 #include "dot-column.hh"
22 #include "rhythmic-head.hh"
24 #include "translator.icc"
27 * This class implements the notation specific aspects of Vaticana
28 * style ligatures for Gregorian chant notation.
32 * TODO: Maybe move handling of dots/mora to
33 * Gregorian_ligature_engraver? It's probably common for all types of
34 * Gregorian chant notation that have dotted notes.
36 * FIXME: The horizontal alignment of the mora column is bad (too far
37 * to the left), if the last dotted note is not the last primitive in
38 * the ligature. Fortunately, in practice this bug should have no
39 * negative impact, since dotted notes appear within a ligature
40 * usually always at the end of the ligature, such that the bug never
41 * should apply for valid ligatures.
43 class Vaticana_ligature_engraver : public Gregorian_ligature_engraver
48 need_extra_horizontal_space (int prev_prefix_set, int prefix_set,
49 int context_info, int delta_pitch);
50 bool is_stacked_head (int prefix_set,
52 Real align_heads (vector<Grob_info> primitives,
55 void check_for_prefix_loss (Item *primitive);
56 void check_for_ambiguous_dot_pitch (Grob_info primitive);
57 void add_mora_column (Paper_column *column);
58 vector<Grob_info> augmented_primitives_;
61 TRANSLATOR_DECLARATIONS (Vaticana_ligature_engraver);
64 virtual Spanner *create_ligature_spanner ();
65 virtual void transform_heads (Spanner *ligature,
66 vector<Grob_info> primitives);
67 DECLARE_TRANSLATOR_LISTENER (pes_or_flexa);
68 DECLARE_TRANSLATOR_LISTENER (ligature);
71 IMPLEMENT_TRANSLATOR_LISTENER (Vaticana_ligature_engraver, pes_or_flexa);
73 Vaticana_ligature_engraver::listen_pes_or_flexa (Stream_event *ev)
75 Gregorian_ligature_engraver::listen_pes_or_flexa (ev);
78 IMPLEMENT_TRANSLATOR_LISTENER (Vaticana_ligature_engraver, ligature);
80 Vaticana_ligature_engraver::listen_ligature (Stream_event *ev)
82 Ligature_engraver::listen_ligature (ev);
85 Vaticana_ligature_engraver::Vaticana_ligature_engraver ()
87 brew_ligature_primitive_proc =
88 Vaticana_ligature::brew_ligature_primitive_proc;
89 augmented_primitives_.clear ();
93 Vaticana_ligature_engraver::create_ligature_spanner ()
95 return make_spanner ("VaticanaLigature", SCM_EOL);
99 Vaticana_ligature_engraver::is_stacked_head (int prefix_set,
104 // upper head of pes is stacked upon lower head of pes ...
105 is_stacked_b = context_info & PES_UPPER;
107 // ... unless this note starts a flexa
108 if (context_info & FLEXA_LEFT)
109 is_stacked_b = false;
111 // ... or another pes
112 if (context_info & PES_LOWER)
113 is_stacked_b = false;
115 // ... or the previous note is a semivocalis or inclinatum
116 if (context_info & AFTER_DEMINUTUM)
117 is_stacked_b = false;
119 // auctum head is never stacked upon preceding note
120 if (prefix_set & AUCTUM)
121 is_stacked_b = false;
123 // virga is never stacked upon preceding note
124 if (prefix_set & VIRGA)
125 is_stacked_b = false;
127 // oriscus is never stacked upon preceding note
128 if (prefix_set & ORISCUS)
129 is_stacked_b = false;
131 if ((prefix_set & DEMINUTUM)
132 && ! (prefix_set & INCLINATUM)
133 && (context_info & FLEXA_RIGHT))
134 is_stacked_b = true; // semivocalis head of deminutus form
140 * When aligning the heads, sometimes extra space is needed, e.g. to
141 * avoid clashing with the appendix of an adjacent notehead or with an
142 * adjacent notehead itself if it has the same pitch. Extra space is
143 * added at most once between to heads.
146 Vaticana_ligature_engraver::need_extra_horizontal_space (int prev_prefix_set, int prefix_set,
147 int context_info, int delta_pitch)
149 if (prev_prefix_set & VIRGA)
151 * After a virga, make a an additional small space such that the
152 * appendix on the right side of the head does not touch the
157 if ((prefix_set & INCLINATUM)
158 && ! (prev_prefix_set & INCLINATUM))
160 * Always start a series of inclinatum heads with an extra space.
164 if ((context_info & FLEXA_LEFT) && ! (context_info & PES_UPPER))
166 * Before a flexa (but not within a torculus), make a an
167 * additional small space such that the appendix on the left side
168 * of the flexa does not touch the this head.
172 if (delta_pitch == 0)
174 * If there are two adjacent noteheads with the same pitch, add
175 * additional small space between them, such that they do not
184 Vaticana_ligature_engraver::align_heads (vector<Grob_info> primitives,
188 if (!primitives.size ())
190 programming_error ("Vaticana_ligature: "
191 "empty ligature [ignored]");
196 * The paper column where we put the whole ligature into.
199 = dynamic_cast<Item *> (primitives[0].grob ())->get_column ();
202 = thickness * column->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
205 * Amount of extra space two put between some particular
206 * configurations of adjacent heads.
208 * TODO: make this a property of primtive grobs.
210 Real extra_space = 4.0 * join_thickness;
213 * Keep track of the total width of the ligature.
215 Real ligature_width = 0.0;
217 Item *prev_primitive = 0;
218 int prev_prefix_set = 0;
219 for (vsize i = 0; i < primitives.size (); i++)
221 Item *primitive = dynamic_cast<Item *> (primitives[i].grob ());
223 = scm_to_int (primitive->get_property ("prefix-set"));
225 = scm_to_int (primitive->get_property ("context-info"));
228 * Get glyph_name, delta_pitch and context_info for this head.
231 SCM glyph_name_scm = primitive->get_property ("glyph-name");
232 if (glyph_name_scm == SCM_EOL)
234 primitive->programming_error ("Vaticana_ligature:"
235 "undefined glyph-name -> "
239 string glyph_name = ly_scm2string (glyph_name_scm);
242 if (prev_primitive) /* urgh, need prev_primitive only here */
244 SCM delta_pitch_scm = prev_primitive->get_property ("delta-position");
245 if (delta_pitch_scm != SCM_EOL)
246 delta_pitch = scm_to_int (delta_pitch_scm);
249 primitive->programming_error ("Vaticana_ligature:"
250 "delta-position undefined -> "
257 * Now determine width and x-offset of head.
263 if (context_info & STACKED_HEAD)
266 * This head is stacked upon the previous one; hence, it
267 * does not contribute to the total width of the ligature,
268 * and its width is assumed to be 0.0. Moreover, it is
269 * shifted to the left by its width such that the right side
270 * of this and the other head are horizontally aligned.
273 x_offset = join_thickness
274 - Font_interface::get_default_font (primitive)->
275 find_by_name ("noteheads.s" + glyph_name).extent (X_AXIS).length ();
277 else if (glyph_name == "flexa" || glyph_name == "")
280 * This head represents either half of a flexa shape.
281 * Hence, it is assigned half the width of this shape.
283 head_width = 0.5 * flexa_width;
289 * This is a regular head, placed right to the previous one.
290 * Retrieve its width from corresponding font.
293 = Font_interface::get_default_font (primitive)->
294 find_by_name ("noteheads.s" + glyph_name).extent (X_AXIS).length ();
299 * Save the head's final x-offset.
301 primitive->set_property ("x-offset",
302 scm_from_double (x_offset));
305 * If the head is the 2nd head of a pes or flexa (but not a
306 * flexa shape), mark this head to be joined with the left-side
307 * neighbour head (i.e. the previous head) by a vertical beam.
309 if ((context_info & PES_UPPER)
310 || ((context_info & FLEXA_RIGHT)
311 && ! (context_info & PES_LOWER)))
315 primitive->programming_error ("vaticana ligature: add-join: "
316 "missing previous primitive");
320 prev_primitive->set_property ("add-join",
324 * Create a small overlap of adjacent heads so that the join
325 * can be drawn perfectly between them.
327 ligature_width -= join_thickness;
330 else if (glyph_name == "")
333 * This is the 2nd (virtual) head of flexa shape. Join it
334 * tightly with 1st head, i.e. do *not* add additional
335 * space, such that next head will not be off from the flexa
340 if (need_extra_horizontal_space (prev_prefix_set, prefix_set,
341 context_info, delta_pitch))
342 ligature_width += extra_space;
345 * Horizontally line-up this head to form a ligature.
347 get_set_column (primitive, column);
348 primitive->translate_axis (ligature_width, X_AXIS);
349 ligature_width += head_width;
351 prev_primitive = primitive;
352 prev_prefix_set = prefix_set;
356 * Add extra horizontal padding space after ligature, such that
357 * neighbouring ligatures do not touch each other.
359 ligature_width += extra_space;
361 return ligature_width;
365 * Depending on the typographical features of a particular ligature
366 * style, some prefixes may be ignored. In particular, if a curved
367 * flexa shape is produced, any prefixes to either of the two
368 * contributing heads that would select a head other than punctum, is
369 * by definition ignored.
371 * This function prints a warning, if the given primitive is prefixed
372 * such that a head other than punctum would be chosen, if this
373 * primitive were engraved as a stand-alone head.
376 Vaticana_ligature_engraver::check_for_prefix_loss (Item *primitive)
379 = scm_to_int (primitive->get_property ("prefix-set"));
380 if (prefix_set & ~PES_OR_FLEXA)
382 string prefs = Gregorian_ligature::prefixes_to_str (primitive);
383 primitive->warning (_f ("ignored prefix (es) `%s' of this head according "
384 "to restrictions of the selected ligature style",
390 Vaticana_ligature_engraver::add_mora_column (Paper_column *column)
392 if (augmented_primitives_.size () == 0) // no dot for column
394 if (!column) // empty ligature???
396 augmented_primitives_[0].grob ()->
397 programming_error ("no paper column to add dot");
400 Item *dotcol = make_item ("DotColumn", SCM_EOL);
401 dotcol->set_parent (column, X_AXIS);
402 for (vsize i = 0; i < augmented_primitives_.size (); i++)
405 dynamic_cast<Item *> (augmented_primitives_[i].grob ());
406 Item *dot = make_item ("Dots", primitive->self_scm ());
407 dot->set_property ("dot-count", scm_from_int (1));
408 dot->set_parent (primitive, Y_AXIS);
409 primitive->set_object ("dot", dot->self_scm ());
410 Dot_column::add_head (dotcol, primitive);
415 * This function prints a warning, if the given primitive has the same
416 * pitch as at least one of the primitives already stored in the
417 * augmented_primitives_ array.
419 * The rationale of this check is, that, if there are two dotted
420 * primitives with the same pitch, then collecting all dots in a dot
421 * column behind the ligature leads to a notational ambiguity of to
422 * which head the corresponding dot refers.
424 * Such a case should be treated as a badly specified ligature. The
425 * user should split the ligature to make the notation of dots
429 Vaticana_ligature_engraver::check_for_ambiguous_dot_pitch (Grob_info primitive)
431 // TODO: Fix performance, which is currently O(n^2) (since this
432 // method is called O(n) times and takes O(n) steps in the for
433 // loop), but could be O(n) (by replacing the for loop by e.g. a
434 // bitmask based O(1) test); where n=<number of primitives in the
435 // ligature> (which is typically small (n<10), though).
436 Stream_event *new_cause = primitive.event_cause ();
437 int new_pitch = unsmob_pitch (new_cause->get_property ("pitch"))->steps ();
438 for (vsize i = 0; i < augmented_primitives_.size (); i++)
440 Stream_event *cause = augmented_primitives_[i].event_cause ();
441 int pitch = unsmob_pitch (cause->get_property ("pitch"))->steps ();
442 if (pitch == new_pitch)
445 warning ("Ambiguous use of dots in ligature: there are "
446 "multiple dotted notes with the same pitch. "
447 "The ligature should be split.");
448 return; // supress multiple identical warnings
454 Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
455 vector<Grob_info> primitives)
457 Real flexa_width = robust_scm2double (ligature->get_property ("flexa-width"), 2);
459 Real thickness = robust_scm2double (ligature->get_property ("thickness"), 1);
461 Item *prev_primitive = 0;
462 int prev_prefix_set = 0;
463 int prev_context_info = 0;
464 int prev_delta_pitch = 0;
465 string prev_glyph_name = "";
466 augmented_primitives_.clear ();
467 for (vsize i = 0; i < primitives.size (); i++)
469 Item *primitive = dynamic_cast<Item *> (primitives[i].grob ());
472 SCM delta_pitch_scm = primitive->get_property ("delta-position");
473 if (delta_pitch_scm != SCM_EOL)
474 delta_pitch = scm_to_int (delta_pitch_scm);
477 primitive->programming_error ("Vaticana_ligature:"
478 "delta-position undefined -> "
483 /* retrieve & complete prefix_set and context_info */
485 = scm_to_int (primitive->get_property ("prefix-set"));
487 = scm_to_int (primitive->get_property ("context-info"));
489 if (Rhythmic_head::dot_count (primitive) > 0)
490 // remove dots from primitive and add remember primitive for
491 // creating a dot column
493 Rhythmic_head::get_dots (primitive)->set_property ("dot-count",
495 // TODO: Maybe completely remove grob "Dots" (dots->suicide
496 // () ?) rather than setting property "dot-count" to 0.
498 check_for_ambiguous_dot_pitch (primitives[i]);
499 augmented_primitives_.push_back (primitives[i]);
501 else if (augmented_primitives_.size () > 0)
503 primitive->warning ("This ligature has a dotted head followed by "
504 "a non-dotted head. The ligature should be "
505 "split after the last dotted head before "
509 if (is_stacked_head (prefix_set, context_info))
511 context_info |= STACKED_HEAD;
512 primitive->set_property ("context-info",
513 scm_from_int (context_info));
517 * Now determine which head to typeset (this is context sensitive
518 * information, since it depends on neighbouring heads; therefore,
519 * this decision must be made here in the engraver rather than in
523 if (prefix_set & VIRGA)
525 glyph_name = "vaticana.punctum";
526 primitive->set_property ("add-stem", ly_bool2scm (true));
528 else if (prefix_set & QUILISMA)
529 glyph_name = "vaticana.quilisma";
530 else if (prefix_set & ORISCUS)
531 glyph_name = "solesmes.oriscus";
532 else if (prefix_set & STROPHA)
533 if (prefix_set & AUCTUM)
534 glyph_name = "solesmes.stropha.aucta";
535 else glyph_name = "solesmes.stropha";
536 else if (prefix_set & INCLINATUM)
537 if (prefix_set & AUCTUM)
538 glyph_name = "solesmes.incl.auctum";
539 else if (prefix_set & DEMINUTUM)
540 glyph_name = "solesmes.incl.parvum";
542 glyph_name = "vaticana.inclinatum";
543 else if (prefix_set & DEMINUTUM)
547 glyph_name = "vaticana.reverse.plica";
549 else if (prev_delta_pitch > 0)
552 if (! (prev_context_info & FLEXA_RIGHT))
553 /* correct head of previous primitive */
554 if (prev_delta_pitch > 1)
555 prev_glyph_name = "vaticana.epiphonus";
557 prev_glyph_name = "vaticana.vepiphonus";
558 if (prev_delta_pitch > 1)
559 glyph_name = "vaticana.plica";
561 glyph_name = "vaticana.vplica";
563 else if (prev_delta_pitch < 0)
566 if (! (prev_context_info & FLEXA_RIGHT))
567 /* correct head of previous primitive */
571 /* cephalicus head with fixed size cauda */
572 prev_glyph_name = "vaticana.inner.cephalicus";
576 /* cephalicus head without cauda */
577 prev_glyph_name = "vaticana.cephalicus";
581 * Flexa has no variable size cauda if its left head is
582 * stacked on the right head. This is true for
583 * cephalicus. Hence, remove the cauda.
585 * Urgh: for the current implementation, this rule only
586 * applies for cephalicus; but it is a fundamental rule.
587 * Therefore, the following line of code should be
588 * placed somewhere else.
590 prev_primitive->set_property ("add-cauda",
591 ly_bool2scm (false));
593 if (prev_delta_pitch < - 1)
594 glyph_name = "vaticana.reverse.plica";
596 glyph_name = "vaticana.reverse.vplica";
598 else // (prev_delta_pitch == 0)
600 primitive->programming_error ("Vaticana_ligature:"
601 "deminutum head must have different "
602 "pitch -> ignoring grob");
604 else if (prefix_set & (CAVUM | LINEA))
605 if ((prefix_set & CAVUM) && (prefix_set & LINEA))
606 glyph_name = "vaticana.linea.punctum.cavum";
607 else if (prefix_set & CAVUM)
608 glyph_name = "vaticana.punctum.cavum";
610 glyph_name = "vaticana.linea.punctum";
611 else if (prefix_set & AUCTUM)
612 if (prefix_set & ASCENDENS)
613 glyph_name = "solesmes.auct.asc";
615 glyph_name = "solesmes.auct.desc";
616 else if ((context_info & STACKED_HEAD)
617 && (context_info & PES_UPPER))
618 if (prev_delta_pitch > 1)
619 glyph_name = "vaticana.upes";
621 glyph_name = "vaticana.vupes";
623 glyph_name = "vaticana.punctum";
626 * This head needs a cauda, if it starts a flexa, is not the upper
627 * head of a pes, and if it is a punctum.
629 if ((context_info & FLEXA_LEFT) && ! (context_info & PES_UPPER))
630 if (glyph_name == "vaticana.punctum")
631 primitive->set_property ("add-cauda", ly_bool2scm (true));
634 * Execptional rule for porrectus:
636 * If the current head is preceded by a \flexa and succeded by a
637 * \pes (e.g. "a \flexa g \pes a"), then join the current head and
638 * the previous head into a single curved flexa shape.
640 if ((context_info & FLEXA_RIGHT) && (context_info & PES_LOWER))
642 check_for_prefix_loss (prev_primitive);
643 prev_glyph_name = "flexa";
644 prev_primitive->set_property ("flexa-height",
645 scm_from_int (prev_delta_pitch));
646 prev_primitive->set_property ("flexa-width",
647 scm_from_double (flexa_width));
648 bool add_cauda = !(prev_prefix_set && PES_OR_FLEXA);
649 prev_primitive->set_property ("add-cauda",
650 ly_bool2scm (add_cauda));
651 check_for_prefix_loss (primitive);
653 primitive->set_property ("flexa-width",
654 scm_from_double (flexa_width));
658 * Exceptional rule for pes:
660 * If this head is stacked on the previous one due to a \pes, then
661 * set the glyph of the previous head to that for this special
662 * case, thereby avoiding potential vertical collision with the
665 if (prefix_set & PES_OR_FLEXA)
667 if ((context_info & PES_UPPER) && (context_info & STACKED_HEAD))
669 if (prev_glyph_name == "vaticana.punctum")
670 if (prev_delta_pitch > 1)
671 prev_glyph_name = "vaticana.lpes";
673 prev_glyph_name = "vaticana.vlpes";
678 prev_primitive->set_property ("glyph-name",
679 scm_makfrom0str (prev_glyph_name.c_str ()));
682 * In the backend, flexa shapes and joins need to know about line
683 * thickness. Hence, for simplicity, let's distribute the
684 * ligature grob's value for thickness to each ligature head (even
685 * if not all of them need to know).
687 primitive->set_property ("thickness", scm_from_double (thickness));
689 prev_primitive = primitive;
690 prev_prefix_set = prefix_set;
691 prev_context_info = context_info;
692 prev_delta_pitch = delta_pitch;
693 prev_glyph_name = glyph_name;
696 prev_primitive->set_property ("glyph-name",
697 scm_makfrom0str (prev_glyph_name.c_str ()));
699 align_heads (primitives, flexa_width, thickness);
701 // append all dots to paper column of ligature's last head
702 add_mora_column (prev_primitive->get_column ());
704 #if 0 // experimental code to collapse spacing after ligature
705 /* TODO: set to max (old/new spacing-increment), since other
706 voices/staves also may want to set this property. */
707 Item *first_primitive = dynamic_cast<Item *> (primitives[0].grob ());
708 Paper_column *paper_column = first_primitive->get_column ();
709 paper_column->warning (_f ("Vaticana_ligature_engraver: "
710 "setting `spacing-increment = %f': ptr =%ul",
711 ligature_width, paper_column));
713 set_property ("forced-spacing", scm_from_double (ligature_width));
717 ADD_ACKNOWLEDGER (Vaticana_ligature_engraver, rest);
718 ADD_ACKNOWLEDGER (Vaticana_ligature_engraver, note_head);
719 ADD_TRANSLATOR (Vaticana_ligature_engraver,
720 /* doc */ "Handles ligatures by glueing special ligature heads together.",
721 /* create */ "VaticanaLigature DotColumn",
722 /* accept */ "ligature-event",