]> git.donarmstrong.com Git - lilypond.git/blob - lily/spacing-interface.cc
bcad9d741caf6316c873d661e325c190e7937283
[lilypond.git] / lily / spacing-interface.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2007--2015 Joe Neeman <joeneeman@gmail.com>
5
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "spacing-interface.hh"
21
22 #include "grob.hh"
23 #include "grob-array.hh"
24 #include "item.hh"
25 #include "note-column.hh"
26 #include "pointer-group-interface.hh"
27 #include "paper-column.hh"
28 #include "separation-item.hh"
29 #include "skyline.hh"
30 #include "skyline-pair.hh"
31 #include "system.hh"
32
33 using std::vector;
34
35 /* return the right-pointing skyline of the left-items and the left-pointing
36    skyline of the right-items (with the skyline of the left-items in
37    ret[LEFT]) */
38 Drul_array<Skyline>
39 Spacing_interface::skylines (Grob *me, Grob *right_col)
40 {
41   /* the logic here is a little convoluted.
42      A {Staff,Note}_spacing doesn't copy left-items when it clones,
43      so in order to find the separation items, we need to use the original
44      spacing grob. But once we find the separation items, we need to get back
45      the broken piece.
46   */
47
48   Grob *orig = me->original () ? me->original () : me;
49   Drul_array<Direction> break_dirs (dynamic_cast<Item *> (me)->break_status_dir (),
50                                     dynamic_cast<Item *> (right_col)->break_status_dir ());
51   Drul_array<Skyline> skylines = Drul_array<Skyline> (Skyline (RIGHT), Skyline (LEFT));
52   Drul_array<vector<Grob *> > items (ly_scm2link_array (orig->get_object ("left-items")),
53                                      ly_scm2link_array (orig->get_object ("right-items")));
54
55   Grob *system = me->get_system ();
56   Grob *left_col = dynamic_cast<Item *> (me)->get_column ();
57
58   Drul_array<Grob *> columns (left_col, right_col);
59
60   for (LEFT_and_RIGHT (d))
61     {
62       for (vsize i = 0; i < items[d].size (); i++)
63         {
64           Item *g = dynamic_cast<Item *> (items[d][i]);
65           if (g)
66             if (Item *piece = g->find_prebroken_piece (break_dirs[d]))
67               g = piece;
68
69           if (has_interface<Separation_item> (g) && g->get_column () == columns[d])
70             {
71               SCM sky_scm = g->get_property ("horizontal-skylines");
72               Skyline_pair *sky = unsmob<Skyline_pair> (sky_scm);
73
74               extract_grob_set (g, "elements", elts);
75               Grob *ycommon = common_refpoint_of_array (elts, g, Y_AXIS);
76               Real shift = ycommon->pure_relative_y_coordinate (system, 0, INT_MAX);
77
78               skylines[d].shift (-shift);
79
80               if (sky)
81                 skylines[d].merge ((*sky)[-d]);
82               else
83                 programming_error ("separation item has no skyline");
84
85               if (d == RIGHT && items[LEFT].size ())
86                 skylines[d].merge (Separation_item::conditional_skyline (items[d][i], items[LEFT][0]));
87
88               skylines[d].shift (shift);
89             }
90         }
91     }
92
93   return skylines;
94 }
95
96 Real
97 Spacing_interface::minimum_distance (Grob *me, Grob *right)
98 {
99   Drul_array<Skyline> skylines = Spacing_interface::skylines (me, right);
100
101   return max (0.0, skylines[LEFT].distance (skylines[RIGHT]));
102 }
103
104 /*
105   Compute the left-most column of the right-items.
106 */
107 Item *
108 Spacing_interface::right_column (Grob *me)
109 {
110   if (!me->is_live ())
111     return 0;
112
113   Grob_array *a = unsmob<Grob_array> (me->get_object ("right-items"));
114   Item *mincol = 0;
115   int min_rank = INT_MAX;
116   for (vsize i = 0; a && i < a->size (); i++)
117     {
118       Item *ri = a->item (i);
119       Item *col = ri->get_column ();
120
121       int rank = Paper_column::get_rank (col);
122
123       if (rank < min_rank)
124         {
125           min_rank = rank;
126           mincol = col;
127         }
128     }
129
130   return mincol;
131 }
132
133 Item *
134 Spacing_interface::left_column (Grob *me)
135 {
136   if (!me->is_live ())
137     return 0;
138
139   return dynamic_cast<Item *> (me)->get_column ();
140 }
141
142 static vector<Item *>
143 get_note_columns (vector<Grob *> const &elts)
144 {
145   vector<Item *> ret;
146
147   for (vsize i = 0; i < elts.size (); i++)
148     {
149       if (has_interface<Note_column> (elts[i]))
150         ret.push_back (dynamic_cast<Item *> (elts[i]));
151       else if (has_interface<Separation_item> (elts[i]))
152         {
153           extract_grob_set (elts[i], "elements", more_elts);
154           vector<Item *> ncs = get_note_columns (more_elts);
155
156           ret.insert (ret.end (), ncs.begin (), ncs.end ());
157         }
158     }
159
160   return ret;
161 }
162
163 vector<Item *>
164 Spacing_interface::right_note_columns (Grob *me)
165 {
166   extract_grob_set (me, "right-items", elts);
167   return get_note_columns (elts);
168 }
169
170 vector<Item *>
171 Spacing_interface::left_note_columns (Grob *me)
172 {
173   extract_grob_set (me, "left-items", elts);
174   return get_note_columns (elts);
175 }
176
177 /*
178   Try to find the break-aligned symbol that belongs on the D-side
179   of ME, sticking out in direction -D. The x size is put in LAST_EXT
180 */
181 Grob *
182 Spacing_interface::extremal_break_aligned_grob (Grob *me,
183                                                 Direction d,
184                                                 Direction break_dir,
185                                                 Interval *last_ext)
186 {
187   Grob *col = 0;
188   last_ext->set_empty ();
189   Grob *last_grob = 0;
190
191   extract_grob_set (me, d == LEFT ? "left-break-aligned" : "right-break-aligned", elts);
192
193   for (vsize i = elts.size (); i--;)
194     {
195       Item *break_item = dynamic_cast<Item *> (elts[i]);
196
197       if (break_item->break_status_dir () != break_dir)
198         break_item = break_item->find_prebroken_piece (break_dir);
199
200       if (!break_item || !scm_is_pair (break_item->get_property ("space-alist")))
201         continue;
202
203       if (!col)
204         col = dynamic_cast<Item *> (elts[0])->get_column ()->find_prebroken_piece (break_dir);
205
206       Interval ext = break_item->extent (col, X_AXIS);
207
208       if (ext.is_empty ())
209         continue;
210
211       if (!last_grob
212           || (last_grob && d * (ext[-d] - (*last_ext)[-d]) < 0))
213         {
214           *last_ext = ext;
215           last_grob = break_item;
216         }
217     }
218
219   return last_grob;
220 }
221
222 ADD_INTERFACE (Spacing_interface,
223                "This object calculates the desired and minimum distances"
224                " between two columns.",
225
226                /* properties */
227                "left-items "
228                "right-items "
229               );