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