2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2000--2011 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);
147 Align_interface::get_minimum_translations (Grob *me,
148 vector<Grob*> const &all_grobs,
151 return internal_get_minimum_translations (me, all_grobs, a, true, false, 0, 0);
155 Align_interface::get_pure_minimum_translations (Grob *me,
156 vector<Grob*> const &all_grobs,
157 Axis a, int start, int end)
159 return internal_get_minimum_translations (me, all_grobs, a, true, true, start, end);
163 Align_interface::get_minimum_translations_without_min_dist (Grob *me,
164 vector<Grob*> const &all_grobs,
167 return internal_get_minimum_translations (me, all_grobs, a, false, false, 0, 0);
170 // If include_fixed_spacing is false, the only constraints that will be measured
171 // here are those that result from collisions (+ padding). That is, all
172 // minimum-distances, line-break-system-details, basic-distance+stretchable=0
173 // constraints will be ignored.
174 // - If you want to find the minimum height of a system, include_fixed_spacing should be true.
175 // - If you're going to actually lay out the page, then it should be false (or
176 // else centered dynamics will break when there is a fixed alignment).
178 Align_interface::internal_get_minimum_translations (Grob *me,
179 vector<Grob*> const &all_grobs,
181 bool include_fixed_spacing,
182 bool pure, int start, int end)
184 if (!pure && a == Y_AXIS && dynamic_cast<Spanner*> (me) && !me->get_system ())
185 me->programming_error ("vertical alignment called before line-breaking");
187 // If include_fixed_spacing is true, we look at things like system-system-spacing
188 // and alignment-distances, which only make sense for the toplevel VerticalAlignment.
189 // If we aren't toplevel, we're working on something like BassFigureAlignment
190 // and so we definitely don't want to include alignment-distances!
191 if (!dynamic_cast<System*> (me->get_parent (Y_AXIS)))
192 include_fixed_spacing = false;
194 Direction stacking_dir = robust_scm2dir (me->get_property ("stacking-dir"),
196 vector<Grob*> elems (all_grobs); // writable copy
197 vector<Skyline_pair> skylines;
199 get_skylines (me, &elems, a, pure, start, end, &skylines);
202 Real default_padding = robust_scm2double (me->get_property ("padding"), 0.0);
203 vector<Real> translates;
204 Skyline down_skyline (stacking_dir);
205 Real last_spaceable_element_pos = 0;
206 Grob *last_spaceable_element = 0;
207 int spaceable_count = 0;
208 for (vsize j = 0; j < elems.size (); j++)
211 Real padding = default_padding;
214 dy = skylines[j][-stacking_dir].max_height ();
217 down_skyline.merge (skylines[j-1][stacking_dir]);
218 dy = down_skyline.distance (skylines[j][-stacking_dir]);
220 SCM spec = Page_layout_problem::get_spacing_spec (elems[j-1], elems[j], pure, start, end);
221 Page_layout_problem::read_spacing_spec (spec, &padding, ly_symbol2scm ("padding"));
223 if (include_fixed_spacing)
225 Real min_distance = 0;
226 if (Page_layout_problem::read_spacing_spec (spec, &min_distance, ly_symbol2scm ("minimum-distance")))
227 dy = max (dy, min_distance);
229 dy = max (dy, Page_layout_problem::get_fixed_spacing (elems[j-1], elems[j], spaceable_count, pure, start, end));
232 if (Page_layout_problem::is_spaceable (elems[j]) && last_spaceable_element)
234 // Spaceable staves may have
235 // constraints coming from the previous spaceable staff
236 // as well as from the previous staff.
237 spec = Page_layout_problem::get_spacing_spec (last_spaceable_element, elems[j], pure, start, end);
238 Real spaceable_padding = 0;
239 Page_layout_problem::read_spacing_spec (spec,
241 ly_symbol2scm ("padding"));
242 padding = max (padding, spaceable_padding);
244 if (include_fixed_spacing)
246 Real min_distance = 0;
247 if (Page_layout_problem::read_spacing_spec (spec,
249 ly_symbol2scm ("minimum-distance")))
250 dy = max (dy, min_distance + stacking_dir*(last_spaceable_element_pos - where));
252 dy = max (dy, Page_layout_problem::get_fixed_spacing (last_spaceable_element, elems[j], spaceable_count,
258 if (isinf (dy)) /* if the skyline is empty, maybe max_height is infinity_f */
261 dy = max (0.0, dy + padding);
262 down_skyline.raise (-stacking_dir * dy);
263 where += stacking_dir * dy;
264 translates.push_back (where);
266 if (Page_layout_problem::is_spaceable (elems[j]))
269 last_spaceable_element = elems[j];
270 last_spaceable_element_pos = where;
274 // So far, we've computed the translates for all the non-empty elements.
275 // Here, we set the translates for the empty elements: an empty element
276 // gets the same translation as the last non-empty element before it.
277 vector<Real> all_translates;
278 if (!translates.empty ())
280 Real w = translates[0];
281 for (vsize i = 0, j = 0; j < all_grobs.size (); j++)
283 if (i < elems.size () && all_grobs[j] == elems[i])
285 all_translates.push_back (w);
288 return all_translates;
292 Align_interface::align_elements_to_ideal_distances (Grob *me)
294 System *sys = me->get_system ();
297 Page_layout_problem layout (NULL, SCM_EOL, scm_list_1 (sys->self_scm ()));
298 layout.solution (true);
301 programming_error ("vertical alignment called before line breaking");
305 Align_interface::align_elements_to_minimum_distances (Grob *me, Axis a)
307 extract_grob_set (me, "elements", all_grobs);
309 vector<Real> translates = get_minimum_translations (me, all_grobs, a);
310 if (translates.size ())
311 for (vsize j = 0; j < all_grobs.size (); j++)
312 all_grobs[j]->translate_axis (translates[j], a);
316 Align_interface::get_pure_child_y_translation (Grob *me, Grob *ch, int start, int end)
318 extract_grob_set (me, "elements", all_grobs);
319 vector<Real> translates = get_pure_minimum_translations (me, all_grobs, Y_AXIS, start, end);
321 if (translates.size ())
323 for (vsize i = 0; i < all_grobs.size (); i++)
324 if (all_grobs[i] == ch)
325 return translates[i];
330 programming_error ("tried to get a translation for something that is no child of mine");
335 Align_interface::axis (Grob *me)
337 return Axis (scm_to_int (scm_car (me->get_property ("axes"))));
341 Align_interface::add_element (Grob *me, Grob *element)
343 Axis a = Align_interface::axis (me);
344 SCM sym = axis_offset_symbol (a);
345 SCM proc = axis_parent_positioning (a);
347 element->set_property (sym, proc);
348 Axis_group_interface::add_element (me, element);
352 Align_interface::set_ordered (Grob *me)
354 SCM ga_scm = me->get_object ("elements");
355 Grob_array *ga = unsmob_grob_array (ga_scm);
358 ga_scm = Grob_array::make_array ();
359 ga = unsmob_grob_array (ga_scm);
360 me->set_object ("elements", ga_scm);
363 ga->set_ordered (true);
366 ADD_INTERFACE (Align_interface,
367 "Order grobs from top to bottom, left to right, right to left"
368 " or bottom to top. For vertical alignments of staves, the"
369 " @code{break-system-details} of the left"
370 " @rinternals{NonMusicalPaperColumn} may be set to tune"
371 " vertical spacing.",