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