]> git.donarmstrong.com Git - lilypond.git/blob - lily/side-position-interface.cc
``slikken kreng''
[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->remove_grob_property ("minimum-space");
107
108   Real total_off = dim.linear_combination (dir) - off;
109   SCM padding = me->remove_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       SCM pad = me->get_grob_property ("padding");
212       if (gh_number_p (pad))
213         o += d *gh_scm2double (pad) ; 
214     }
215   return gh_double2scm (o);
216 }
217
218 void
219 Side_position_interface::add_staff_support (Grob*me)
220 {
221   Grob* st = Staff_symbol_referencer::get_staff_symbol (me);
222   if (st && get_axis (me) == Y_AXIS)
223     {
224       add_support (me,st);
225     }
226 }
227
228 void
229 Side_position_interface::set_axis (Grob*me, Axis a)
230 {
231   me->add_offset_callback (Side_position_interface::aligned_side_proc, a);
232 }
233
234
235
236 // ugh. doesn't cactch all variants. 
237 Axis
238 Side_position_interface::get_axis (Grob*me)
239 {
240   if (me->has_offset_callback_b (Side_position_interface::aligned_side_proc, X_AXIS)
241       || me->has_offset_callback_b (Side_position_interface::aligned_side_proc , X_AXIS))
242     return X_AXIS;
243
244   
245   return Y_AXIS;
246 }
247
248 void
249 Side_position_interface::set_direction (Grob*me, Direction d)
250 {
251   me->set_grob_property ("direction", gh_int2scm (d));
252 }
253
254 void
255 Side_position_interface::set_minimum_space (Grob*me, Real m)
256 {
257   me->set_grob_property ("minimum-space", gh_double2scm (m));
258 }
259
260 void
261 Side_position_interface::set_padding (Grob*me, Real p)
262 {
263   me->set_grob_property ("padding", gh_double2scm (p));
264 }
265
266
267 bool
268 Side_position_interface::supported_b (Grob*me) 
269 {
270   SCM s = me->get_grob_property ("side-support-elements"); 
271   return gh_pair_p (s);
272 }
273
274
275
276
277 ADD_INTERFACE (Side_position_interface,"side-position-interface",
278   "Position a victim object (this one) next to other objects (the
279 support).  In this case, the direction signifies where to put the
280 victim object relative to the support (left or right, up or down?)
281 ",
282   "side-support-elements direction-source direction side-relative-direction minimum-space padding");