Use scalar instead of embedded_scm for context mod overrides.
[lilypond/mpolesky.git] / lily / side-position-interface.cc
blobaab1045ee9c17d64e48153392be3c901ebf24c9b
1 /*
2 side-position-interface.cc -- implement Side_position_interface
4 source file of the GNU LilyPond music typesetter
6 (c) 1998--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
9 #include "side-position-interface.hh"
11 #include <cmath> // ceil.
12 #include <algorithm>
14 using namespace std;
16 #include "axis-group-interface.hh"
17 #include "directional-element-interface.hh"
18 #include "grob.hh"
19 #include "grob-array.hh"
20 #include "main.hh"
21 #include "misc.hh"
22 #include "note-head.hh"
23 #include "pointer-group-interface.hh"
24 #include "staff-symbol-referencer.hh"
25 #include "staff-symbol.hh"
26 #include "stem.hh"
27 #include "string-convert.hh"
28 #include "system.hh"
29 #include "warn.hh"
31 void
32 Side_position_interface::add_support (Grob *me, Grob *e)
34 Pointer_group_interface::add_unordered_grob (me, ly_symbol2scm ("side-support-elements"), e);
37 /* Put the element next to the support, optionally taking in
38 account the extent of the support.
40 Does not take into account the extent of ME.
42 SCM
43 Side_position_interface::general_side_position (Grob *me, Axis a, bool use_extents,
44 bool include_my_extent,
45 bool pure, int start, int end,
46 Real *current_offset)
48 Real ss = Staff_symbol_referencer::staff_space (me);
50 extract_grob_set (me, "side-support-elements", support);
52 Grob *common = common_refpoint_of_array (support, me->get_parent (a), a);
53 Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
54 bool include_staff =
55 staff_symbol
56 && a == Y_AXIS
57 && scm_is_number (me->get_property ("staff-padding"))
58 && !to_boolean (me->get_property ("quantize-position"));
60 Interval dim;
61 Interval staff_extents;
62 if (include_staff)
64 common = staff_symbol->common_refpoint (common, Y_AXIS);
65 staff_extents = staff_symbol->maybe_pure_extent (common, Y_AXIS, pure, start, end);
67 if (include_staff)
68 dim.unite (staff_extents);
71 Direction dir = get_grob_direction (me);
73 for (vsize i = 0; i < support.size (); i++)
75 Grob *e = support[i];
77 // In the case of a stem, we will find a note head as well
78 // ignoring the stem solves cyclic dependencies if the stem is
79 // attached to a cross-staff beam.
80 if (a == Y_AXIS
81 && Stem::has_interface (e)
82 && dir == - get_grob_direction (e))
83 continue;
85 if (e)
87 if (use_extents)
88 dim.unite (e->maybe_pure_extent (common, a, pure, start, end));
89 else
91 Real x = e->maybe_pure_coordinate (common, a, pure, start, end);
92 dim.unite (Interval (x, x));
97 if (dim.is_empty ())
98 dim = Interval (0, 0);
100 Real off = me->get_parent (a)->maybe_pure_coordinate (common, a, pure, start, end);
101 Real minimum_space = ss * robust_scm2double (me->get_property ("minimum-space"), -1);
103 Real total_off = dim.linear_combination (dir) - off;
104 total_off += dir * ss * robust_scm2double (me->get_property ("padding"), 0);
106 if (minimum_space >= 0
107 && dir
108 && total_off * dir < minimum_space)
109 total_off = minimum_space * dir;
111 if (include_my_extent)
113 Interval iv = me->maybe_pure_extent (me, a, pure, start, end);
114 if (!iv.is_empty ())
116 if (!dir)
118 programming_error ("direction unknown, but aligned-side wanted");
119 dir = DOWN;
121 total_off += -iv[-dir];
125 if (current_offset)
126 total_off = dir * max (dir * total_off,
127 dir * (*current_offset));
130 /* FIXME: 1000 should relate to paper size. */
131 if (fabs (total_off) > 1000)
133 string msg
134 = String_convert::form_string ("Improbable offset for grob %s: %f",
135 me->name ().c_str (), total_off);
137 programming_error (msg);
138 if (strict_infinity_checking)
139 scm_misc_error (__FUNCTION__, "Improbable offset.", SCM_EOL);
141 return scm_from_double (total_off);
145 MAKE_SCHEME_CALLBACK (Side_position_interface, y_aligned_on_support_refpoints, 1);
147 Side_position_interface::y_aligned_on_support_refpoints (SCM smob)
149 return general_side_position (unsmob_grob (smob), Y_AXIS, false, false, false, 0, 0, 0);
152 MAKE_SCHEME_CALLBACK (Side_position_interface, pure_y_aligned_on_support_refpoints, 3);
154 Side_position_interface::pure_y_aligned_on_support_refpoints (SCM smob, SCM start, SCM end)
156 return general_side_position (unsmob_grob (smob), Y_AXIS, false, false,
157 true, scm_to_int (start), scm_to_int (end), 0);
162 Position next to support, taking into account my own dimensions and padding.
165 axis_aligned_side_helper (SCM smob, Axis a, bool pure, int start, int end, SCM current_off_scm)
167 Real r;
168 Real *current_off_ptr = 0;
169 if (scm_is_number (current_off_scm))
171 r = scm_to_double (current_off_scm);
172 current_off_ptr = &r;
175 return Side_position_interface::aligned_side (unsmob_grob (smob), a, pure, start, end, current_off_ptr);
179 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Side_position_interface, x_aligned_side, 2, 1, "");
181 Side_position_interface::x_aligned_side (SCM smob, SCM current_off)
183 return axis_aligned_side_helper (smob, X_AXIS, false, 0, 0, current_off);
186 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Side_position_interface, y_aligned_side, 2, 1, "");
188 Side_position_interface::y_aligned_side (SCM smob, SCM current_off)
190 return axis_aligned_side_helper (smob, Y_AXIS, false, 0, 0, current_off);
193 MAKE_SCHEME_CALLBACK_WITH_OPTARGS (Side_position_interface, pure_y_aligned_side, 4, 1, "");
195 Side_position_interface::pure_y_aligned_side (SCM smob, SCM start, SCM end, SCM cur_off)
197 return axis_aligned_side_helper (smob, Y_AXIS, true,
198 scm_to_int (start),
199 scm_to_int (end),
200 cur_off);
203 MAKE_SCHEME_CALLBACK (Side_position_interface, calc_cross_staff, 1)
205 Side_position_interface::calc_cross_staff (SCM smob)
207 Grob *me = unsmob_grob (smob);
208 extract_grob_set (me, "side-support-elements", elts);
210 for (vsize i = 0; i < elts.size (); i++)
211 if (to_boolean (elts[i]->get_property ("cross-staff")))
212 return SCM_BOOL_T;
214 Grob *common = common_refpoint_of_array (elts, me->get_parent (Y_AXIS), Y_AXIS);
215 return scm_from_bool (common != me->get_parent (Y_AXIS));
219 Side_position_interface::aligned_side (Grob *me, Axis a, bool pure, int start, int end,
220 Real *current_off)
222 Direction dir = get_grob_direction (me);
224 Real o = scm_to_double (general_side_position (me, a, true, true, pure, start, end, current_off));
227 Maintain a minimum distance to the staff. This is similar to side
228 position with padding, but it will put adjoining objects on a row if
229 stuff sticks out of the staff a little.
231 Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
232 if (staff && a == Y_AXIS)
234 if (to_boolean (me->get_property ("quantize-position")))
236 Grob *common = me->common_refpoint (staff, Y_AXIS);
237 Real my_off = me->get_parent (Y_AXIS)->maybe_pure_coordinate (common, Y_AXIS, pure, start, end);
238 Real staff_off = staff->maybe_pure_coordinate (common, Y_AXIS, pure, start, end);
239 Real ss = Staff_symbol::staff_space (staff);
240 Real position = 2 * (my_off + o - staff_off) / ss;
241 Real rounded = directed_round (position, dir);
242 Grob *head = me->get_parent (X_AXIS);
244 if (fabs (position) <= 2 * Staff_symbol_referencer::staff_radius (me) + 1
245 /* In case of a ledger lines, quantize even if we're outside the staff. */
246 || (Note_head::has_interface (head)
248 && abs (Staff_symbol_referencer::get_position (head)) > abs (position)))
250 o += (rounded - position) * 0.5 * ss;
251 if (Staff_symbol_referencer::on_line (me, int (rounded)))
252 o += dir * 0.5 * ss;
255 else if (scm_is_number (me->get_property ("staff-padding")) && dir)
257 Interval iv = me->maybe_pure_extent (me, a, pure, start, end);
259 Real padding
260 = Staff_symbol_referencer::staff_space (me)
261 * scm_to_double (me->get_property ("staff-padding"));
263 Grob *common = me->common_refpoint (staff, Y_AXIS);
265 Interval staff_size = staff->maybe_pure_extent (common, Y_AXIS, pure, start, end);
266 Real diff = dir*staff_size[dir] + padding - dir * (o + iv[-dir]);
267 o += dir * max (diff, 0.0);
270 return scm_from_double (o);
273 void
274 Side_position_interface::set_axis (Grob *me, Axis a)
276 if (!scm_is_number (me->get_property ("side-axis")))
278 me->set_property ("side-axis", scm_from_int (a));
279 chain_offset_callback (me,
280 (a==X_AXIS)
281 ? x_aligned_side_proc
282 : y_aligned_side_proc,
287 Axis
288 Side_position_interface::get_axis (Grob *me)
290 if (scm_is_number (me->get_property ("side-axis")))
291 return Axis (scm_to_int (me->get_property ("side-axis")));
293 string msg = String_convert::form_string ("side-axis not set for grob %s.",
294 me->name ().c_str ());
295 me->programming_error (msg);
296 return NO_AXES;
299 MAKE_SCHEME_CALLBACK (Side_position_interface, move_to_extremal_staff, 1);
301 Side_position_interface::move_to_extremal_staff (SCM smob)
303 Grob *me = unsmob_grob (smob);
304 System *sys = dynamic_cast<System*> (me->get_system ());
305 Direction dir = get_grob_direction (me);
306 if (dir != DOWN)
307 dir = UP;
309 Interval iv = me->extent (sys, X_AXIS);
310 iv.widen (1.0);
311 Grob *top_staff = sys->get_extremal_staff (dir, iv);
313 if (!top_staff)
314 return SCM_BOOL_F;
316 // Only move this grob if it is a direct child of the system. We
317 // are not interested in moving marks from other staves to the top
318 // staff; we only want to move marks from the system to the top
319 // staff.
320 if (sys != me->get_parent (Y_AXIS))
321 return SCM_BOOL_F;
323 me->set_parent (top_staff, Y_AXIS);
324 me->flush_extent_cache (Y_AXIS);
325 Axis_group_interface::add_element (top_staff, me);
327 // Remove any cross-staff side-support dependencies
328 Grob_array *ga = unsmob_grob_array (me->get_object ("side-support-elements"));
329 if (ga)
331 vector<Grob*> const& elts = ga->array ();
332 vector<Grob*> new_elts;
333 for (vsize i = 0; i < elts.size (); ++i)
335 if (me->common_refpoint (elts[i], Y_AXIS) == top_staff)
336 new_elts.push_back (elts[i]);
338 ga->set_array (new_elts);
340 return SCM_BOOL_T;
344 ADD_INTERFACE (Side_position_interface,
345 "Position a victim object (this one) next to other objects"
346 " (the support). The property @code{direction} signifies where"
347 " to put the victim object relative to the support (left or"
348 " right, up or down?)\n"
349 "\n"
350 "The routine also takes the size of the staff into account if"
351 " @code{staff-padding} is set. If undefined, the staff symbol"
352 " is ignored.",
354 /* properties */
355 "direction "
356 "minimum-space "
357 "padding "
358 "quantize-position "
359 "side-axis "
360 "side-support-elements "
361 "slur-padding "
362 "staff-padding "