]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-interface.cc
Merge branch 'master' of ssh+git://hanwen@git.sv.gnu.org/srv/git/lilypond
[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 Item *
101 Spacing_interface::right_column (Grob *me)
102 {
103   if (!me->is_live ())
104     return 0;
105
106   Grob_array *a = unsmob_grob_array (me->get_object ("right-items"));
107   Item *mincol = 0;
108   int min_rank = INT_MAX;
109   bool prune = false;
110   for (vsize i = 0; a && i < a->size (); i++)
111     {
112       Item *ri = a->item (i);
113       Item *col = ri->get_column ();
114
115       int rank = Paper_column::get_rank (col);
116
117       if (rank < min_rank)
118         {
119           min_rank = rank;
120           if (mincol)
121             prune = true;
122
123           mincol = col;
124         }
125     }
126
127   if (prune && a)
128     {
129       vector<Grob*> &right = a->array_reference ();
130       for (vsize i = right.size (); i--;)
131         {
132           if (dynamic_cast<Item *> (right[i])->get_column () != mincol)
133             right.erase (right.begin () + i);
134         }
135     }
136
137   return mincol;
138 }
139
140 Item *
141 Spacing_interface::left_column (Grob *me)
142 {
143   if (!me->is_live ())
144     return 0;
145
146   return dynamic_cast<Item *> (me)->get_column ();
147 }
148
149 static vector<Item*>
150 get_note_columns (vector<Grob*> const &elts)
151 {
152   vector<Item*> ret;
153
154   for (vsize i = 0; i < elts.size (); i++)
155     {
156       if (Note_column::has_interface (elts[i]))
157         ret.push_back (dynamic_cast<Item*> (elts[i]));
158       else if (Separation_item::has_interface (elts[i]))
159         {
160           extract_grob_set (elts[i], "elements", more_elts);
161           vector<Item*> ncs = get_note_columns (more_elts);
162
163           ret.insert (ret.end (), ncs.begin (), ncs.end ());
164         }
165     }
166
167   return ret;
168 }
169
170 vector<Item*>
171 Spacing_interface::right_note_columns (Grob *me)
172 {
173   extract_grob_set (me, "right-items", elts);
174   return get_note_columns (elts);
175 }
176
177 vector<Item*>
178 Spacing_interface::left_note_columns (Grob *me)
179 {
180   extract_grob_set (me, "left-items", elts);
181   return get_note_columns (elts);
182 }
183
184 /*
185   Try to find the break-aligned symbol that belongs on the D-side
186   of ME, sticking out in direction -D. The x size is put in LAST_EXT
187 */
188 Grob *
189 Spacing_interface::extremal_break_aligned_grob (Grob *me,
190                                                 Direction d,
191                                                 Direction break_dir,
192                                                 Interval *last_ext)
193 {
194   Grob *col = 0;
195   last_ext->set_empty ();
196   Grob *last_grob = 0;
197
198   extract_grob_set (me, d == LEFT ? "left-break-aligned" : "right-break-aligned", elts);
199
200   for (vsize i = elts.size (); i--;)
201     {
202       Item *break_item = dynamic_cast<Item*> (elts[i]);
203
204       if (break_item->break_status_dir () != break_dir)
205         break_item = break_item->find_prebroken_piece (break_dir);
206
207       if (!break_item || !scm_is_pair (break_item->get_property ("space-alist")))
208         continue;
209
210       if (!col)
211         col = dynamic_cast<Item*> (elts[0])->get_column ()->find_prebroken_piece (break_dir);
212
213       Interval ext = break_item->extent (col, X_AXIS);
214
215       if (ext.is_empty ())
216         continue;
217
218       if (!last_grob
219           || (last_grob && d * (ext[-d]- (*last_ext)[-d]) < 0))
220         {
221           *last_ext = ext;
222           last_grob = break_item;
223         }
224     }
225
226   return last_grob;
227 }
228
229
230 ADD_INTERFACE (Spacing_interface,
231                "This object calculates the desired and minimum distances between two columns.",
232
233                "left-items "
234                "right-items "
235                );