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