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 = a == Y_AXIS
87 ? Skyline_pair::unsmob (g->get_property ("vertical-skylines"))
88 : Skyline_pair::unsmob (g->get_property ("horizontal-skylines"));
93 /* This skyline was calculated relative to the grob g. In order to compare it to
94 skylines belonging to other grobs, we need to shift it so that it is relative
95 to the common reference. */
96 Real offset = g->relative_coordinate (other_common, other_axis (a));
97 skylines.shift (offset);
101 assert (a == Y_AXIS);
102 Interval extent = g->pure_height (g, start, end);
104 // This is a hack to get better accuracy on the pure-height of VerticalAlignment.
105 // It's quite common for a treble clef to be the highest element of one system
106 // and for a low note (or lyrics) to be the lowest note on another. The two will
107 // never collide, but the pure-height stuff only works with bounding boxes, so it
108 // doesn't know that. The result is a significant over-estimation of the pure-height,
109 // especially on systems with many staves. To correct for this, we build a skyline
110 // in two parts: the part we did above contains most of the grobs (note-heads, etc.)
111 // while the bit we're about to do only contains the breakable grobs at the beginning
112 // of the system. This way, the tall treble clefs are only compared with the treble
113 // clefs of the other staff and they will be ignored if the staff above is, for example,
115 if (Axis_group_interface::has_interface (g)
116 && !Hara_kiri_group_spanner::request_suicide (g, start, end))
118 extent = Axis_group_interface::rest_of_line_pure_height (g, start, end);
119 Interval begin_of_line_extent = Axis_group_interface::begin_of_line_pure_height (g, start);
120 if (!begin_of_line_extent.is_empty ())
123 b[a] = begin_of_line_extent;
124 b[other_axis (a)] = Interval (-infinity_f, -1);
125 skylines.insert (b, other_axis (a));
129 if (!extent.is_empty ())
133 b[other_axis (a)] = Interval (0, infinity_f);
134 skylines.insert (b, other_axis (a));
138 if (skylines.is_empty ())
139 elements->erase (elements->begin () + i);
141 ret->push_back (skylines);
147 Align_interface::get_minimum_translations (Grob *me,
148 vector<Grob *> const &all_grobs,
151 return internal_get_minimum_translations (me, all_grobs, a, true, false, 0, 0);
155 Align_interface::get_pure_minimum_translations (Grob *me,
156 vector<Grob *> const &all_grobs,
157 Axis a, int start, int end)
159 return internal_get_minimum_translations (me, all_grobs, a, true, true, start, end);
163 Align_interface::get_minimum_translations_without_min_dist (Grob *me,
164 vector<Grob *> const &all_grobs,
167 return internal_get_minimum_translations (me, all_grobs, a, false, false, 0, 0);
170 // If include_fixed_spacing is false, the only constraints that will be measured
171 // here are those that result from collisions (+ padding) and the spacing spec
172 // between adjacent staves.
173 // If include_fixed_spacing is true, constraints from line-break-system-details,
174 // basic-distance+stretchable=0, and staff-staff-spacing of spaceable staves
175 // with loose lines in between, are included as well.
176 // - If you want to find the minimum height of a system, include_fixed_spacing should be true.
177 // - If you're going to actually lay out the page, then it should be false (or
178 // else centered dynamics will break when there is a fixed alignment).
180 Align_interface::internal_get_minimum_translations (Grob *me,
181 vector<Grob *> const &all_grobs,
183 bool include_fixed_spacing,
184 bool pure, int start, int end)
186 if (!pure && a == Y_AXIS && dynamic_cast<Spanner *> (me) && !me->get_system ())
187 me->programming_error ("vertical alignment called before line-breaking");
192 SCM fv = ly_assoc_get (scm_cons (scm_from_int (start), scm_from_int (end)),
193 me->get_property ("minimum-translations-alist"),
196 return ly_scm2floatvector (fv);
199 // If include_fixed_spacing is true, we look at things like system-system-spacing
200 // and alignment-distances, which only make sense for the toplevel VerticalAlignment.
201 // If we aren't toplevel, we're working on something like BassFigureAlignment
202 // and so we definitely don't want to include alignment-distances!
203 if (!dynamic_cast<System *> (me->get_parent (Y_AXIS)))
204 include_fixed_spacing = false;
206 Direction stacking_dir = robust_scm2dir (me->get_property ("stacking-dir"),
208 vector<Grob *> elems (all_grobs); // writable copy
209 vector<Skyline_pair> skylines;
211 get_skylines (me, &elems, a, pure, start, end, &skylines);
214 Real default_padding = robust_scm2double (me->get_property ("padding"), 0.0);
215 vector<Real> translates;
216 Skyline down_skyline (stacking_dir);
217 Real last_spaceable_element_pos = 0;
218 Grob *last_spaceable_element = 0;
219 Skyline last_spaceable_skyline (stacking_dir);
220 int spaceable_count = 0;
221 for (vsize j = 0; j < elems.size (); j++)
224 Real padding = default_padding;
227 dy = skylines[j][-stacking_dir].max_height () + padding;
230 SCM spec = Page_layout_problem::get_spacing_spec (elems[j - 1], elems[j], pure, start, end);
231 Page_layout_problem::read_spacing_spec (spec, &padding, ly_symbol2scm ("padding"));
233 dy = down_skyline.distance (skylines[j][-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 = 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 = 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 = max (dy, (last_spaceable_skyline.distance (skylines[j][-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 = max (dy, spaceable_min_distance + stacking_dir * (last_spaceable_element_pos - where));
262 dy = max (dy, Page_layout_problem::get_fixed_spacing (last_spaceable_element, elems[j], spaceable_count,
267 if (isinf (dy)) /* if the skyline is empty, maybe max_height is infinity_f */
271 down_skyline.raise (-stacking_dir * dy);
272 down_skyline.merge (skylines[j][stacking_dir]);
273 where += stacking_dir * dy;
274 translates.push_back (where);
276 if (Page_layout_problem::is_spaceable (elems[j]))
279 last_spaceable_element = elems[j];
280 last_spaceable_element_pos = where;
281 last_spaceable_skyline = down_skyline;
285 // So far, we've computed the translates for all the non-empty elements.
286 // Here, we set the translates for the empty elements: an empty element
287 // gets the same translation as the last non-empty element before it.
288 vector<Real> all_translates;
289 if (!translates.empty ())
291 Real w = translates[0];
292 for (vsize i = 0, j = 0; j < all_grobs.size (); j++)
294 if (i < elems.size () && all_grobs[j] == elems[i])
296 all_translates.push_back (w);
302 SCM mta = me->get_property ("minimum-translations-alist");
303 mta = scm_cons (scm_cons (scm_cons (scm_from_int (start), scm_from_int (end)),
304 ly_floatvector2scm (all_translates)),
306 me->set_property ("minimum-translations-alist", mta);
308 return all_translates;
312 Align_interface::align_elements_to_ideal_distances (Grob *me)
314 System *sys = me->get_system ();
317 Page_layout_problem layout (NULL, SCM_EOL, scm_list_1 (sys->self_scm ()));
318 layout.solution (true);
321 programming_error ("vertical alignment called before line breaking");
325 Align_interface::align_elements_to_minimum_distances (Grob *me, Axis a)
327 extract_grob_set (me, "elements", all_grobs);
329 vector<Real> translates = get_minimum_translations (me, all_grobs, a);
330 if (translates.size ())
331 for (vsize j = 0; j < all_grobs.size (); j++)
332 all_grobs[j]->translate_axis (translates[j], a);
336 Align_interface::get_pure_child_y_translation (Grob *me, Grob *ch, int start, int end)
338 extract_grob_set (me, "elements", all_grobs);
339 vector<Real> translates = get_pure_minimum_translations (me, all_grobs, Y_AXIS, start, end);
341 if (translates.size ())
343 for (vsize i = 0; i < all_grobs.size (); i++)
344 if (all_grobs[i] == ch)
345 return translates[i];
350 programming_error ("tried to get a translation for something that is no child of mine");
355 Align_interface::axis (Grob *me)
357 return Axis (scm_to_int (scm_car (me->get_property ("axes"))));
361 Align_interface::add_element (Grob *me, Grob *element)
363 Axis a = Align_interface::axis (me);
364 SCM sym = axis_offset_symbol (a);
365 SCM proc = axis_parent_positioning (a);
367 element->set_property (sym, proc);
368 Axis_group_interface::add_element (me, element);
372 Align_interface::set_ordered (Grob *me)
374 SCM ga_scm = me->get_object ("elements");
375 Grob_array *ga = unsmob_grob_array (ga_scm);
378 ga_scm = Grob_array::make_array ();
379 ga = unsmob_grob_array (ga_scm);
380 me->set_object ("elements", ga_scm);
383 ga->set_ordered (true);
386 ADD_INTERFACE (Align_interface,
387 "Order grobs from top to bottom, left to right, right to left"
388 " or bottom to top. For vertical alignments of staves, the"
389 " @code{break-system-details} of the left"
390 " @rinternals{NonMusicalPaperColumn} may be set to tune"
391 " vertical spacing.",
397 "minimum-translations-alist "