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