+ if (min_distance_ > distance_)
+ blocking_force_ = (min_distance_ - distance_) / inverse_stretch_strength_;
+ else
+ blocking_force_ = (min_distance_ - distance_) / inverse_compress_strength_;
+
+ // If the spring is fixed, it's not clear what the natural value
+ // of blocking_force_ would be (because it always blocks).
+ // -infinity_f works fine for now.
+ // If inverse_stretch_strength > 0, the spring is not fixed (because it can stretch).
+ if (isnan (blocking_force_) || blocking_force_ == infinity_f)
+ blocking_force_ = (inverse_stretch_strength_ > 0) ? 0.0 : -infinity_f;
+
+ if (blocking_force_ >= 0)
+ inverse_compress_strength_ = 0;
+}
+
+/* scale a spring, but in a way that doesn't violate min_distance */
+void
+Spring::operator*= (Real r)
+{
+ distance_ = max (min_distance_, distance_ * r);
+ inverse_compress_strength_ = max (0.0, distance_ - min_distance_);
+ inverse_stretch_strength_ *= 0.8;
+ update_blocking_force ();
+}
+
+bool
+Spring::operator> (Spring const &other) const
+{
+ return blocking_force_ > other.blocking_force_;
+}
+
+/* merge springs, basically by averaging them, but leave a little headroom
+ above the largest minimum distance so that things don't get too cramped */
+Spring
+merge_springs (vector<Spring> const &springs)
+{
+ Real avg_distance = 0;
+ Real min_distance = 0;
+ Real avg_stretch = 0;
+ Real avg_compress = 0;
+
+ for (vsize i = 0; i < springs.size (); i++)