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