]> git.donarmstrong.com Git - lilypond.git/blob - lily/coherent-ligature-engraver.cc
acbbbf8e183ad8e221d6fa25848b93c8fd6e042c
[lilypond.git] / lily / coherent-ligature-engraver.cc
1 /*
2   coherent-ligature-engraver.cc -- implement Coherent_ligature_engraver
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2003--2007 Juergen Reuter <reuter@ipd.uka.de>
7 */
8
9 #include "coherent-ligature-engraver.hh"
10
11 #include "warn.hh"
12 #include "axis-group-interface.hh"
13 #include "paper-column.hh"
14 #include "pitch.hh"
15 #include "pointer-group-interface.hh"
16 #include "spanner.hh"
17 #include "staff-symbol-referencer.hh"
18 #include "stream-event.hh"
19
20 /*
21  * This abstract class serves as common superclass for all ligature
22  * engravers thet produce a single connected graphical object of fixed
23  * width, consisting of noteheads and other primitives (see class
24  * Ligature_engraver for more information on the interaction between
25  * this class and its superclass).  In particular, it cares for the
26  * following tasks:
27  *
28  * - provide a function for putting all grobs of the ligature into a
29  * single paper column,
30  *
31  * - delegate actual creation of ligature to concrete subclass,
32  *
33  * - collect all accidentals that occur within the ligature and put
34  * them at the left side of the ligature (TODO; see function
35  * collect_accidentals ()),
36  *
37  * - collapse superflous space after each ligature (TODO).
38  *
39  * Concrete subclasses must implement function build_ligature (Spanner
40  * *, vector<Grob_info>).  This function is responsible for actually
41  * building the ligature by transforming the array of noteheads.
42  *
43  * Currently, there are two subclasses: Gregorian_ligature_engraver
44  * for Gregorian chant notation (also known as plain song or cantus
45  * planus) and Mensural_ligature_engraver for white mensural notation.
46  * Subclasses for other music notation styles such as modal notation
47  * or ars nova notation may eventually be added.
48  */
49
50 /*
51  * TODO: local accidentals: collect accidentals that occur within a
52  * ligature and put them before the ligature.  If an accidental
53  * changes within a ligature, print a warning (user error) and ignore
54  * any further accidental for that pitch within that ligature
55  * (actually, in such a case, the user should split the ligature into
56  * two separate ligatures).  Similarly, any object that, in ordinary
57  * notation, may be put to the left or to the right of a note-head,
58  * should be collected and put before or after the ligature.
59  *
60  * TODO: make spacing more robust: do not screw up spacing if user
61  * erroneously puts rest in ligature.
62  *
63  * TODO: for each ligature, add Rod that represents the total length
64  * of the ligature (to preemptively avoid collision with adjacent
65  * notes); or maybe just additionally create a
66  * mensural/vaticana/whatever-ligature grob (e.g. via
67  * Mensural_ligature::print (SCM)) that just consists of a
68  * bounding box around all primitives of the ligature.
69  *
70  * TODO: Maybe move functions fold_up_primitives () and
71  * join_primitives () from subclasses to here?  N.B. it is not
72  * appropriate to put these into Ligature_engraver, since, for
73  * example, Ligature_bracket_engraver does not share any of this code.
74  */
75
76
77 /*
78  * TODO: move this function to class Item?
79  */
80 void
81 Coherent_ligature_engraver::move_related_items_to_column
82 (Item *item, Paper_column *target_column, Real offset)
83 {
84   Paper_column *source_column = item->get_column ();
85   Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (item);
86   extract_item_set (source_column, "elements", elements);
87   for (vsize i = elements.size (); i--;)
88     {
89       Item *sibling = elements[i];
90       if (!sibling)
91         // should not occur, but who knows... -jr
92         continue;
93
94       if (Staff_symbol_referencer::get_staff_symbol (sibling) != staff_symbol)
95         // sibling is from a staff different than that of the item of
96         // interest
97         continue;
98
99       if (dynamic_cast<Item*> (sibling)->get_column () != source_column)
100         continue;
101
102 #if 0 /* experimental code to collapse spacing after ligature */
103       Grob *sibling_parent = sibling->get_parent (X_AXIS);
104       sibling_parent->warning (_f ("Coherent_ligature_engraver: "
105                                    "setting `spacing-increment="
106                                    "0.01': ptr=%ul", parent));
107       sibling_parent->set_property ("forced-spacing",
108                                     scm_from_double (0.01));
109 #endif
110
111       Axis_group_interface::add_element (target_column, sibling);
112       sibling->set_parent (target_column, X_AXIS);
113       sibling->translate_axis (offset, X_AXIS);
114     }
115 }
116
117 /*
118  * TODO: This function should collect all accidentals that occur
119  * within the ligature (by scanning through the primitives array) and
120  * place all of them at the left of the ligature.  If there is an
121  * alteration within the ligature (e.g. an "f" followed by a "fis"
122  * somewhere later in the ligature), issue a warning (and maybe create
123  * an additional natural symbol to explicitly make clear that there is
124  * an "f" first?).  The warning should suggest the user to break the
125  * ligature into two or more smaller ligatures such that no alteration
126  * occurs within the broken ligatures any more.
127  */
128 void
129 Coherent_ligature_engraver::collect_accidentals (Spanner *, vector<Grob_info>)
130 {
131   /* TODO */
132 }
133
134 void
135 compute_delta_pitches (vector<Grob_info> primitives)
136 {
137   int prev_pitch = 0;
138   int delta_pitch = 0;
139   Item *prev_primitive = 0, *primitive = 0;
140   for (vsize i = 0; i < primitives.size (); i++)
141     {
142       primitive = dynamic_cast<Item *> (primitives[i].grob ());
143       Stream_event *cause = primitives[i].event_cause ();
144       int pitch
145         = unsmob_pitch (cause->get_property ("pitch"))->steps ();
146       if (prev_primitive)
147         {
148           delta_pitch = pitch - prev_pitch;
149           prev_primitive->set_property ("delta-position",
150                                         scm_from_int (delta_pitch));
151         }
152       prev_pitch = pitch;
153       prev_primitive = primitive;
154     }
155   primitive->set_property ("delta-position", scm_from_int (0));
156 }
157
158 void
159 Coherent_ligature_engraver::typeset_ligature (Spanner *ligature,
160                                               vector<Grob_info> primitives)
161 {
162   // compute some commonly needed context info stored as grob
163   // properties
164   compute_delta_pitches (primitives);
165
166   // prepare ligature for typesetting
167   build_ligature (ligature, primitives);
168   collect_accidentals (ligature, primitives);
169 }
170
171 // no ADD_ACKNOWLEDGER / ADD_ACKNOWLEDGER / ADD_TRANSLATOR macro calls
172 // since this class is abstract