Use scalar instead of embedded_scm for context mod overrides.
[lilypond/mpolesky.git] / lily / vaticana-ligature-engraver.cc
blob414020db7a7de54ec0e816e376f23866b491e0e8
1 /*
2 vaticana-ligature-engraver.cc -- implement Vaticana_ligature_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 2003--2009 Juergen Reuter <reuter@ipd.uka.de>
7 */
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"
16 #include "separation-item.hh"
17 #include "spanner.hh"
18 #include "staff-symbol-referencer.hh"
19 #include "stream-event.hh"
20 #include "vaticana-ligature.hh"
21 #include "warn.hh"
22 #include "dot-column.hh"
23 #include "rhythmic-head.hh"
24 #include "pitch.hh"
25 #include "translator.icc"
28 * This class implements the notation specific aspects of Vaticana
29 * style ligatures for Gregorian chant notation.
33 * TODO: Maybe move handling of dots/mora to
34 * Gregorian_ligature_engraver? It's probably common for all types of
35 * Gregorian chant notation that have dotted notes.
37 * FIXME: The horizontal alignment of the mora column is bad (too far
38 * to the left), if the last dotted note is not the last primitive in
39 * the ligature. Fortunately, in practice this bug should have no
40 * negative impact, since dotted notes appear within a ligature
41 * usually always at the end of the ligature, such that the bug never
42 * should apply for valid ligatures.
44 * TODO: Graduale Triplex, tempus per annum, hebdomada septima,
45 * alleluia (page 280) shows a counter-example for collecting dots
46 * always in a single column behind the ligature. Maybe only the last
47 * two dots in a ligature should be collected and all other dots put
48 * behind or on top of the head?
50 class Vaticana_ligature_engraver : public Gregorian_ligature_engraver
53 private:
54 static bool
55 need_extra_horizontal_space (int prev_prefix_set, int prefix_set,
56 int context_info, int delta_pitch);
57 bool is_stacked_head (int prefix_set,
58 int context_info);
59 Real align_heads (vector<Grob_info> primitives,
60 Real flexa_width,
61 Real thickness);
62 void check_for_prefix_loss (Item *primitive);
63 void check_for_ambiguous_dot_pitch (Grob_info primitive);
64 void add_mora_column (Paper_column *column);
65 vector<Grob_info> augmented_primitives_;
67 public:
68 TRANSLATOR_DECLARATIONS (Vaticana_ligature_engraver);
70 protected:
71 virtual Spanner *create_ligature_spanner ();
72 virtual void transform_heads (Spanner *ligature,
73 vector<Grob_info> primitives);
74 DECLARE_TRANSLATOR_LISTENER (pes_or_flexa);
75 DECLARE_TRANSLATOR_LISTENER (ligature);
78 IMPLEMENT_TRANSLATOR_LISTENER (Vaticana_ligature_engraver, pes_or_flexa);
79 void
80 Vaticana_ligature_engraver::listen_pes_or_flexa (Stream_event *ev)
82 Gregorian_ligature_engraver::listen_pes_or_flexa (ev);
85 IMPLEMENT_TRANSLATOR_LISTENER (Vaticana_ligature_engraver, ligature);
86 void
87 Vaticana_ligature_engraver::listen_ligature (Stream_event *ev)
89 Ligature_engraver::listen_ligature (ev);
92 Vaticana_ligature_engraver::Vaticana_ligature_engraver ()
94 brew_ligature_primitive_proc =
95 Vaticana_ligature::brew_ligature_primitive_proc;
96 augmented_primitives_.clear ();
99 Spanner *
100 Vaticana_ligature_engraver::create_ligature_spanner ()
102 return make_spanner ("VaticanaLigature", SCM_EOL);
105 bool
106 Vaticana_ligature_engraver::is_stacked_head (int prefix_set,
107 int context_info)
109 bool is_stacked;
111 // upper head of pes is stacked upon lower head of pes ...
112 is_stacked = context_info & PES_UPPER;
114 // ... unless this note starts a flexa
115 if (context_info & FLEXA_LEFT)
116 is_stacked = false;
118 // ... or another pes
119 if (context_info & PES_LOWER)
120 is_stacked = false;
122 // ... or the previous note is a semivocalis or inclinatum
123 if (context_info & AFTER_DEMINUTUM)
124 is_stacked = false;
126 // auctum head is never stacked upon preceding note
127 if (prefix_set & AUCTUM)
128 is_stacked = false;
130 // virga is never stacked upon preceding note
131 if (prefix_set & VIRGA)
132 is_stacked = false;
134 // oriscus is never stacked upon preceding note
135 if (prefix_set & ORISCUS)
136 is_stacked = false;
138 if ((prefix_set & DEMINUTUM)
139 && ! (prefix_set & INCLINATUM)
140 && (context_info & FLEXA_RIGHT))
141 is_stacked = true; // semivocalis head of deminutus form
143 return is_stacked;
147 * When aligning the heads, sometimes extra space is needed, e.g. to
148 * avoid clashing with the appendix of an adjacent notehead or with an
149 * adjacent notehead itself if it has the same pitch. Extra space is
150 * added at most once between to heads.
152 bool
153 Vaticana_ligature_engraver::need_extra_horizontal_space (int prev_prefix_set, int prefix_set,
154 int context_info, int delta_pitch)
156 if (prev_prefix_set & VIRGA)
158 * After a virga, make a an additional small space such that the
159 * appendix on the right side of the head does not touch the
160 * following head.
162 return true;
164 if ((prefix_set & INCLINATUM)
165 && ! (prev_prefix_set & INCLINATUM))
167 * Always start a series of inclinatum heads with an extra space.
169 return true;
171 if ((context_info & FLEXA_LEFT) && ! (context_info & PES_UPPER))
173 * Before a flexa (but not within a torculus), make a an
174 * additional small space such that the appendix on the left side
175 * of the flexa does not touch the this head.
177 return true;
179 if (delta_pitch == 0)
181 * If there are two adjacent noteheads with the same pitch, add
182 * additional small space between them, such that they do not
183 * touch each other.
185 return true;
187 return false;
190 Real
191 Vaticana_ligature_engraver::align_heads (vector<Grob_info> primitives,
192 Real flexa_width,
193 Real thickness)
195 if (!primitives.size ())
197 programming_error ("Vaticana_ligature: "
198 "empty ligature [ignored]");
199 return 0.0;
203 * The paper column where we put the whole ligature into.
205 Paper_column *column
206 = dynamic_cast<Item *> (primitives[0].grob ())->get_column ();
208 Real join_thickness
209 = thickness * column->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
212 * Amount of extra space two put between some particular
213 * configurations of adjacent heads.
215 * TODO: make this a property of primtive grobs.
217 Real extra_space = 4.0 * join_thickness;
220 * Keep track of the total width of the ligature.
222 Real ligature_width = 0.0;
224 Item *prev_primitive = 0;
225 int prev_prefix_set = 0;
226 for (vsize i = 0; i < primitives.size (); i++)
228 Item *primitive = dynamic_cast<Item *> (primitives[i].grob ());
229 int prefix_set
230 = scm_to_int (primitive->get_property ("prefix-set"));
231 int context_info
232 = scm_to_int (primitive->get_property ("context-info"));
235 * Get glyph_name, delta_pitch and context_info for this head.
238 SCM glyph_name_scm = primitive->get_property ("glyph-name");
239 if (glyph_name_scm == SCM_EOL)
241 primitive->programming_error ("Vaticana_ligature:"
242 " undefined glyph-name ->"
243 " ignoring grob");
244 continue;
246 string glyph_name = ly_scm2string (glyph_name_scm);
248 int delta_pitch = 0;
249 if (prev_primitive) /* urgh, need prev_primitive only here */
251 SCM delta_pitch_scm = prev_primitive->get_property ("delta-position");
252 if (delta_pitch_scm != SCM_EOL)
253 delta_pitch = scm_to_int (delta_pitch_scm);
254 else
256 primitive->programming_error ("Vaticana_ligature:"
257 " delta-position undefined ->"
258 " ignoring grob");
259 continue;
264 * Now determine width and x-offset of head.
267 Real head_width;
268 Real x_offset;
270 if (context_info & STACKED_HEAD)
273 * This head is stacked upon the previous one; hence, it
274 * does not contribute to the total width of the ligature,
275 * and its width is assumed to be 0.0. Moreover, it is
276 * shifted to the left by its width such that the right side
277 * of this and the other head are horizontally aligned.
279 head_width = 0.0;
280 x_offset = join_thickness
281 - Font_interface::get_default_font (primitive)->
282 find_by_name ("noteheads.s" + glyph_name).extent (X_AXIS).length ();
284 else if (glyph_name == "flexa" || glyph_name == "")
287 * This head represents either half of a flexa shape.
288 * Hence, it is assigned half the width of this shape.
290 head_width = 0.5 * flexa_width;
291 x_offset = 0.0;
293 else
296 * This is a regular head, placed right to the previous one.
297 * Retrieve its width from corresponding font.
299 head_width
300 = Font_interface::get_default_font (primitive)->
301 find_by_name ("noteheads.s" + glyph_name).extent (X_AXIS).length ();
302 x_offset = 0.0;
306 * Save the head's final x-offset.
308 primitive->set_property ("x-offset",
309 scm_from_double (x_offset));
312 * If the head is the 2nd head of a pes or flexa (but not a
313 * flexa shape), mark this head to be joined with the left-side
314 * neighbour head (i.e. the previous head) by a vertical beam.
316 if ((context_info & PES_UPPER)
317 || ((context_info & FLEXA_RIGHT)
318 && ! (context_info & PES_LOWER)))
320 if (!prev_primitive)
322 primitive->programming_error ("vaticana ligature: add-join: "
323 "missing previous primitive");
325 else
327 prev_primitive->set_property ("add-join",
328 ly_bool2scm (true));
331 * Create a small overlap of adjacent heads so that the join
332 * can be drawn perfectly between them.
334 ligature_width -= join_thickness;
337 else if (glyph_name == "")
340 * This is the 2nd (virtual) head of flexa shape. Join it
341 * tightly with 1st head, i.e. do *not* add additional
342 * space, such that next head will not be off from the flexa
343 * shape.
347 if (need_extra_horizontal_space (prev_prefix_set, prefix_set,
348 context_info, delta_pitch))
349 ligature_width += extra_space;
352 * Horizontally line-up this head to form a ligature.
354 move_related_items_to_column (primitive, column, ligature_width);
355 ligature_width += head_width;
357 prev_primitive = primitive;
358 prev_prefix_set = prefix_set;
362 * Add extra horizontal padding space after ligature, such that
363 * neighbouring ligatures do not touch each other.
365 ligature_width += extra_space;
367 return ligature_width;
371 * Depending on the typographical features of a particular ligature
372 * style, some prefixes may be ignored. In particular, if a curved
373 * flexa shape is produced, any prefixes to either of the two
374 * contributing heads that would select a head other than punctum, is
375 * by definition ignored.
377 * This function prints a warning, if the given primitive is prefixed
378 * such that a head other than punctum would be chosen, if this
379 * primitive were engraved as a stand-alone head.
381 void
382 Vaticana_ligature_engraver::check_for_prefix_loss (Item *primitive)
384 int prefix_set
385 = scm_to_int (primitive->get_property ("prefix-set"));
386 if (prefix_set & ~PES_OR_FLEXA)
388 string prefs = Gregorian_ligature::prefixes_to_str (primitive);
389 primitive->warning (_f ("ignored prefix (es) `%s' of this head according "
390 "to restrictions of the selected ligature style",
391 prefs.c_str ()));
395 void
396 Vaticana_ligature_engraver::add_mora_column (Paper_column *column)
398 if (augmented_primitives_.size () == 0) // no dot for column
399 return;
400 if (!column) // empty ligature???
402 augmented_primitives_[0].grob ()->
403 programming_error ("no paper column to add dot");
404 return;
406 Item *dotcol = make_item ("DotColumn", SCM_EOL);
407 dotcol->set_parent (column, X_AXIS);
408 for (vsize i = 0; i < augmented_primitives_.size (); i++)
410 Item *primitive =
411 dynamic_cast<Item *> (augmented_primitives_[i].grob ());
412 Item *dot = make_item ("Dots", primitive->self_scm ());
413 dot->set_property ("dot-count", scm_from_int (1));
414 dot->set_parent (primitive, Y_AXIS);
415 primitive->set_object ("dot", dot->self_scm ());
416 Dot_column::add_head (dotcol, primitive);
418 // FIXME: why isn't the dot picked up by Paper_column_engraver?
419 Separation_item::add_item (column, dot);
424 * This function prints a warning, if the given primitive has the same
425 * pitch as at least one of the primitives already stored in the
426 * augmented_primitives_ array.
428 * The rationale of this check is, that, if there are two dotted
429 * primitives with the same pitch, then collecting all dots in a dot
430 * column behind the ligature leads to a notational ambiguity of to
431 * which head the corresponding dot refers.
433 * Such a case should be treated as a badly specified ligature. The
434 * user should split the ligature to make the notation of dots
435 * unambiguous.
437 void
438 Vaticana_ligature_engraver::check_for_ambiguous_dot_pitch (Grob_info primitive)
440 // TODO: Fix performance, which is currently O (n^2) (since this
441 // method is called O (n) times and takes O (n) steps in the for
442 // loop), but could be O (n) (by replacing the for loop by e.g. a
443 // bitmask based O (1) test); where n=<number of primitives in the
444 // ligature> (which is typically small (n<10), though).
445 Stream_event *new_cause = primitive.event_cause ();
446 int new_pitch = unsmob_pitch (new_cause->get_property ("pitch"))->steps ();
447 for (vsize i = 0; i < augmented_primitives_.size (); i++)
449 Stream_event *cause = augmented_primitives_[i].event_cause ();
450 int pitch = unsmob_pitch (cause->get_property ("pitch"))->steps ();
451 if (pitch == new_pitch)
453 primitive.grob ()->
454 warning ("Ambiguous use of dots in ligature: there are "
455 "multiple dotted notes with the same pitch. "
456 "The ligature should be split.");
457 return; // supress multiple identical warnings
462 void
463 Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
464 vector<Grob_info> primitives)
466 Real flexa_width = robust_scm2double (ligature->get_property ("flexa-width"), 2);
468 Real thickness = robust_scm2double (ligature->get_property ("thickness"), 1);
470 Item *prev_primitive = 0;
471 int prev_prefix_set = 0;
472 int prev_context_info = 0;
473 int prev_delta_pitch = 0;
474 string prev_glyph_name = "";
475 augmented_primitives_.clear ();
476 for (vsize i = 0; i < primitives.size (); i++)
478 Item *primitive = dynamic_cast<Item *> (primitives[i].grob ());
480 int delta_pitch;
481 SCM delta_pitch_scm = primitive->get_property ("delta-position");
482 if (delta_pitch_scm != SCM_EOL)
483 delta_pitch = scm_to_int (delta_pitch_scm);
484 else
486 primitive->programming_error ("Vaticana_ligature:"
487 " delta-position undefined ->"
488 " ignoring grob");
489 continue;
492 /* retrieve & complete prefix_set and context_info */
493 int prefix_set
494 = scm_to_int (primitive->get_property ("prefix-set"));
495 int context_info
496 = scm_to_int (primitive->get_property ("context-info"));
498 if (Rhythmic_head::dot_count (primitive) > 0)
499 // remove dots from primitive and add remember primitive for
500 // creating a dot column
502 Rhythmic_head::get_dots (primitive)->set_property ("dot-count",
503 scm_from_int (0));
504 // TODO: Maybe completely remove grob "Dots" (dots->suicide
505 // () ?) rather than setting property "dot-count" to 0.
507 check_for_ambiguous_dot_pitch (primitives[i]);
508 augmented_primitives_.push_back (primitives[i]);
510 else if (augmented_primitives_.size () > 0)
512 primitive->warning ("This ligature has a dotted head followed by "
513 "a non-dotted head. The ligature should be "
514 "split after the last dotted head before "
515 "this head.");
518 if (is_stacked_head (prefix_set, context_info))
520 context_info |= STACKED_HEAD;
521 primitive->set_property ("context-info",
522 scm_from_int (context_info));
526 * Now determine which head to typeset (this is context sensitive
527 * information, since it depends on neighbouring heads; therefore,
528 * this decision must be made here in the engraver rather than in
529 * the backend).
531 string glyph_name;
532 if (prefix_set & VIRGA)
534 glyph_name = "vaticana.punctum";
535 primitive->set_property ("add-stem", ly_bool2scm (true));
537 else if (prefix_set & QUILISMA)
538 glyph_name = "vaticana.quilisma";
539 else if (prefix_set & ORISCUS)
540 glyph_name = "solesmes.oriscus";
541 else if (prefix_set & STROPHA)
542 if (prefix_set & AUCTUM)
543 glyph_name = "solesmes.stropha.aucta";
544 else glyph_name = "solesmes.stropha";
545 else if (prefix_set & INCLINATUM)
546 if (prefix_set & AUCTUM)
547 glyph_name = "solesmes.incl.auctum";
548 else if (prefix_set & DEMINUTUM)
549 glyph_name = "solesmes.incl.parvum";
550 else
551 glyph_name = "vaticana.inclinatum";
552 else if (prefix_set & DEMINUTUM)
554 if (i == 0)
556 // initio debilis
557 glyph_name = "vaticana.reverse.plica";
559 else if (prev_delta_pitch > 0)
561 // epiphonus
562 if (! (prev_context_info & FLEXA_RIGHT))
564 /* correct head of previous primitive */
565 if (prev_delta_pitch > 1)
566 prev_glyph_name = "vaticana.epiphonus";
567 else
568 prev_glyph_name = "vaticana.vepiphonus";
570 if (prev_delta_pitch > 1)
571 glyph_name = "vaticana.plica";
572 else
573 glyph_name = "vaticana.vplica";
575 else if (prev_delta_pitch < 0)
577 // cephalicus
578 if (! (prev_context_info & FLEXA_RIGHT))
579 /* correct head of previous primitive */
581 if (i > 1)
583 /* cephalicus head with fixed size cauda */
584 prev_glyph_name = "vaticana.inner.cephalicus";
586 else
588 /* cephalicus head without cauda */
589 prev_glyph_name = "vaticana.cephalicus";
593 * Flexa has no variable size cauda if its left head is
594 * stacked on the right head. This is true for
595 * cephalicus. Hence, remove the cauda.
597 * Urgh: for the current implementation, this rule only
598 * applies for cephalicus; but it is a fundamental rule.
599 * Therefore, the following line of code should be
600 * placed somewhere else.
602 prev_primitive->set_property ("add-cauda",
603 ly_bool2scm (false));
605 if (prev_delta_pitch < - 1)
606 glyph_name = "vaticana.reverse.plica";
607 else
608 glyph_name = "vaticana.reverse.vplica";
610 else // (prev_delta_pitch == 0)
612 primitive->programming_error ("Vaticana_ligature:"
613 " deminutum head must have different"
614 " pitch -> ignoring grob");
617 else if (prefix_set & (CAVUM | LINEA))
618 if ((prefix_set & CAVUM) && (prefix_set & LINEA))
619 glyph_name = "vaticana.linea.punctum.cavum";
620 else if (prefix_set & CAVUM)
621 glyph_name = "vaticana.punctum.cavum";
622 else
623 glyph_name = "vaticana.linea.punctum";
624 else if (prefix_set & AUCTUM)
625 if (prefix_set & ASCENDENS)
626 glyph_name = "solesmes.auct.asc";
627 else
628 glyph_name = "solesmes.auct.desc";
629 else if ((context_info & STACKED_HEAD)
630 && (context_info & PES_UPPER))
631 if (prev_delta_pitch > 1)
632 glyph_name = "vaticana.upes";
633 else
634 glyph_name = "vaticana.vupes";
635 else
636 glyph_name = "vaticana.punctum";
639 * This head needs a cauda, if it starts a flexa, is not the upper
640 * head of a pes, and if it is a punctum.
642 if ((context_info & FLEXA_LEFT) && ! (context_info & PES_UPPER))
643 if (glyph_name == "vaticana.punctum")
644 primitive->set_property ("add-cauda", ly_bool2scm (true));
647 * Execptional rule for porrectus:
649 * If the current head is preceded by a \flexa and succeded by a
650 * \pes (e.g. "a \flexa g \pes a"), then join the current head and
651 * the previous head into a single curved flexa shape.
653 if ((context_info & FLEXA_RIGHT) && (context_info & PES_LOWER))
655 check_for_prefix_loss (prev_primitive);
656 prev_glyph_name = "flexa";
657 prev_primitive->set_property ("flexa-height",
658 scm_from_int (prev_delta_pitch));
659 prev_primitive->set_property ("flexa-width",
660 scm_from_double (flexa_width));
661 bool add_cauda = !(prev_prefix_set && PES_OR_FLEXA);
662 prev_primitive->set_property ("add-cauda",
663 ly_bool2scm (add_cauda));
664 check_for_prefix_loss (primitive);
665 glyph_name = "";
666 primitive->set_property ("flexa-width",
667 scm_from_double (flexa_width));
671 * Exceptional rule for pes:
673 * If this head is stacked on the previous one due to a \pes, then
674 * set the glyph of the previous head to that for this special
675 * case, thereby avoiding potential vertical collision with the
676 * current head.
678 if (prefix_set & PES_OR_FLEXA)
680 if ((context_info & PES_UPPER) && (context_info & STACKED_HEAD))
682 if (prev_glyph_name == "vaticana.punctum")
684 if (prev_delta_pitch > 1)
685 prev_glyph_name = "vaticana.lpes";
686 else
687 prev_glyph_name = "vaticana.vlpes";
692 if (prev_primitive)
693 prev_primitive->set_property ("glyph-name",
694 ly_string2scm (prev_glyph_name));
697 * In the backend, flexa shapes and joins need to know about line
698 * thickness. Hence, for simplicity, let's distribute the
699 * ligature grob's value for thickness to each ligature head (even
700 * if not all of them need to know).
702 primitive->set_property ("thickness", scm_from_double (thickness));
704 prev_primitive = primitive;
705 prev_prefix_set = prefix_set;
706 prev_context_info = context_info;
707 prev_delta_pitch = delta_pitch;
708 prev_glyph_name = glyph_name;
711 prev_primitive->set_property ("glyph-name",
712 ly_string2scm (prev_glyph_name));
714 align_heads (primitives, flexa_width, thickness);
716 // append all dots to paper column of ligature's last head
717 add_mora_column (prev_primitive->get_column ());
719 #if 0 // experimental code to collapse spacing after ligature
720 /* TODO: set to max (old/new spacing-increment), since other
721 voices/staves also may want to set this property. */
722 Item *first_primitive = dynamic_cast<Item *> (primitives[0].grob ());
723 Paper_column *paper_column = first_primitive->get_column ();
724 paper_column->warning (_f ("Vaticana_ligature_engraver: "
725 "setting `spacing-increment = %f': ptr =%ul",
726 ligature_width, paper_column));
727 paper_column->
728 set_property ("forced-spacing", scm_from_double (ligature_width));
729 #endif
732 ADD_ACKNOWLEDGER (Vaticana_ligature_engraver, rest);
733 ADD_ACKNOWLEDGER (Vaticana_ligature_engraver, note_head);
734 ADD_TRANSLATOR (Vaticana_ligature_engraver,
735 /* doc */
736 "Handle ligatures by glueing special ligature heads together.",
738 /* create */
739 "VaticanaLigature "
740 "DotColumn ",
742 /* read */
745 /* write */