]> git.donarmstrong.com Git - lilypond.git/blob - lily/vaticana-ligature-engraver.cc
f84ecaa6b214cefbece2a3a473fe4d3454385eb2
[lilypond.git] / lily / vaticana-ligature-engraver.cc
1 /*
2   vaticana-ligature-engraver.cc -- implement Vaticana_ligature_engraver
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2003--2006 Juergen Reuter <reuter@ipd.uka.de>
7 */
8
9 #include "gregorian-ligature-engraver.hh"
10
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 "spanner.hh"
17 #include "staff-symbol-referencer.hh"
18 #include "stream-event.hh"
19 #include "vaticana-ligature.hh"
20 #include "warn.hh"
21
22 #include "translator.icc"
23
24 /*
25  * This class implements the notation specific aspects of Vaticana
26  * style ligatures for Gregorian chant notation.
27  */
28
29 class Vaticana_ligature_engraver : public Gregorian_ligature_engraver
30 {
31
32 private:
33   static bool
34   need_extra_horizontal_space (int prev_prefix_set, int prefix_set,
35                                int context_info, int delta_pitch);
36   bool is_stacked_head (int prefix_set,
37                         int context_info);
38   Real align_heads (vector<Grob_info> primitives,
39                     Real flexa_width,
40                     Real thickness);
41
42 public:
43   TRANSLATOR_DECLARATIONS (Vaticana_ligature_engraver);
44
45 protected:
46   virtual Spanner *create_ligature_spanner ();
47   virtual void transform_heads (Spanner *ligature,
48                                 vector<Grob_info> primitives);
49   DECLARE_TRANSLATOR_LISTENER (pes_or_flexa);
50   DECLARE_TRANSLATOR_LISTENER (ligature);
51 };
52
53 IMPLEMENT_TRANSLATOR_LISTENER (Vaticana_ligature_engraver, pes_or_flexa);
54 void
55 Vaticana_ligature_engraver::listen_pes_or_flexa (Stream_event *ev)
56 {
57   Gregorian_ligature_engraver::listen_pes_or_flexa (ev);
58 }
59
60 IMPLEMENT_TRANSLATOR_LISTENER (Vaticana_ligature_engraver, ligature);
61 void
62 Vaticana_ligature_engraver::listen_ligature (Stream_event *ev)
63 {
64   Ligature_engraver::listen_ligature (ev);
65 }
66
67 Vaticana_ligature_engraver::Vaticana_ligature_engraver ()
68 {
69   brew_ligature_primitive_proc = 
70     Vaticana_ligature::brew_ligature_primitive_proc;
71 }
72
73 Spanner *
74 Vaticana_ligature_engraver::create_ligature_spanner ()
75 {
76   return make_spanner ("VaticanaLigature", SCM_EOL);
77 }
78
79 bool
80 Vaticana_ligature_engraver::is_stacked_head (int prefix_set,
81                                              int context_info)
82 {
83   bool is_stacked_b;
84
85   // upper head of pes is stacked upon lower head of pes ...
86   is_stacked_b = context_info & PES_UPPER;
87
88   // ... unless this note starts a flexa
89   if (context_info & FLEXA_LEFT)
90     is_stacked_b = false;
91
92   // ... or another pes
93   if (context_info & PES_LOWER)
94     is_stacked_b = false;
95
96   // ... or the previous note is a semivocalis or inclinatum
97   if (context_info & AFTER_DEMINUTUM)
98     is_stacked_b = false;
99
100   // auctum head is never stacked upon preceding note
101   if (prefix_set & AUCTUM)
102     is_stacked_b = false;
103
104   // virga is never stacked upon preceding note
105   if (prefix_set & VIRGA)
106     is_stacked_b = false;
107
108   // oriscus is never stacked upon preceding note
109   if (prefix_set & ORISCUS)
110     is_stacked_b = false;
111
112   if ((prefix_set & DEMINUTUM)
113       && ! (prefix_set & INCLINATUM)
114       && (context_info & FLEXA_RIGHT))
115     is_stacked_b = true; // semivocalis head of deminutus form
116
117   return is_stacked_b;
118 }
119
120 /*
121  * When aligning the heads, sometimes extra space is needed, e.g. to
122  * avoid clashing with the appendix of an adjacent notehead or with an
123  * adjacent notehead itself if it has the same pitch.  Extra space is
124  * added at most once between to heads.
125  */
126 bool
127 Vaticana_ligature_engraver::need_extra_horizontal_space (int prev_prefix_set, int prefix_set,
128                                                          int context_info, int delta_pitch)
129 {
130   if (prev_prefix_set & VIRGA)
131     /*
132      * After a virga, make a an additional small space such that the
133      * appendix on the right side of the head does not touch the
134      * following head.
135      */
136     return true;
137
138   if ((prefix_set & INCLINATUM)
139       && ! (prev_prefix_set & INCLINATUM))
140     /*
141      * Always start a series of inclinatum heads with an extra space.
142      */
143     return true;
144
145   if ((context_info & FLEXA_LEFT) && ! (context_info & PES_UPPER))
146     /*
147      * Before a flexa (but not within a torculus), make a an
148      * additional small space such that the appendix on the left side
149      * of the flexa does not touch the this head.
150      */
151     return true;
152
153   if (delta_pitch == 0)
154     /*
155      * If there are two adjacent noteheads with the same pitch, add
156      * additional small space between them, such that they do not
157      * touch each other.
158      */
159     return true;
160
161   return false;
162 }
163
164 Real
165 Vaticana_ligature_engraver::align_heads (vector<Grob_info> primitives,
166                                          Real flexa_width,
167                                          Real thickness)
168 {
169   if (!primitives.size ())
170     {
171       programming_error ("Vaticana_ligature: "
172                          "empty ligature [ignored]");
173       return 0.0;
174     }
175
176   /*
177    * The paper column where we put the whole ligature into.
178    */
179   Paper_column *column
180     = dynamic_cast<Item *> (primitives[0].grob ())->get_column ();
181
182   Real join_thickness
183     = thickness * column->layout ()->get_dimension (ly_symbol2scm ("line-thickness"));
184
185   /*
186    * Amount of extra space two put between some particular
187    * configurations of adjacent heads.
188    *
189    * TODO: make this a property of primtive grobs.
190    */
191   Real extra_space = 4.0 * join_thickness;
192
193   /*
194    * Keep track of the total width of the ligature.
195    */
196   Real ligature_width = 0.0;
197
198   Item *prev_primitive = 0;
199   int prev_prefix_set = 0;
200   for (vsize i = 0; i < primitives.size (); i++)
201     {
202       Item *primitive = dynamic_cast<Item *> (primitives[i].grob ());
203       int prefix_set
204         = scm_to_int (primitive->get_property ("prefix-set"));
205       int context_info
206         = scm_to_int (primitive->get_property ("context-info"));
207
208       /*
209        * Get glyph_name, delta_pitch and context_info for this head.
210        */
211
212       SCM glyph_name_scm = primitive->get_property ("glyph-name");
213       if (glyph_name_scm == SCM_EOL)
214         {
215           primitive->programming_error ("Vaticana_ligature:"
216                                         "undefined glyph-name -> "
217                                         "ignoring grob");
218           continue;
219         }
220       string glyph_name = ly_scm2string (glyph_name_scm);
221
222       int delta_pitch = 0;
223       if (prev_primitive) /* urgh, need prev_primitive only here */
224         {
225           SCM delta_pitch_scm = prev_primitive->get_property ("delta-position");
226           if (delta_pitch_scm != SCM_EOL)
227             delta_pitch = scm_to_int (delta_pitch_scm);
228           else
229             {
230               primitive->programming_error ("Vaticana_ligature:"
231                                             "delta-position undefined -> "
232                                             "ignoring grob");
233               continue;
234             }
235         }
236
237       /*
238        * Now determine width and x-offset of head.
239        */
240
241       Real head_width;
242       Real x_offset;
243
244       if (context_info & STACKED_HEAD)
245         {
246           /*
247            * This head is stacked upon the previous one; hence, it
248            * does not contribute to the total width of the ligature,
249            * and its width is assumed to be 0.0.  Moreover, it is
250            * shifted to the left by its width such that the right side
251            * of this and the other head are horizontally aligned.
252            */
253           head_width = 0.0;
254           x_offset = join_thickness
255             - Font_interface::get_default_font (primitive)->
256             find_by_name ("noteheads.s" + glyph_name).extent (X_AXIS).length ();
257         }
258       else if (glyph_name == "flexa" || glyph_name == "")
259         {
260           /*
261            * This head represents either half of a flexa shape.
262            * Hence, it is assigned half the width of this shape.
263            */
264           head_width = 0.5 * flexa_width;
265           x_offset = 0.0;
266         }
267       else
268         {
269           /*
270            * This is a regular head, placed right to the previous one.
271            * Retrieve its width from corresponding font.
272            */
273           head_width
274             = Font_interface::get_default_font (primitive)->
275             find_by_name ("noteheads.s" + glyph_name).extent (X_AXIS).length ();
276           x_offset = 0.0;
277         }
278
279       /*
280        * Save the head's final x-offset.
281        */
282       primitive->set_property ("x-offset",
283                                scm_from_double (x_offset));
284
285       /*
286        * If the head is the 2nd head of a pes or flexa (but not a
287        * flexa shape), mark this head to be joined with the left-side
288        * neighbour head (i.e. the previous head) by a vertical beam.
289        */
290       if ((context_info & PES_UPPER)
291           || ((context_info & FLEXA_RIGHT)
292               && ! (context_info & PES_LOWER)))
293         {
294           if (!prev_primitive)
295             {
296               primitive->programming_error ("vaticana ligature: add-join: "
297                                             "missing previous primitive");
298             }
299           else
300             {
301               prev_primitive->set_property ("add-join",
302                                             ly_bool2scm (true));
303
304               /*
305                * Create a small overlap of adjacent heads so that the join
306                * can be drawn perfectly between them.
307                */
308               ligature_width -= join_thickness;
309             }
310         }
311       else if (glyph_name == "")
312         {
313           /*
314            * This is the 2nd (virtual) head of flexa shape.  Join it
315            * tightly with 1st head, i.e. do *not* add additional
316            * space, such that next head will not be off from the flexa
317            * shape.
318            */
319         }
320
321       if (need_extra_horizontal_space (prev_prefix_set, prefix_set,
322                                        context_info, delta_pitch))
323         ligature_width += extra_space;
324
325       /*
326        * Horizontally line-up this head to form a ligature.
327        */
328       get_set_column (primitive, column);
329       primitive->translate_axis (ligature_width, X_AXIS);
330       ligature_width += head_width;
331
332       prev_primitive = primitive;
333       prev_prefix_set = prefix_set;
334     }
335
336   /*
337    * Add extra horizontal padding space after ligature, such that
338    * neighbouring ligatures do not touch each other.
339    */
340   ligature_width += extra_space;
341
342   return ligature_width;
343 }
344
345 /*
346  * Depending on the typographical features of a particular ligature
347  * style, some prefixes may be ignored.  In particular, if a curved
348  * flexa shape is produced, any prefixes to either of the two
349  * contributing heads that would select a head other than punctum, is
350  * by definition ignored.
351  *
352  * This function prints a warning, if the given primitive is prefixed
353  * such that a head other than punctum would be chosen, if this
354  * primitive were engraved as a stand-alone head.
355  */
356 void
357 check_for_prefix_loss (Item *primitive)
358 {
359   int prefix_set
360     = scm_to_int (primitive->get_property ("prefix-set"));
361   if (prefix_set & ~PES_OR_FLEXA)
362     {
363       string prefs = Gregorian_ligature::prefixes_to_str (primitive);
364       primitive->warning (_f ("ignored prefix (es) `%s' of this head according "
365                               "to restrictions of the selected ligature style",
366                               prefs.c_str ()));
367     }
368 }
369
370 void
371 Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
372                                              vector<Grob_info> primitives)
373 {
374   Real flexa_width = robust_scm2double (ligature->get_property ("flexa-width"), 2);
375
376   Real thickness = robust_scm2double (ligature->get_property ("thickness"), 1);
377
378   Item *prev_primitive = 0;
379   int prev_prefix_set = 0;
380   int prev_context_info = 0;
381   int prev_delta_pitch = 0;
382   string prev_glyph_name = "";
383   for (vsize i = 0; i < primitives.size (); i++)
384     {
385       Item *primitive = dynamic_cast<Item *> (primitives[i].grob ());
386
387       int delta_pitch;
388       SCM delta_pitch_scm = primitive->get_property ("delta-position");
389       if (delta_pitch_scm != SCM_EOL)
390         delta_pitch = scm_to_int (delta_pitch_scm);
391       else
392         {
393           primitive->programming_error ("Vaticana_ligature:"
394                                         "delta-position undefined -> "
395                                         "ignoring grob");
396           continue;
397         }
398
399       /* retrieve & complete prefix_set and context_info */
400       int prefix_set
401         = scm_to_int (primitive->get_property ("prefix-set"));
402       int context_info
403         = scm_to_int (primitive->get_property ("context-info"));
404       if (is_stacked_head (prefix_set, context_info))
405         {
406           context_info |= STACKED_HEAD;
407           primitive->set_property ("context-info",
408                                    scm_from_int (context_info));
409         }
410
411       /*
412        * Now determine which head to typeset (this is context sensitive
413        * information, since it depends on neighbouring heads; therefore,
414        * this decision must be made here in the engraver rather than in
415        * the backend).
416        */
417       string glyph_name;
418       if (prefix_set & VIRGA)
419         {
420           glyph_name = "vaticana.punctum";
421           primitive->set_property ("add-stem", ly_bool2scm (true));
422         }
423       else if (prefix_set & QUILISMA)
424         glyph_name = "vaticana.quilisma";
425       else if (prefix_set & ORISCUS)
426         glyph_name = "solesmes.oriscus";
427       else if (prefix_set & STROPHA)
428         if (prefix_set & AUCTUM)
429           glyph_name = "solesmes.stropha.aucta";
430         else glyph_name = "solesmes.stropha";
431       else if (prefix_set & INCLINATUM)
432         if (prefix_set & AUCTUM)
433           glyph_name = "solesmes.incl.auctum";
434         else if (prefix_set & DEMINUTUM)
435           glyph_name = "solesmes.incl.parvum";
436         else
437           glyph_name = "vaticana.inclinatum";
438       else if (prefix_set & DEMINUTUM)
439         if (i == 0)
440           {
441             // initio debilis
442             glyph_name = "vaticana.reverse.plica";
443           }
444         else if (prev_delta_pitch > 0)
445           {
446             // epiphonus
447             if (! (prev_context_info & FLEXA_RIGHT))
448               /* correct head of previous primitive */
449               if (prev_delta_pitch > 1)
450                 prev_glyph_name = "vaticana.epiphonus";
451               else
452                 prev_glyph_name = "vaticana.vepiphonus";
453             if (prev_delta_pitch > 1)
454               glyph_name = "vaticana.plica";
455             else
456               glyph_name = "vaticana.vplica";
457           }
458         else if (prev_delta_pitch < 0)
459           {
460             // cephalicus
461             if (! (prev_context_info & FLEXA_RIGHT))
462               /* correct head of previous primitive */
463               {
464                 if (i > 1)
465                   {
466                     /* cephalicus head with fixed size cauda */
467                     prev_glyph_name = "vaticana.inner.cephalicus";
468                   }
469                 else
470                   {
471                     /* cephalicus head without cauda */
472                     prev_glyph_name = "vaticana.cephalicus";
473                   }
474
475                 /*
476                  * Flexa has no variable size cauda if its left head is
477                  * stacked on the right head.  This is true for
478                  * cephalicus.  Hence, remove the cauda.
479                  *
480                  * Urgh: for the current implementation, this rule only
481                  * applies for cephalicus; but it is a fundamental rule.
482                  * Therefore, the following line of code should be
483                  * placed somewhere else.
484                  */
485                 prev_primitive->set_property ("add-cauda",
486                                               ly_bool2scm (false));
487               }
488             if (prev_delta_pitch < - 1)
489               glyph_name = "vaticana.reverse.plica";
490             else
491               glyph_name = "vaticana.reverse.vplica";
492           }
493         else // (prev_delta_pitch == 0)
494           {
495             primitive->programming_error ("Vaticana_ligature:"
496                                           "deminutum head must have different "
497                                           "pitch -> ignoring grob");
498           }
499       else if (prefix_set & (CAVUM | LINEA))
500         if ((prefix_set & CAVUM) && (prefix_set & LINEA))
501           glyph_name = "vaticana.linea.punctum.cavum";
502         else if (prefix_set & CAVUM)
503           glyph_name = "vaticana.punctum.cavum";
504         else
505           glyph_name = "vaticana.linea.punctum";
506       else if (prefix_set & AUCTUM)
507         if (prefix_set & ASCENDENS)
508           glyph_name = "solesmes.auct.asc";
509         else
510           glyph_name = "solesmes.auct.desc";
511       else if ((context_info & STACKED_HEAD)
512                && (context_info & PES_UPPER))
513         if (prev_delta_pitch > 1)
514           glyph_name = "vaticana.upes";
515         else
516           glyph_name = "vaticana.vupes";
517       else
518         glyph_name = "vaticana.punctum";
519
520       /*
521        * This head needs a cauda, if it starts a flexa, is not the upper
522        * head of a pes, and if it is a punctum.
523        */
524       if ((context_info & FLEXA_LEFT) && ! (context_info & PES_UPPER))
525         if (glyph_name == "vaticana.punctum")
526           primitive->set_property ("add-cauda", ly_bool2scm (true));
527
528       /*
529        * Execptional rule for porrectus:
530        *
531        * If the current head is preceded by a \flexa and succeded by a
532        * \pes (e.g. "a \flexa g \pes a"), then join the current head and
533        * the previous head into a single curved flexa shape.
534        */
535       if ((context_info & FLEXA_RIGHT) && (context_info & PES_LOWER))
536         {
537           check_for_prefix_loss (prev_primitive);
538           prev_glyph_name = "flexa";
539           prev_primitive->set_property ("flexa-height",
540                                         scm_from_int (prev_delta_pitch));
541           prev_primitive->set_property ("flexa-width",
542                                         scm_from_double (flexa_width));
543           bool add_cauda = !(prev_prefix_set && PES_OR_FLEXA);
544           prev_primitive->set_property ("add-cauda",
545                                         ly_bool2scm (add_cauda));
546           check_for_prefix_loss (primitive);
547           glyph_name = "";
548           primitive->set_property ("flexa-width",
549                                    scm_from_double (flexa_width));
550         }
551
552       /*
553        * Exceptional rule for pes:
554        *
555        * If this head is stacked on the previous one due to a \pes, then
556        * set the glyph of the previous head to that for this special
557        * case, thereby avoiding potential vertical collision with the
558        * current head.
559        */
560       if (prefix_set & PES_OR_FLEXA)
561         {
562           if ((context_info & PES_UPPER) && (context_info & STACKED_HEAD))
563             {
564               if (prev_glyph_name == "vaticana.punctum")
565                 if (prev_delta_pitch > 1)
566                   prev_glyph_name = "vaticana.lpes";
567                 else
568                   prev_glyph_name = "vaticana.vlpes";
569             }
570         }
571
572       if (prev_primitive)
573         prev_primitive->set_property ("glyph-name",
574                                       scm_makfrom0str (prev_glyph_name.c_str ()));
575
576       /*
577        * In the backend, flexa shapes and joins need to know about line
578        * thickness.  Hence, for simplicity, let's distribute the
579        * ligature grob's value for thickness to each ligature head (even
580        * if not all of them need to know).
581        */
582       primitive->set_property ("thickness", scm_from_double (thickness));
583
584       prev_primitive = primitive;
585       prev_prefix_set = prefix_set;
586       prev_context_info = context_info;
587       prev_delta_pitch = delta_pitch;
588       prev_glyph_name = glyph_name;
589     }
590
591   prev_primitive->set_property ("glyph-name",
592                                 scm_makfrom0str (prev_glyph_name.c_str ()));
593
594   align_heads (primitives, flexa_width, thickness);
595
596 #if 0 // experimental code to collapse spacing after ligature
597   /* TODO: set to max (old/new spacing-increment), since other
598      voices/staves also may want to set this property. */
599   Item *first_primitive = dynamic_cast<Item *> (primitives[0].grob ());
600   Paper_column *paper_column = first_primitive->get_column ();
601   paper_column->warning (_f ("Vaticana_ligature_engraver: "
602                              "setting `spacing-increment = %f': ptr =%ul",
603                              ligature_width, paper_column));
604   paper_column->
605     set_property ("forced-spacing", scm_from_double (ligature_width));
606 #endif
607 }
608
609 ADD_ACKNOWLEDGER (Vaticana_ligature_engraver, rest);
610 ADD_ACKNOWLEDGER (Vaticana_ligature_engraver, note_head);
611 ADD_TRANSLATOR (Vaticana_ligature_engraver,
612                 /* doc */ "Handles ligatures by glueing special ligature heads together.",
613                 /* create */ "VaticanaLigature",
614                 /* accept */ "ligature-event",
615                 /* read */ "",
616                 /* write */ "");