2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2000--2014 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 /* Return upper and lower skylines for VerticalAxisGroup g. If the extent
65 is 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 *g,
73 bool pure, int start, int end)
75 Skyline_pair skylines;
79 Skyline_pair *skys = Skyline_pair::unsmob (g->get_property (a == Y_AXIS
81 : "horizontal-skylines"));
85 /* This skyline was calculated relative to the grob g. In order to compare it to
86 skylines belonging to other grobs, we need to shift it so that it is relative
87 to the common reference. */
88 Real offset = g->relative_coordinate (other_common, other_axis (a));
89 skylines.shift (offset);
91 else if (Hara_kiri_group_spanner::request_suicide (g, start, end))
96 Interval extent = g->pure_height (g, start, end);
98 // This is a hack to get better accuracy on the pure-height of VerticalAlignment.
99 // It's quite common for a treble clef to be the highest element of one system
100 // and for a low note (or lyrics) to be the lowest note on another. The two will
101 // never collide, but the pure-height stuff only works with bounding boxes, so it
102 // doesn't know that. The result is a significant over-estimation of the pure-height,
103 // especially on systems with many staves. To correct for this, we build a skyline
104 // in two parts: the part we did above contains most of the grobs (note-heads, etc.)
105 // while the bit we're about to do only contains the breakable grobs at the beginning
106 // of the system. This way, the tall treble clefs are only compared with the treble
107 // clefs of the other staff and they will be ignored if the staff above is, for example,
109 if (Axis_group_interface::has_interface (g))
111 extent = Axis_group_interface::rest_of_line_pure_height (g, start, end);
112 Interval begin_of_line_extent = Axis_group_interface::begin_of_line_pure_height (g, start);
113 if (!begin_of_line_extent.is_empty ())
116 b[a] = begin_of_line_extent;
117 b[other_axis (a)] = Interval (-infinity_f, -1);
118 skylines.insert (b, other_axis (a));
122 if (!extent.is_empty ())
126 b[other_axis (a)] = Interval (0, infinity_f);
127 skylines.insert (b, other_axis (a));
134 Align_interface::get_minimum_translations (Grob *me,
135 vector<Grob *> const &all_grobs,
138 return internal_get_minimum_translations (me, all_grobs, a, true, false, 0, 0);
142 Align_interface::get_pure_minimum_translations (Grob *me,
143 vector<Grob *> const &all_grobs,
144 Axis a, int start, int end)
146 return internal_get_minimum_translations (me, all_grobs, a, true, true, start, end);
150 Align_interface::get_minimum_translations_without_min_dist (Grob *me,
151 vector<Grob *> const &all_grobs,
154 return internal_get_minimum_translations (me, all_grobs, a, false, false, 0, 0);
157 // If include_fixed_spacing is false, the only constraints that will be measured
158 // here are those that result from collisions (+ padding) and the spacing spec
159 // between adjacent staves.
160 // If include_fixed_spacing is true, constraints from line-break-system-details,
161 // basic-distance+stretchable=0, and staff-staff-spacing of spaceable staves
162 // with loose lines in between, are included as well.
163 // - If you want to find the minimum height of a system, include_fixed_spacing should be true.
164 // - If you're going to actually lay out the page, then it should be false (or
165 // else centered dynamics will break when there is a fixed alignment).
167 Align_interface::internal_get_minimum_translations (Grob *me,
168 vector<Grob *> const &elems,
170 bool include_fixed_spacing,
171 bool pure, int start, int end)
173 if (!pure && a == Y_AXIS && dynamic_cast<Spanner *> (me) && !me->get_system ())
174 me->programming_error ("vertical alignment called before line-breaking");
179 SCM fv = ly_assoc_get (scm_cons (scm_from_int (start), scm_from_int (end)),
180 me->get_property ("minimum-translations-alist"),
183 return ly_scm2floatvector (fv);
186 // If include_fixed_spacing is true, we look at things like system-system-spacing
187 // and alignment-distances, which only make sense for the toplevel VerticalAlignment.
188 // If we aren't toplevel, we're working on something like BassFigureAlignment
189 // and so we definitely don't want to include alignment-distances!
190 if (!dynamic_cast<System *> (me->get_parent (Y_AXIS)))
191 include_fixed_spacing = false;
193 Direction stacking_dir = robust_scm2dir (me->get_property ("stacking-dir"),
196 Grob *other_common = common_refpoint_of_array (elems, me, other_axis (a));
199 Real default_padding = robust_scm2double (me->get_property ("padding"), 0.0);
200 vector<Real> translates;
201 Skyline down_skyline (stacking_dir);
202 Grob *last_nonempty_element = 0;
203 Real last_spaceable_element_pos = 0;
204 Grob *last_spaceable_element = 0;
205 Skyline last_spaceable_skyline (stacking_dir);
206 int spaceable_count = 0;
207 for (vsize j = 0; j < elems.size (); j++)
210 Real padding = default_padding;
212 Skyline_pair skyline = get_skylines (elems[j], a, other_common, pure, start, end);
214 if (skyline.is_empty ())
216 translates.push_back (where);
220 if (!last_nonempty_element)
222 dy = skyline[-stacking_dir].max_height () + padding;
223 for (vsize k = j; k-- > 0;)
224 translates[k] = stacking_dir * dy;
228 SCM spec = Page_layout_problem::get_spacing_spec (last_nonempty_element, elems[j], pure, start, end);
229 Page_layout_problem::read_spacing_spec (spec, &padding, ly_symbol2scm ("padding"));
231 dy = down_skyline.distance (skyline[-stacking_dir]) + padding;
233 Real spec_distance = 0;
234 if (Page_layout_problem::read_spacing_spec (spec, &spec_distance, ly_symbol2scm ("minimum-distance")))
235 dy = max (dy, spec_distance);
236 // Consider the likely final spacing when estimating distance between staves of the full score
237 if (INT_MAX == end && 0 == start
238 && Page_layout_problem::read_spacing_spec (spec, &spec_distance, ly_symbol2scm ("basic-distance")))
239 dy = max (dy, spec_distance);
241 if (include_fixed_spacing && Page_layout_problem::is_spaceable (elems[j]) && last_spaceable_element)
243 // Spaceable staves may have
244 // constraints coming from the previous spaceable staff
245 // as well as from the previous staff.
246 spec = Page_layout_problem::get_spacing_spec (last_spaceable_element, elems[j], pure, start, end);
247 Real spaceable_padding = 0;
248 Page_layout_problem::read_spacing_spec (spec,
250 ly_symbol2scm ("padding"));
251 dy = max (dy, (last_spaceable_skyline.distance (skyline[-stacking_dir])
252 + stacking_dir * (last_spaceable_element_pos - where) + spaceable_padding));
254 Real spaceable_min_distance = 0;
255 if (Page_layout_problem::read_spacing_spec (spec,
256 &spaceable_min_distance,
257 ly_symbol2scm ("minimum-distance")))
258 dy = max (dy, spaceable_min_distance + stacking_dir * (last_spaceable_element_pos - where));
260 dy = max (dy, Page_layout_problem::get_fixed_spacing (last_spaceable_element, elems[j], spaceable_count,
266 down_skyline.raise (-stacking_dir * dy);
267 down_skyline.merge (skyline[stacking_dir]);
268 where += stacking_dir * dy;
269 translates.push_back (where);
271 if (Page_layout_problem::is_spaceable (elems[j]))
274 last_spaceable_element = elems[j];
275 last_spaceable_element_pos = where;
276 last_spaceable_skyline = down_skyline;
278 last_nonempty_element = elems[j];
283 SCM mta = me->get_property ("minimum-translations-alist");
284 mta = scm_cons (scm_cons (scm_cons (scm_from_int (start), scm_from_int (end)),
285 ly_floatvector2scm (translates)),
287 me->set_property ("minimum-translations-alist", mta);
293 Align_interface::align_elements_to_ideal_distances (Grob *me)
295 System *sys = me->get_system ();
298 Page_layout_problem layout (NULL, SCM_EOL, scm_list_1 (sys->self_scm ()));
299 layout.solution (true);
302 programming_error ("vertical alignment called before line breaking");
306 Align_interface::align_elements_to_minimum_distances (Grob *me, Axis a)
308 extract_grob_set (me, "elements", all_grobs);
310 vector<Real> translates = get_minimum_translations (me, all_grobs, a);
311 if (translates.size ())
312 for (vsize j = 0; j < all_grobs.size (); j++)
313 all_grobs[j]->translate_axis (translates[j], a);
317 Align_interface::get_pure_child_y_translation (Grob *me, Grob *ch, int start, int end)
319 extract_grob_set (me, "elements", all_grobs);
320 vector<Real> translates = get_pure_minimum_translations (me, all_grobs, Y_AXIS, start, end);
322 if (translates.size ())
324 for (vsize i = 0; i < all_grobs.size (); i++)
325 if (all_grobs[i] == ch)
326 return translates[i];
331 programming_error ("tried to get a translation for something that is no child of mine");
336 Align_interface::axis (Grob *me)
338 return Axis (scm_to_int (scm_car (me->get_property ("axes"))));
342 Align_interface::add_element (Grob *me, Grob *element)
344 Axis a = Align_interface::axis (me);
345 SCM sym = axis_offset_symbol (a);
346 SCM proc = axis_parent_positioning (a);
348 element->set_property (sym, proc);
349 Axis_group_interface::add_element (me, element);
353 Align_interface::set_ordered (Grob *me)
355 SCM ga_scm = me->get_object ("elements");
356 Grob_array *ga = unsmob_grob_array (ga_scm);
359 ga_scm = Grob_array::make_array ();
360 ga = unsmob_grob_array (ga_scm);
361 me->set_object ("elements", ga_scm);
364 ga->set_ordered (true);
367 ADD_INTERFACE (Align_interface,
368 "Order grobs from top to bottom, left to right, right to left"
369 " or bottom to top. For vertical alignments of staves, the"
370 " @code{break-system-details} of the left"
371 " @rinternals{NonMusicalPaperColumn} may be set to tune"
372 " vertical spacing.",
378 "minimum-translations-alist "