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