]> git.donarmstrong.com Git - lilypond.git/blob - lily/spring.cc
Merge branch 'master' of ssh://jomand@git.sv.gnu.org/srv/git/lilypond
[lilypond.git] / lily / spring.cc
1 /*
2   spring.cc -- declare Spring
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 2007--2009 Joe Neeman <joeneeman@gmail.com>
7 */
8
9 #include "spring.hh"
10
11 Spring::Spring ()
12 {
13   distance_ = 1.0;
14   min_distance_ = 1.0;
15   inverse_stretch_strength_ = 1.0;
16   inverse_compress_strength_ = 1.0;
17
18   update_blocking_force ();
19 }
20
21 Spring::Spring (Real dist, Real min_dist)
22 {
23   distance_ = 1.0;
24   min_distance_ = 1.0;
25
26   set_distance (dist);
27   set_min_distance (min_dist);
28   set_default_strength ();
29   update_blocking_force ();
30 }
31
32 void
33 Spring::update_blocking_force ()
34 {
35   if (min_distance_ > distance_)
36     blocking_force_ = (min_distance_ - distance_) / inverse_stretch_strength_;
37   else
38     blocking_force_ = (min_distance_ - distance_) / inverse_compress_strength_;
39
40   if (isnan (blocking_force_) || blocking_force_ == infinity_f)
41     blocking_force_ = 0;
42
43   if (blocking_force_ >= 0)
44     inverse_compress_strength_ = 0;
45 }
46
47 /* scale a spring, but in a way that doesn't violate min_distance */
48 void
49 Spring::operator*= (Real r)
50 {
51   distance_ = max (min_distance_, distance_ * r);
52   inverse_compress_strength_ = max (0.0, distance_ - min_distance_);
53   inverse_stretch_strength_ *= 0.8;
54 }
55
56 bool
57 Spring::operator> (Spring const &other) const
58 {
59   return blocking_force_ > other.blocking_force_;
60 }
61
62 /* merge springs, basically by averaging them, but leave a little headroom
63    above the largest minimum distance so that things don't get too cramped */
64 Spring
65 merge_springs (vector<Spring> const &springs)
66 {
67   Real avg_distance = 0;
68   Real min_distance = 0;
69   Real avg_stretch = 0;
70   Real avg_compress = 0;
71
72   for (vsize i = 0; i < springs.size (); i++)
73     {
74       avg_distance += springs[i].distance ();
75       avg_stretch += springs[i].inverse_stretch_strength ();
76       avg_compress += 1 / springs[i].inverse_compress_strength ();
77       min_distance = max (springs[i].min_distance (), min_distance);
78     }
79
80   avg_stretch /= springs.size ();
81   avg_compress /= springs.size ();
82   avg_distance /= springs.size ();
83   avg_distance = max (min_distance + 0.3, avg_distance);
84
85   Spring ret = Spring (avg_distance, min_distance);
86   ret.set_inverse_stretch_strength (avg_stretch);
87   ret.set_inverse_compress_strength (1 / avg_compress);
88
89   return ret;
90 }
91
92 void
93 Spring::set_distance (Real d)
94 {
95   if (d < 0 || isinf (d) || isnan (d))
96     programming_error ("insane spring distance requested, ignoring it");
97   else
98     {
99       distance_ = d;
100       update_blocking_force ();
101     }
102 }
103
104 void
105 Spring::set_min_distance (Real d)
106 {
107   if (d < 0 || isinf (d) || isnan (d))
108     programming_error ("insane spring min_distance requested, ignoring it");
109   else
110     {
111       min_distance_ = d;
112       update_blocking_force ();
113     }
114 }
115
116 void
117 Spring::ensure_min_distance (Real d)
118 {
119   set_min_distance (max (d, min_distance_));
120 }
121
122 void
123 Spring::set_inverse_stretch_strength (Real f)
124 {
125   if (isinf (f) || isnan (f) || f < 0)
126     programming_error ("insane spring constant");
127   else
128     inverse_stretch_strength_ = f;
129 }
130
131 void
132 Spring::set_inverse_compress_strength (Real f)
133 {
134   if (isinf (f) || isnan (f) || f < 0)
135     programming_error ("insane spring constant");
136   else
137     inverse_compress_strength_ = f;
138
139   update_blocking_force ();
140 }
141
142 void
143 Spring::set_blocking_force (Real f)
144 {
145   if (isinf (f) || isnan (f))
146     {
147       programming_error ("insane blocking force");
148       return;
149     }
150
151   blocking_force_ = -infinity_f;
152   min_distance_ = length (f);
153   distance_ = max (distance_, min_distance_);
154   update_blocking_force ();
155 }
156
157 void
158 Spring::set_default_strength ()
159 {
160   inverse_compress_strength_ = (distance_ >= min_distance_) ? distance_ - min_distance_ : 0;
161   inverse_stretch_strength_ = distance_;
162 }
163
164 Real
165 Spring::length (Real f) const
166 {
167   Real force = max (f, blocking_force_);
168   Real inv_k = force < 0.0 ? inverse_compress_strength_ : inverse_stretch_strength_;
169
170   if (isinf (force))
171     {
172       programming_error ("cruelty to springs");
173       force = 0.0;
174     }
175
176   return max (min_distance_, distance_ + force * inv_k);
177 }