]> git.donarmstrong.com Git - lilypond.git/blob - lily/side-position-interface.cc
(aligned_side): don't add
[lilypond.git] / lily / side-position-interface.cc
1 /*   
2   staff-side.cc --  implement Staff_side_element
3   
4   source file of the GNU LilyPond music typesetter
5   
6   (c) 1998--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7   
8  */
9 #include <math.h>               // ceil.
10
11 #include "side-position-interface.hh"
12 #include "warn.hh"
13 #include "warn.hh"
14 #include "dimensions.hh"
15 #include "staff-symbol-referencer.hh"
16 #include "group-interface.hh"
17 #include "directional-element-interface.hh"
18
19 void
20 Side_position_interface::add_support (Grob*me, Grob*e)
21 {
22   Pointer_group_interface::add_grob (me, ly_symbol2scm ("side-support-elements"), e);
23 }
24
25
26
27 Direction
28 Side_position_interface::get_direction (Grob*me)
29 {
30   SCM d = me->get_grob_property ("direction");
31   if (ly_dir_p (d) && to_dir (d))
32     return to_dir (d);
33
34   Direction relative_dir = Direction (1);
35   SCM reldir = me->get_grob_property ("side-relative-direction");       // should use a lambda.
36   if (ly_dir_p (reldir))
37     {
38       relative_dir = to_dir (reldir);
39     }
40   
41   SCM other_elt = me->get_grob_property ("direction-source");
42   Grob * e = unsmob_grob (other_elt);
43   if (e)
44     {
45       return (Direction) (relative_dir * Directional_element_interface::get (e));
46     }
47   
48   return CENTER;
49 }
50   
51
52 MAKE_SCHEME_CALLBACK (Side_position_interface,aligned_on_support_extents, 2);
53 SCM
54 Side_position_interface::aligned_on_support_extents (SCM element_smob, SCM axis)
55 {
56   Grob *me = unsmob_grob (element_smob);
57   Axis a = (Axis) gh_scm2int (axis);
58
59   return general_side_position (me, a, true);
60 }
61
62
63 /*
64  Puts the element next to the support, optionally taking in
65  account the extent of the support.
66 */
67 SCM
68 Side_position_interface::general_side_position (Grob * me, Axis a, bool use_extents)
69 {
70
71
72   /*
73     As this is only used as a callback, this is called only once. We
74     could wipe SIDE-SUPPORT-ELEMENTS after we retrieve it to conserve
75     memory; however -- we should look more into benefits of such actions?
76
77     The benefit is small, it seems: total GC times taken don't
78     differ. Would this also hamper Generational GC ?
79     
80   */
81   SCM support = me->get_grob_property ("side-support-elements");
82   Grob *common = common_refpoint_of_list (support, me->get_parent (a), a);
83   
84   Interval dim;
85   for (SCM s = support; s != SCM_EOL; s = ly_cdr (s))
86     {
87       Grob * e  = unsmob_grob (ly_car (s));
88       if (e)
89         if (use_extents)
90           dim.unite (e->extent (common, a));
91         else
92           {
93             Real x = e->relative_coordinate (common, a);
94             dim.unite (Interval (x,x));
95           }
96     }
97
98   if (dim.empty_b ())
99     {
100       dim = Interval (0,0);
101     }
102
103   Direction dir = Side_position_interface::get_direction (me);
104     
105   Real off =  me->get_parent (a)->relative_coordinate (common, a);
106   SCM minimum = me->get_grob_property ("minimum-space");
107
108   Real total_off = dim.linear_combination (dir) - off;
109   SCM padding = me->get_grob_property ("padding");
110   if (gh_number_p (padding))
111     {
112       total_off += gh_scm2double (padding) * dir;
113     }
114
115   if (gh_number_p (minimum) 
116       && dir
117       && total_off * dir < gh_scm2double (minimum))
118     {
119       total_off = gh_scm2double (minimum) * dir;
120     }
121
122   if (fabs (total_off) > 100 CM)
123     programming_error ("Huh ? Improbable staff side dim.");
124
125   return gh_double2scm (total_off);
126 }
127
128 /*
129   Cut & paste (ugh.)
130  */
131 MAKE_SCHEME_CALLBACK (Side_position_interface,aligned_on_support_refpoints,2);
132 SCM
133 Side_position_interface::aligned_on_support_refpoints (SCM smob, SCM axis)
134 {
135   Grob *me = unsmob_grob (smob);
136   Axis a = (Axis) gh_scm2int (axis);
137
138   return  general_side_position (me, a, false); 
139 }
140
141
142
143
144 Real
145 directed_round (Real f, Direction d)
146 {
147   if (d < 0)
148     return floor (f);
149   else
150     return ceil (f);
151 }
152
153 /*
154   Callback that quantises in staff-spaces, rounding in the direction
155   of the elements "direction" elt property.
156
157   Only rounds when we're inside the staff, as determined by
158   Staff_symbol_referencer::staff_radius () */
159 MAKE_SCHEME_CALLBACK (Side_position_interface,quantised_position,2);
160 SCM
161 Side_position_interface::quantised_position (SCM element_smob, SCM)
162 {
163   Grob *me = unsmob_grob (element_smob);
164   
165   
166   Direction d = Side_position_interface::get_direction (me);
167
168   if (Staff_symbol_referencer::has_interface (me))
169     {
170       Real p = Staff_symbol_referencer::get_position (me);
171       Real rp = directed_round (p, d);
172       Real rad = Staff_symbol_referencer::staff_radius (me) *2 ;
173       int ip = int (rp);
174
175       if (abs (ip) <= rad && Staff_symbol_referencer::on_staffline (me,ip))
176         {
177           ip += d;
178           rp += d;
179         }
180
181       return gh_double2scm ((rp - p) * Staff_symbol_referencer::staff_space (me) / 2.0);
182     }
183   return gh_double2scm (0.0);
184 }
185
186 /*
187   Position next to support, taking into account my own dimensions and padding.
188  */
189 MAKE_SCHEME_CALLBACK (Side_position_interface,aligned_side,2);
190 SCM
191 Side_position_interface::aligned_side (SCM element_smob, SCM axis)
192 {
193   Grob *me = unsmob_grob (element_smob);
194   Axis a = (Axis) gh_scm2int (axis);
195   
196   Direction d = Side_position_interface::get_direction (me);
197   
198   Real o = gh_scm2double (aligned_on_support_extents (element_smob,axis));
199
200   Interval iv =  me->extent (me, a);
201
202   if (!iv.empty_b ())
203     {
204       if (!d)
205         {
206           programming_error ("Direction unknown, but aligned-side wanted.");
207           d = DOWN;
208         }
209       o += - iv[-d];
210     }
211   return gh_double2scm (o);
212 }
213
214 void
215 Side_position_interface::add_staff_support (Grob*me)
216 {
217   Grob* st = Staff_symbol_referencer::get_staff_symbol (me);
218   if (st && get_axis (me) == Y_AXIS)
219     {
220       add_support (me,st);
221     }
222 }
223
224 void
225 Side_position_interface::set_axis (Grob*me, Axis a)
226 {
227   me->add_offset_callback (Side_position_interface::aligned_side_proc, a);
228 }
229
230
231
232 // ugh. doesn't cactch all variants. 
233 Axis
234 Side_position_interface::get_axis (Grob*me)
235 {
236   if (me->has_offset_callback_b (Side_position_interface::aligned_side_proc, X_AXIS)
237       || me->has_offset_callback_b (Side_position_interface::aligned_side_proc , X_AXIS))
238     return X_AXIS;
239
240   
241   return Y_AXIS;
242 }
243
244 void
245 Side_position_interface::set_direction (Grob*me, Direction d)
246 {
247   me->set_grob_property ("direction", gh_int2scm (d));
248 }
249
250 void
251 Side_position_interface::set_minimum_space (Grob*me, Real m)
252 {
253   me->set_grob_property ("minimum-space", gh_double2scm (m));
254 }
255
256 void
257 Side_position_interface::set_padding (Grob*me, Real p)
258 {
259   me->set_grob_property ("padding", gh_double2scm (p));
260 }
261
262
263 bool
264 Side_position_interface::supported_b (Grob*me) 
265 {
266   SCM s = me->get_grob_property ("side-support-elements"); 
267   return gh_pair_p (s);
268 }
269
270
271
272
273 ADD_INTERFACE (Side_position_interface,"side-position-interface",
274   "Position a victim object (this one) next to other objects (the
275 support).  In this case, the direction signifies where to put the
276 victim object relative to the support (left or right, up or down?)
277 ",
278   "side-support-elements direction-source direction side-relative-direction minimum-space padding");