]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-interface.cc
Run `make grand-replace'.
[lilypond.git] / lily / spacing-interface.cc
1 /*
2   spacing-interface.cc -- functionality that is shared between Note_spacing
3   and Staff_spacing
4
5   source file of the GNU LilyPond music typesetter
6
7   (c) 2007--2008 Joe Neeman <joeneeman@gmail.com>
8 */
9
10 #include "spacing-interface.hh"
11
12 #include "grob.hh"
13 #include "grob-array.hh"
14 #include "item.hh"
15 #include "note-column.hh"
16 #include "pointer-group-interface.hh"
17 #include "paper-column.hh"
18 #include "separation-item.hh"
19 #include "skyline.hh"
20 #include "skyline-pair.hh"
21 #include "system.hh"
22
23 /* return the right-pointing skyline of the left-items and the left-pointing
24    skyline of the right-items (with the skyline of the left-items in
25    ret[LEFT]) */
26 Drul_array<Skyline>
27 Spacing_interface::skylines (Grob *me, Grob *right_col)
28 {
29   /* the logic here is a little convoluted.
30      A {Staff,Note}_spacing doesn't copy left-items when it clones,
31      so in order to find the separation items, we need to use the original
32      spacing grob. But once we find the separation items, we need to get back
33      the broken piece.
34   */
35
36   Grob *orig = me->original () ? me->original () : me;
37   Drul_array<Direction> break_dirs (dynamic_cast<Item*> (me)->break_status_dir (),
38                                     dynamic_cast<Item*> (right_col)->break_status_dir ());
39   Drul_array<Skyline> skylines = Drul_array<Skyline> (Skyline (RIGHT), Skyline (LEFT));
40   Drul_array<vector<Grob*> > items (ly_scm2link_array (orig->get_object ("left-items")),
41                                     ly_scm2link_array (orig->get_object ("right-items")));
42
43   Grob *system = me->get_system ();
44   Grob *left_col = dynamic_cast<Item*> (me)->get_column ();
45
46   Drul_array<Grob*> columns (left_col, right_col);
47
48   Direction d = LEFT;
49   do
50     {
51       for (vsize i = 0; i < items[d].size (); i++)
52         {
53           Item *g = dynamic_cast<Item*> (items[d][i]);
54           if (g)
55             if (Item *piece = g->find_prebroken_piece (break_dirs[d]))
56               g = piece;
57
58           if (g && Separation_item::has_interface (g) && g->get_column () == columns[d])
59             {
60               SCM sky_scm = g->get_property ("horizontal-skylines");
61               Skyline_pair *sky = Skyline_pair::unsmob (sky_scm);
62
63               extract_grob_set (g, "elements", elts);
64               Grob *ycommon = common_refpoint_of_array (elts, g, Y_AXIS);
65               Real shift = ycommon->pure_relative_y_coordinate (system, 0, INT_MAX);
66
67               skylines[d].shift (-shift);
68
69               if (sky)
70                 skylines[d].merge ((*sky)[-d]);
71               else
72                 programming_error ("separation item has no skyline");
73
74               if (d == RIGHT && items[LEFT].size ())
75                 skylines[d].merge (Separation_item::conditional_skyline (items[d][i], items[LEFT][0]));
76
77               skylines[d].shift (shift);
78             }
79         }
80     }
81   while (flip (&d) != LEFT);
82
83   return skylines;
84 }
85
86 Real
87 Spacing_interface::minimum_distance (Grob *me, Grob *right)
88 {
89   Drul_array<Skyline> skylines = Spacing_interface::skylines (me, right);
90
91   return max (0.0, skylines[LEFT].distance (skylines[RIGHT]));
92 }
93
94 /*
95   Compute the column of the right-items.  This is a big function,
96   since RIGHT-ITEMS may span more columns (eg. if a clef is inserted,
97   this will add a new column to RIGHT-ITEMS. Here we look at the
98   columns, and return the left-most. If there are multiple columns, we
99   prune RIGHT-ITEMS.
100
101   If we end up pruning, we add a left-neighbor to every column that
102   gets pruned. This ensures that loose columns in cross-staff music
103   do indeed get marked as loose. The problem situation is when a voice
104   passes from staff 1 to staff 2 and a clef appears later on in staff 1.
105   Then the NoteSpacing attached to the last note in staff 1 has two
106   right-items: one pointing to the next note in staff 2 and one pointing
107   to the clef. We will prune the clef right-item here and, unless we add
108   a left-neighbor to the clef, it won't get marked as loose.
109 */
110 Item *
111 Spacing_interface::right_column (Grob *me)
112 {
113   if (!me->is_live ())
114     return 0;
115
116   Grob_array *a = unsmob_grob_array (me->get_object ("right-items"));
117   Item *mincol = 0;
118   int min_rank = INT_MAX;
119   bool prune = false;
120   for (vsize i = 0; a && i < a->size (); i++)
121     {
122       Item *ri = a->item (i);
123       Item *col = ri->get_column ();
124
125       int rank = Paper_column::get_rank (col);
126
127       if (rank < min_rank)
128         {
129           min_rank = rank;
130           if (mincol)
131             prune = true;
132
133           mincol = col;
134         }
135       else if (rank > min_rank)
136         prune = true;
137     }
138
139   if (prune && a)
140     {
141       vector<Grob*> &right = a->array_reference ();
142       for (vsize i = right.size (); i--;)
143         {
144           if (dynamic_cast<Item *> (right[i])->get_column () != mincol)
145             {
146               extract_grob_set (right[i], "left-neighbors", lns);
147               if (lns.empty ())
148                 Pointer_group_interface::add_grob (right[i],
149                                                    ly_symbol2scm ("left-neighbors"),
150                                                    dynamic_cast<Item*> (me)->get_column ());
151
152               right.erase (right.begin () + i);
153             }
154         }
155     }
156
157   return mincol;
158 }
159
160 Item *
161 Spacing_interface::left_column (Grob *me)
162 {
163   if (!me->is_live ())
164     return 0;
165
166   return dynamic_cast<Item *> (me)->get_column ();
167 }
168
169 static vector<Item*>
170 get_note_columns (vector<Grob*> const &elts)
171 {
172   vector<Item*> ret;
173
174   for (vsize i = 0; i < elts.size (); i++)
175     {
176       if (Note_column::has_interface (elts[i]))
177         ret.push_back (dynamic_cast<Item*> (elts[i]));
178       else if (Separation_item::has_interface (elts[i]))
179         {
180           extract_grob_set (elts[i], "elements", more_elts);
181           vector<Item*> ncs = get_note_columns (more_elts);
182
183           ret.insert (ret.end (), ncs.begin (), ncs.end ());
184         }
185     }
186
187   return ret;
188 }
189
190 vector<Item*>
191 Spacing_interface::right_note_columns (Grob *me)
192 {
193   extract_grob_set (me, "right-items", elts);
194   return get_note_columns (elts);
195 }
196
197 vector<Item*>
198 Spacing_interface::left_note_columns (Grob *me)
199 {
200   extract_grob_set (me, "left-items", elts);
201   return get_note_columns (elts);
202 }
203
204 /*
205   Try to find the break-aligned symbol that belongs on the D-side
206   of ME, sticking out in direction -D. The x size is put in LAST_EXT
207 */
208 Grob *
209 Spacing_interface::extremal_break_aligned_grob (Grob *me,
210                                                 Direction d,
211                                                 Direction break_dir,
212                                                 Interval *last_ext)
213 {
214   Grob *col = 0;
215   last_ext->set_empty ();
216   Grob *last_grob = 0;
217
218   extract_grob_set (me, d == LEFT ? "left-break-aligned" : "right-break-aligned", elts);
219
220   for (vsize i = elts.size (); i--;)
221     {
222       Item *break_item = dynamic_cast<Item*> (elts[i]);
223
224       if (break_item->break_status_dir () != break_dir)
225         break_item = break_item->find_prebroken_piece (break_dir);
226
227       if (!break_item || !scm_is_pair (break_item->get_property ("space-alist")))
228         continue;
229
230       if (!col)
231         col = dynamic_cast<Item*> (elts[0])->get_column ()->find_prebroken_piece (break_dir);
232
233       Interval ext = break_item->extent (col, X_AXIS);
234
235       if (ext.is_empty ())
236         continue;
237
238       if (!last_grob
239           || (last_grob && d * (ext[-d]- (*last_ext)[-d]) < 0))
240         {
241           *last_ext = ext;
242           last_grob = break_item;
243         }
244     }
245
246   return last_grob;
247 }
248
249
250 ADD_INTERFACE (Spacing_interface,
251                "This object calculates the desired and minimum distances"
252                " between two columns.",
253
254                /* properties */
255                "left-items "
256                "right-items "
257                );