]> git.donarmstrong.com Git - lilypond.git/blob - lily/align-interface.cc
release: 1.3.147
[lilypond.git] / lily / align-interface.cc
1 /*   
2   align-interface.cc --  implement Align_interface
3   
4   source file of the GNU LilyPond music typesetter
5   
6   (c) 2000--2001 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7   
8  */
9
10 #include "align-interface.hh"
11 #include "grob.hh"
12 #include "group-interface.hh"
13 #include "axis-group-interface.hh"
14 #include "paper-def.hh"
15
16 /*
17   This callback is set in the children of the align element. It does
18   not compute anything, but a side effect of a->do_side_processing ()
19   is that the elements are placed correctly.  */
20 MAKE_SCHEME_CALLBACK (Align_interface,alignment_callback,2);
21 SCM
22 Align_interface::alignment_callback (SCM element_smob, SCM axis)
23 {
24   Grob * me = unsmob_grob (element_smob);
25   Axis ax = (Axis)gh_scm2int (axis);
26   Grob * par = me->parent_l (ax);
27   if (par && !to_boolean (par->get_grob_property ("alignment-done")))
28     {
29       Align_interface::align_elements_to_extents (par, ax);
30     }
31   return gh_double2scm (0.0);
32 }
33
34 MAKE_SCHEME_CALLBACK (Align_interface,fixed_distance_alignment_callback,2);
35 SCM
36 Align_interface::fixed_distance_alignment_callback (SCM element_smob, SCM axis)
37 {
38   Grob * me = unsmob_grob (element_smob);
39   Axis ax = (Axis)gh_scm2int (axis);
40   Grob * par = me->parent_l (ax);
41   if (par && !to_boolean (par->get_grob_property ("alignment-done")))
42     {
43       Align_interface::align_to_fixed_distance (par, ax);
44     }
45   return gh_double2scm (0.0);
46 }
47
48 /*
49   merge with align-to-extents? 
50  */
51 void
52 Align_interface::align_to_fixed_distance (Grob *me , Axis a)
53 {
54   me->set_grob_property ("alignment-done", SCM_BOOL_T);
55   
56   SCM d =   me->get_grob_property ("stacking-dir");
57
58   
59   Direction stacking_dir = gh_number_p (d) ? to_dir (d) : CENTER;
60   if (!stacking_dir)
61     stacking_dir = DOWN;
62
63
64   SCM force = me->get_grob_property ("forced-distance");
65
66   Real dy = 0.0;
67   if (gh_number_p (force))
68     {
69       dy = gh_scm2double (force);
70     }
71   
72   Link_array<Grob> elems
73     = Pointer_group_interface__extract_elements (me, (Grob*) 0, "elements");
74
75   Real where_f=0;
76
77   Interval v;
78   v.set_empty ();
79   Array<Real> translates;
80   
81   for (int j=0 ;  j < elems.size (); j++) 
82     {
83       where_f += stacking_dir * dy;
84       translates.push (where_f);
85       v.unite (Interval (where_f, where_f));
86     }
87
88   /*
89     TODO: support self-alignment-{Y,X}
90    */
91   for (int i = 0; i < translates.size (); i++)
92     {
93       elems[i]->translate_axis (translates[i] - v.center (), a);
94     }
95 }
96
97 /*
98   Hairy function to put elements where they should be. Can be tweaked
99   from the outside by setting minimum-space and extra-space in its
100   children
101
102   We assume that the children the refpoints of the children are still
103   found at 0.0 -- we will fuck up with thresholds if children's
104   extents are already moved to locations such as (-16, -8), since the
105   dy needed to put things in a row doesn't relate to the distances
106   between original refpoints.
107
108   TODO: maybe we should rethink and throw out thresholding altogether.
109   The original function has been taken over by
110   align_to_fixed_distance().
111 */
112 void
113 Align_interface::align_elements_to_extents (Grob * me, Axis a)
114 {
115   me->set_grob_property ("alignment-done", SCM_BOOL_T);
116   
117   SCM d =   me->get_grob_property ("stacking-dir");
118
119   
120   Direction stacking_dir = gh_number_p (d) ? to_dir (d) : CENTER;
121   if (!stacking_dir)
122     stacking_dir = DOWN;
123
124
125   
126   Interval threshold = Interval (0, Interval::infinity ());
127   SCM thr = me->get_grob_property ("threshold");
128   if (gh_pair_p (thr))
129     {
130       threshold[SMALLER] = gh_scm2double (gh_car (thr));
131       threshold[BIGGER] = gh_scm2double (gh_cdr (thr));      
132     }
133
134   
135   Array<Interval> dims;
136
137   Link_array<Grob> elems;
138   Link_array<Grob> all_grobs
139     = Pointer_group_interface__extract_elements (me, (Grob*) 0, "elements");
140   for (int i=0; i < all_grobs.size (); i++) 
141     {
142       Interval y = all_grobs[i]->extent (me, a);
143       if (!y.empty_b ())
144         {
145           Grob *e =dynamic_cast<Grob*> (all_grobs[i]);
146
147           // todo: fucks up if item both in Halign & Valign. 
148           SCM min_dims = e->remove_grob_property ("minimum-space");
149           if (gh_pair_p (min_dims) &&
150               gh_number_p (gh_car (min_dims))
151               && gh_number_p (gh_cdr (min_dims)))
152             {
153               y.unite (ly_scm2interval (min_dims));
154             }
155           
156           SCM extra_dims = e->remove_grob_property ("extra-space");
157           if (gh_pair_p (extra_dims) &&
158               gh_number_p (gh_car (extra_dims))
159               && gh_number_p (gh_cdr (extra_dims)))
160             {
161               y[LEFT] += gh_scm2double (gh_car (extra_dims));
162               y[RIGHT] += gh_scm2double (gh_cdr (extra_dims));
163             }
164
165           elems.push (e);
166           dims.push (y);          
167         }
168     }
169   
170  
171   /*
172     Read self-alignment-X and self-alignment-Y. This may seem like
173     code duplication. (and really: it is), but this is necessary to
174     prevent ugly cyclic dependencies that arise when you combine
175     self-alignment on a child with alignment of children.
176   */
177
178   String s ("self-alignment-");
179
180   s += (a == X_AXIS) ? "X" : "Y";
181
182   SCM align (me->get_grob_property (s.ch_C ()));
183      
184   Array<Real> translates ;
185   Interval total;
186   Real where_f=0;
187   
188   for (int j=0 ;  j < elems.size (); j++) 
189     {
190       Real dy = -  dims[j][-stacking_dir];
191       if (j)
192         dy += dims[j-1][stacking_dir];
193
194
195       /*
196         we want dy to be > 0
197        */
198       dy *= stacking_dir; 
199       if (j)
200         {
201           dy = (dy >? threshold[SMALLER])
202             <? threshold[BIGGER];
203         }
204
205       where_f += stacking_dir * dy;
206       total.unite (dims[j] +   where_f);
207       translates.push (where_f);
208     }
209
210   
211   Grob * align_center = unsmob_grob (align);
212   Real center_offset = 0.0;
213   
214   /*
215     also move the grobs that were empty, to maintain spatial order. 
216    */
217   Array<Real> all_translates;
218   if (translates.size ())
219     {
220       int i =0;
221       int j =0;
222       Real w = translates[0];
223       while (j  < all_grobs.size ())
224         {
225           if (i < elems.size () && all_grobs[j] == elems[i])
226             {
227               w = translates[i++];
228             }
229           if (all_grobs[j] == align_center)
230             center_offset = w;
231           all_translates .push (w);
232           j++;
233         }
234
235
236       /*
237         FIXME: uncommenting freaks out the Y-alignment of
238         line-of-score.
239        */
240       // Real align_param = isdir_b (align)  ? gh_scm2double (align) : 0.0;
241       
242       if (gh_number_p (align))
243         center_offset = total.linear_combination (gh_scm2double (align));
244
245       for (int j = 0 ;  j < all_grobs.size (); j++)
246         all_grobs[j]->translate_axis (all_translates[j] - center_offset, a);
247     }
248 }
249 Axis
250 Align_interface::axis (Grob*me)
251 {
252   return  Axis (gh_scm2int (gh_car (me->get_grob_property ("axes"))));
253 }
254
255
256 /*
257   should  use generic Scm funcs.
258  */
259 int
260 Align_interface::get_count (Grob*me,Grob*s)
261 {
262   SCM e = me->get_grob_property ("elements");
263   int c =0;
264   while (gh_pair_p (e))
265     {
266       if (gh_car (e) == s->self_scm ())
267         break;
268       c++;
269       e = gh_cdr (e);
270     }
271   return c;
272 }
273
274 void
275 Align_interface::add_element (Grob*me,Grob* s, SCM cb)
276 {
277   s->add_offset_callback (cb, Align_interface::axis (me));
278   Axis_group_interface::add_element (me, s);
279 }
280
281
282 void
283 Align_interface::set_interface (Grob*me)
284 {
285   me->set_interface (ly_symbol2scm ("align-interface"));
286
287   Axis_group_interface::set_interface (me);
288 }
289
290 void
291 Align_interface::set_axis (Grob*me,Axis a)
292 {
293   Axis_group_interface::set_axes (me, a,a);
294 }
295
296 bool
297 Align_interface::has_interface (Grob*me)
298 {
299   return me && me->has_interface (ly_symbol2scm ("align-interface"));
300 }
301