]> git.donarmstrong.com Git - lilypond.git/blob - lily/side-position-interface.cc
Fix some bugs in the dynamic engraver and PostScript backend
[lilypond.git] / lily / side-position-interface.cc
1 /*
2   side-position-interface.cc -- implement Side_position_interface
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1998--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "side-position-interface.hh"
10
11 #include <cmath>                // ceil.
12 #include <algorithm>
13 using namespace std;
14
15 #include "note-head.hh"
16 #include "warn.hh"
17 #include "main.hh"
18 #include "staff-symbol-referencer.hh"
19 #include "pointer-group-interface.hh"
20 #include "directional-element-interface.hh"
21 #include "staff-symbol-referencer.hh"
22 #include "staff-symbol.hh"
23 #include "string-convert.hh"
24 #include "misc.hh"
25
26 void
27 Side_position_interface::add_support (Grob *me, Grob *e)
28 {
29   Pointer_group_interface::add_unordered_grob (me, ly_symbol2scm ("side-support-elements"), e);
30 }
31
32 Direction
33 Side_position_interface::get_direction (Grob *me)
34 {
35   Direction relative_dir = Direction (1);
36   SCM reldir = me->get_property ("side-relative-direction");
37   if (is_direction (reldir))
38     relative_dir = to_dir (reldir);
39
40   SCM other_elt = me->get_object ("direction-source");
41   Grob *e = unsmob_grob (other_elt);
42   if (e)
43     return (Direction) (relative_dir * get_grob_direction (e));
44
45   return CENTER;
46 }
47
48 /* Put the element next to the support, optionally taking in
49    account the extent of the support.  */
50 SCM
51 Side_position_interface::general_side_position (Grob *me, Axis a, bool use_extents)
52 {
53   Real ss = Staff_symbol_referencer::staff_space (me);
54
55   extract_grob_set (me, "side-support-elements", support);
56
57   Grob *common = common_refpoint_of_array (support, me->get_parent (a), a);
58   Grob *staff_symbol = Staff_symbol_referencer::get_staff_symbol (me);
59   bool include_staff = 
60     staff_symbol
61     && a == Y_AXIS
62     && scm_is_number (me->get_property ("staff-padding"))
63     && !to_boolean (me->get_property ("quantize-position"));
64
65   Interval dim;
66   Interval staff_extents;
67   if (include_staff)
68     {
69       common = staff_symbol->common_refpoint (common, Y_AXIS);
70       staff_extents = staff_symbol->extent (common, Y_AXIS);
71
72       if (include_staff)
73         dim.unite (staff_extents);
74     }
75
76   for (vsize i = 0; i < support.size (); i++)
77     {
78       Grob *e = support[i];
79       if (e)
80         if (use_extents)
81           dim.unite (e->extent (common, a));
82         else
83           {
84             Real x = e->relative_coordinate (common, a);
85             dim.unite (Interval (x, x));
86           }
87     }
88
89   if (dim.is_empty ())
90     dim = Interval (0, 0);
91
92   Direction dir = get_grob_direction (me);
93
94   Real off = me->get_parent (a)->relative_coordinate (common, a);
95   Real minimum_space = ss * robust_scm2double (me->get_property ("minimum-space"), -1);
96
97   Real total_off = dim.linear_combination (dir) - off;
98   total_off += dir * ss * robust_scm2double (me->get_property ("padding"), 0);
99
100   if (minimum_space >= 0
101       && dir
102       && total_off * dir < minimum_space)
103     total_off = minimum_space * dir;
104   
105   /* FIXME: 1000 should relate to paper size.  */
106   if (fabs (total_off) > 1000)
107     {
108       string msg
109         = String_convert::form_string ("Improbable offset for grob %s: %f",
110                                        me->name ().c_str (), total_off);
111
112       programming_error (msg);
113       if (strict_infinity_checking)
114         scm_misc_error (__FUNCTION__, "Improbable offset.", SCM_EOL);
115     }
116   return scm_from_double (total_off);
117 }
118
119
120 MAKE_SCHEME_CALLBACK (Side_position_interface, y_aligned_on_support_refpoints, 1);
121
122 SCM
123 Side_position_interface::y_aligned_on_support_refpoints (SCM smob)
124 {
125   return general_side_position (unsmob_grob (smob), Y_AXIS, false); 
126 }
127
128
129
130 /*
131   Position next to support, taking into account my own dimensions and padding.
132 */
133
134 MAKE_SCHEME_CALLBACK (Side_position_interface, x_aligned_side, 1);
135 SCM
136 Side_position_interface::x_aligned_side (SCM smob)
137 {
138   return aligned_side (unsmob_grob (smob), X_AXIS);
139 }
140
141 MAKE_SCHEME_CALLBACK (Side_position_interface, y_aligned_side, 1);
142 SCM
143 Side_position_interface::y_aligned_side (SCM smob)
144 {
145   return aligned_side (unsmob_grob (smob), Y_AXIS);
146 }
147
148 SCM
149 Side_position_interface::aligned_side (Grob *me, Axis a)
150 {
151   Direction dir = get_grob_direction (me);
152
153   Real o = scm_to_double (general_side_position (me, a, true));
154   Interval iv = me->extent (me, a);
155
156   if (!iv.is_empty ())
157     {
158       if (!dir)
159         {
160           programming_error ("direction unknown, but aligned-side wanted");
161           dir = DOWN;
162         }
163       o += -iv[-dir];
164     }
165
166   /*
167     Maintain a minimum distance to the staff. This is similar to side
168     position with padding, but it will put adjoining objects on a row if
169     stuff sticks out of the staff a little.
170   */
171   Grob *staff = Staff_symbol_referencer::get_staff_symbol (me);
172   if (staff && a == Y_AXIS)
173     {
174       if (to_boolean (me->get_property ("quantize-position")))
175         {
176           Grob *common = me->common_refpoint (staff, Y_AXIS);
177           Real my_off = me->relative_coordinate (common, Y_AXIS);
178           Real staff_off = staff->relative_coordinate (common, Y_AXIS);
179           Real ss = Staff_symbol::staff_space (staff);
180           Real position = 2 * (my_off + o - staff_off) / ss;
181           Real rounded = directed_round (position, dir);
182           Grob *head = me->get_parent (X_AXIS);
183
184           if (fabs (position) <= 2 * Staff_symbol_referencer::staff_radius (me) + 1 
185               || (Note_head::has_interface (head)
186                   && sign (Staff_symbol_referencer::get_position (head)) == - dir))
187             {
188               o += (rounded - position) * 0.5 * ss;
189               if (Staff_symbol_referencer::on_line (me, int (rounded)))
190                 o += dir * 0.5 * ss;
191             }
192         }
193       else if (scm_is_number (me->get_property ("staff-padding")))
194         {
195           Real padding
196             = Staff_symbol_referencer::staff_space (me)
197             * scm_to_double (me->get_property ("staff-padding"));
198
199           Grob *common = me->common_refpoint (staff, Y_AXIS);
200
201           Interval staff_size = staff->extent (common, Y_AXIS);
202           Real diff = dir*staff_size[dir] + padding - dir * (o + iv[-dir]);
203           o += dir * max (diff, 0.0);
204         }
205     }
206   return scm_from_double (o);
207 }
208
209 void
210 Side_position_interface::set_axis (Grob *me, Axis a)
211 {
212   if (!scm_is_number (me->get_property ("side-axis")))
213     {
214       me->set_property ("side-axis", scm_from_int (a));
215       add_offset_callback (me,
216                            (a==X_AXIS)
217                            ? x_aligned_side_proc
218                            : y_aligned_side_proc,
219                            a);
220     }
221 }
222 Axis
223 Side_position_interface::get_axis (Grob *me)
224 {
225   if (scm_is_number (me->get_property ("side-axis")))
226     return Axis (scm_to_int (me->get_property ("side-axis")));
227   
228   me->programming_error ("side-axis not set.");
229   return NO_AXES;
230 }
231
232 ADD_INTERFACE (Side_position_interface, "side-position-interface",
233                "Position a victim object (this one) next to other objects (the "
234                "support).   The property @code{direction} signifies where to put the  "
235                "victim object relative to the support (left or right, up or down?)\n\n "
236                "The routine also takes the size the staff into account if "
237                "@code{staff-padding} is set. If undefined, the staff symbol is ignored.",
238
239                /* properties */
240                "direction "
241                "direction-source "
242                "minimum-space "
243                "padding "
244                "quantize-position "
245                "side-axis "
246                "side-relative-direction "
247                "side-support-elements "
248                "slur-padding "
249                "staff-padding "
250                );