Fixes Issue 1504, allowing feather beam line breaking.
[lilypond/patrick.git] / lily / align-interface.cc
blobcb876f5bbf1de0297f590d7e79c3902a26af3ebe
1 /*
2 This file is part of LilyPond, the GNU music typesetter.
4 Copyright (C) 2000--2011 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"
25 #include "item.hh"
26 #include "page-layout-problem.hh"
27 #include "paper-book.hh"
28 #include "paper-column.hh"
29 #include "pointer-group-interface.hh"
30 #include "spanner.hh"
31 #include "skyline-pair.hh"
32 #include "system.hh"
33 #include "warn.hh"
36 MAKE_SCHEME_CALLBACK (Align_interface, align_to_minimum_distances, 1);
37 SCM
38 Align_interface::align_to_minimum_distances (SCM smob)
40 Grob *me = unsmob_grob (smob);
42 me->set_property ("positioning-done", SCM_BOOL_T);
44 SCM axis = scm_car (me->get_property ("axes"));
45 Axis ax = Axis (scm_to_int (axis));
47 Align_interface::align_elements_to_minimum_distances (me, ax);
49 return SCM_BOOL_T;
52 MAKE_SCHEME_CALLBACK (Align_interface, align_to_ideal_distances, 1);
53 SCM
54 Align_interface::align_to_ideal_distances (SCM smob)
56 Grob *me = unsmob_grob (smob);
58 me->set_property ("positioning-done", SCM_BOOL_T);
60 Align_interface::align_elements_to_ideal_distances (me);
62 return SCM_BOOL_T;
65 /* for each grob, find its upper and lower skylines. If the grob has
66 an empty extent, delete it from the list instead. If the extent is
67 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?
71 static void
72 get_skylines (Grob *me,
73 vector<Grob*> *const elements,
74 Axis a,
75 bool pure, int start, int end,
76 vector<Skyline_pair> *const ret)
78 Grob *other_common = common_refpoint_of_array (*elements, me, other_axis (a));
80 for (vsize i = elements->size (); i--;)
82 Grob *g = (*elements)[i];
83 Skyline_pair skylines;
85 if (!pure)
87 Skyline_pair *skys = Skyline_pair::unsmob (g->get_property (a == Y_AXIS
88 ? "vertical-skylines"
89 : "horizontal-skylines"));
90 if (skys)
91 skylines = *skys;
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);
99 else
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,
114 // lyrics.
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 ())
122 Box b;
123 b[a] = begin_of_line_extent;
124 b[other_axis (a)] = Interval (-infinity_f, -1);
125 skylines.insert (b, 0, other_axis (a));
129 if (!extent.is_empty ())
131 Box b;
132 b[a] = extent;
133 b[other_axis (a)] = Interval (0, infinity_f);
134 skylines.insert (b, 0, other_axis (a));
138 if (skylines.is_empty ())
139 elements->erase (elements->begin () + i);
140 else
141 ret->push_back (skylines);
143 reverse (*ret);
146 vector<Real>
147 Align_interface::get_minimum_translations (Grob *me,
148 vector<Grob*> const &all_grobs,
149 Axis a)
151 return internal_get_minimum_translations (me, all_grobs, a, true, false, 0, 0);
154 vector<Real>
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);
162 vector<Real>
163 Align_interface::get_minimum_translations_without_min_dist (Grob *me,
164 vector<Grob*> const &all_grobs,
165 Axis a)
167 return internal_get_minimum_translations (me, all_grobs, a, false, false, 0, 0);
170 // If include_fixed_spacing is true, the manually fixed spacings
171 // induced by stretchable=0 or alignment-distances are included
172 // in the minimum translations here. If you want to find the minimum
173 // height of a system, include_fixed_spacing should be true. If you
174 // want to actually lay out the page, then it should be false (or
175 // else centered dynamics will break when there is a fixed alignment).
176 vector<Real>
177 Align_interface::internal_get_minimum_translations (Grob *me,
178 vector<Grob*> const &all_grobs,
179 Axis a,
180 bool include_fixed_spacing,
181 bool pure, int start, int end)
183 if (!pure && a == Y_AXIS && dynamic_cast<Spanner*> (me) && !me->get_system ())
184 me->programming_error ("vertical alignment called before line-breaking");
186 Direction stacking_dir = robust_scm2dir (me->get_property ("stacking-dir"),
187 DOWN);
188 vector<Grob*> elems (all_grobs); // writable copy
189 vector<Skyline_pair> skylines;
191 get_skylines (me, &elems, a, pure, start, end, &skylines);
193 Real where = 0;
194 Real default_padding = robust_scm2double (me->get_property ("padding"), 0.0);
195 vector<Real> translates;
196 Skyline down_skyline (stacking_dir);
197 Real last_spaceable_element_pos = 0;
198 Grob *last_spaceable_element = 0;
199 int spaceable_count = 0;
200 for (vsize j = 0; j < elems.size (); j++)
202 Real dy = 0;
203 Real padding = default_padding;
205 if (j == 0)
206 dy = skylines[j][-stacking_dir].max_height ();
207 else
209 down_skyline.merge (skylines[j-1][stacking_dir]);
210 dy = down_skyline.distance (skylines[j][-stacking_dir]);
212 SCM spec = Page_layout_problem::get_spacing_spec (elems[j-1], elems[j], pure, start, end);
213 Page_layout_problem::read_spacing_spec (spec, &padding, ly_symbol2scm ("padding"));
215 Real min_distance = 0;
216 if (Page_layout_problem::read_spacing_spec (spec, &min_distance, ly_symbol2scm ("minimum-distance")))
217 dy = max (dy, min_distance);
219 if (include_fixed_spacing)
220 dy = max (dy, Page_layout_problem::get_fixed_spacing (elems[j-1], elems[j], spaceable_count, pure, start, end));
222 if (Page_layout_problem::is_spaceable (elems[j]) && last_spaceable_element)
224 // Spaceable staves may have
225 // constraints coming from the previous spaceable staff
226 // as well as from the previous staff.
227 spec = Page_layout_problem::get_spacing_spec (last_spaceable_element, elems[j], pure, start, end);
228 Real spaceable_padding = 0;
229 Page_layout_problem::read_spacing_spec (spec,
230 &spaceable_padding,
231 ly_symbol2scm ("padding"));
232 padding = max (padding, spaceable_padding);
234 Real min_distance = 0;
235 if (Page_layout_problem::read_spacing_spec (spec,
236 &min_distance,
237 ly_symbol2scm ("minimum-distance")))
238 dy = max (dy, min_distance + stacking_dir*(last_spaceable_element_pos - where));
240 if (include_fixed_spacing)
241 dy = max (dy, Page_layout_problem::get_fixed_spacing (last_spaceable_element, elems[j], spaceable_count,
242 pure, start, end));
246 if (isinf (dy)) /* if the skyline is empty, maybe max_height is infinity_f */
247 dy = 0.0;
249 dy = max (0.0, dy + padding);
250 down_skyline.raise (-stacking_dir * dy);
251 where += stacking_dir * dy;
252 translates.push_back (where);
254 if (Page_layout_problem::is_spaceable (elems[j]))
256 spaceable_count++;
257 last_spaceable_element = elems[j];
258 last_spaceable_element_pos = where;
262 // So far, we've computed the translates for all the non-empty elements.
263 // Here, we set the translates for the empty elements: an empty element
264 // gets the same translation as the last non-empty element before it.
265 vector<Real> all_translates;
266 if (!translates.empty ())
268 Real w = translates[0];
269 for (vsize i = 0, j = 0; j < all_grobs.size (); j++)
271 if (i < elems.size () && all_grobs[j] == elems[i])
272 w = translates[i++];
273 all_translates.push_back (w);
276 return all_translates;
279 void
280 Align_interface::align_elements_to_ideal_distances (Grob *me)
282 System *sys = me->get_system ();
283 if (sys)
285 Page_layout_problem layout (NULL, SCM_EOL, scm_list_1 (sys->self_scm ()));
286 layout.solution (true);
288 else
289 programming_error ("vertical alignment called before line breaking");
292 void
293 Align_interface::align_elements_to_minimum_distances (Grob *me, Axis a)
295 extract_grob_set (me, "elements", all_grobs);
297 vector<Real> translates = get_minimum_translations (me, all_grobs, a);
298 if (translates.size ())
299 for (vsize j = 0; j < all_grobs.size (); j++)
300 all_grobs[j]->translate_axis (translates[j], a);
303 Real
304 Align_interface::get_pure_child_y_translation (Grob *me, Grob *ch, int start, int end)
306 extract_grob_set (me, "elements", all_grobs);
307 vector<Real> translates = get_pure_minimum_translations (me, all_grobs, Y_AXIS, start, end);
309 if (translates.size ())
311 for (vsize i = 0; i < all_grobs.size (); i++)
312 if (all_grobs[i] == ch)
313 return translates[i];
315 else
316 return 0;
318 programming_error ("tried to get a translation for something that is no child of mine");
319 return 0;
322 Axis
323 Align_interface::axis (Grob *me)
325 return Axis (scm_to_int (scm_car (me->get_property ("axes"))));
328 void
329 Align_interface::add_element (Grob *me, Grob *element)
331 Axis a = Align_interface::axis (me);
332 SCM sym = axis_offset_symbol (a);
333 SCM proc = axis_parent_positioning (a);
335 element->set_property (sym, proc);
336 Axis_group_interface::add_element (me, element);
339 void
340 Align_interface::set_ordered (Grob *me)
342 SCM ga_scm = me->get_object ("elements");
343 Grob_array *ga = unsmob_grob_array (ga_scm);
344 if (!ga)
346 ga_scm = Grob_array::make_array ();
347 ga = unsmob_grob_array (ga_scm);
348 me->set_object ("elements", ga_scm);
351 ga->set_ordered (true);
354 ADD_INTERFACE (Align_interface,
355 "Order grobs from top to bottom, left to right, right to left"
356 " or bottom to top. For vertical alignments of staves, the"
357 " @code{break-system-details} of the left"
358 " @rinternals{NonMusicalPaperColumn} may be set to tune"
359 " vertical spacing.",
361 /* properties */
362 "align-dir "
363 "axes "
364 "elements "
365 "padding "
366 "positioning-done "
367 "stacking-dir "