2 align-interface.cc -- implement Align_interface
4 source file of the GNU LilyPond music typesetter
6 (c) 2000--2006 Han-Wen Nienhuys <hanwen@xs4all.nl>
9 #include "align-interface.hh"
12 #include "axis-group-interface.hh"
13 #include "pointer-group-interface.hh"
14 #include "hara-kiri-group-spanner.hh"
15 #include "grob-array.hh"
18 TODO: for vertical spacing, should also include a rod & spring
19 scheme of sorts into this: the alignment should default to a certain
20 distance between element refpoints, unless bbox force a bigger
24 MAKE_SCHEME_CALLBACK (Align_interface, calc_positioning_done, 1);
26 Align_interface::calc_positioning_done (SCM smob)
28 Grob *me = unsmob_grob (smob);
29 SCM axis = scm_car (me->get_property ("axes"));
30 Axis ax = (Axis)scm_to_int (axis);
32 SCM force = me->get_property ("forced-distance");
33 if (scm_is_number (force))
34 Align_interface::align_to_fixed_distance (me, ax);
36 Align_interface::align_elements_to_extents (me, ax);
42 merge with align-to-extents?
44 MAKE_SCHEME_CALLBACK(Align_interface, stretch_after_break, 1)
46 Align_interface::stretch_after_break (SCM grob)
48 Grob *me = unsmob_grob (grob);
50 Spanner *me_spanner = dynamic_cast<Spanner *> (me);
51 extract_grob_set (me, "elements", elems);
53 if (me_spanner && elems.size ())
55 Grob *common = common_refpoint_of_array (elems, me, Y_AXIS);
57 /* force position callbacks */
58 for (vsize i = 0; i < elems.size (); i++)
59 elems[i]->relative_coordinate (common, Y_AXIS);
61 SCM details = me_spanner->get_bound (LEFT)->get_property ("line-break-system-details");
62 SCM extra_space_handle = scm_assoc (ly_symbol2scm ("fixed-alignment-extra-space"), details);
64 Real extra_space = robust_scm2double (scm_is_pair (extra_space_handle)
65 ? scm_cdr (extra_space_handle)
69 Direction stacking_dir = robust_scm2dir (me->get_property ("stacking-dir"),
71 Real delta = extra_space / elems.size() * stacking_dir;
72 for (vsize i = 0; i < elems.size (); i++)
73 elems[i]->translate_axis (i * delta, Y_AXIS);
76 return SCM_UNSPECIFIED;
80 merge with align-to-extents?
83 Align_interface::align_to_fixed_distance (Grob *me, Axis a)
85 Direction stacking_dir = robust_scm2dir (me->get_property ("stacking-dir"),
88 Real dy = robust_scm2double (me->get_property ("forced-distance"), 0.0);
90 extract_grob_set (me, "elements", elem_source);
92 Link_array__Grob_ elems (elem_source); // writable..
98 std::vector<Real> translates;
100 for (vsize j = elems.size (); j--;)
103 This is not very elegant, in that we need special support for
104 hara-kiri. Unfortunately, the generic wiring of
105 force_hara_kiri_callback () (extent and offset callback) is
106 such that we might get into a loop if we call extent () or
107 offset () the elements.
110 && Hara_kiri_group_spanner::has_interface (elems[j]))
111 Hara_kiri_group_spanner::consider_suicide (elems[j]);
113 if (!elems[j]->is_live ())
114 elems.erase (elems.begin () + j);
117 for (vsize j = 0; j < elems.size (); j++)
119 where_f += stacking_dir * dy;
120 translates.push_back (where_f);
121 v.unite (Interval (where_f, where_f));
125 TODO: support self-alignment-{Y, X}
127 for (vsize i = 0; i < translates.size (); i++)
128 elems[i]->translate_axis (translates[i] - v.center (), a);
132 Hairy function to put elements where they should be. Can be tweaked
133 from the outside by setting extra-space in its
136 We assume that the children the refpoints of the children are still
137 found at 0.0 -- we will fuck up with thresholds if children's
138 extents are already moved to locations such as (-16, -8), since the
139 dy needed to put things in a row doesn't relate to the distances
140 between original refpoints.
142 TODO: maybe we should rethink and throw out thresholding altogether.
143 The original function has been taken over by
144 align_to_fixed_distance ().
148 Align_interface::align_elements_to_extents (Grob *me, Axis a)
150 Spanner *me_spanner = dynamic_cast<Spanner *> (me);
153 SCM line_break_details = SCM_EOL;
154 if (a == Y_AXIS && me_spanner)
155 line_break_details = me_spanner->get_bound (LEFT)->get_property ("line-break-system-details");
157 Direction stacking_dir = robust_scm2dir (me->get_property ("stacking-dir"),
160 Interval threshold = robust_scm2interval (me->get_property ("threshold"),
161 Interval (0, Interval::infinity ()));
163 std::vector<Interval> dims;
164 Link_array__Grob_ elems;
166 extract_grob_set (me, "elements", all_grobs);
167 for (vsize i = 0; i < all_grobs.size (); i++)
169 Interval y = all_grobs[i]->extent (me, a);
172 Grob *e = dynamic_cast<Grob *> (all_grobs[i]);
180 Read self-alignment-X and self-alignment-Y. This may seem like
181 code duplication. (and really: it is), but this is necessary to
182 prevent ugly cyclic dependencies that arise when you combine
183 self-alignment on a child with alignment of children.
185 SCM align ((a == X_AXIS)
186 ? me->get_property ("self-alignment-X")
187 : me->get_property ("self-alignment-Y"));
191 Real extra_space = 0.0;
192 SCM extra_space_handle = scm_assq (ly_symbol2scm ("alignment-extra-space"), line_break_details);
194 extra_space = robust_scm2double (scm_is_pair (extra_space_handle)
195 ? scm_cdr (extra_space_handle)
199 std::vector<Real> translates;
200 for (vsize j = 0; j < elems.size (); j++)
202 Real dy = -dims[j][-stacking_dir];
204 dy += dims[j - 1][stacking_dir];
211 dy = min (max (dy, threshold[SMALLER]), threshold[BIGGER]);
213 where += stacking_dir * (dy + extra_space / elems.size ());
214 total.unite (dims[j] + where);
215 translates.push_back (where);
218 SCM offsets_handle = scm_assq (ly_symbol2scm ("alignment-offsets"), line_break_details);
219 if (scm_is_pair (offsets_handle))
223 for (SCM s = scm_cdr (offsets_handle); scm_is_pair (s) && i < translates.size (); s = scm_cdr (s), i++)
225 if (scm_is_number (scm_car (s)))
226 translates[i] = scm_to_double (scm_car (s));
231 Real center_offset = 0.0;
234 also move the grobs that were empty, to maintain spatial order.
236 std::vector<Real> all_translates;
237 if (translates.size ())
239 Real w = translates[0];
240 for (vsize i = 0, j = 0; j < all_grobs.size (); j++)
242 if (i < elems.size () && all_grobs[j] == elems[i])
244 all_translates.push_back (w);
248 FIXME: uncommenting freaks out the Y-alignment of
251 if (scm_is_number (align))
252 center_offset = total.linear_combination (scm_to_double (align));
254 for (vsize j = 0; j < all_grobs.size (); j++)
255 all_grobs[j]->translate_axis (all_translates[j] - center_offset, a);
259 Align_interface::axis (Grob *me)
261 return Axis (scm_to_int (scm_car (me->get_property ("axes"))));
265 Align_interface::add_element (Grob *me, Grob *element)
267 Axis a = Align_interface::axis (me);
268 SCM sym = axis_offset_symbol (a);
269 SCM proc = axis_parent_positioning (a);
271 element->internal_set_property (sym, proc);
272 Axis_group_interface::add_element (me, element);
276 Align_interface::set_ordered (Grob *me)
278 SCM ga_scm = me->get_object ("elements");
279 Grob_array *ga = unsmob_grob_array (ga_scm);
282 ga_scm = Grob_array::make_array ();
283 ga = unsmob_grob_array (ga_scm);
284 me->set_object ("elements", ga_scm);
287 ga->set_ordered (true);
291 Find Y-axis parent of G that has a #'forced-distance property. This
292 has the effect of finding the piano-staff given an object in that
296 find_fixed_alignment_parent (Grob *g)
300 if (scm_is_number (g->get_property ("forced-distance")))
303 g = g->get_parent (Y_AXIS);
309 ADD_INTERFACE (Align_interface,
312 "Order grobs from top to bottom, left to right, right to left or bottom "
314 "For vertical alignments of staves, the @code{break-system-details} of "
315 "the left @internalsref{NonMusicalPaperColumn} may be set to tune vertical spacing "
316 "Set @code{alignment-extra-space} to add extra space for staves. Set "
317 "@code{fixed-alignment-extra-space} to force staves in PianoStaves further apart."
332 bool has_interface (Grob *);