]> git.donarmstrong.com Git - lilypond.git/blob - lily/vaticana-ligature-engraver.cc
* Documentation/user/refman.itely,
[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   Real finish_primitive (Item *first_primitive,
29                          Item *primitive,
30                          int prefix_set,
31                          int context_info,
32                          String glyph_name,
33                          int pitch_delta,
34                          Real flexa_width,
35                          Real join_thickness,
36                          Real distance);
37
38 public:
39   TRANSLATOR_DECLARATIONS(Vaticana_ligature_engraver);
40
41 protected:
42   virtual Spanner *create_ligature_spanner ();
43   virtual void transform_heads (Spanner *ligature,
44                                 Array<Grob_info> primitives);
45 };
46
47 Vaticana_ligature_engraver::Vaticana_ligature_engraver ()
48 {
49 }
50
51 Spanner *
52 Vaticana_ligature_engraver::create_ligature_spanner ()
53 {
54   return new Spanner (get_property ("VaticanaLigature"));
55 }
56
57 Real
58 Vaticana_ligature_engraver::finish_primitive (Item *first_primitive,
59                                               Item *primitive,
60                                               int prefix_set,
61                                               int context_info,
62                                               String glyph_name,
63                                               int pitch_delta,
64                                               Real flexa_width,
65                                               Real join_thickness,
66                                               Real distance)
67 {
68   Real next_distance = distance;
69   if (primitive)
70     { // determine width of previous head and x-offset
71       Real head_width;
72       Real x_offset;
73       bool is_stacked;
74
75       // upper head of pes is stacked upon lower head of pes ...
76       is_stacked = context_info & PES_UPPER;
77
78       // ... unless this note starts a flexa
79       if (context_info & FLEXA_LEFT)
80         is_stacked = false;
81
82       // ... or another pes
83       if (context_info & PES_LOWER)
84         is_stacked = false;
85
86       // ... or the previous note is a semivocalis or inclinatum
87       /* TODO:
88         if (prev_prefix_set & DEMINUTUM)
89            is_stacked = false;
90       */
91
92       // auctum head is never stacked upon preceding note
93       if (prefix_set & AUCTUM)
94         is_stacked = false;
95
96       // virga is never stacked upon preceding note
97       if (prefix_set & VIRGA)
98         is_stacked = false;
99
100       // oriscus is never stacked upon preceding note
101       if (prefix_set & ORISCUS)
102         is_stacked = false;
103
104       if ((prefix_set & DEMINUTUM) &&
105           !(prefix_set & INCLINATUM) &&
106           (context_info & FLEXA_RIGHT))
107         is_stacked = true; // semivocalis head of deminutus form
108
109       if (is_stacked)
110         {
111           /*
112            * This head is stacked upon the previous one; hence, it
113            * does not contribute to the total width of the ligature,
114            * and its width is assumed to be 0.0.  Moreover, it is
115            * shifted to the left by its width such that the right side
116            * of this and the other head are horizontally aligned.
117            */
118           head_width = 0.0;
119           x_offset = join_thickness -
120             Font_interface::get_default_font (primitive)->
121             find_by_name ("noteheads-" + glyph_name).extent (X_AXIS).length ();
122         }
123       else if (!String::compare (glyph_name, "flexa") ||
124                !String::compare (glyph_name, ""))
125         {
126           /*
127            * This head represents either half of a flexa shape.
128            * Hence, it is assigned half the width of this shape.
129            */
130           head_width = 0.5 * flexa_width;
131           x_offset = 0.0;
132         }
133       else // retrieve width from corresponding font
134         {
135           head_width =
136             Font_interface::get_default_font (primitive)->
137             find_by_name ("noteheads-" + glyph_name).extent (X_AXIS).length ();
138           x_offset = 0.0;
139         }
140
141       /*
142        * Save the head's final shape and x-offset.
143        */
144       primitive->set_grob_property ("glyph-name",
145                                     scm_makfrom0str (glyph_name.to_str0 ()));
146       primitive->set_grob_property ("x-offset",
147                                     gh_double2scm (x_offset));
148
149       /*
150        * If the head is the 2nd head of a pes or flexa (but not a
151        * flexa shape), mark this head to be joined with the left-side
152        * neighbour head (i.e. the previous head) by a vertical beam.
153        */
154       if ((context_info & PES_UPPER) ||
155           ((context_info & FLEXA_RIGHT) &&
156            !(context_info & PES_LOWER)))
157         {
158           primitive->set_grob_property ("join-left",
159                                         gh_int2scm (pitch_delta));
160
161           /*
162            * Create a small overlap of adjacent heads so that the join
163            * can be drawn perfectly between them.
164            */
165           next_distance -= join_thickness;
166         }
167       else if (!String::compare (glyph_name, ""))
168         {
169           /*
170            * 2nd (virtual) head of flexa shape: join tightly with 1st
171            * head, i.e. do *not* add additional space, such that next
172            * head will not be off from the flexa shape.
173            */
174         }
175       else if (context_info & AFTER_VIRGA)
176         {
177           /*
178            * Make a small space after a virga.
179            */
180           next_distance += 2 * join_thickness;
181         }
182       else if (pitch_delta == 0)
183         {
184           /*
185            * Make a small space between two adjacent notes with the
186            * same pitch.
187            */
188           next_distance += 2 * join_thickness;
189         }
190
191       /*
192        * Horizontally line-up this head to form a ligature.
193        */
194       get_set_column (primitive, first_primitive->get_column ());
195       primitive->translate_axis (next_distance, X_AXIS);
196       next_distance += head_width;
197
198     }
199
200   return next_distance;
201 }
202
203 void
204 Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
205                                              Array<Grob_info> primitives)
206 {
207   Real flexa_width;
208   SCM flexa_width_scm = ligature->get_grob_property ("flexa-width");
209   if (flexa_width_scm != SCM_EOL)
210     {
211       flexa_width = gh_scm2double (flexa_width_scm);
212     }
213   else
214     {
215       programming_error ("Vaticana_ligature_engraver:"
216                          "flexa-width undefined; assuming 2.0 staff space");
217       flexa_width =
218         2.0 * Staff_symbol_referencer::staff_space (ligature);
219     }
220
221   Real join_thickness;
222   SCM join_thickness_scm = ligature->get_grob_property ("thickness");
223   if (join_thickness_scm != SCM_EOL)
224     {
225       join_thickness = gh_scm2double (join_thickness_scm);
226     }
227   else
228     {
229       programming_error ("Vaticana_ligature_engraver:"
230                          "thickness undefined; assuming 1.4 linethickness");
231       join_thickness = 1.4;
232     }
233   join_thickness *= ligature->get_paper ()->get_var ("linethickness");
234
235   Item *first_primitive = 0;
236   Item *prev_primitive = 0;
237   int prev_prefix_set = 0;
238   int prev_context_info = 0;
239   int prev_pitch = 0;
240   int prev_pitch_delta = 0;
241   String prev_glyph_name = "";
242   Real prev_distance = 0.0;
243   for (int i = 0; i < primitives.size(); i++) {
244     Item *primitive = dynamic_cast<Item*> (primitives[i].grob_);
245     Music *music_cause = primitives[i].music_cause ();
246     int context_info = gh_scm2int (primitive->get_grob_property ("context-info"));
247     int pitch = unsmob_pitch (music_cause->get_mus_property ("pitch"))->steps ();
248     String glyph_name;
249     if (!first_primitive)
250       first_primitive = primitive;
251     int prefix_set = gh_scm2int (primitive->get_grob_property ("prefix-set"));
252
253     /*
254      * Now determine which head to typeset (this is context sensitive
255      * information, since it depends on neighbouring heads; therefore,
256      * this decision must be made here in the engraver rather than in
257      * the backend).
258      */
259     if (prefix_set & VIRGA)
260       glyph_name = "vaticana_virga";
261     else if (prefix_set & QUILISMA)
262       glyph_name = "vaticana_quilisma";
263     else if (prefix_set & ORISCUS)
264       glyph_name = "solesmes_oriscus";
265     else if (prefix_set & STROPHA)
266       if (prefix_set & AUCTUM)
267         glyph_name = "solesmes_stropha_aucta";
268       else glyph_name = "solesmes_stropha";
269     else if (prefix_set & INCLINATUM)
270       if (prefix_set & AUCTUM)
271         glyph_name = "solesmes_incl_auctum";
272       else if (prefix_set & DEMINUTUM)
273         glyph_name = "solesmes_incl_parvum";
274       else
275         glyph_name = "vaticana_inclinatum";
276     else if (prefix_set & DEMINUTUM)
277       if (primitive == first_primitive)
278         {
279           // initio debilis
280           glyph_name = "vaticana_reverse_plica";
281         }
282       else if (pitch > prev_pitch)
283         {
284           // epiphonus
285           if (!(prev_context_info & FLEXA_RIGHT))
286             {
287               prev_glyph_name = "vaticana_epiphonus";
288             }
289           glyph_name = "vaticana_plica";
290         }
291       else // (pitch <= prev_pitch)
292         {
293           // cephalicus
294           if (!(prev_context_info & FLEXA_RIGHT))
295             {
296               if (prev_primitive == first_primitive)
297                 {
298                   prev_glyph_name = "vaticana_cephalicus";
299                 }
300               else
301                 {
302                   prev_glyph_name = "vaticana_inner_cephalicus";
303                 }
304             }
305           glyph_name = "vaticana_reverse_plica";
306         }
307     else if (prefix_set & (CAVUM | LINEA))
308       if ((prefix_set & CAVUM) && (prefix_set & LINEA))
309         glyph_name = "vaticana_linea_punctum_cavum";
310       else if (prefix_set & CAVUM)
311         glyph_name = "vaticana_punctum_cavum";
312       else
313         glyph_name = "vaticana_linea_punctum";
314     else if (prefix_set & AUCTUM)
315       if (prefix_set & ASCENDENS)
316         glyph_name = "solesmes_auct_asc";
317       else
318         glyph_name = "solesmes_auct_desc";
319     else if ((prefix_set & PES_OR_FLEXA) &&
320              (context_info & PES_LOWER) &&
321              (context_info & FLEXA_RIGHT))
322       glyph_name = ""; // second head of flexa shape
323     else if (context_info & PES_UPPER)
324       if (pitch - prev_pitch > 1)
325         glyph_name = "vaticana_upes";
326       else
327         glyph_name = "vaticana_vupes";
328     else
329       glyph_name = "vaticana_punctum";
330
331     /*
332      * May need updating previous head, depending on the current head.
333      */
334     if (prefix_set & PES_OR_FLEXA)
335       if ((context_info & PES_LOWER) &&
336           (context_info & FLEXA_RIGHT)) // flexa shape
337         {
338           prev_glyph_name = "flexa";
339           prev_primitive->set_grob_property ("flexa-height",
340                                              gh_int2scm (pitch - prev_pitch));
341           prev_primitive->set_grob_property ("flexa-width",
342                                              gh_double2scm (flexa_width));
343           bool add_stem =
344             !(prev_context_info & PES_UPPER) &&
345             !(prev_context_info & FLEXA_RIGHT);
346           prev_primitive->set_grob_property ("add-stem",
347                                              gh_bool2scm (add_stem));
348         }
349       else if (context_info & PES_UPPER)
350         {
351           if (!String::compare (prev_glyph_name, "vaticana_punctum"))
352             prev_glyph_name = "vaticana_lpes";
353         }
354       else // flexa
355         {
356           if (!String::compare (prev_glyph_name, "vaticana_punctum"))
357             prev_glyph_name = "vaticana_rvirga";
358         }
359
360     /*
361      * In the backend, flexa shapes and joins need to know about
362      * thickness.  Hence, for simplicity, let's distribute the
363      * ligature grob's value for thickness to each ligature head (even
364      * if not all of them need to know).
365      */
366     primitive->set_grob_property ("thickness", gh_double2scm (join_thickness));
367
368     /*
369      * The head of the current iteration still may change during the
370      * next iteration due to the context sensitiveness of the
371      * transformation.  However, the head of the previous iteration is
372      * now fully attributed and can be prepared for the backend.
373      */
374
375     /*
376      * Finish head of previous iteration for backend.
377      */
378     prev_distance =
379       finish_primitive (first_primitive, prev_primitive, prev_prefix_set,
380                         prev_context_info, prev_glyph_name, prev_pitch_delta,
381                         flexa_width, join_thickness, prev_distance);
382
383     prev_primitive = primitive;
384     prev_prefix_set = prefix_set;
385     prev_context_info = context_info;
386     prev_pitch_delta = pitch - prev_pitch;
387     prev_pitch = pitch;
388     prev_glyph_name = glyph_name;
389   }
390
391   /*
392    * Finish head of last iteration for backend.
393    */
394   prev_distance =
395     finish_primitive (first_primitive, prev_primitive, prev_prefix_set,
396                       prev_context_info, prev_glyph_name, prev_pitch_delta,
397                       flexa_width, join_thickness, prev_distance);
398
399   /* TODO: make this cfg'able via SCM */
400   Real padding = join_thickness;
401
402   /* horizontal padding space after ligature */
403   prev_distance += padding;
404
405 #if 0 // experimental code to collapse spacing after ligature
406   /* TODO: set to max(old/new spacing-increment), since other
407      voices/staves also may want to set this property. */
408   Paper_column *paper_column = first_primitive->get_column();
409   paper_column->warning (_f ("Vaticana_ligature_engraver: "
410                              "setting `spacing-increment = %f': ptr=%ul",
411                              prev_distance, paper_column));
412   paper_column->
413     set_grob_property("forced-spacing", gh_double2scm (prev_distance));
414 #endif
415 }
416
417
418 ENTER_DESCRIPTION (Vaticana_ligature_engraver,
419 /* descr */       "Handles ligatures by glueing special ligature heads together.",
420 /* creats*/       "VaticanaLigature",
421 /* accepts */     "ligature-event abort-event",
422 /* acks  */      "note-head-interface rest-interface",
423 /* reads */       "",
424 /* write */       "");