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;
68 { // determine width of previous head and x-offset
73 // upper head of pes is stacked upon lower head of pes ...
74 is_stacked = context_info & PES_UPPER;
76 // ... unless this note starts a flexa
77 if (context_info & FLEXA_LEFT)
80 // auctum head is never stacked upon preceding note
81 if (context_info & AUCTUM)
84 // semivocalis head of epiphonus or cephalicus is stacked upon
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
92 is_stacked = true; // semivocalis head of cephalicus
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.
104 x_offset = join_thickness -
105 Font_interface::get_default_font (primitive)->
106 find_by_name ("noteheads-" + glyph_name).extent (X_AXIS).length ();
108 else if (!String::compare (glyph_name, "flexa") ||
109 !String::compare (glyph_name, ""))
112 * This head represents either half of a flexa shape.
113 * Hence, it is assigned half the width of this shape.
115 head_width = 0.5 * flexa_width;
118 else // retrieve width from corresponding font
121 Font_interface::get_default_font (primitive)->
122 find_by_name ("noteheads-" + glyph_name).extent (X_AXIS).length ();
127 * Save the head's final shape and x-offset.
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));
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.
139 if ((context_info & PES_UPPER) ||
140 ((context_info & FLEXA_RIGHT) &&
141 !(context_info & PES_LOWER)))
143 primitive->set_grob_property ("join-left",
144 gh_int2scm (pitch_delta));
147 * Create a small overlap of adjacent heads so that the join
148 * can be drawn perfectly between them.
150 next_distance -= join_thickness;
152 else if (!String::compare (glyph_name, ""))
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.
160 else if (context_info & AFTER_VIRGA)
163 * Make a small space after a virga.
165 next_distance += 2 * join_thickness;
167 else if (pitch_delta == 0)
170 * Make a small space between two adjacent notes with the
173 next_distance += 2 * join_thickness;
177 * Horizontally line-up this head to form a ligature.
179 get_set_column (primitive, first_primitive->get_column ());
180 primitive->translate_axis (next_distance, X_AXIS);
181 next_distance += head_width;
185 return next_distance;
189 Vaticana_ligature_engraver::transform_heads (Spanner *ligature,
190 Array<Grob_info> primitives)
193 SCM flexa_width_scm = ligature->get_grob_property ("flexa-width");
194 if (flexa_width_scm != SCM_EOL)
196 flexa_width = gh_scm2double (flexa_width_scm);
200 programming_error ("Vaticana_ligature_engraver:"
201 "flexa-width undefined; assuming 2.0 staff space");
203 2.0 * Staff_symbol_referencer::staff_space (ligature);
207 SCM join_thickness_scm = ligature->get_grob_property ("thickness");
208 if (join_thickness_scm != SCM_EOL)
210 join_thickness = gh_scm2double (join_thickness_scm);
214 programming_error ("Vaticana_ligature_engraver:"
215 "thickness undefined; assuming 1.4 linethickness");
216 join_thickness = 1.4;
218 join_thickness *= ligature->get_paper ()->get_var ("linethickness");
220 Item *first_primitive = 0;
221 Item *prev_primitive = 0;
222 int prev_context_info = 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 ();
233 if (!first_primitive)
234 first_primitive = primitive;
235 int prefix_set = gh_scm2int (primitive->get_grob_property ("prefix-set"));
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
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";
259 glyph_name = "vaticana_inclinatum";
260 else if (prefix_set & DEMINUTUM)
261 if (pitch > prev_pitch)
263 prev_glyph_name = "vaticana_epiphonus";
264 glyph_name = "vaticana_plica";
268 prev_glyph_name = "vaticana_cephalicus";
269 glyph_name = "vaticana_reverse_plica";
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";
277 glyph_name = "vaticana_linea_punctum";
278 else if (prefix_set & AUCTUM)
279 if (prefix_set & ASCENDENS)
280 glyph_name = "solesmes_auct_asc";
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";
291 glyph_name = "vaticana_vupes";
293 glyph_name = "vaticana_punctum";
296 * May need updating previous head, depending on the current head.
298 if (prefix_set & PES_OR_FLEXA)
299 if ((context_info & PES_LOWER) &&
300 (context_info & FLEXA_RIGHT)) // flexa shape
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));
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));
313 else if (context_info & PES_UPPER)
315 if (!String::compare (prev_glyph_name, "vaticana_punctum"))
316 prev_glyph_name = "vaticana_lpes";
320 if (!String::compare (prev_glyph_name, "vaticana_punctum"))
321 prev_glyph_name = "vaticana_rvirga";
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).
330 primitive->set_grob_property ("thickness", gh_double2scm (join_thickness));
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.
340 * Finish head of previous iteration for backend.
343 finish_primitive (first_primitive, prev_primitive,
344 prev_context_info, prev_glyph_name, prev_pitch_delta,
345 flexa_width, join_thickness, prev_distance);
347 prev_primitive = primitive;
348 prev_context_info = context_info;
349 prev_pitch_delta = pitch - prev_pitch;
351 prev_glyph_name = glyph_name;
355 * Finish head of last iteration for backend.
358 finish_primitive (first_primitive, prev_primitive,
359 prev_context_info, prev_glyph_name, prev_pitch_delta,
360 flexa_width, join_thickness, prev_distance);
362 /* TODO: make this cfg'able via SCM */
363 Real padding = join_thickness;
365 /* horizontal padding space after ligature */
366 prev_distance += padding;
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));
376 set_grob_property("forced-spacing", gh_double2scm (prev_distance));
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",