2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2000--2010 Han-Wen Nienhuys <hanwen@xs4all.nl>
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.
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.
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/>.
20 #include "align-interface.hh"
21 #include "axis-group-interface.hh"
22 #include "grob-array.hh"
23 #include "hara-kiri-group-spanner.hh"
24 #include "international.hh"
26 #include "page-layout-problem.hh"
27 #include "paper-book.hh"
28 #include "paper-column.hh"
29 #include "pointer-group-interface.hh"
31 #include "skyline-pair.hh"
36 MAKE_SCHEME_CALLBACK (Align_interface, align_to_minimum_distances, 1);
38 Align_interface::align_to_minimum_distances (SCM smob)
40 Grob *me = unsmob_grob (smob);
42 me->set_property ("positioning-done", SCM_BOOL_T);
44 SCM axis = scm_car (me->get_property ("axes"));
45 Axis ax = Axis (scm_to_int (axis));
47 Align_interface::align_elements_to_minimum_distances (me, ax);
52 MAKE_SCHEME_CALLBACK (Align_interface, align_to_ideal_distances, 1);
54 Align_interface::align_to_ideal_distances (SCM smob)
56 Grob *me = unsmob_grob (smob);
58 me->set_property ("positioning-done", SCM_BOOL_T);
60 Align_interface::align_elements_to_ideal_distances (me);
65 /* for each grob, find its upper and lower skylines. If the grob has
66 an empty extent, delete it from the list instead. If the extent is
67 non-empty but there is no skyline available (or pure is true), just
68 create a flat skyline from the bounding box */
69 // TODO(jneem): the pure and non-pure parts seem to share very little
70 // code. Split them into 2 functions, perhaps?
72 get_skylines (Grob *me,
73 vector<Grob*> *const elements,
75 bool pure, int start, int end,
76 vector<Skyline_pair> *const ret)
78 Grob *other_common = common_refpoint_of_array (*elements, me, other_axis (a));
80 for (vsize i = elements->size (); i--;)
82 Grob *g = (*elements)[i];
83 Skyline_pair skylines;
87 Skyline_pair *skys = Skyline_pair::unsmob (g->get_property (a == Y_AXIS
89 : "horizontal-skylines"));
93 /* This skyline was calculated relative to the grob g. In order to compare it to
94 skylines belonging to other grobs, we need to shift it so that it is relative
95 to the common reference. */
96 Real offset = g->relative_coordinate (other_common, other_axis (a));
97 skylines.shift (offset);
101 assert (a == Y_AXIS);
102 Interval extent = g->pure_height (g, start, end);
104 // This is a hack to get better accuracy on the pure-height of VerticalAlignment.
105 // It's quite common for a treble clef to be the highest element of one system
106 // and for a low note (or lyrics) to be the lowest note on another. The two will
107 // never collide, but the pure-height stuff only works with bounding boxes, so it
108 // doesn't know that. The result is a significant over-estimation of the pure-height,
109 // especially on systems with many staves. To correct for this, we build a skyline
110 // in two parts: the part we did above contains most of the grobs (note-heads, etc.)
111 // while the bit we're about to do only contains the breakable grobs at the beginning
112 // of the system. This way, the tall treble clefs are only compared with the treble
113 // clefs of the other staff and they will be ignored if the staff above is, for example,
115 if (Axis_group_interface::has_interface (g)
116 && !Hara_kiri_group_spanner::request_suicide (g, start, end))
118 extent = Axis_group_interface::rest_of_line_pure_height (g, start, end);
119 Interval begin_of_line_extent = Axis_group_interface::begin_of_line_pure_height (g, start);
120 if (!begin_of_line_extent.is_empty ())
123 b[a] = begin_of_line_extent;
124 b[other_axis (a)] = Interval (-infinity_f, -1);
125 skylines.insert (b, 0, other_axis (a));
129 if (!extent.is_empty ())
133 b[other_axis (a)] = Interval (0, infinity_f);
134 skylines.insert (b, 0, other_axis (a));
138 if (skylines.is_empty ())
139 elements->erase (elements->begin () + i);
141 ret->push_back (skylines);
146 // If include_fixed_spacing is true, the manually fixed spacings
147 // induced by stretchable=0 or alignment-distances are included
148 // in the minimum translations here. If you want to find the minimum
149 // height of a system, include_fixed_spacing should be true. If you
150 // want to actually lay out the page, then it should be false (or
151 // else centered dynamics will break when there is a fixed alignment).
153 Align_interface::get_minimum_translations (Grob *me,
154 vector<Grob*> const &all_grobs,
156 bool include_fixed_spacing,
157 bool pure, int start, int end)
159 if (!pure && a == Y_AXIS && dynamic_cast<Spanner*> (me) && !me->get_system ())
160 me->programming_error ("vertical alignment called before line-breaking");
162 Direction stacking_dir = robust_scm2dir (me->get_property ("stacking-dir"),
164 vector<Grob*> elems (all_grobs); // writable copy
165 vector<Skyline_pair> skylines;
167 get_skylines (me, &elems, a, pure, start, end, &skylines);
170 Real default_padding = robust_scm2double (me->get_property ("padding"), 0.0);
171 vector<Real> translates;
172 Skyline down_skyline (stacking_dir);
173 Real last_spaceable_element_pos = 0;
174 Grob *last_spaceable_element = 0;
175 int spaceable_count = 0;
176 for (vsize j = 0; j < elems.size (); j++)
179 Real padding = default_padding;
182 dy = skylines[j][-stacking_dir].max_height ();
185 down_skyline.merge (skylines[j-1][stacking_dir]);
186 dy = down_skyline.distance (skylines[j][-stacking_dir]);
188 SCM spec = Page_layout_problem::get_spacing_spec (elems[j-1], elems[j], pure, start, end);
189 Page_layout_problem::read_spacing_spec (spec, &padding, ly_symbol2scm ("padding"));
191 Real min_distance = 0;
192 if (Page_layout_problem::read_spacing_spec (spec, &min_distance, ly_symbol2scm ("minimum-distance")))
193 dy = max (dy, min_distance);
195 if (include_fixed_spacing)
196 dy = max (dy, Page_layout_problem::get_fixed_spacing (elems[j-1], elems[j], spaceable_count, pure, start, end));
198 if (Page_layout_problem::is_spaceable (elems[j]) && last_spaceable_element)
200 // Spaceable staves may have
201 // constraints coming from the previous spaceable staff
202 // as well as from the previous staff.
203 spec = Page_layout_problem::get_spacing_spec (last_spaceable_element, elems[j], pure, start, end);
204 Real spaceable_padding = 0;
205 Page_layout_problem::read_spacing_spec (spec,
207 ly_symbol2scm ("padding"));
208 padding = max (padding, spaceable_padding);
210 Real min_distance = 0;
211 if (Page_layout_problem::read_spacing_spec (spec,
213 ly_symbol2scm ("minimum-distance")))
214 dy = max (dy, min_distance + stacking_dir*(last_spaceable_element_pos - where));
216 if (include_fixed_spacing)
217 dy = max (dy, Page_layout_problem::get_fixed_spacing (last_spaceable_element, elems[j], spaceable_count,
222 if (isinf (dy)) /* if the skyline is empty, maybe max_height is infinity_f */
225 dy = max (0.0, dy + padding);
226 down_skyline.raise (-stacking_dir * dy);
227 where += stacking_dir * dy;
228 translates.push_back (where);
230 if (Page_layout_problem::is_spaceable (elems[j]))
233 last_spaceable_element = elems[j];
234 last_spaceable_element_pos = where;
238 // So far, we've computed the translates for all the non-empty elements.
239 // Here, we set the translates for the empty elements: an empty element
240 // gets the same translation as the last non-empty element before it.
241 vector<Real> all_translates;
242 if (!translates.empty ())
244 Real w = translates[0];
245 for (vsize i = 0, j = 0; j < all_grobs.size (); j++)
247 if (i < elems.size () && all_grobs[j] == elems[i])
249 all_translates.push_back (w);
252 return all_translates;
256 Align_interface::align_elements_to_ideal_distances (Grob *me)
258 System *sys = me->get_system ();
261 Page_layout_problem layout (NULL, SCM_EOL, scm_list_1 (sys->self_scm ()));
262 layout.solution (true);
265 programming_error ("vertical alignment called before line breaking");
269 Align_interface::align_elements_to_minimum_distances (Grob *me, Axis a)
271 extract_grob_set (me, "elements", all_grobs);
273 vector<Real> translates = get_minimum_translations (me, all_grobs, a, true, false, 0, 0);
274 if (translates.size ())
275 for (vsize j = 0; j < all_grobs.size (); j++)
276 all_grobs[j]->translate_axis (translates[j], a);
280 Align_interface::get_pure_child_y_translation (Grob *me, Grob *ch, int start, int end)
282 extract_grob_set (me, "elements", all_grobs);
283 vector<Real> translates = get_minimum_translations (me, all_grobs, Y_AXIS, true, true, start, end);
285 if (translates.size ())
287 for (vsize i = 0; i < all_grobs.size (); i++)
288 if (all_grobs[i] == ch)
289 return translates[i];
294 programming_error ("tried to get a translation for something that is no child of mine");
299 Align_interface::axis (Grob *me)
301 return Axis (scm_to_int (scm_car (me->get_property ("axes"))));
305 Align_interface::add_element (Grob *me, Grob *element)
307 Axis a = Align_interface::axis (me);
308 SCM sym = axis_offset_symbol (a);
309 SCM proc = axis_parent_positioning (a);
311 element->set_property (sym, proc);
312 Axis_group_interface::add_element (me, element);
316 Align_interface::set_ordered (Grob *me)
318 SCM ga_scm = me->get_object ("elements");
319 Grob_array *ga = unsmob_grob_array (ga_scm);
322 ga_scm = Grob_array::make_array ();
323 ga = unsmob_grob_array (ga_scm);
324 me->set_object ("elements", ga_scm);
327 ga->set_ordered (true);
330 ADD_INTERFACE (Align_interface,
331 "Order grobs from top to bottom, left to right, right to left"
332 " or bottom to top. For vertical alignments of staves, the"
333 " @code{break-system-details} of the left"
334 " @rinternals{NonMusicalPaperColumn} may be set to tune"
335 " vertical spacing.",