]> git.donarmstrong.com Git - lilypond.git/blob - lily/align-interface.cc
Adds in-notes to LilyPond.
[lilypond.git] / lily / align-interface.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2000--2011 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 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?
70 static void
71 get_skylines (Grob *me,
72               vector<Grob *> *const elements,
73               Axis a,
74               bool pure, int start, int end,
75               vector<Skyline_pair> *const ret)
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
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               && !Hara_kiri_group_spanner::request_suicide (g, start, end))
116             {
117               extent = Axis_group_interface::rest_of_line_pure_height (g, start, end);
118               Interval begin_of_line_extent = Axis_group_interface::begin_of_line_pure_height (g, start);
119               if (!begin_of_line_extent.is_empty ())
120                 {
121                   Box b;
122                   b[a] = begin_of_line_extent;
123                   b[other_axis (a)] = Interval (-infinity_f, -1);
124                   skylines.insert (b, 0, other_axis (a));
125                 }
126             }
127
128           if (!extent.is_empty ())
129             {
130               Box b;
131               b[a] = extent;
132               b[other_axis (a)] = Interval (0, infinity_f);
133               skylines.insert (b, 0, other_axis (a));
134             }
135         }
136
137       if (skylines.is_empty ())
138         elements->erase (elements->begin () + i);
139       else
140         ret->push_back (skylines);
141     }
142   reverse (*ret);
143 }
144
145 vector<Real>
146 Align_interface::get_minimum_translations (Grob *me,
147                                            vector<Grob *> const &all_grobs,
148                                            Axis a)
149 {
150   return internal_get_minimum_translations (me, all_grobs, a, true, false, 0, 0);
151 }
152
153 vector<Real>
154 Align_interface::get_pure_minimum_translations (Grob *me,
155                                                 vector<Grob *> const &all_grobs,
156                                                 Axis a, int start, int end)
157 {
158   return internal_get_minimum_translations (me, all_grobs, a, true, true, start, end);
159 }
160
161 vector<Real>
162 Align_interface::get_minimum_translations_without_min_dist (Grob *me,
163                                                             vector<Grob *> const &all_grobs,
164                                                             Axis a)
165 {
166   return internal_get_minimum_translations (me, all_grobs, a, false, false, 0, 0);
167 }
168
169 // If include_fixed_spacing is false, the only constraints that will be measured
170 // here are those that result from collisions (+ padding) and minimum-distance
171 // between adjacent staves.
172 // If include_fixed_spacing is true, constraints from line-break-system-details,
173 // basic-distance+stretchable=0, and staff-staff-spacing of spaceable staves
174 // with loose lines in between, are included as well.
175 // - If you want to find the minimum height of a system, include_fixed_spacing should be true.
176 // - If you're going to actually lay out the page, then it should be false (or
177 //   else centered dynamics will break when there is a fixed alignment).
178 vector<Real>
179 Align_interface::internal_get_minimum_translations (Grob *me,
180                                                     vector<Grob *> const &all_grobs,
181                                                     Axis a,
182                                                     bool include_fixed_spacing,
183                                                     bool pure, int start, int end)
184 {
185   if (!pure && a == Y_AXIS && dynamic_cast<Spanner *> (me) && !me->get_system ())
186     me->programming_error ("vertical alignment called before line-breaking");
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   vector<Grob *> elems (all_grobs); // writable copy
198   vector<Skyline_pair> skylines;
199
200   get_skylines (me, &elems, a, pure, start, end, &skylines);
201
202   Real where = 0;
203   Real default_padding = robust_scm2double (me->get_property ("padding"), 0.0);
204   vector<Real> translates;
205   Skyline down_skyline (stacking_dir);
206   Real last_spaceable_element_pos = 0;
207   Grob *last_spaceable_element = 0;
208   Skyline last_spaceable_skyline (stacking_dir);
209   int spaceable_count = 0;
210   for (vsize j = 0; j < elems.size (); j++)
211     {
212       Real dy = 0;
213       Real padding = default_padding;
214
215       if (j == 0)
216         dy = skylines[j][-stacking_dir].max_height () + padding;
217       else
218         {
219           SCM spec = Page_layout_problem::get_spacing_spec (elems[j - 1], elems[j], pure, start, end);
220           Page_layout_problem::read_spacing_spec (spec, &padding, ly_symbol2scm ("padding"));
221
222           dy = down_skyline.distance (skylines[j][-stacking_dir]) + padding;
223
224           Real min_distance = 0;
225           if (Page_layout_problem::read_spacing_spec (spec, &min_distance, ly_symbol2scm ("minimum-distance")))
226             dy = max (dy, min_distance);
227
228           if (include_fixed_spacing && Page_layout_problem::is_spaceable (elems[j]) && last_spaceable_element)
229             {
230               // Spaceable staves may have
231               // constraints coming from the previous spaceable staff
232               // as well as from the previous staff.
233               spec = Page_layout_problem::get_spacing_spec (last_spaceable_element, elems[j], pure, start, end);
234               Real spaceable_padding = 0;
235               Page_layout_problem::read_spacing_spec (spec,
236                                                       &spaceable_padding,
237                                                       ly_symbol2scm ("padding"));
238               dy = max (dy, (last_spaceable_skyline.distance (skylines[j][-stacking_dir])
239                              + stacking_dir * (last_spaceable_element_pos - where) + spaceable_padding));
240
241               Real spaceable_min_distance = 0;
242               if (Page_layout_problem::read_spacing_spec (spec,
243                                                           &spaceable_min_distance,
244                                                           ly_symbol2scm ("minimum-distance")))
245                 dy = max (dy, spaceable_min_distance + stacking_dir * (last_spaceable_element_pos - where));
246
247               dy = max (dy, Page_layout_problem::get_fixed_spacing (last_spaceable_element, elems[j], spaceable_count,
248                                                                     pure, start, end));
249             }
250         }
251
252       if (isinf (dy)) /* if the skyline is empty, maybe max_height is infinity_f */
253         dy = 0.0;
254
255       dy = max (0.0, dy);
256       down_skyline.raise (-stacking_dir * dy);
257       down_skyline.merge (skylines[j][stacking_dir]);
258       where += stacking_dir * dy;
259       translates.push_back (where);
260
261       if (Page_layout_problem::is_spaceable (elems[j]))
262         {
263           spaceable_count++;
264           last_spaceable_element = elems[j];
265           last_spaceable_element_pos = where;
266           last_spaceable_skyline = down_skyline;
267         }
268     }
269
270   // So far, we've computed the translates for all the non-empty elements.
271   // Here, we set the translates for the empty elements: an empty element
272   // gets the same translation as the last non-empty element before it.
273   vector<Real> all_translates;
274   if (!translates.empty ())
275     {
276       Real w = translates[0];
277       for (vsize i = 0, j = 0; j < all_grobs.size (); j++)
278         {
279           if (i < elems.size () && all_grobs[j] == elems[i])
280             w = translates[i++];
281           all_translates.push_back (w);
282         }
283     }
284   return all_translates;
285 }
286
287 void
288 Align_interface::align_elements_to_ideal_distances (Grob *me)
289 {
290   System *sys = me->get_system ();
291   if (sys)
292     {
293       Page_layout_problem layout (NULL, SCM_EOL, scm_list_1 (sys->self_scm ()));
294       layout.solution (true);
295     }
296   else
297     programming_error ("vertical alignment called before line breaking");
298 }
299
300 void
301 Align_interface::align_elements_to_minimum_distances (Grob *me, Axis a)
302 {
303   extract_grob_set (me, "elements", all_grobs);
304
305   vector<Real> translates = get_minimum_translations (me, all_grobs, a);
306   if (translates.size ())
307     for (vsize j = 0; j < all_grobs.size (); j++)
308       all_grobs[j]->translate_axis (translates[j], a);
309 }
310
311 Real
312 Align_interface::get_pure_child_y_translation (Grob *me, Grob *ch, int start, int end)
313 {
314   extract_grob_set (me, "elements", all_grobs);
315   vector<Real> translates = get_pure_minimum_translations (me, all_grobs, Y_AXIS, start, end);
316
317   if (translates.size ())
318     {
319       for (vsize i = 0; i < all_grobs.size (); i++)
320         if (all_grobs[i] == ch)
321           return translates[i];
322     }
323   else
324     return 0;
325
326   programming_error ("tried to get a translation for something that is no child of mine");
327   return 0;
328 }
329
330 Axis
331 Align_interface::axis (Grob *me)
332 {
333   return Axis (scm_to_int (scm_car (me->get_property ("axes"))));
334 }
335
336 void
337 Align_interface::add_element (Grob *me, Grob *element)
338 {
339   Axis a = Align_interface::axis (me);
340   SCM sym = axis_offset_symbol (a);
341   SCM proc = axis_parent_positioning (a);
342
343   element->set_property (sym, proc);
344   Axis_group_interface::add_element (me, element);
345 }
346
347 void
348 Align_interface::set_ordered (Grob *me)
349 {
350   SCM ga_scm = me->get_object ("elements");
351   Grob_array *ga = unsmob_grob_array (ga_scm);
352   if (!ga)
353     {
354       ga_scm = Grob_array::make_array ();
355       ga = unsmob_grob_array (ga_scm);
356       me->set_object ("elements", ga_scm);
357     }
358
359   ga->set_ordered (true);
360 }
361
362 ADD_INTERFACE (Align_interface,
363                "Order grobs from top to bottom, left to right, right to left"
364                " or bottom to top.  For vertical alignments of staves, the"
365                " @code{break-system-details} of the left"
366                " @rinternals{NonMusicalPaperColumn} may be set to tune"
367                " vertical spacing.",
368
369                /* properties */
370                "align-dir "
371                "axes "
372                "elements "
373                "padding "
374                "positioning-done "
375                "stacking-dir "
376               );