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