]> git.donarmstrong.com Git - lilypond.git/blob - lily/align-interface.cc
Prevents chord names from being pushed into the staff (issue 3160)
[lilypond.git] / lily / align-interface.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2000--2012 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 MAKE_SCHEME_CALLBACK (Align_interface, align_to_minimum_distances, 1);
36 SCM
37 Align_interface::align_to_minimum_distances (SCM smob)
38 {
39   Grob *me = unsmob_grob (smob);
40
41   me->set_property ("positioning-done", SCM_BOOL_T);
42
43   SCM axis = scm_car (me->get_property ("axes"));
44   Axis ax = Axis (scm_to_int (axis));
45
46   Align_interface::align_elements_to_minimum_distances (me, ax);
47
48   return SCM_BOOL_T;
49 }
50
51 MAKE_SCHEME_CALLBACK (Align_interface, align_to_ideal_distances, 1);
52 SCM
53 Align_interface::align_to_ideal_distances (SCM smob)
54 {
55   Grob *me = unsmob_grob (smob);
56
57   me->set_property ("positioning-done", SCM_BOOL_T);
58
59   Align_interface::align_elements_to_ideal_distances (me);
60
61   return SCM_BOOL_T;
62 }
63
64 /* for each grob, find its upper and lower skylines. If the extent is
65    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?
69 static void
70 get_skylines (Grob *me,
71               vector<Grob *> const &elements,
72               Axis a,
73               bool pure, int start, int end,
74               vector<Skyline_pair> *ret,
75               vector<bool> const *skip_elt)
76 {
77   Grob *other_common = common_refpoint_of_array (elements, me, other_axis (a));
78
79   for (vsize i = elements.size (); i--;)
80     {
81       Grob *g = elements[i];
82       Skyline_pair skylines;
83
84       if (!pure)
85         {
86           Skyline_pair *skys = Skyline_pair::unsmob (g->get_property (a == Y_AXIS
87                                                                       ? "vertical-skylines"
88                                                                       : "horizontal-skylines"));
89           if (skys)
90             skylines = *skys;
91
92           /* This skyline was calculated relative to the grob g. In order to compare it to
93              skylines belonging to other grobs, we need to shift it so that it is relative
94              to the common reference. */
95           Real offset = g->relative_coordinate (other_common, other_axis (a));
96           skylines.shift (offset);
97         }
98       else if (!(*skip_elt)[i])
99         {
100           assert (a == Y_AXIS);
101           Interval extent = g->pure_height (g, start, end);
102
103           // This is a hack to get better accuracy on the pure-height of VerticalAlignment.
104           // It's quite common for a treble clef to be the highest element of one system
105           // and for a low note (or lyrics) to be the lowest note on another. The two will
106           // never collide, but the pure-height stuff only works with bounding boxes, so it
107           // doesn't know that. The result is a significant over-estimation of the pure-height,
108           // especially on systems with many staves. To correct for this, we build a skyline
109           // in two parts: the part we did above contains most of the grobs (note-heads, etc.)
110           // while the bit we're about to do only contains the breakable grobs at the beginning
111           // of the system. This way, the tall treble clefs are only compared with the treble
112           // clefs of the other staff and they will be ignored if the staff above is, for example,
113           // lyrics.
114           if (Axis_group_interface::has_interface (g))
115             {
116               extent = Axis_group_interface::rest_of_line_pure_height (g, start, end);
117               Interval begin_of_line_extent = Axis_group_interface::begin_of_line_pure_height (g, start);
118               if (!begin_of_line_extent.is_empty ())
119                 {
120                   Box b;
121                   b[a] = begin_of_line_extent;
122                   b[other_axis (a)] = Interval (-infinity_f, -1);
123                   skylines.insert (b, other_axis (a));
124                 }
125             }
126
127           if (!extent.is_empty ())
128             {
129               Box b;
130               b[a] = extent;
131               b[other_axis (a)] = Interval (0, infinity_f);
132               skylines.insert (b, other_axis (a));
133             }
134         }
135
136       // even if the skyline is empty, we want to push it back
137       // the heap because we will use things like system-system-distance
138       // to account for its presence
139       ret->push_back (skylines);
140     }
141   reverse (*ret);
142 }
143
144 vector<Real>
145 Align_interface::get_minimum_translations (Grob *me,
146                                            vector<Grob *> const &all_grobs,
147                                            Axis a)
148 {
149   return internal_get_minimum_translations (me, all_grobs, a, true, false, 0, 0);
150 }
151
152 vector<Real>
153 Align_interface::get_pure_minimum_translations (Grob *me,
154                                                 vector<Grob *> const &all_grobs,
155                                                 Axis a, int start, int end)
156 {
157   return internal_get_minimum_translations (me, all_grobs, a, true, true, start, end);
158 }
159
160 vector<Real>
161 Align_interface::get_minimum_translations_without_min_dist (Grob *me,
162                                                             vector<Grob *> const &all_grobs,
163                                                             Axis a)
164 {
165   return internal_get_minimum_translations (me, all_grobs, a, false, false, 0, 0);
166 }
167
168 // If include_fixed_spacing is false, the only constraints that will be measured
169 // here are those that result from collisions (+ padding) and the spacing spec
170 // between adjacent staves.
171 // If include_fixed_spacing is true, constraints from line-break-system-details,
172 // basic-distance+stretchable=0, and staff-staff-spacing of spaceable staves
173 // with loose lines in between, are included as well.
174 // - If you want to find the minimum height of a system, include_fixed_spacing should be true.
175 // - If you're going to actually lay out the page, then it should be false (or
176 //   else centered dynamics will break when there is a fixed alignment).
177 vector<Real>
178 Align_interface::internal_get_minimum_translations (Grob *me,
179                                                     vector<Grob *> const &elems,
180                                                     Axis a,
181                                                     bool include_fixed_spacing,
182                                                     bool pure, int start, int end)
183 {
184   if (!pure && a == Y_AXIS && dynamic_cast<Spanner *> (me) && !me->get_system ())
185     me->programming_error ("vertical alignment called before line-breaking");
186
187   // check the cache
188   if (pure)
189     {
190       SCM fv = ly_assoc_get (scm_cons (scm_from_int (start), scm_from_int (end)),
191                              me->get_property ("minimum-translations-alist"),
192                              SCM_EOL);
193       if (fv != SCM_EOL)
194         return ly_scm2floatvector (fv);
195     }
196
197   // If include_fixed_spacing is true, we look at things like system-system-spacing
198   // and alignment-distances, which only make sense for the toplevel VerticalAlignment.
199   // If we aren't toplevel, we're working on something like BassFigureAlignment
200   // and so we definitely don't want to include alignment-distances!
201   if (!dynamic_cast<System *> (me->get_parent (Y_AXIS)))
202     include_fixed_spacing = false;
203
204   Direction stacking_dir = robust_scm2dir (me->get_property ("stacking-dir"),
205                                            DOWN);
206   vector<Skyline_pair> skylines;
207   vector<bool> skip_elt;
208   // only add to skip elt if pure
209   // if not pure, no dead element should be in the list
210   for (vsize i = 0; i < elems.size (); i++)
211     {
212       if (!pure && !elems[i]->is_live ())
213         elems[i]->programming_error ("I should be dead by now...");
214       skip_elt.push_back (pure && Hara_kiri_group_spanner::request_suicide (elems[i], start, end));
215     }
216
217   get_skylines (me, elems, a, pure, start, end, &skylines, &skip_elt);
218
219   Real where = 0;
220   Real default_padding = robust_scm2double (me->get_property ("padding"), 0.0);
221   vector<Real> translates;
222   Skyline down_skyline (stacking_dir);
223   Real last_spaceable_element_pos = 0;
224   Grob *last_spaceable_element = 0;
225   Skyline last_spaceable_skyline (stacking_dir);
226   int spaceable_count = 0;
227   for (vsize j = 0; j < elems.size (); j++)
228     {
229       // This means that it will be suicided later downstream, so we
230       // skip it so that its padding is not added in.
231       if (pure && skip_elt[j])
232         continue;
233
234       Real dy = 0;
235       Real padding = default_padding;
236
237       if (j == 0)
238         dy = skylines[j][-stacking_dir].max_height () + padding;
239       else
240         {
241           SCM spec = Page_layout_problem::get_spacing_spec (elems[j - 1], elems[j], pure, start, end);
242           Page_layout_problem::read_spacing_spec (spec, &padding, ly_symbol2scm ("padding"));
243
244           dy = down_skyline.distance (skylines[j][-stacking_dir]) + padding;
245
246           Real spec_distance = 0;
247           if (Page_layout_problem::read_spacing_spec (spec, &spec_distance, ly_symbol2scm ("minimum-distance")))
248             dy = max (dy, spec_distance);
249           // Consider the likely final spacing when estimating distance between staves of the full score
250           if (INT_MAX == end && 0 == start
251               && Page_layout_problem::read_spacing_spec (spec, &spec_distance, ly_symbol2scm ("basic-distance")))
252             dy = max (dy, spec_distance);
253
254           if (include_fixed_spacing && Page_layout_problem::is_spaceable (elems[j]) && last_spaceable_element)
255             {
256               // Spaceable staves may have
257               // constraints coming from the previous spaceable staff
258               // as well as from the previous staff.
259               spec = Page_layout_problem::get_spacing_spec (last_spaceable_element, elems[j], pure, start, end);
260               Real spaceable_padding = 0;
261               Page_layout_problem::read_spacing_spec (spec,
262                                                       &spaceable_padding,
263                                                       ly_symbol2scm ("padding"));
264               dy = max (dy, (last_spaceable_skyline.distance (skylines[j][-stacking_dir])
265                              + stacking_dir * (last_spaceable_element_pos - where) + spaceable_padding));
266
267               Real spaceable_min_distance = 0;
268               if (Page_layout_problem::read_spacing_spec (spec,
269                                                           &spaceable_min_distance,
270                                                           ly_symbol2scm ("minimum-distance")))
271                 dy = max (dy, spaceable_min_distance + stacking_dir * (last_spaceable_element_pos - where));
272
273               dy = max (dy, Page_layout_problem::get_fixed_spacing (last_spaceable_element, elems[j], spaceable_count,
274                                                                     pure, start, end));
275             }
276         }
277
278       if (isinf (dy)) /* if the skyline is empty, maybe max_height is infinity_f */
279         dy = 0.0;
280
281       dy = max (0.0, dy);
282       down_skyline.raise (-stacking_dir * dy);
283       down_skyline.merge (skylines[j][stacking_dir]);
284       where += stacking_dir * dy;
285       translates.push_back (where);
286
287       if (Page_layout_problem::is_spaceable (elems[j]))
288         {
289           spaceable_count++;
290           last_spaceable_element = elems[j];
291           last_spaceable_element_pos = where;
292           last_spaceable_skyline = down_skyline;
293         }
294     }
295
296   // So far, we've computed the translates for all the non-empty elements.
297   // Here, we set the translates for the empty elements: an empty element
298   // gets the same translation as the last non-empty element before it.
299   vector<Grob *> non_empty_elems;
300   for (vsize i = 0; i < elems.size (); i++)
301     if (!skip_elt[i])
302       non_empty_elems.push_back (elems[i]);
303
304   vector<Real> all_translates;
305   if (!translates.empty ())
306     {
307       Real w = translates[0];
308       for (vsize i = 0, j = 0; j < elems.size (); j++)
309         {
310           if (i < non_empty_elems.size () && elems[j] == non_empty_elems[i])
311             w = translates[i++];
312           all_translates.push_back (w);
313         }
314     }
315
316   if (pure)
317     {
318       SCM mta = me->get_property ("minimum-translations-alist");
319       mta = scm_cons (scm_cons (scm_cons (scm_from_int (start), scm_from_int (end)),
320                                 ly_floatvector2scm (all_translates)),
321                       mta);
322       me->set_property ("minimum-translations-alist", mta);
323     }
324   return all_translates;
325 }
326
327 void
328 Align_interface::align_elements_to_ideal_distances (Grob *me)
329 {
330   System *sys = me->get_system ();
331   if (sys)
332     {
333       Page_layout_problem layout (NULL, SCM_EOL, scm_list_1 (sys->self_scm ()));
334       layout.solution (true);
335     }
336   else
337     programming_error ("vertical alignment called before line breaking");
338 }
339
340 void
341 Align_interface::align_elements_to_minimum_distances (Grob *me, Axis a)
342 {
343   extract_grob_set (me, "elements", all_grobs);
344
345   vector<Real> translates = get_minimum_translations (me, all_grobs, a);
346   if (translates.size ())
347     for (vsize j = 0; j < all_grobs.size (); j++)
348       all_grobs[j]->translate_axis (translates[j], a);
349 }
350
351 Real
352 Align_interface::get_pure_child_y_translation (Grob *me, Grob *ch, int start, int end)
353 {
354   extract_grob_set (me, "elements", all_grobs);
355   vector<Real> translates = get_pure_minimum_translations (me, all_grobs, Y_AXIS, start, end);
356
357   if (translates.size ())
358     {
359       for (vsize i = 0; i < all_grobs.size (); i++)
360         if (all_grobs[i] == ch)
361           return translates[i];
362     }
363   else
364     return 0;
365
366   programming_error ("tried to get a translation for something that is no child of mine");
367   return 0;
368 }
369
370 Axis
371 Align_interface::axis (Grob *me)
372 {
373   return Axis (scm_to_int (scm_car (me->get_property ("axes"))));
374 }
375
376 void
377 Align_interface::add_element (Grob *me, Grob *element)
378 {
379   Axis a = Align_interface::axis (me);
380   SCM sym = axis_offset_symbol (a);
381   SCM proc = axis_parent_positioning (a);
382
383   element->set_property (sym, proc);
384   Axis_group_interface::add_element (me, element);
385 }
386
387 void
388 Align_interface::set_ordered (Grob *me)
389 {
390   SCM ga_scm = me->get_object ("elements");
391   Grob_array *ga = unsmob_grob_array (ga_scm);
392   if (!ga)
393     {
394       ga_scm = Grob_array::make_array ();
395       ga = unsmob_grob_array (ga_scm);
396       me->set_object ("elements", ga_scm);
397     }
398
399   ga->set_ordered (true);
400 }
401
402 ADD_INTERFACE (Align_interface,
403                "Order grobs from top to bottom, left to right, right to left"
404                " or bottom to top.  For vertical alignments of staves, the"
405                " @code{break-system-details} of the left"
406                " @rinternals{NonMusicalPaperColumn} may be set to tune"
407                " vertical spacing.",
408
409                /* properties */
410                "align-dir "
411                "axes "
412                "elements "
413                "minimum-translations-alist "
414                "padding "
415                "positioning-done "
416                "stacking-dir "
417               );