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