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