]> git.donarmstrong.com Git - lilypond.git/blob - lily/align-interface.cc
Revert "Issue 4550 (2/2) Avoid "using namespace std;" in included files"
[lilypond.git] / lily / align-interface.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2000--2015 Han-Wen Nienhuys <hanwen@xs4all.nl>
5
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.
10
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.
15
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/>.
18 */
19
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"
34
35 using std::vector;
36
37 MAKE_SCHEME_CALLBACK (Align_interface, align_to_minimum_distances, 1);
38 SCM
39 Align_interface::align_to_minimum_distances (SCM smob)
40 {
41   Grob *me = unsmob<Grob> (smob);
42
43   me->set_property ("positioning-done", SCM_BOOL_T);
44
45   SCM axis = scm_car (me->get_property ("axes"));
46   Axis ax = Axis (scm_to_int (axis));
47
48   Align_interface::align_elements_to_minimum_distances (me, ax);
49
50   return SCM_BOOL_T;
51 }
52
53 MAKE_SCHEME_CALLBACK (Align_interface, align_to_ideal_distances, 1);
54 SCM
55 Align_interface::align_to_ideal_distances (SCM smob)
56 {
57   Grob *me = unsmob<Grob> (smob);
58
59   me->set_property ("positioning-done", SCM_BOOL_T);
60
61   Align_interface::align_elements_to_ideal_distances (me);
62
63   return SCM_BOOL_T;
64 }
65
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?
71 static Skyline_pair
72 get_skylines (Grob *g,
73               Axis a,
74               Grob *other_common,
75               bool pure, int start, int end)
76 {
77   Skyline_pair skylines;
78
79   if (!pure)
80     {
81       Skyline_pair *skys = unsmob<Skyline_pair> (g->get_property (a == Y_AXIS
82                                                                   ? "vertical-skylines"
83                                                                   : "horizontal-skylines"));
84       if (skys)
85         skylines = *skys;
86
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);
92     }
93   else if (Hara_kiri_group_spanner::request_suicide (g, start, end))
94     return skylines;
95   else
96     {
97       assert (a == Y_AXIS);
98       Interval extent = g->pure_y_extent (g, start, end);
99
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,
110       // lyrics.
111       if (has_interface<Axis_group_interface> (g))
112         {
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 ())
116             {
117               Box b;
118               b[a] = begin_of_line_extent;
119               b[other_axis (a)] = Interval (-infinity_f, -1);
120               skylines.insert (b, other_axis (a));
121             }
122         }
123
124       if (!extent.is_empty ())
125         {
126           Box b;
127           b[a] = extent;
128           b[other_axis (a)] = Interval (0, infinity_f);
129           skylines.insert (b, other_axis (a));
130         }
131     }
132   return skylines;
133 }
134
135 vector<Real>
136 Align_interface::get_minimum_translations (Grob *me,
137                                            vector<Grob *> const &all_grobs,
138                                            Axis a)
139 {
140   return internal_get_minimum_translations (me, all_grobs, a, true, false, 0, 0);
141 }
142
143 vector<Real>
144 Align_interface::get_pure_minimum_translations (Grob *me,
145                                                 vector<Grob *> const &all_grobs,
146                                                 Axis a, int start, int end)
147 {
148   return internal_get_minimum_translations (me, all_grobs, a, true, true, start, end);
149 }
150
151 vector<Real>
152 Align_interface::get_minimum_translations_without_min_dist (Grob *me,
153                                                             vector<Grob *> const &all_grobs,
154                                                             Axis a)
155 {
156   return internal_get_minimum_translations (me, all_grobs, a, false, false, 0, 0);
157 }
158
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).
168 vector<Real>
169 Align_interface::internal_get_minimum_translations (Grob *me,
170                                                     vector<Grob *> const &elems,
171                                                     Axis a,
172                                                     bool include_fixed_spacing,
173                                                     bool pure, int start, int end)
174 {
175   if (!pure && a == Y_AXIS && dynamic_cast<Spanner *> (me) && !me->get_system ())
176     me->programming_error ("vertical alignment called before line-breaking");
177
178   // check the cache
179   if (pure)
180     {
181       SCM fv = ly_assoc_get (scm_cons (scm_from_int (start), scm_from_int (end)),
182                              me->get_property ("minimum-translations-alist"),
183                              SCM_EOL);
184       if (!scm_is_null (fv))
185         return ly_scm2floatvector (fv);
186     }
187
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;
194
195   Direction stacking_dir = robust_scm2dir (me->get_property ("stacking-dir"),
196                                            DOWN);
197
198   Grob *other_common = common_refpoint_of_array (elems, me, other_axis (a));
199
200   Real where = 0;
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++)
210     {
211       Real dy = 0;
212       Real padding = default_padding;
213
214       Skyline_pair skyline = get_skylines (elems[j], a, other_common, pure, start, end);
215
216       if (skyline.is_empty ())
217         {
218           translates.push_back (where);
219           continue;
220         }
221
222       if (!last_nonempty_element)
223         {
224           dy = skyline[-stacking_dir].max_height () + padding;
225           for (vsize k = j; k-- > 0;)
226             translates[k] = stacking_dir * dy;
227         }
228       else
229         {
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"));
232
233           dy = down_skyline.distance (skyline[-stacking_dir]) + padding;
234
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);
242
243           if (include_fixed_spacing && Page_layout_problem::is_spaceable (elems[j]) && last_spaceable_element)
244             {
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,
251                                                       &spaceable_padding,
252                                                       ly_symbol2scm ("padding"));
253               dy = max (dy, (last_spaceable_skyline.distance (skyline[-stacking_dir])
254                              + stacking_dir * (last_spaceable_element_pos - where) + spaceable_padding));
255
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));
261
262               dy = max (dy, Page_layout_problem::get_fixed_spacing (last_spaceable_element, elems[j], spaceable_count,
263                                                                     pure, start, end));
264             }
265         }
266
267       dy = 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);
272
273       if (Page_layout_problem::is_spaceable (elems[j]))
274         {
275           spaceable_count++;
276           last_spaceable_element = elems[j];
277           last_spaceable_element_pos = where;
278           last_spaceable_skyline = down_skyline;
279         }
280       last_nonempty_element = elems[j];
281     }
282
283   if (pure)
284     {
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)),
288                       mta);
289       me->set_property ("minimum-translations-alist", mta);
290     }
291   return translates;
292 }
293
294 void
295 Align_interface::align_elements_to_ideal_distances (Grob *me)
296 {
297   System *sys = me->get_system ();
298   if (sys)
299     {
300       Page_layout_problem layout (NULL, SCM_EOL, scm_list_1 (sys->self_scm ()));
301       layout.solution (true);
302     }
303   else
304     programming_error ("vertical alignment called before line breaking");
305 }
306
307 void
308 Align_interface::align_elements_to_minimum_distances (Grob *me, Axis a)
309 {
310   extract_grob_set (me, "elements", all_grobs);
311
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);
316 }
317
318 Real
319 Align_interface::get_pure_child_y_translation (Grob *me, Grob *ch, int start, int end)
320 {
321   extract_grob_set (me, "elements", all_grobs);
322   vector<Real> translates = get_pure_minimum_translations (me, all_grobs, Y_AXIS, start, end);
323
324   if (translates.size ())
325     {
326       for (vsize i = 0; i < all_grobs.size (); i++)
327         if (all_grobs[i] == ch)
328           return translates[i];
329     }
330   else
331     return 0;
332
333   programming_error ("tried to get a translation for something that is no child of mine");
334   return 0;
335 }
336
337 Axis
338 Align_interface::axis (Grob *me)
339 {
340   return Axis (scm_to_int (scm_car (me->get_property ("axes"))));
341 }
342
343 void
344 Align_interface::add_element (Grob *me, Grob *element)
345 {
346   Axis a = Align_interface::axis (me);
347   SCM sym = axis_offset_symbol (a);
348   SCM proc = axis_parent_positioning (a);
349
350   element->set_property (sym, proc);
351   Axis_group_interface::add_element (me, element);
352 }
353
354 void
355 Align_interface::set_ordered (Grob *me)
356 {
357   SCM ga_scm = me->get_object ("elements");
358   Grob_array *ga = unsmob<Grob_array> (ga_scm);
359   if (!ga)
360     {
361       ga_scm = Grob_array::make_array ();
362       ga = unsmob<Grob_array> (ga_scm);
363       me->set_object ("elements", ga_scm);
364     }
365
366   ga->set_ordered (true);
367 }
368
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.",
375
376                /* properties */
377                "align-dir "
378                "axes "
379                "elements "
380                "minimum-translations-alist "
381                "padding "
382                "positioning-done "
383                "stacking-dir "
384               );