2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2000--2012 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"
35 MAKE_SCHEME_CALLBACK (Align_interface, align_to_minimum_distances, 1);
37 Align_interface::align_to_minimum_distances (SCM smob)
39 Grob *me = unsmob_grob (smob);
41 me->set_property ("positioning-done", SCM_BOOL_T);
43 SCM axis = scm_car (me->get_property ("axes"));
44 Axis ax = Axis (scm_to_int (axis));
46 Align_interface::align_elements_to_minimum_distances (me, ax);
51 MAKE_SCHEME_CALLBACK (Align_interface, align_to_ideal_distances, 1);
53 Align_interface::align_to_ideal_distances (SCM smob)
55 Grob *me = unsmob_grob (smob);
57 me->set_property ("positioning-done", SCM_BOOL_T);
59 Align_interface::align_elements_to_ideal_distances (me);
64 /* for each grob, find its upper and lower skylines. If the extent is
65 non-empty but there is no skyline available (or pure is true), just
66 create a flat skyline from the bounding box */
67 // TODO(jneem): the pure and non-pure parts seem to share very little
68 // code. Split them into 2 functions, perhaps?
70 get_skylines (Grob *me,
71 vector<Grob *> const &elements,
73 bool pure, int start, int end,
74 vector<Skyline_pair> *ret,
75 vector<bool> const *skip_elt)
77 Grob *other_common = common_refpoint_of_array (elements, me, other_axis (a));
79 for (vsize i = elements.size (); i--;)
81 Grob *g = elements[i];
82 Skyline_pair skylines;
86 Skyline_pair *skys = Skyline_pair::unsmob (g->get_property (a == Y_AXIS
88 : "horizontal-skylines"));
92 /* This skyline was calculated relative to the grob g. In order to compare it to
93 skylines belonging to other grobs, we need to shift it so that it is relative
94 to the common reference. */
95 Real offset = g->relative_coordinate (other_common, other_axis (a));
96 skylines.shift (offset);
98 else if (!(*skip_elt)[i])
100 assert (a == Y_AXIS);
101 Interval extent = g->pure_height (g, start, end);
103 // This is a hack to get better accuracy on the pure-height of VerticalAlignment.
104 // It's quite common for a treble clef to be the highest element of one system
105 // and for a low note (or lyrics) to be the lowest note on another. The two will
106 // never collide, but the pure-height stuff only works with bounding boxes, so it
107 // doesn't know that. The result is a significant over-estimation of the pure-height,
108 // especially on systems with many staves. To correct for this, we build a skyline
109 // in two parts: the part we did above contains most of the grobs (note-heads, etc.)
110 // while the bit we're about to do only contains the breakable grobs at the beginning
111 // of the system. This way, the tall treble clefs are only compared with the treble
112 // clefs of the other staff and they will be ignored if the staff above is, for example,
114 if (Axis_group_interface::has_interface (g))
116 extent = Axis_group_interface::rest_of_line_pure_height (g, start, end);
117 Interval begin_of_line_extent = Axis_group_interface::begin_of_line_pure_height (g, start);
118 if (!begin_of_line_extent.is_empty ())
121 b[a] = begin_of_line_extent;
122 b[other_axis (a)] = Interval (-infinity_f, -1);
123 skylines.insert (b, other_axis (a));
127 if (!extent.is_empty ())
131 b[other_axis (a)] = Interval (0, infinity_f);
132 skylines.insert (b, other_axis (a));
136 // even if the skyline is empty, we want to push it back
137 // the heap because we will use things like system-system-distance
138 // to account for its presence
139 ret->push_back (skylines);
145 Align_interface::get_minimum_translations (Grob *me,
146 vector<Grob *> const &all_grobs,
149 return internal_get_minimum_translations (me, all_grobs, a, true, false, 0, 0);
153 Align_interface::get_pure_minimum_translations (Grob *me,
154 vector<Grob *> const &all_grobs,
155 Axis a, int start, int end)
157 return internal_get_minimum_translations (me, all_grobs, a, true, true, start, end);
161 Align_interface::get_minimum_translations_without_min_dist (Grob *me,
162 vector<Grob *> const &all_grobs,
165 return internal_get_minimum_translations (me, all_grobs, a, false, false, 0, 0);
168 // If include_fixed_spacing is false, the only constraints that will be measured
169 // here are those that result from collisions (+ padding) and the spacing spec
170 // between adjacent staves.
171 // If include_fixed_spacing is true, constraints from line-break-system-details,
172 // basic-distance+stretchable=0, and staff-staff-spacing of spaceable staves
173 // with loose lines in between, are included as well.
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 &elems,
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");
190 SCM fv = ly_assoc_get (scm_cons (scm_from_int (start), scm_from_int (end)),
191 me->get_property ("minimum-translations-alist"),
194 return ly_scm2floatvector (fv);
197 // If include_fixed_spacing is true, we look at things like system-system-spacing
198 // and alignment-distances, which only make sense for the toplevel VerticalAlignment.
199 // If we aren't toplevel, we're working on something like BassFigureAlignment
200 // and so we definitely don't want to include alignment-distances!
201 if (!dynamic_cast<System *> (me->get_parent (Y_AXIS)))
202 include_fixed_spacing = false;
204 Direction stacking_dir = robust_scm2dir (me->get_property ("stacking-dir"),
206 vector<Skyline_pair> skylines;
207 vector<bool> skip_elt;
208 // only add to skip elt if pure
209 // if not pure, no dead element should be in the list
210 for (vsize i = 0; i < elems.size (); i++)
212 if (!pure && !elems[i]->is_live ())
213 elems[i]->programming_error ("I should be dead by now...");
214 skip_elt.push_back (pure && Hara_kiri_group_spanner::request_suicide (elems[i], start, end));
217 get_skylines (me, elems, a, pure, start, end, &skylines, &skip_elt);
220 Real default_padding = robust_scm2double (me->get_property ("padding"), 0.0);
221 vector<Real> translates;
222 Skyline down_skyline (stacking_dir);
223 Real last_spaceable_element_pos = 0;
224 Grob *last_spaceable_element = 0;
225 Skyline last_spaceable_skyline (stacking_dir);
226 int spaceable_count = 0;
227 for (vsize j = 0; j < elems.size (); j++)
229 // This means that it will be suicided later downstream, so we
230 // skip it so that its padding is not added in.
231 if (pure && skip_elt[j])
235 Real padding = default_padding;
238 dy = skylines[j][-stacking_dir].max_height () + padding;
241 SCM spec = Page_layout_problem::get_spacing_spec (elems[j - 1], elems[j], pure, start, end);
242 Page_layout_problem::read_spacing_spec (spec, &padding, ly_symbol2scm ("padding"));
244 dy = down_skyline.distance (skylines[j][-stacking_dir]) + padding;
246 Real spec_distance = 0;
247 if (Page_layout_problem::read_spacing_spec (spec, &spec_distance, ly_symbol2scm ("minimum-distance")))
248 dy = max (dy, spec_distance);
249 // Consider the likely final spacing when estimating distance between staves of the full score
250 if (INT_MAX == end && 0 == start
251 && Page_layout_problem::read_spacing_spec (spec, &spec_distance, ly_symbol2scm ("basic-distance")))
252 dy = max (dy, spec_distance);
254 if (include_fixed_spacing && Page_layout_problem::is_spaceable (elems[j]) && last_spaceable_element)
256 // Spaceable staves may have
257 // constraints coming from the previous spaceable staff
258 // as well as from the previous staff.
259 spec = Page_layout_problem::get_spacing_spec (last_spaceable_element, elems[j], pure, start, end);
260 Real spaceable_padding = 0;
261 Page_layout_problem::read_spacing_spec (spec,
263 ly_symbol2scm ("padding"));
264 dy = max (dy, (last_spaceable_skyline.distance (skylines[j][-stacking_dir])
265 + stacking_dir * (last_spaceable_element_pos - where) + spaceable_padding));
267 Real spaceable_min_distance = 0;
268 if (Page_layout_problem::read_spacing_spec (spec,
269 &spaceable_min_distance,
270 ly_symbol2scm ("minimum-distance")))
271 dy = max (dy, spaceable_min_distance + stacking_dir * (last_spaceable_element_pos - where));
273 dy = max (dy, Page_layout_problem::get_fixed_spacing (last_spaceable_element, elems[j], spaceable_count,
278 if (isinf (dy)) /* if the skyline is empty, maybe max_height is infinity_f */
282 down_skyline.raise (-stacking_dir * dy);
283 down_skyline.merge (skylines[j][stacking_dir]);
284 where += stacking_dir * dy;
285 translates.push_back (where);
287 if (Page_layout_problem::is_spaceable (elems[j]))
290 last_spaceable_element = elems[j];
291 last_spaceable_element_pos = where;
292 last_spaceable_skyline = down_skyline;
296 // So far, we've computed the translates for all the non-empty elements.
297 // Here, we set the translates for the empty elements: an empty element
298 // gets the same translation as the last non-empty element before it.
299 vector<Grob *> non_empty_elems;
300 for (vsize i = 0; i < elems.size (); i++)
302 non_empty_elems.push_back (elems[i]);
304 vector<Real> all_translates;
305 if (!translates.empty ())
307 Real w = translates[0];
308 for (vsize i = 0, j = 0; j < elems.size (); j++)
310 if (i < non_empty_elems.size () && elems[j] == non_empty_elems[i])
312 all_translates.push_back (w);
318 SCM mta = me->get_property ("minimum-translations-alist");
319 mta = scm_cons (scm_cons (scm_cons (scm_from_int (start), scm_from_int (end)),
320 ly_floatvector2scm (all_translates)),
322 me->set_property ("minimum-translations-alist", mta);
324 return all_translates;
328 Align_interface::align_elements_to_ideal_distances (Grob *me)
330 System *sys = me->get_system ();
333 Page_layout_problem layout (NULL, SCM_EOL, scm_list_1 (sys->self_scm ()));
334 layout.solution (true);
337 programming_error ("vertical alignment called before line breaking");
341 Align_interface::align_elements_to_minimum_distances (Grob *me, Axis a)
343 extract_grob_set (me, "elements", all_grobs);
345 vector<Real> translates = get_minimum_translations (me, all_grobs, a);
346 if (translates.size ())
347 for (vsize j = 0; j < all_grobs.size (); j++)
348 all_grobs[j]->translate_axis (translates[j], a);
352 Align_interface::get_pure_child_y_translation (Grob *me, Grob *ch, int start, int end)
354 extract_grob_set (me, "elements", all_grobs);
355 vector<Real> translates = get_pure_minimum_translations (me, all_grobs, Y_AXIS, start, end);
357 if (translates.size ())
359 for (vsize i = 0; i < all_grobs.size (); i++)
360 if (all_grobs[i] == ch)
361 return translates[i];
366 programming_error ("tried to get a translation for something that is no child of mine");
371 Align_interface::axis (Grob *me)
373 return Axis (scm_to_int (scm_car (me->get_property ("axes"))));
377 Align_interface::add_element (Grob *me, Grob *element)
379 Axis a = Align_interface::axis (me);
380 SCM sym = axis_offset_symbol (a);
381 SCM proc = axis_parent_positioning (a);
383 element->set_property (sym, proc);
384 Axis_group_interface::add_element (me, element);
388 Align_interface::set_ordered (Grob *me)
390 SCM ga_scm = me->get_object ("elements");
391 Grob_array *ga = unsmob_grob_array (ga_scm);
394 ga_scm = Grob_array::make_array ();
395 ga = unsmob_grob_array (ga_scm);
396 me->set_object ("elements", ga_scm);
399 ga->set_ordered (true);
402 ADD_INTERFACE (Align_interface,
403 "Order grobs from top to bottom, left to right, right to left"
404 " or bottom to top. For vertical alignments of staves, the"
405 " @code{break-system-details} of the left"
406 " @rinternals{NonMusicalPaperColumn} may be set to tune"
407 " vertical spacing.",
413 "minimum-translations-alist "