Fix InstrumentSwitch grob definition.
[lilypond.git] / lily / spacing-interface.cc
blobd494c318c5ef820ca7759264c30c9a0f880c3842
1 /*
2 spacing-interface.cc -- functionality that is shared between Note_spacing
3 and Staff_spacing
5 source file of the GNU LilyPond music typesetter
7 (c) 2007 Joe Neeman <joeneeman@gmail.com>
8 */
10 #include "spacing-interface.hh"
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"
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)
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.
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")));
43 Grob *system = me->get_system ();
44 Grob *left_col = dynamic_cast<Item*> (me)->get_column ();
46 Drul_array<Grob*> columns (left_col, right_col);
48 Direction d = LEFT;
51 for (vsize i = 0; i < items[d].size (); i++)
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;
58 if (g && Separation_item::has_interface (g) && g->get_column () == columns[d])
60 SCM sky_scm = g->get_property ("horizontal-skylines");
61 Skyline_pair *sky = Skyline_pair::unsmob (sky_scm);
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);
67 skylines[d].shift (-shift);
69 if (sky)
70 skylines[d].merge ((*sky)[-d]);
71 else
72 programming_error ("separation item has no skyline");
74 if (d == RIGHT && items[LEFT].size ())
75 skylines[d].merge (Separation_item::conditional_skyline (items[d][i], items[LEFT][0]));
77 skylines[d].shift (shift);
81 while (flip (&d) != LEFT);
83 return skylines;
86 Real
87 Spacing_interface::minimum_distance (Grob *me, Grob *right)
89 Drul_array<Skyline> skylines = Spacing_interface::skylines (me, right);
91 return max (0.0, skylines[LEFT].distance (skylines[RIGHT]));
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.
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.
110 Item *
111 Spacing_interface::right_column (Grob *me)
113 if (!me->is_live ())
114 return 0;
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++)
122 Item *ri = a->item (i);
123 Item *col = ri->get_column ();
125 int rank = Paper_column::get_rank (col);
127 if (rank < min_rank)
129 min_rank = rank;
130 if (mincol)
131 prune = true;
133 mincol = col;
135 else if (rank > min_rank)
136 prune = true;
139 if (prune && a)
141 vector<Grob*> &right = a->array_reference ();
142 for (vsize i = right.size (); i--;)
144 if (dynamic_cast<Item *> (right[i])->get_column () != mincol)
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 ());
152 right.erase (right.begin () + i);
157 return mincol;
160 Item *
161 Spacing_interface::left_column (Grob *me)
163 if (!me->is_live ())
164 return 0;
166 return dynamic_cast<Item *> (me)->get_column ();
169 static vector<Item*>
170 get_note_columns (vector<Grob*> const &elts)
172 vector<Item*> ret;
174 for (vsize i = 0; i < elts.size (); i++)
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]))
180 extract_grob_set (elts[i], "elements", more_elts);
181 vector<Item*> ncs = get_note_columns (more_elts);
183 ret.insert (ret.end (), ncs.begin (), ncs.end ());
187 return ret;
190 vector<Item*>
191 Spacing_interface::right_note_columns (Grob *me)
193 extract_grob_set (me, "right-items", elts);
194 return get_note_columns (elts);
197 vector<Item*>
198 Spacing_interface::left_note_columns (Grob *me)
200 extract_grob_set (me, "left-items", elts);
201 return get_note_columns (elts);
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
208 Grob *
209 Spacing_interface::extremal_break_aligned_grob (Grob *me,
210 Direction d,
211 Direction break_dir,
212 Interval *last_ext)
214 Grob *col = 0;
215 last_ext->set_empty ();
216 Grob *last_grob = 0;
218 extract_grob_set (me, d == LEFT ? "left-break-aligned" : "right-break-aligned", elts);
220 for (vsize i = elts.size (); i--;)
222 Item *break_item = dynamic_cast<Item*> (elts[i]);
224 if (break_item->break_status_dir () != break_dir)
225 break_item = break_item->find_prebroken_piece (break_dir);
227 if (!break_item || !scm_is_pair (break_item->get_property ("space-alist")))
228 continue;
230 if (!col)
231 col = dynamic_cast<Item*> (elts[0])->get_column ()->find_prebroken_piece (break_dir);
233 Interval ext = break_item->extent (col, X_AXIS);
235 if (ext.is_empty ())
236 continue;
238 if (!last_grob
239 || (last_grob && d * (ext[-d]- (*last_ext)[-d]) < 0))
241 *last_ext = ext;
242 last_grob = break_item;
246 return last_grob;
250 ADD_INTERFACE (Spacing_interface,
251 "This object calculates the desired and minimum distances"
252 " between two columns.",
254 /* properties */
255 "left-items "
256 "right-items "