2 spacing-determine-loose-columns.cc -- implement Spacing_spanner
3 methods that decide which columns to turn loose.
5 source file of the GNU LilyPond music typesetter
7 (c) 2005--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
10 #include "staff-spacing.hh"
12 #include "spacing-options.hh"
14 #include "paper-column.hh"
15 #include "column-x-positions.hh"
16 #include "pointer-group-interface.hh"
17 #include "spacing-interface.hh"
18 #include "spacing-spanner.hh"
19 #include "note-spacing.hh"
21 #include "grob-array.hh"
22 #include "break-align-interface.hh"
26 Return whether COL is fixed to its neighbors by some kind of spacing
30 If in doubt, then we're not loose; the spacing engine should space
31 for it, risking suboptimal spacing.
33 (Otherwise, we might risk core dumps, and other weird stuff.)
36 is_loose_column (Grob
*l
, Grob
*col
, Grob
*r
, Spacing_options
const *options
)
38 if (!to_boolean (col
->get_property ("allow-loose-spacing")))
42 if ((options
->float_nonmusical_columns_
43 || options
->float_grace_columns_
)
44 && Paper_column::when_mom (col
).grace_part_
)
50 if (Paper_column::is_musical (col
)
51 || Paper_column::is_breakable (col
))
55 If this column doesn't have a proper neighbor, we should really
56 make it loose, but spacing it correctly is more than we can
59 (this happens in the following situation:
70 the column containing the clef is really loose, and should be
71 attached right to the first column, but that is a lot of work for
72 such a borderline case.)
76 extract_grob_set (col
, "right-neighbors", rns
);
77 extract_grob_set (col
, "left-neighbors", lns
);
79 if (lns
.empty () || rns
.empty ())
83 Item
*l_neighbor
= dynamic_cast<Item
*> (lns
[0]);
84 Item
*r_neighbor
= dynamic_cast<Item
*> (rns
[0]);
86 if (!l_neighbor
|| !r_neighbor
)
89 l_neighbor
= l_neighbor
->get_column ();
90 r_neighbor
= dynamic_cast<Item
*> (Spacing_interface::right_column (r_neighbor
));
92 if (l
== l_neighbor
&& r
== r_neighbor
)
95 if (!l_neighbor
|| !r_neighbor
)
99 Only declare loose if the bounds make a little sense. This means
100 some cases (two isolated, consecutive clef changes) won't be
101 nicely folded, but hey, then don't do that.
103 if (! ((Paper_column::is_musical (l_neighbor
) || Paper_column::is_breakable (l_neighbor
))
104 && (Paper_column::is_musical (r_neighbor
) || Paper_column::is_breakable (r_neighbor
))))
108 in any case, we don't want to move bar lines.
110 extract_grob_set (col
, "elements", elts
);
111 for (vsize i
= elts
.size (); i
--;)
114 if (g
&& Break_alignment_interface::has_interface (g
))
116 extract_grob_set (g
, "elements", gelts
);
117 for (vsize j
= gelts
.size (); j
--;)
121 if (h
&& h
->get_property ("break-align-symbol") == ly_symbol2scm ("staff-bar"))
131 Spacing_spanner::set_distances_for_loose_col (Grob
*me
, Grob
*c
,
132 Drul_array
<Item
*> next_door
,
133 Spacing_options
const *options
)
136 Drul_array
<Real
> dists (0, 0);
140 Item
*lc
= dynamic_cast<Item
*> ((d
== LEFT
) ? next_door
[LEFT
] : c
);
141 Item
*rc
= dynamic_cast<Item
*> (d
== LEFT
? c
: next_door
[RIGHT
]);
143 extract_grob_set (lc
, "spacing-wishes", wishes
);
144 for (vsize k
= wishes
.size (); k
--;)
146 Grob
*sp
= wishes
[k
];
147 if (Spacing_interface::left_column (sp
) != lc
148 || Spacing_interface::right_column (sp
) != rc
)
151 if (Note_spacing::has_interface (sp
))
154 The note spacing should be taken from the musical
157 Real base
= note_spacing (me
, lc
, rc
, options
);
158 Spring spring
= Note_spacing::get_spacing (sp
, rc
, base
, options
->increment_
);
160 dists
[d
] = max (dists
[d
], spring
.min_distance ());
162 else if (Staff_spacing::has_interface (sp
))
164 Spring spring
= Staff_spacing::get_spacing (sp
, rc
);
166 dists
[d
] = max (dists
[d
], spring
.min_distance ());
169 programming_error ("Subversive spacing wish");
172 while (flip (&d
) != LEFT
);
175 r
.distance_
= dists
[LEFT
] + dists
[RIGHT
];
176 r
.item_drul_
= next_door
;
183 Remove columns that are not tightly fitting from COLS. In the
184 removed columns, set 'between-cols to the columns where it is in
188 Spacing_spanner::prune_loose_columns (Grob
*me
,
190 Spacing_options
*options
)
192 vector
<Grob
*> newcols
;
194 for (vsize i
= 0; i
< cols
->size (); i
++)
196 Grob
*c
= cols
->at (i
);
198 bool loose
= (i
> 0 && i
+ 1 < cols
->size ())
199 && is_loose_column (cols
->at (i
- 1), c
, cols
->at (i
+ 1), options
);
203 extract_grob_set (c
, "right-neighbors", rns_arr
);
204 extract_grob_set (c
, "left-neighbors", lns_arr
);
206 SCM lns
= lns_arr
.size () ? lns_arr
.back ()->self_scm () : SCM_BOOL_F
;
207 SCM rns
= rns_arr
.size () ? rns_arr
.back ()->self_scm () : SCM_BOOL_F
;
210 Either object can be non existent, if the score ends
214 extract_grob_set (unsmob_grob (rns
), "right-items", right_items
);
215 if (right_items
.size () == 0 || !unsmob_grob (lns
))
217 c
->programming_error ("Cannot determine neighbors for floating column. ");
218 c
->set_object ("between-cols", scm_cons (cols
->at (i
-1)->self_scm (),
219 cols
->at (i
+1)->self_scm ()));
224 int min_rank
= INT_MAX
;
225 for (vsize j
= 0; j
< right_items
.size (); j
++)
227 int rank
= dynamic_cast<Item
*> (right_items
[j
])->get_column ()->get_rank ();
230 min_item
= right_items
[j
];
235 c
->set_object ("between-cols", scm_cons (lns
,
236 min_item
->self_scm ()));
239 Set distance constraints for loose columns
241 Drul_array
<Item
*> next_door (dynamic_cast<Item
*> (cols
->at (i
- 1)),
242 dynamic_cast<Item
*> (cols
->at (i
+ 1)));
244 set_distances_for_loose_col (me
, c
, next_door
, options
);
249 newcols
.push_back (c
);
256 Set neighboring columns determined by the spacing-wishes grob property.
259 Spacing_spanner::set_explicit_neighbor_columns (vector
<Grob
*> const &cols
)
261 for (vsize i
= 0; i
< cols
.size (); i
++)
263 SCM right_neighbors
= Grob_array::make_array ();
264 Grob_array
*rn_arr
= unsmob_grob_array (right_neighbors
);
265 int min_rank
= INT_MAX
;
267 extract_grob_set (cols
[i
], "spacing-wishes", wishes
);
268 for (vsize k
= wishes
.size (); k
--;)
270 Item
*wish
= dynamic_cast<Item
*> (wishes
[k
]);
272 Item
*lc
= wish
->get_column ();
273 Grob
*right
= Spacing_interface::right_column (wish
);
278 Item
*rc
= dynamic_cast<Item
*> (right
);
280 int right_rank
= Paper_column::get_rank (rc
);
281 int left_rank
= Paper_column::get_rank (lc
);
284 update the left column.
286 if (right_rank
<= min_rank
)
288 if (right_rank
< min_rank
)
291 min_rank
= right_rank
;
296 update the right column of the wish.
300 extract_grob_set (rc
, "left-neighbors", lns_arr
);
303 Item
*it
= dynamic_cast<Item
*> (lns_arr
.back ());
304 maxrank
= Paper_column::get_rank (it
->get_column ());
307 if (left_rank
>= maxrank
)
310 if (left_rank
> maxrank
)
312 Grob_array
*ga
= unsmob_grob_array (rc
->get_object ("left-neighbors"));
317 Pointer_group_interface::add_grob (rc
, ly_symbol2scm ("left-neighbors"), wish
);
322 cols
[i
]->set_object ("right-neighbors", right_neighbors
);
327 Set neighboring columns that have no left/right-neighbor set
328 yet. Only do breakable non-musical columns, and musical columns.
331 Spacing_spanner::set_implicit_neighbor_columns (vector
<Grob
*> const &cols
)
333 for (vsize i
= 0; i
< cols
.size (); i
++)
335 Item
*it
= dynamic_cast<Item
*> (cols
[i
]);
336 if (!Paper_column::is_breakable (it
) && !Paper_column::is_musical (it
))
340 sloppy with typing left/right-neighbors should take list, but paper-column found instead.
342 extract_grob_set (cols
[i
], "left-neighbors", lns
);
343 if (lns
.empty () && i
)
345 SCM ga_scm
= Grob_array::make_array ();
346 Grob_array
*ga
= unsmob_grob_array (ga_scm
);
347 ga
->add (cols
[i
- 1]);
348 cols
[i
]->set_object ("left-neighbors", ga_scm
);
350 extract_grob_set (cols
[i
], "right-neighbors", rns
);
351 if (rns
.empty () && i
+ 1 < cols
.size ())
353 SCM ga_scm
= Grob_array::make_array ();
354 Grob_array
*ga
= unsmob_grob_array (ga_scm
);
355 ga
->add (cols
[i
+ 1]);
356 cols
[i
]->set_object ("right-neighbors", ga_scm
);