2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2000--2015 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"
37 MAKE_SCHEME_CALLBACK (Align_interface, align_to_minimum_distances, 1);
39 Align_interface::align_to_minimum_distances (SCM smob)
41 Grob *me = unsmob<Grob> (smob);
43 me->set_property ("positioning-done", SCM_BOOL_T);
45 SCM axis = scm_car (me->get_property ("axes"));
46 Axis ax = Axis (scm_to_int (axis));
48 Align_interface::align_elements_to_minimum_distances (me, ax);
53 MAKE_SCHEME_CALLBACK (Align_interface, align_to_ideal_distances, 1);
55 Align_interface::align_to_ideal_distances (SCM smob)
57 Grob *me = unsmob<Grob> (smob);
59 me->set_property ("positioning-done", SCM_BOOL_T);
61 Align_interface::align_elements_to_ideal_distances (me);
66 /* Return upper and lower skylines for VerticalAxisGroup g. If the extent
67 is 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 *g,
75 bool pure, int start, int end)
77 Skyline_pair skylines;
81 Skyline_pair *skys = unsmob<Skyline_pair> (g->get_property (a == Y_AXIS
83 : "horizontal-skylines"));
87 /* This skyline was calculated relative to the grob g. In order to compare it to
88 skylines belonging to other grobs, we need to shift it so that it is relative
89 to the common reference. */
90 Real offset = g->relative_coordinate (other_common, other_axis (a));
91 skylines.shift (offset);
93 else if (Hara_kiri_group_spanner::request_suicide (g, start, end))
98 Interval extent = g->pure_y_extent (g, start, end);
100 // This is a hack to get better accuracy on the pure-height of VerticalAlignment.
101 // It's quite common for a treble clef to be the highest element of one system
102 // and for a low note (or lyrics) to be the lowest note on another. The two will
103 // never collide, but the pure-height stuff only works with bounding boxes, so it
104 // doesn't know that. The result is a significant over-estimation of the pure-height,
105 // especially on systems with many staves. To correct for this, we build a skyline
106 // in two parts: the part we did above contains most of the grobs (note-heads, etc.)
107 // while the bit we're about to do only contains the breakable grobs at the beginning
108 // of the system. This way, the tall treble clefs are only compared with the treble
109 // clefs of the other staff and they will be ignored if the staff above is, for example,
111 if (has_interface<Axis_group_interface> (g))
113 extent = Axis_group_interface::rest_of_line_pure_height (g, start, end);
114 Interval begin_of_line_extent = Axis_group_interface::begin_of_line_pure_height (g, start);
115 if (!begin_of_line_extent.is_empty ())
118 b[a] = begin_of_line_extent;
119 b[other_axis (a)] = Interval (-infinity_f, -1);
120 skylines.insert (b, other_axis (a));
124 if (!extent.is_empty ())
128 b[other_axis (a)] = Interval (0, infinity_f);
129 skylines.insert (b, other_axis (a));
136 Align_interface::get_minimum_translations (Grob *me,
137 vector<Grob *> const &all_grobs,
140 return internal_get_minimum_translations (me, all_grobs, a, true, false, 0, 0);
144 Align_interface::get_pure_minimum_translations (Grob *me,
145 vector<Grob *> const &all_grobs,
146 Axis a, int start, int end)
148 return internal_get_minimum_translations (me, all_grobs, a, true, true, start, end);
152 Align_interface::get_minimum_translations_without_min_dist (Grob *me,
153 vector<Grob *> const &all_grobs,
156 return internal_get_minimum_translations (me, all_grobs, a, false, false, 0, 0);
159 // If include_fixed_spacing is false, the only constraints that will be measured
160 // here are those that result from collisions (+ padding) and the spacing spec
161 // between adjacent staves.
162 // If include_fixed_spacing is true, constraints from line-break-system-details,
163 // basic-distance+stretchable=0, and staff-staff-spacing of spaceable staves
164 // with loose lines in between, are included as well.
165 // - If you want to find the minimum height of a system, include_fixed_spacing should be true.
166 // - If you're going to actually lay out the page, then it should be false (or
167 // else centered dynamics will break when there is a fixed alignment).
169 Align_interface::internal_get_minimum_translations (Grob *me,
170 vector<Grob *> const &elems,
172 bool include_fixed_spacing,
173 bool pure, int start, int end)
175 if (!pure && a == Y_AXIS && dynamic_cast<Spanner *> (me) && !me->get_system ())
176 me->programming_error ("vertical alignment called before line-breaking");
181 SCM fv = ly_assoc_get (scm_cons (scm_from_int (start), scm_from_int (end)),
182 me->get_property ("minimum-translations-alist"),
184 if (!scm_is_null (fv))
185 return ly_scm2floatvector (fv);
188 // If include_fixed_spacing is true, we look at things like system-system-spacing
189 // and alignment-distances, which only make sense for the toplevel VerticalAlignment.
190 // If we aren't toplevel, we're working on something like BassFigureAlignment
191 // and so we definitely don't want to include alignment-distances!
192 if (!dynamic_cast<System *> (me->get_parent (Y_AXIS)))
193 include_fixed_spacing = false;
195 Direction stacking_dir = robust_scm2dir (me->get_property ("stacking-dir"),
198 Grob *other_common = common_refpoint_of_array (elems, me, other_axis (a));
201 Real default_padding = robust_scm2double (me->get_property ("padding"), 0.0);
202 vector<Real> translates;
203 Skyline down_skyline (stacking_dir);
204 Grob *last_nonempty_element = 0;
205 Real last_spaceable_element_pos = 0;
206 Grob *last_spaceable_element = 0;
207 Skyline last_spaceable_skyline (stacking_dir);
208 int spaceable_count = 0;
209 for (vsize j = 0; j < elems.size (); j++)
212 Real padding = default_padding;
214 Skyline_pair skyline = get_skylines (elems[j], a, other_common, pure, start, end);
216 if (skyline.is_empty ())
218 translates.push_back (where);
222 if (!last_nonempty_element)
224 dy = skyline[-stacking_dir].max_height () + padding;
225 for (vsize k = j; k-- > 0;)
226 translates[k] = stacking_dir * dy;
230 SCM spec = Page_layout_problem::get_spacing_spec (last_nonempty_element, elems[j], pure, start, end);
231 Page_layout_problem::read_spacing_spec (spec, &padding, ly_symbol2scm ("padding"));
233 dy = down_skyline.distance (skyline[-stacking_dir]) + padding;
235 Real spec_distance = 0;
236 if (Page_layout_problem::read_spacing_spec (spec, &spec_distance, ly_symbol2scm ("minimum-distance")))
237 dy = std::max (dy, spec_distance);
238 // Consider the likely final spacing when estimating distance between staves of the full score
239 if (INT_MAX == end && 0 == start
240 && Page_layout_problem::read_spacing_spec (spec, &spec_distance, ly_symbol2scm ("basic-distance")))
241 dy = std::max (dy, spec_distance);
243 if (include_fixed_spacing && Page_layout_problem::is_spaceable (elems[j]) && last_spaceable_element)
245 // Spaceable staves may have
246 // constraints coming from the previous spaceable staff
247 // as well as from the previous staff.
248 spec = Page_layout_problem::get_spacing_spec (last_spaceable_element, elems[j], pure, start, end);
249 Real spaceable_padding = 0;
250 Page_layout_problem::read_spacing_spec (spec,
252 ly_symbol2scm ("padding"));
253 dy = std::max (dy, (last_spaceable_skyline.distance (skyline[-stacking_dir])
254 + stacking_dir * (last_spaceable_element_pos - where) + spaceable_padding));
256 Real spaceable_min_distance = 0;
257 if (Page_layout_problem::read_spacing_spec (spec,
258 &spaceable_min_distance,
259 ly_symbol2scm ("minimum-distance")))
260 dy = std::max (dy, spaceable_min_distance + stacking_dir * (last_spaceable_element_pos - where));
262 dy = std::max (dy, Page_layout_problem::get_fixed_spacing (last_spaceable_element, elems[j], spaceable_count,
267 dy = std::max (0.0, dy);
268 down_skyline.raise (-stacking_dir * dy);
269 down_skyline.merge (skyline[stacking_dir]);
270 where += stacking_dir * dy;
271 translates.push_back (where);
273 if (Page_layout_problem::is_spaceable (elems[j]))
276 last_spaceable_element = elems[j];
277 last_spaceable_element_pos = where;
278 last_spaceable_skyline = down_skyline;
280 last_nonempty_element = elems[j];
285 SCM mta = me->get_property ("minimum-translations-alist");
286 mta = scm_cons (scm_cons (scm_cons (scm_from_int (start), scm_from_int (end)),
287 ly_floatvector2scm (translates)),
289 me->set_property ("minimum-translations-alist", mta);
295 Align_interface::align_elements_to_ideal_distances (Grob *me)
297 System *sys = me->get_system ();
300 Page_layout_problem layout (NULL, SCM_EOL, scm_list_1 (sys->self_scm ()));
301 layout.solution (true);
304 programming_error ("vertical alignment called before line breaking");
308 Align_interface::align_elements_to_minimum_distances (Grob *me, Axis a)
310 extract_grob_set (me, "elements", all_grobs);
312 vector<Real> translates = get_minimum_translations (me, all_grobs, a);
313 if (translates.size ())
314 for (vsize j = 0; j < all_grobs.size (); j++)
315 all_grobs[j]->translate_axis (translates[j], a);
319 Align_interface::get_pure_child_y_translation (Grob *me, Grob *ch, int start, int end)
321 extract_grob_set (me, "elements", all_grobs);
322 vector<Real> translates = get_pure_minimum_translations (me, all_grobs, Y_AXIS, start, end);
324 if (translates.size ())
326 for (vsize i = 0; i < all_grobs.size (); i++)
327 if (all_grobs[i] == ch)
328 return translates[i];
333 programming_error ("tried to get a translation for something that is no child of mine");
338 Align_interface::axis (Grob *me)
340 return Axis (scm_to_int (scm_car (me->get_property ("axes"))));
344 Align_interface::add_element (Grob *me, Grob *element)
346 Axis a = Align_interface::axis (me);
347 SCM sym = axis_offset_symbol (a);
348 SCM proc = axis_parent_positioning (a);
350 element->set_property (sym, proc);
351 Axis_group_interface::add_element (me, element);
355 Align_interface::set_ordered (Grob *me)
357 SCM ga_scm = me->get_object ("elements");
358 Grob_array *ga = unsmob<Grob_array> (ga_scm);
361 ga_scm = Grob_array::make_array ();
362 ga = unsmob<Grob_array> (ga_scm);
363 me->set_object ("elements", ga_scm);
366 ga->set_ordered (true);
369 ADD_INTERFACE (Align_interface,
370 "Order grobs from top to bottom, left to right, right to left"
371 " or bottom to top. For vertical alignments of staves, the"
372 " @code{line-break-system-details} of the left"
373 " @rinternals{NonMusicalPaperColumn} may be set to tune"
374 " vertical spacing.",
380 "minimum-translations-alist "