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 grob has
65 an empty extent, delete it from the list instead. If the extent is
66 non-empty but there is no skyline available (or pure is true), just
67 create a flat skyline from the bounding box */
68 // TODO(jneem): the pure and non-pure parts seem to share very little
69 // code. Split them into 2 functions, perhaps?
71 get_skylines (Grob *me,
72 vector<Grob *> *const elements,
74 bool pure, int start, int end,
75 vector<Skyline_pair> *const ret)
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);
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)
115 && !Hara_kiri_group_spanner::request_suicide (g, start, end))
117 extent = Axis_group_interface::rest_of_line_pure_height (g, start, end);
118 Interval begin_of_line_extent = Axis_group_interface::begin_of_line_pure_height (g, start);
119 if (!begin_of_line_extent.is_empty ())
122 b[a] = begin_of_line_extent;
123 b[other_axis (a)] = Interval (-infinity_f, -1);
124 skylines.insert (b, other_axis (a));
128 if (!extent.is_empty ())
132 b[other_axis (a)] = Interval (0, infinity_f);
133 skylines.insert (b, other_axis (a));
137 if (skylines.is_empty ())
138 elements->erase (elements->begin () + i);
140 ret->push_back (skylines);
146 Align_interface::get_minimum_translations (Grob *me,
147 vector<Grob *> const &all_grobs,
150 return internal_get_minimum_translations (me, all_grobs, a, true, false, 0, 0);
154 Align_interface::get_pure_minimum_translations (Grob *me,
155 vector<Grob *> const &all_grobs,
156 Axis a, int start, int end)
158 return internal_get_minimum_translations (me, all_grobs, a, true, true, start, end);
162 Align_interface::get_minimum_translations_without_min_dist (Grob *me,
163 vector<Grob *> const &all_grobs,
166 return internal_get_minimum_translations (me, all_grobs, a, false, false, 0, 0);
169 // If include_fixed_spacing is false, the only constraints that will be measured
170 // here are those that result from collisions (+ padding) and the spacing spec
171 // between adjacent staves.
172 // If include_fixed_spacing is true, constraints from line-break-system-details,
173 // basic-distance+stretchable=0, and staff-staff-spacing of spaceable staves
174 // with loose lines in between, are included as well.
175 // - If you want to find the minimum height of a system, include_fixed_spacing should be true.
176 // - If you're going to actually lay out the page, then it should be false (or
177 // else centered dynamics will break when there is a fixed alignment).
179 Align_interface::internal_get_minimum_translations (Grob *me,
180 vector<Grob *> const &all_grobs,
182 bool include_fixed_spacing,
183 bool pure, int start, int end)
185 if (!pure && a == Y_AXIS && dynamic_cast<Spanner *> (me) && !me->get_system ())
186 me->programming_error ("vertical alignment called before line-breaking");
191 SCM fv = ly_assoc_get (scm_cons (scm_from_int (start), scm_from_int (end)),
192 me->get_property ("minimum-translations-alist"),
195 return ly_scm2floatvector (fv);
198 // If include_fixed_spacing is true, we look at things like system-system-spacing
199 // and alignment-distances, which only make sense for the toplevel VerticalAlignment.
200 // If we aren't toplevel, we're working on something like BassFigureAlignment
201 // and so we definitely don't want to include alignment-distances!
202 if (!dynamic_cast<System *> (me->get_parent (Y_AXIS)))
203 include_fixed_spacing = false;
205 Direction stacking_dir = robust_scm2dir (me->get_property ("stacking-dir"),
207 vector<Grob *> elems (all_grobs); // writable copy
208 vector<Skyline_pair> skylines;
210 get_skylines (me, &elems, a, pure, start, end, &skylines);
213 Real default_padding = robust_scm2double (me->get_property ("padding"), 0.0);
214 vector<Real> translates;
215 Skyline down_skyline (stacking_dir);
216 Real last_spaceable_element_pos = 0;
217 Grob *last_spaceable_element = 0;
218 Skyline last_spaceable_skyline (stacking_dir);
219 int spaceable_count = 0;
220 for (vsize j = 0; j < elems.size (); j++)
223 Real padding = default_padding;
226 dy = skylines[j][-stacking_dir].max_height () + padding;
229 SCM spec = Page_layout_problem::get_spacing_spec (elems[j - 1], elems[j], pure, start, end);
230 Page_layout_problem::read_spacing_spec (spec, &padding, ly_symbol2scm ("padding"));
232 dy = down_skyline.distance (skylines[j][-stacking_dir]) + padding;
234 Real spec_distance = 0;
235 if (Page_layout_problem::read_spacing_spec (spec, &spec_distance, ly_symbol2scm ("minimum-distance")))
236 dy = max (dy, spec_distance);
237 // Consider the likely final spacing when estimating distance between staves of the full score
238 if (INT_MAX == end && 0 == start
239 && Page_layout_problem::read_spacing_spec (spec, &spec_distance, ly_symbol2scm ("basic-distance")))
240 dy = max (dy, spec_distance);
242 if (include_fixed_spacing && Page_layout_problem::is_spaceable (elems[j]) && last_spaceable_element)
244 // Spaceable staves may have
245 // constraints coming from the previous spaceable staff
246 // as well as from the previous staff.
247 spec = Page_layout_problem::get_spacing_spec (last_spaceable_element, elems[j], pure, start, end);
248 Real spaceable_padding = 0;
249 Page_layout_problem::read_spacing_spec (spec,
251 ly_symbol2scm ("padding"));
252 dy = max (dy, (last_spaceable_skyline.distance (skylines[j][-stacking_dir])
253 + stacking_dir * (last_spaceable_element_pos - where) + spaceable_padding));
255 Real spaceable_min_distance = 0;
256 if (Page_layout_problem::read_spacing_spec (spec,
257 &spaceable_min_distance,
258 ly_symbol2scm ("minimum-distance")))
259 dy = max (dy, spaceable_min_distance + stacking_dir * (last_spaceable_element_pos - where));
261 dy = max (dy, Page_layout_problem::get_fixed_spacing (last_spaceable_element, elems[j], spaceable_count,
266 if (isinf (dy)) /* if the skyline is empty, maybe max_height is infinity_f */
270 down_skyline.raise (-stacking_dir * dy);
271 down_skyline.merge (skylines[j][stacking_dir]);
272 where += stacking_dir * dy;
273 translates.push_back (where);
275 if (Page_layout_problem::is_spaceable (elems[j]))
278 last_spaceable_element = elems[j];
279 last_spaceable_element_pos = where;
280 last_spaceable_skyline = down_skyline;
284 // So far, we've computed the translates for all the non-empty elements.
285 // Here, we set the translates for the empty elements: an empty element
286 // gets the same translation as the last non-empty element before it.
287 vector<Real> all_translates;
288 if (!translates.empty ())
290 Real w = translates[0];
291 for (vsize i = 0, j = 0; j < all_grobs.size (); j++)
293 if (i < elems.size () && all_grobs[j] == elems[i])
295 all_translates.push_back (w);
301 SCM mta = me->get_property ("minimum-translations-alist");
302 mta = scm_cons (scm_cons (scm_cons (scm_from_int (start), scm_from_int (end)),
303 ly_floatvector2scm (all_translates)),
305 me->set_property ("minimum-translations-alist", mta);
307 return all_translates;
311 Align_interface::align_elements_to_ideal_distances (Grob *me)
313 System *sys = me->get_system ();
316 Page_layout_problem layout (NULL, SCM_EOL, scm_list_1 (sys->self_scm ()));
317 layout.solution (true);
320 programming_error ("vertical alignment called before line breaking");
324 Align_interface::align_elements_to_minimum_distances (Grob *me, Axis a)
326 extract_grob_set (me, "elements", all_grobs);
328 vector<Real> translates = get_minimum_translations (me, all_grobs, a);
329 if (translates.size ())
330 for (vsize j = 0; j < all_grobs.size (); j++)
331 all_grobs[j]->translate_axis (translates[j], a);
335 Align_interface::get_pure_child_y_translation (Grob *me, Grob *ch, int start, int end)
337 extract_grob_set (me, "elements", all_grobs);
338 vector<Real> translates = get_pure_minimum_translations (me, all_grobs, Y_AXIS, start, end);
340 if (translates.size ())
342 for (vsize i = 0; i < all_grobs.size (); i++)
343 if (all_grobs[i] == ch)
344 return translates[i];
349 programming_error ("tried to get a translation for something that is no child of mine");
354 Align_interface::axis (Grob *me)
356 return Axis (scm_to_int (scm_car (me->get_property ("axes"))));
360 Align_interface::add_element (Grob *me, Grob *element)
362 Axis a = Align_interface::axis (me);
363 SCM sym = axis_offset_symbol (a);
364 SCM proc = axis_parent_positioning (a);
366 element->set_property (sym, proc);
367 Axis_group_interface::add_element (me, element);
371 Align_interface::set_ordered (Grob *me)
373 SCM ga_scm = me->get_object ("elements");
374 Grob_array *ga = unsmob_grob_array (ga_scm);
377 ga_scm = Grob_array::make_array ();
378 ga = unsmob_grob_array (ga_scm);
379 me->set_object ("elements", ga_scm);
382 ga->set_ordered (true);
385 ADD_INTERFACE (Align_interface,
386 "Order grobs from top to bottom, left to right, right to left"
387 " or bottom to top. For vertical alignments of staves, the"
388 " @code{break-system-details} of the left"
389 " @rinternals{NonMusicalPaperColumn} may be set to tune"
390 " vertical spacing.",
396 "minimum-translations-alist "