Use scalar instead of embedded_scm for context mod overrides.
[lilypond/mpolesky.git] / lily / align-interface.cc
blob3d68bce912115b9bf16baba295895b428e8d6002
1 /*
2 align-interface.cc -- implement Align_interface
4 source file of the GNU LilyPond music typesetter
6 (c) 2000--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
9 #include "align-interface.hh"
10 #include "axis-group-interface.hh"
11 #include "grob-array.hh"
12 #include "hara-kiri-group-spanner.hh"
13 #include "international.hh"
14 #include "item.hh"
15 #include "page-layout-problem.hh"
16 #include "paper-book.hh"
17 #include "paper-column.hh"
18 #include "pointer-group-interface.hh"
19 #include "spanner.hh"
20 #include "skyline-pair.hh"
21 #include "system.hh"
22 #include "warn.hh"
25 MAKE_SCHEME_CALLBACK (Align_interface, align_to_minimum_distances, 1);
26 SCM
27 Align_interface::align_to_minimum_distances (SCM smob)
29 Grob *me = unsmob_grob (smob);
31 me->set_property ("positioning-done", SCM_BOOL_T);
33 SCM axis = scm_car (me->get_property ("axes"));
34 Axis ax = Axis (scm_to_int (axis));
36 Align_interface::align_elements_to_minimum_distances (me, ax);
38 return SCM_BOOL_T;
41 MAKE_SCHEME_CALLBACK (Align_interface, align_to_ideal_distances, 1);
42 SCM
43 Align_interface::align_to_ideal_distances (SCM smob)
45 Grob *me = unsmob_grob (smob);
47 me->set_property ("positioning-done", SCM_BOOL_T);
49 Align_interface::align_elements_to_ideal_distances (me);
51 return SCM_BOOL_T;
54 /* for each grob, find its upper and lower skylines. If the grob has
55 an empty extent, delete it from the list instead. If the extent is
56 non-empty but there is no skyline available (or pure is true), just
57 create a flat skyline from the bounding box */
58 // TODO(jneem): the pure and non-pure parts seem to share very little
59 // code. Split them into 2 functions, perhaps?
60 static void
61 get_skylines (Grob *me,
62 vector<Grob*> *const elements,
63 Axis a,
64 bool pure, int start, int end,
65 vector<Skyline_pair> *const ret)
67 Grob *other_common = common_refpoint_of_array (*elements, me, other_axis (a));
69 for (vsize i = elements->size (); i--;)
71 Grob *g = (*elements)[i];
72 Skyline_pair skylines;
74 if (!pure)
76 Skyline_pair *skys = Skyline_pair::unsmob (g->get_property (a == Y_AXIS
77 ? "vertical-skylines"
78 : "horizontal-skylines"));
79 if (skys)
80 skylines = *skys;
82 /* This skyline was calculated relative to the grob g. In order to compare it to
83 skylines belonging to other grobs, we need to shift it so that it is relative
84 to the common reference. */
85 Real offset = g->relative_coordinate (other_common, other_axis (a));
86 skylines.shift (offset);
88 else
90 assert (a == Y_AXIS);
91 Interval extent = g->pure_height (g, start, end);
93 // This is a hack to get better accuracy on the pure-height of VerticalAlignment.
94 // It's quite common for a treble clef to be the highest element of one system
95 // and for a low note (or lyrics) to be the lowest note on another. The two will
96 // never collide, but the pure-height stuff only works with bounding boxes, so it
97 // doesn't know that. The result is a significant over-estimation of the pure-height,
98 // especially on systems with many staves. To correct for this, we build a skyline
99 // in two parts: the part we did above contains most of the grobs (note-heads, etc.)
100 // while the bit we're about to do only contains the breakable grobs at the beginning
101 // of the system. This way, the tall treble clefs are only compared with the treble
102 // clefs of the other staff and they will be ignored if the staff above is, for example,
103 // lyrics.
104 if (Axis_group_interface::has_interface (g)
105 && !Hara_kiri_group_spanner::request_suicide (g, start, end))
107 extent = Axis_group_interface::rest_of_line_pure_height (g, start, end);
108 Interval begin_of_line_extent = Axis_group_interface::begin_of_line_pure_height (g, start);
109 if (!begin_of_line_extent.is_empty ())
111 Box b;
112 b[a] = begin_of_line_extent;
113 b[other_axis (a)] = Interval (-infinity_f, -1);
114 skylines.insert (b, 0, other_axis (a));
118 if (!extent.is_empty ())
120 Box b;
121 b[a] = extent;
122 b[other_axis (a)] = Interval (0, infinity_f);
123 skylines.insert (b, 0, other_axis (a));
127 if (skylines.is_empty ())
128 elements->erase (elements->begin () + i);
129 else
130 ret->push_back (skylines);
132 reverse (*ret);
135 vector<Real>
136 Align_interface::get_minimum_translations (Grob *me,
137 vector<Grob*> const &all_grobs,
138 Axis a,
139 bool pure, int start, int end)
141 if (!pure && a == Y_AXIS && dynamic_cast<Spanner*> (me) && !me->get_system ())
142 me->programming_error ("vertical alignment called before line-breaking");
144 Direction stacking_dir = robust_scm2dir (me->get_property ("stacking-dir"),
145 DOWN);
146 vector<Grob*> elems (all_grobs); // writable copy
147 vector<Skyline_pair> skylines;
149 get_skylines (me, &elems, a, pure, start, end, &skylines);
151 SCM forced_distances = ly_assoc_get (ly_symbol2scm ("alignment-distances"),
152 Page_layout_problem::get_details (me),
153 SCM_EOL);
155 Real where = 0;
156 Real default_padding = robust_scm2double (me->get_property ("padding"), 0.0);
157 vector<Real> translates;
158 Skyline down_skyline (stacking_dir);
159 Real last_spaceable_element_pos = 0;
160 Grob *last_spaceable_element = 0;
161 for (vsize j = 0; j < elems.size (); j++)
163 Real dy = 0;
164 Real padding = default_padding;
166 if (j == 0)
167 dy = skylines[j][-stacking_dir].max_height ();
168 else
170 down_skyline.merge (skylines[j-1][stacking_dir]);
171 dy = down_skyline.distance (skylines[j][-stacking_dir]);
173 SCM spec = Page_layout_problem::get_spacing_spec (elems[j-1], elems[j]);
174 Page_layout_problem::read_spacing_spec (spec, &padding, ly_symbol2scm ("padding"));
176 Real min_distance = 0;
177 if (Page_layout_problem::read_spacing_spec (spec, &min_distance, ly_symbol2scm ("minimum-distance")))
178 dy = max (dy, min_distance);
180 if (Page_layout_problem::is_spaceable (elems[j]) && last_spaceable_element)
182 // Spaceable staves may have min-distance and padding
183 // constraints coming from the previous spaceable staff
184 // as well as from the previous staff.
185 spec = Page_layout_problem::get_spacing_spec (last_spaceable_element, elems[j]);
186 Real spaceable_padding = 0;
187 Page_layout_problem::read_spacing_spec (spec,
188 &spaceable_padding,
189 ly_symbol2scm ("padding"));
190 padding = max (padding, spaceable_padding);
192 Real min_distance = 0;
193 if (Page_layout_problem::read_spacing_spec (spec,
194 &min_distance,
195 ly_symbol2scm ("minimum-distance")))
196 dy = max (dy, min_distance + stacking_dir*(last_spaceable_element_pos - where));
198 if (scm_is_pair (forced_distances))
200 SCM forced_dist = scm_car (forced_distances);
201 forced_distances = scm_cdr (forced_distances);
203 if (scm_is_number (forced_dist))
204 dy = scm_to_double (forced_dist) + stacking_dir * (last_spaceable_element_pos - where);
209 if (isinf (dy)) /* if the skyline is empty, maybe max_height is infinity_f */
210 dy = 0.0;
212 dy = max (0.0, dy + padding);
213 down_skyline.raise (-stacking_dir * dy);
214 where += stacking_dir * dy;
215 translates.push_back (where);
217 if (Page_layout_problem::is_spaceable (elems[j]))
219 last_spaceable_element = elems[j];
220 last_spaceable_element_pos = where;
224 // So far, we've computed the translates for all the non-empty elements.
225 // Here, we set the translates for the empty elements: an empty element
226 // gets the same translation as the last non-empty element before it.
227 vector<Real> all_translates;
228 if (!translates.empty ())
230 Real w = translates[0];
231 for (vsize i = 0, j = 0; j < all_grobs.size (); j++)
233 if (i < elems.size () && all_grobs[j] == elems[i])
234 w = translates[i++];
235 all_translates.push_back (w);
238 return all_translates;
241 void
242 Align_interface::align_elements_to_ideal_distances (Grob *me)
244 System *sys = me->get_system ();
245 Page_layout_problem layout (NULL, SCM_EOL, scm_list_1 (sys->self_scm ()));
247 layout.solution (true);
250 void
251 Align_interface::align_elements_to_minimum_distances (Grob *me, Axis a)
253 extract_grob_set (me, "elements", all_grobs);
255 vector<Real> translates = get_minimum_translations (me, all_grobs, a, false, 0, 0);
256 if (translates.size ())
257 for (vsize j = 0; j < all_grobs.size (); j++)
258 all_grobs[j]->translate_axis (translates[j], a);
261 Real
262 Align_interface::get_pure_child_y_translation (Grob *me, Grob *ch, int start, int end)
264 extract_grob_set (me, "elements", all_grobs);
265 SCM dy_scm = me->get_property ("forced-distance");
267 if (scm_is_number (dy_scm))
269 Real dy = scm_to_double (dy_scm) * robust_scm2dir (me->get_property ("stacking-dir"), DOWN);
270 Real pos = 0;
271 for (vsize i = 0; i < all_grobs.size (); i++)
273 if (all_grobs[i] == ch)
274 return pos;
275 if (!Hara_kiri_group_spanner::has_interface (all_grobs[i])
276 || !Hara_kiri_group_spanner::request_suicide (all_grobs[i], start, end))
277 pos += dy;
280 else
282 vector<Real> translates = get_minimum_translations (me, all_grobs, Y_AXIS, true, start, end);
284 if (translates.size ())
286 for (vsize i = 0; i < all_grobs.size (); i++)
287 if (all_grobs[i] == ch)
288 return translates[i];
290 else
291 return 0;
294 programming_error ("tried to get a translation for something that is no child of mine");
295 return 0;
298 Axis
299 Align_interface::axis (Grob *me)
301 return Axis (scm_to_int (scm_car (me->get_property ("axes"))));
304 void
305 Align_interface::add_element (Grob *me, Grob *element)
307 Axis a = Align_interface::axis (me);
308 SCM sym = axis_offset_symbol (a);
309 SCM proc = axis_parent_positioning (a);
311 element->set_property (sym, proc);
312 Axis_group_interface::add_element (me, element);
315 void
316 Align_interface::set_ordered (Grob *me)
318 SCM ga_scm = me->get_object ("elements");
319 Grob_array *ga = unsmob_grob_array (ga_scm);
320 if (!ga)
322 ga_scm = Grob_array::make_array ();
323 ga = unsmob_grob_array (ga_scm);
324 me->set_object ("elements", ga_scm);
327 ga->set_ordered (true);
330 ADD_INTERFACE (Align_interface,
331 "Order grobs from top to bottom, left to right, right to left"
332 " or bottom to top. For vertical alignments of staves, the"
333 " @code{break-system-details} of the left"
334 " @rinternals{NonMusicalPaperColumn} may be set to tune"
335 " vertical spacing. Set @code{alignment-extra-space} to add"
336 " extra space for staves. Set"
337 " @code{fixed-alignment-extra-space} to force staves in"
338 " @code{PianoStaff}s further apart.",
340 /* properties */
341 "align-dir "
342 "axes "
343 "elements "
344 "padding "
345 "positioning-done "
346 "stacking-dir "