2 vaticana-ligature-engraver.cc -- implement Vaticana_ligature_engraver
4 source file of the GNU LilyPond music typesetter
6 (c) 2003 Juergen Reuter <reuter@ipd.uka.de>
9 #include "gregorian-ligature-engraver.hh"
10 #include "gregorian-ligature.hh"
11 #include "vaticana-ligature.hh"
14 #include "staff-symbol-referencer.hh"
15 #include "font-interface.hh"
17 #include "paper-def.hh"
18 #include "paper-column.hh"
21 * This class implements the notation specific aspects of Vaticana
22 * style ligatures for Gregorian chant notation.
24 class Vaticana_ligature_engraver : public Gregorian_ligature_engraver
28 Real finish_primitive (Item *first_primitive,
38 TRANSLATOR_DECLARATIONS(Vaticana_ligature_engraver);
41 virtual Spanner *create_ligature_spanner ();
42 virtual void transform_heads (Spanner *ligature,
43 Array<Grob_info> primitives);
46 Vaticana_ligature_engraver::Vaticana_ligature_engraver ()
51 Vaticana_ligature_engraver::create_ligature_spanner ()
53 return new Spanner (get_property ("VaticanaLigature"));
57 Vaticana_ligature_engraver::finish_primitive (Item *first_primitive,
66 Real next_distance = distance;
69 // determine width of previous head and x-offset
73 is_stacked = context_info & PES_UPPER;
74 if (context_info & FLEXA_LEFT)
76 if (!String::compare (glyph_name, "vaticana_cephalicus") &&
77 !(context_info & PES_LOWER))
79 if (context_info & AUCTUM)
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.
91 x_offset = join_thickness -
92 Font_interface::get_default_font (primitive)->
93 find_by_name ("noteheads-" + glyph_name).extent (X_AXIS).length ();
95 else if (!String::compare (glyph_name, "flexa") ||
96 !String::compare (glyph_name, ""))
99 * This head represents either half of a flexa shape.
100 * Hence, it is assigned half the width of this shape.
102 head_width = 0.5 * flexa_width;
105 else // retrieve width from corresponding font
108 Font_interface::get_default_font (primitive)->
109 find_by_name ("noteheads-" + glyph_name).extent (X_AXIS).length ();
114 * Save the head's final shape and x-offset.
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));
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.
126 if ((context_info & PES_UPPER) ||
127 ((context_info & FLEXA_RIGHT) &&
128 !(context_info & PES_LOWER)))
130 primitive->set_grob_property ("join-left",
131 gh_int2scm (pitch_delta));
134 * Create a small overlap of adjacent heads so that the join
135 * can be drawn perfectly between them.
137 next_distance -= join_thickness;
139 else if (!String::compare (glyph_name, ""))
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.
147 else if (context_info & AFTER_VIRGA)
150 * Make a small space after a virga.
152 next_distance += 2 * join_thickness;
156 * Horizontally line-up this head to form a ligature.
158 get_set_column (primitive, first_primitive->get_column ());
159 primitive->translate_axis (next_distance, X_AXIS);
160 next_distance += head_width;
164 return next_distance;
168 Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
169 Array<Grob_info> primitives)
172 SCM flexa_width_scm = ligature->get_grob_property ("flexa-width");
173 if (flexa_width_scm != SCM_EOL)
175 flexa_width = gh_scm2double (flexa_width_scm);
179 programming_error ("Vaticana_ligature_engraver:"
180 "flexa-width undefined; assuming 2.0 staff space");
182 2.0 * Staff_symbol_referencer::staff_space (ligature);
186 SCM join_thickness_scm = ligature->get_grob_property ("thickness");
187 if (join_thickness_scm != SCM_EOL)
189 join_thickness = gh_scm2double (join_thickness_scm);
193 programming_error ("Vaticana_ligature_engraver:"
194 "thickness undefined; assuming 1.4 linethickness");
195 join_thickness = 1.4;
197 join_thickness *= ligature->get_paper ()->get_var ("linethickness");
199 Item *first_primitive = 0;
200 Item *prev_primitive = 0;
201 int prev_context_info = 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 ();
212 if (!first_primitive)
213 first_primitive = primitive;
214 int prefix_set = gh_scm2int (primitive->get_grob_property ("prefix-set"));
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
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";
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";
249 glyph_name = "vaticana_linea_punctum";
250 else if (prefix_set & AUCTUM)
251 if (prefix_set & ASCENDENS)
252 glyph_name = "solesmes_auct_asc";
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";
265 glyph_name = "vaticana_vupes";
267 glyph_name = "vaticana_punctum";
270 * May need updating previous head, depending on the current head.
272 if (prefix_set & PES_OR_FLEXA)
273 if ((context_info & PES_LOWER) &&
274 (context_info & FLEXA_RIGHT)) // flexa shape
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));
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));
287 else if (context_info & PES_UPPER)
289 if (!String::compare (prev_glyph_name, "vaticana_punctum"))
290 prev_glyph_name = "vaticana_lpes";
294 if (!String::compare (prev_glyph_name, "vaticana_punctum"))
295 prev_glyph_name = "vaticana_rvirga";
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).
304 primitive->set_grob_property ("thickness", gh_double2scm (join_thickness));
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.
314 * Finish head of previous iteration for backend.
317 finish_primitive (first_primitive, prev_primitive,
318 prev_context_info, prev_glyph_name, prev_pitch_delta,
319 flexa_width, join_thickness, prev_distance);
321 prev_primitive = primitive;
322 prev_context_info = context_info;
323 prev_pitch_delta = pitch - prev_pitch;
325 prev_glyph_name = glyph_name;
329 * Finish head of last iteration for backend.
332 finish_primitive (first_primitive, prev_primitive,
333 prev_context_info, prev_glyph_name, prev_pitch_delta,
334 flexa_width, join_thickness, prev_distance);
336 /* TODO: make this cfg'able via SCM */
337 Real padding = join_thickness;
339 /* horizontal padding space after ligature */
340 prev_distance += padding;
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));
350 set_grob_property("forced-spacing", gh_double2scm (prev_distance));
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",