]> git.donarmstrong.com Git - lilypond.git/blob - lily/vaticana-ligature-engraver.cc
* lily/coherent-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     {
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
155       /*
156        * Horizontally line-up this head to form a ligature.
157        */
158       get_set_column (primitive, first_primitive->get_column ());
159       primitive->translate_axis (next_distance, X_AXIS);
160       next_distance += head_width;
161
162     }
163
164   return next_distance;
165 }
166
167 void
168 Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
169                                              Array<Grob_info> primitives)
170 {
171   Real flexa_width;
172   SCM flexa_width_scm = ligature->get_grob_property ("flexa-width");
173   if (flexa_width_scm != SCM_EOL)
174     {
175       flexa_width = gh_scm2double (flexa_width_scm);
176     }
177   else
178     {
179       programming_error ("Vaticana_ligature_engraver:"
180                          "flexa-width undefined; assuming 2.0 staff space");
181       flexa_width =
182         2.0 * Staff_symbol_referencer::staff_space (ligature);
183     }
184
185   Real join_thickness;
186   SCM join_thickness_scm = ligature->get_grob_property ("thickness");
187   if (join_thickness_scm != SCM_EOL)
188     {
189       join_thickness = gh_scm2double (join_thickness_scm);
190     }
191   else
192     {
193       programming_error ("Vaticana_ligature_engraver:"
194                          "thickness undefined; assuming 1.4 linethickness");
195       join_thickness = 1.4;
196     }
197   join_thickness *= ligature->get_paper ()->get_var ("linethickness");
198
199   Item *first_primitive = 0;
200   Item *prev_primitive = 0;
201   int prev_context_info = 0;
202   int prev_pitch = 0;
203   int prev_pitch_delta = 0;
204   String prev_glyph_name = "";
205   Real prev_distance = 0.0;
206   for (int i = 0; i < primitives.size(); i++) {
207     Item *primitive = dynamic_cast<Item*> (primitives[i].grob_);
208     Music *music_cause = primitives[i].music_cause ();
209     int context_info = gh_scm2int (primitive->get_grob_property ("context-info"));
210     int pitch = unsmob_pitch (music_cause->get_mus_property ("pitch"))->steps ();
211     String glyph_name;
212     if (!first_primitive)
213       first_primitive = primitive;
214     int prefix_set = gh_scm2int (primitive->get_grob_property ("prefix-set"));
215
216     /*
217      * Now determine which head to typeset (this is context sensitive
218      * information, since it depends on neighbouring heads; therefore,
219      * this decision must be made here in the engraver rather than in
220      * the backend).
221      */
222     if (prefix_set & VIRGA)
223       glyph_name = "vaticana_virga";
224     else if (prefix_set & QUILISMA)
225       glyph_name = "vaticana_quilisma";
226     else if (prefix_set & ORISCUS)
227       glyph_name = "solesmes_oriscus";
228     else if (prefix_set & STROPHA)
229       if (prefix_set & AUCTUM)
230         glyph_name = "solesmes_stropha_aucta";
231       else glyph_name = "solesmes_stropha";
232     else if (prefix_set & SEMIVOCALIS)
233       if (pitch > prev_pitch)
234         glyph_name = "vaticana_epiphonus";
235       else glyph_name = "vaticana_cephalicus";
236     else if (prefix_set & INCLINATUM)
237       if (prefix_set & AUCTUM)
238         glyph_name = "solesmes_incl_auctum";
239       else if (prefix_set & DEMINUTUM)
240         glyph_name = "solesmes_incl_parvum";
241       else
242         glyph_name = "vaticana_inclinatum";
243     else if (prefix_set & (CAVUM | LINEA))
244       if ((prefix_set & CAVUM) && (prefix_set & LINEA))
245         glyph_name = "vaticana_linea_punctum_cavum";
246       else if (prefix_set & CAVUM)
247         glyph_name = "vaticana_punctum_cavum";
248       else
249         glyph_name = "vaticana_linea_punctum";
250     else if (prefix_set & AUCTUM)
251       if (prefix_set & ASCENDENS)
252         glyph_name = "solesmes_auct_asc";
253       else
254         glyph_name = "solesmes_auct_desc";
255     else if (prefix_set & DEMINUTUM)
256       glyph_name = "vaticana_plica";
257     else if ((prefix_set & PES_OR_FLEXA) &&
258              (context_info & PES_LOWER) &&
259              (context_info & FLEXA_RIGHT))
260       glyph_name = ""; // second head of flexa shape
261     else if (context_info & PES_UPPER)
262       if (pitch - prev_pitch > 1)
263         glyph_name = "vaticana_upes";
264       else
265         glyph_name = "vaticana_vupes";
266     else
267       glyph_name = "vaticana_punctum";
268
269     /*
270      * May need updating previous head, depending on the current head.
271      */
272     if (prefix_set & PES_OR_FLEXA)
273       if ((context_info & PES_LOWER) &&
274           (context_info & FLEXA_RIGHT)) // flexa shape
275         {
276           prev_glyph_name = "flexa";
277           prev_primitive->set_grob_property ("flexa-height",
278                                              gh_int2scm (pitch - prev_pitch));
279           prev_primitive->set_grob_property ("flexa-width",
280                                              gh_double2scm (flexa_width));
281           bool add_stem =
282             !(prev_context_info & PES_UPPER) &&
283             !(prev_context_info & FLEXA_RIGHT);
284           prev_primitive->set_grob_property ("add-stem",
285                                              gh_bool2scm (add_stem));
286         }
287       else if (context_info & PES_UPPER)
288         {
289           if (!String::compare (prev_glyph_name, "vaticana_punctum"))
290             prev_glyph_name = "vaticana_lpes";
291         }
292       else // flexa
293         {
294           if (!String::compare (prev_glyph_name, "vaticana_punctum"))
295             prev_glyph_name = "vaticana_rvirga";
296         }
297
298     /*
299      * In the backend, flexa shapes and joins need to know about
300      * thickness.  Hence, for simplicity, let's distribute the
301      * ligature grob's value for thickness to each ligature head (even
302      * if not all of them need to know).
303      */
304     primitive->set_grob_property ("thickness", gh_double2scm (join_thickness));
305
306     /*
307      * The head of the current iteration still may change during the
308      * next iteration due to the context sensitiveness of the
309      * transformation.  However, the head of the previous iteration is
310      * now fully attributed and can be prepared for the backend.
311      */
312
313     /*
314      * Finish head of previous iteration for backend.
315      */
316     prev_distance =
317       finish_primitive (first_primitive, prev_primitive,
318                         prev_context_info, prev_glyph_name, prev_pitch_delta,
319                         flexa_width, join_thickness, prev_distance);
320
321     prev_primitive = primitive;
322     prev_context_info = context_info;
323     prev_pitch_delta = pitch - prev_pitch;
324     prev_pitch = pitch;
325     prev_glyph_name = glyph_name;
326   }
327
328   /*
329    * Finish head of last iteration for backend.
330    */
331   prev_distance =
332     finish_primitive (first_primitive, prev_primitive,
333                       prev_context_info, prev_glyph_name, prev_pitch_delta,
334                       flexa_width, join_thickness, prev_distance);
335
336   /* TODO: make this cfg'able via SCM */
337   Real padding = join_thickness;
338
339   /* horizontal padding space after ligature */
340   prev_distance += padding;
341
342 #if 0 // experimental code to collapse spacing after ligature
343   /* TODO: set to max(old/new spacing-increment), since other
344      voices/staves also may want to set this property. */
345   Paper_column *paper_column = first_primitive->get_column();
346   paper_column->warning (_f ("Vaticana_ligature_engraver: "
347                              "setting `spacing-increment = %f': ptr=%ul",
348                              prev_distance, paper_column));
349   paper_column->
350     set_grob_property("forced-spacing", gh_double2scm (prev_distance));
351 #endif
352 }
353
354
355 ENTER_DESCRIPTION (Vaticana_ligature_engraver,
356 /* descr */       "Handles ligatures by glueing special ligature heads together.",
357 /* creats*/       "VaticanaLigature",
358 /* accepts */     "ligature-event abort-event",
359 /* acks  */      "ligature-head-interface note-head-interface rest-interface",
360 /* reads */       "",
361 /* write */       "");