2 simple-spacer.cc -- implement Simple_spacer
4 source file of the GNU LilyPond music typesetter
6 (c) 1999--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
9 - add support for different stretch/shrink constants?
14 #include "column-x-positions.hh"
15 #include "dimensions.hh"
16 #include "international.hh"
17 #include "libc-extension.hh" // isinf
18 #include "paper-column.hh"
19 #include "simple-spacer.hh"
20 #include "spaceable-grob.hh"
25 A simple spacing constraint solver. The approach:
27 Stretch the line uniformly until none of the constraints (rods)
28 block. It then is very wide.
30 Compress until the next constraint blocks,
32 Mark the springs over the constrained part to be non-active.
34 Repeat with the smaller set of non-active constraints, until all
35 constraints blocked, or until the line is as short as desired.
37 This is much simpler, and much much faster than full scale
38 Constrained QP. On the other hand, a situation like this will not
39 be typeset as dense as possible, because
42 veryveryverylongsyllable2 veryveryverylongsyllable2
43 " "4 veryveryverylongsyllable2 syllable4
46 can be further compressed to
50 veryveryverylongsyllable2 veryveryverylongsyllable2
51 " "4 veryveryverylongsyllable2 syllable4
54 Perhaps this is not a bad thing, because the 1st looks better anyway. */
57 positive force = expanding, negative force = compressing.
60 Simple_spacer::Simple_spacer ()
69 Simple_spacer::force () const
75 Simple_spacer::fits () const
81 Simple_spacer::rod_force (int l
, int r
, Real dist
)
83 Real d
= range_ideal_len (l
, r
);
84 Real c
= range_stiffness (l
, r
, dist
> d
);
85 Real block_stretch
= dist
- d
;
87 if (isinf (c
) && block_stretch
== 0) /* take care of the 0*infinity_f case */
89 return c
* block_stretch
;
93 Simple_spacer::add_rod (int l
, int r
, Real dist
)
95 if (isinf (dist
) || isnan (dist
))
97 programming_error ("ignoring weird minimum distance");
101 Real block_force
= rod_force (l
, r
, dist
);
103 if (isinf (block_force
))
105 Real spring_dist
= range_ideal_len (l
, r
);
106 if (spring_dist
< dist
)
107 for (int i
= l
; i
< r
; i
++)
110 springs_
[i
].set_distance (springs_
[i
].distance () * dist
/ spring_dist
);
112 springs_
[i
].set_distance (dist
/ (r
- l
));
117 force_
= max (force_
, block_force
);
118 for (int i
= l
; i
< r
; i
++)
119 springs_
[i
].set_blocking_force (max (block_force
, springs_
[i
].blocking_force ()));
123 Simple_spacer::range_ideal_len (int l
, int r
) const
126 for (int i
= l
; i
< r
; i
++)
127 d
+= springs_
[i
].distance ();
132 Simple_spacer::range_stiffness (int l
, int r
, bool stretch
) const
135 for (int i
= l
; i
< r
; i
++)
136 den
+= stretch
? springs_
[i
].inverse_stretch_strength ()
137 : springs_
[i
].inverse_compress_strength ();
143 Simple_spacer::configuration_length (Real force
) const
146 for (vsize i
= 0; i
< springs_
.size (); i
++)
147 l
+= springs_
[i
].length (force
);
153 Simple_spacer::solve (Real line_len
, bool ragged
)
155 Real conf
= configuration_length (force_
);
158 line_len_
= line_len
;
159 if (conf
< line_len_
)
160 force_
= expand_line ();
161 else if (conf
> line_len_
)
162 force_
= compress_line ();
164 if (ragged
&& force_
< 0)
169 Simple_spacer::expand_line ()
171 double inv_hooke
= 0;
172 double cur_len
= configuration_length (force_
);
175 for (vsize i
=0; i
< springs_
.size (); i
++)
176 inv_hooke
+= springs_
[i
].inverse_stretch_strength ();
178 if (inv_hooke
== 0.0) /* avoid division by zero. If springs are infinitely stiff */
179 return 0.0; /* anyway, then it makes no difference what the force is */
181 assert (cur_len
<= line_len_
);
182 return (line_len_
- cur_len
) / inv_hooke
+ force_
;
186 Simple_spacer::compress_line ()
188 double inv_hooke
= 0;
189 double cur_len
= configuration_length (force_
);
190 double cur_force
= force_
;
191 bool compressed
= false;
193 /* just because we are in compress_line () doesn't mean that the line
194 will actually be compressed (as in, a negative force) because
195 we start out with a stretched line. Here, we check whether we
196 will be compressed or stretched (so we know which spring constant to use) */
197 if (configuration_length (0.0) > line_len_
)
200 cur_len
= configuration_length (0.0);
205 for (vsize i
=0; i
< springs_
.size (); i
++)
206 inv_hooke
+= compressed
207 ? springs_
[i
].inverse_compress_strength ()
208 : springs_
[i
].inverse_stretch_strength ();
210 assert (line_len_
<= cur_len
);
212 vector
<Spring
> sorted_springs
= springs_
;
213 sort (sorted_springs
.begin (), sorted_springs
.end (), greater
<Spring
> ());
215 for (vsize i
= 0; i
< sorted_springs
.size (); i
++)
217 Spring sp
= sorted_springs
[i
];
219 if (sp
.blocking_force () > cur_force
)
222 if (isinf (sp
.blocking_force ()))
225 double block_dist
= (cur_force
- sp
.blocking_force ()) * inv_hooke
;
226 if (cur_len
- block_dist
< line_len_
)
228 cur_force
+= (line_len_
- cur_len
) / inv_hooke
;
234 assert (fabs (configuration_length (cur_force
) - cur_len
) < 1e-6);
238 cur_len
-= block_dist
;
239 inv_hooke
-= compressed
? sp
.inverse_compress_strength () : sp
.inverse_stretch_strength ();
240 cur_force
= sp
.blocking_force ();
248 Simple_spacer::add_spring (Spring
const &sp
)
250 force_
= max (force_
, sp
.blocking_force ());
251 springs_
.push_back (sp
);
255 Simple_spacer::spring_positions () const
260 for (vsize i
= 0; i
< springs_
.size (); i
++)
261 ret
.push_back (ret
.back () + springs_
[i
].length (ragged_
&& force_
> 0 ? 0.0 : force_
));
266 Simple_spacer::force_penalty (bool ragged
) const
268 /* If we are ragged-right, we don't want to penalise according to the force,
269 but according to the amount of whitespace that is present after the end
272 return max (0.0, line_len_
- configuration_length (0.0));
274 /* Use a convex compression penalty. */
276 return f
- (f
< 0 ? f
*f
*f
*f
*2 : 0);
279 /****************************************************************/
281 struct Rod_description
286 bool operator< (const Rod_description r
)
297 Rod_description (vsize r
, Real d
)
304 struct Column_description
306 vector
<Rod_description
> rods_
;
307 vector
<Rod_description
> end_rods_
; /* use these if they end at the last column of the line */
311 SCM break_permission_
;
312 Interval keep_inside_line_
;
314 Column_description ()
316 break_permission_
= SCM_EOL
;
323 return (scm_is_pair (g
->get_object ("between-cols")));
327 maybe_find_prebroken_piece (Grob
*g
, Direction d
)
329 Grob
*ret
= dynamic_cast<Item
*> (g
)->find_prebroken_piece (d
);
336 next_spaceable_column (vector
<Grob
*> const &list
, vsize starting
)
338 for (vsize i
= starting
+1; i
< list
.size (); i
++)
339 if (!is_loose (list
[i
]))
344 static Column_description
345 get_column_description (vector
<Grob
*> const &cols
, vsize col_index
, bool line_starter
)
347 Grob
*col
= cols
[col_index
];
349 col
= maybe_find_prebroken_piece (col
, RIGHT
);
351 Column_description description
;
352 Grob
*next_col
= next_spaceable_column (cols
, col_index
);
354 description
.spring_
= Spaceable_grob::get_spring (col
, next_col
);
356 Grob
*end_col
= dynamic_cast<Item
*> (cols
[col_index
+1])->find_prebroken_piece (LEFT
);
358 description
.end_spring_
= Spaceable_grob::get_spring (col
, end_col
);
360 for (SCM s
= Spaceable_grob::get_minimum_distances (col
);
361 scm_is_pair (s
); s
= scm_cdr (s
))
363 Grob
*other
= unsmob_grob (scm_caar (s
));
364 vsize j
= binary_search (cols
, other
, Paper_column::less_than
, col_index
);
367 if (cols
[j
] == other
)
368 description
.rods_
.push_back (Rod_description (j
, scm_to_double (scm_cdar (s
))));
369 else /* it must end at the LEFT prebroken_piece */
370 description
.end_rods_
.push_back (Rod_description (j
, scm_to_double (scm_cdar (s
))));
374 if (!line_starter
&& to_boolean (col
->get_property ("keep-inside-line")))
375 description
.keep_inside_line_
= col
->extent (col
, X_AXIS
);
377 description
.break_permission_
= col
->get_property ("line-break-permission");
382 get_line_forces (vector
<Grob
*> const &columns
,
383 Real line_len
, Real indent
, bool ragged
)
385 vector
<vsize
> breaks
;
387 vector
<Grob
*> non_loose
;
388 vector
<Column_description
> cols
;
389 SCM force_break
= ly_symbol2scm ("force");
391 for (vsize i
= 0; i
< columns
.size (); i
++)
392 if (!is_loose (columns
[i
]) || Paper_column::is_breakable (columns
[i
]))
393 non_loose
.push_back (columns
[i
]);
396 breaks
.push_back (0);
397 cols
.push_back (Column_description ());
398 for (vsize i
= 1; i
+ 1 < non_loose
.size (); i
++)
400 if (Paper_column::is_breakable (non_loose
[i
]))
401 breaks
.push_back (cols
.size ());
403 cols
.push_back (get_column_description (non_loose
, i
, false));
405 breaks
.push_back (cols
.size ());
406 force
.resize (breaks
.size () * breaks
.size (), infinity_f
);
408 for (vsize b
= 0; b
+ 1 < breaks
.size (); b
++)
410 cols
[breaks
[b
]] = get_column_description (non_loose
, breaks
[b
], true);
411 vsize st
= breaks
[b
];
413 for (vsize c
= b
+1; c
< breaks
.size (); c
++)
415 vsize end
= breaks
[c
];
416 Simple_spacer spacer
;
418 for (vsize i
= breaks
[b
]; i
< end
- 1; i
++)
419 spacer
.add_spring (cols
[i
].spring_
);
420 spacer
.add_spring (cols
[end
-1].end_spring_
);
423 for (vsize i
= breaks
[b
]; i
< end
; i
++)
425 for (vsize r
= 0; r
< cols
[i
].rods_
.size (); r
++)
426 if (cols
[i
].rods_
[r
].r_
< end
)
427 spacer
.add_rod (i
- st
, cols
[i
].rods_
[r
].r_
- st
, cols
[i
].rods_
[r
].dist_
);
428 for (vsize r
= 0; r
< cols
[i
].end_rods_
.size (); r
++)
429 if (cols
[i
].end_rods_
[r
].r_
== end
)
430 spacer
.add_rod (i
- st
, end
- st
, cols
[i
].end_rods_
[r
].dist_
);
431 if (!cols
[i
].keep_inside_line_
.is_empty ())
433 spacer
.add_rod (i
- st
, end
- st
, cols
[i
].keep_inside_line_
[RIGHT
]);
434 spacer
.add_rod (0, i
- st
, -cols
[i
].keep_inside_line_
[LEFT
]);
437 spacer
.solve ((b
== 0) ? line_len
- indent
: line_len
, ragged
);
438 force
[b
* breaks
.size () + c
] = spacer
.force_penalty (ragged
);
443 force
[b
* breaks
.size () + c
] = -200000;
445 force
[b
* breaks
.size () + c
] = infinity_f
;
448 if (end
< cols
.size () && cols
[end
].break_permission_
== force_break
)
456 get_line_configuration (vector
<Grob
*> const &columns
,
461 vector
<Column_description
> cols
;
462 Simple_spacer spacer
;
463 Column_x_positions ret
;
465 ret
.cols_
.push_back (dynamic_cast<Item
*> (columns
[0])->find_prebroken_piece (RIGHT
));
466 for (vsize i
= 1; i
+ 1 < columns
.size (); i
++)
468 if (is_loose (columns
[i
]))
469 ret
.loose_cols_
.push_back (columns
[i
]);
471 ret
.cols_
.push_back (columns
[i
]);
473 ret
.cols_
.push_back (dynamic_cast<Item
*> (columns
.back ())->find_prebroken_piece (LEFT
));
475 /* since we've already put our line-ending column in the column list, we can ignore
476 the end_XXX_ fields of our column_description */
477 for (vsize i
= 0; i
+ 1 < ret
.cols_
.size (); i
++)
479 cols
.push_back (get_column_description (ret
.cols_
, i
, i
== 0));
480 spacer
.add_spring (cols
[i
].spring_
);
482 for (vsize i
= 0; i
< cols
.size (); i
++)
484 for (vsize r
= 0; r
< cols
[i
].rods_
.size (); r
++)
485 spacer
.add_rod (i
, cols
[i
].rods_
[r
].r_
, cols
[i
].rods_
[r
].dist_
);
487 if (!cols
[i
].keep_inside_line_
.is_empty ())
489 spacer
.add_rod (i
, cols
.size (), cols
[i
].keep_inside_line_
[RIGHT
]);
490 spacer
.add_rod (0, i
, -cols
[i
].keep_inside_line_
[LEFT
]);
494 spacer
.solve (line_len
, ragged
);
495 ret
.force_
= spacer
.force_penalty (ragged
);
497 ret
.config_
= spacer
.spring_positions ();
498 for (vsize i
= 0; i
< ret
.config_
.size (); i
++)
499 ret
.config_
[i
] += indent
;
501 ret
.satisfies_constraints_
= spacer
.fits ();
504 Check if breaking constraints are met.
506 for (vsize i
= 1; i
+ 1 < ret
.cols_
.size (); i
++)
508 SCM p
= ret
.cols_
[i
]->get_property ("line-break-permission");
509 if (p
== ly_symbol2scm ("force"))
510 ret
.satisfies_constraints_
= false;