+/****************************************************************/
+
+/*
+ TODO: should a add penalty for widely varying spring forces (caused
+ by constraints, eg.
+
+
+ . =====
+ . | |
+ .o|o|x ##x
+ .
+
+ The ## forces the notes apart; we shouldn't allow the O's to touch
+ this closely.
+*/
+
+struct Rod_description
+{
+ vsize r_;
+ Real dist_;
+
+ bool operator< (const Rod_description r)
+ {
+ return r_ < r.r_;
+ }
+
+ Rod_description ()
+ {
+ r_ = 0;
+ dist_ = 0;
+ }
+
+ Rod_description (vsize r, Real d)
+ {
+ r_ = r;
+ dist_ = d;
+ }
+};
+
+struct Column_description
+{
+ vector<Rod_description> rods_;
+ vector<Rod_description> end_rods_; /* use these if they end at the last column of the line */
+ Real ideal_;
+ Real inverse_hooke_;
+ Real end_ideal_;
+ Real end_inverse_hooke_;
+ SCM break_permission_;
+ Interval keep_inside_line_;
+
+ Column_description ()
+ {
+ ideal_ = 0;
+ inverse_hooke_ = 0;
+ end_ideal_ = 0;
+ end_inverse_hooke_ = 0;
+ break_permission_ = SCM_EOL;
+ }
+};
+
+static int compare_paper_column_rank (Grob *const &a, Grob *const &b);
+
+static bool
+is_loose (Grob *g)
+{
+ return (scm_is_pair (g->get_object ("between-cols")));
+}
+
+static Grob*
+maybe_find_prebroken_piece (Grob *g, Direction d)
+{
+ Grob *ret = dynamic_cast<Item*> (g)->find_prebroken_piece (d);
+ if (ret)
+ return ret;
+ return g;
+}
+
+static Grob*
+next_spaceable_column (vector<Grob*> const &list, vsize starting)
+{
+ for (vsize i = starting+1; i < list.size (); i++)
+ if (!is_loose (list[i]))
+ return list[i];
+ return 0;
+}
+
+/* this only returns non-NULL if the line-ending column is the next
+ spaceable-or-breakable column */
+static Grob*
+next_line_ending_column (vector<Grob*> const &list, vsize starting)
+{
+ vsize i = starting + 1;
+ for (; i < list.size ()
+ && is_loose (list[i])
+ && !Paper_column::is_breakable (list[i]);
+ i++)
+ ;
+ return dynamic_cast<Item*> (list[i])->find_prebroken_piece (LEFT);
+}
+
+static void
+get_column_spring (Grob *this_col, Grob *next_col, Real *ideal, Real *inv_hooke)
+{
+ Spring_smob *spring = 0;
+
+ for (SCM s = this_col->get_object ("ideal-distances");
+ !spring && scm_is_pair (s);
+ s = scm_cdr (s))
+ {
+ Spring_smob *sp = unsmob_spring (scm_car (s));
+
+ if (sp->other_ == next_col)
+ spring = sp;
+ }
+
+ if (!spring)
+ programming_error (_f ("No spring between column %d and next one",
+ Paper_column::get_rank (this_col)));
+
+ *ideal = (spring) ? spring->distance_ : 5.0;
+ *inv_hooke = (spring) ? spring->inverse_strength_ : 1.0;
+}
+
+static Column_description
+get_column_description (vector<Grob*> const &cols, vsize col_index, bool line_starter)
+{
+ Grob *col = cols[col_index];
+ if (line_starter)
+ col = maybe_find_prebroken_piece (col, RIGHT);
+
+ Column_description description;
+ Grob *next_col = next_spaceable_column (cols, col_index);
+ if (next_col)
+ get_column_spring (col, next_col, &description.ideal_, &description.inverse_hooke_);
+ Grob *end_col = next_line_ending_column (cols, col_index);
+ if (end_col)
+ get_column_spring (col, end_col, &description.end_ideal_, &description.end_inverse_hooke_);
+
+ for (SCM s = Spaceable_grob::get_minimum_distances (col);
+ scm_is_pair (s); s = scm_cdr (s))
+ {
+ Grob *other = unsmob_grob (scm_caar (s));
+ vsize j = binary_search (cols, other, &compare_paper_column_rank, col_index);
+ if (j != VPOS)