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 /* 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 else if (!last_nonempty_element)
217 dy = skyline[-stacking_dir].max_height () + padding;
220 SCM spec = Page_layout_problem::get_spacing_spec (last_nonempty_element, elems[j], pure, start, end);
221 Page_layout_problem::read_spacing_spec (spec, &padding, ly_symbol2scm ("padding"));
223 dy = down_skyline.distance (skyline[-stacking_dir]) + padding;
225 Real spec_distance = 0;
226 if (Page_layout_problem::read_spacing_spec (spec, &spec_distance, ly_symbol2scm ("minimum-distance")))
227 dy = max (dy, spec_distance);
228 // Consider the likely final spacing when estimating distance between staves of the full score
229 if (INT_MAX == end && 0 == start
230 && Page_layout_problem::read_spacing_spec (spec, &spec_distance, ly_symbol2scm ("basic-distance")))
231 dy = max (dy, spec_distance);
233 if (include_fixed_spacing && Page_layout_problem::is_spaceable (elems[j]) && last_spaceable_element)
235 // Spaceable staves may have
236 // constraints coming from the previous spaceable staff
237 // as well as from the previous staff.
238 spec = Page_layout_problem::get_spacing_spec (last_spaceable_element, elems[j], pure, start, end);
239 Real spaceable_padding = 0;
240 Page_layout_problem::read_spacing_spec (spec,
242 ly_symbol2scm ("padding"));
243 dy = max (dy, (last_spaceable_skyline.distance (skyline[-stacking_dir])
244 + stacking_dir * (last_spaceable_element_pos - where) + spaceable_padding));
246 Real spaceable_min_distance = 0;
247 if (Page_layout_problem::read_spacing_spec (spec,
248 &spaceable_min_distance,
249 ly_symbol2scm ("minimum-distance")))
250 dy = max (dy, spaceable_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 down_skyline.raise (-stacking_dir * dy);
259 down_skyline.merge (skyline[stacking_dir]);
260 where += stacking_dir * dy;
261 translates.push_back (where);
263 if (Page_layout_problem::is_spaceable (elems[j]))
266 last_spaceable_element = elems[j];
267 last_spaceable_element_pos = where;
268 last_spaceable_skyline = down_skyline;
270 if (!skyline.is_empty ())
271 last_nonempty_element = elems[j];
276 SCM mta = me->get_property ("minimum-translations-alist");
277 mta = scm_cons (scm_cons (scm_cons (scm_from_int (start), scm_from_int (end)),
278 ly_floatvector2scm (translates)),
280 me->set_property ("minimum-translations-alist", mta);
286 Align_interface::align_elements_to_ideal_distances (Grob *me)
288 System *sys = me->get_system ();
291 Page_layout_problem layout (NULL, SCM_EOL, scm_list_1 (sys->self_scm ()));
292 layout.solution (true);
295 programming_error ("vertical alignment called before line breaking");
299 Align_interface::align_elements_to_minimum_distances (Grob *me, Axis a)
301 extract_grob_set (me, "elements", all_grobs);
303 vector<Real> translates = get_minimum_translations (me, all_grobs, a);
304 if (translates.size ())
305 for (vsize j = 0; j < all_grobs.size (); j++)
306 all_grobs[j]->translate_axis (translates[j], a);
310 Align_interface::get_pure_child_y_translation (Grob *me, Grob *ch, int start, int end)
312 extract_grob_set (me, "elements", all_grobs);
313 vector<Real> translates = get_pure_minimum_translations (me, all_grobs, Y_AXIS, start, end);
315 if (translates.size ())
317 for (vsize i = 0; i < all_grobs.size (); i++)
318 if (all_grobs[i] == ch)
319 return translates[i];
324 programming_error ("tried to get a translation for something that is no child of mine");
329 Align_interface::axis (Grob *me)
331 return Axis (scm_to_int (scm_car (me->get_property ("axes"))));
335 Align_interface::add_element (Grob *me, Grob *element)
337 Axis a = Align_interface::axis (me);
338 SCM sym = axis_offset_symbol (a);
339 SCM proc = axis_parent_positioning (a);
341 element->set_property (sym, proc);
342 Axis_group_interface::add_element (me, element);
346 Align_interface::set_ordered (Grob *me)
348 SCM ga_scm = me->get_object ("elements");
349 Grob_array *ga = unsmob_grob_array (ga_scm);
352 ga_scm = Grob_array::make_array ();
353 ga = unsmob_grob_array (ga_scm);
354 me->set_object ("elements", ga_scm);
357 ga->set_ordered (true);
360 ADD_INTERFACE (Align_interface,
361 "Order grobs from top to bottom, left to right, right to left"
362 " or bottom to top. For vertical alignments of staves, the"
363 " @code{break-system-details} of the left"
364 " @rinternals{NonMusicalPaperColumn} may be set to tune"
365 " vertical spacing.",
371 "minimum-translations-alist "