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