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