]> git.donarmstrong.com Git - lilypond.git/blob - lily/spring.cc
Revert "Issue 4550 (2/2) Avoid "using namespace std;" in included files"
[lilypond.git] / lily / spring.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2007--2015 Joe Neeman <joeneeman@gmail.com>
5
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /*
21   Springs help chains of objects, such as the notes in a line of music,
22   distribute themselves evenly.
23   Each spring decides the length from the reference point of one object
24   along the line to the reference point of the next, based on a force
25   applied to the entire chain (see Spring::length() for details):
26      length = distance_ + flexibility * force
27
28   distance_  is the ideal separation between reference points
29   inverse_stretch_strength_ is the flexibility when the force is stretching
30   inverse_compress_strength_ is the flexibility when the force is compressing
31   min_distance_ sets a lower limit on length
32
33   Typically, the force applied to a list of objects ranges from about
34   -1 to about 1, though there are no set limits.
35 */
36
37 #include "spring.hh"
38
39 using std::vector;
40
41 Spring::Spring ()
42 {
43   distance_ = 1.0;
44   min_distance_ = 1.0;
45   inverse_stretch_strength_ = 1.0;
46   inverse_compress_strength_ = 1.0;
47
48   update_blocking_force ();
49 }
50
51 Spring::Spring (Real dist, Real min_dist)
52 {
53   distance_ = 1.0;
54   min_distance_ = 1.0;
55   inverse_stretch_strength_ = 1.0;
56   inverse_compress_strength_ = 1.0;
57
58   set_distance (dist);
59   set_min_distance (min_dist);
60   set_default_strength ();
61   update_blocking_force ();
62 }
63
64 void
65 Spring::update_blocking_force ()
66 {
67   // blocking_force_ is the value of force
68   //   below which length(force) is constant, and
69   //   above which length(force) varies according to inverse_*_strength.
70   // Simple_spacer::compress_line() depends on the condition above.
71   // We assume inverse_*_strength are non-negative.
72   if (min_distance_ > distance_)
73     if (inverse_stretch_strength_ > 0.0)
74       blocking_force_ = (min_distance_ - distance_) / inverse_stretch_strength_;
75     else
76       // Conceptually, this should be +inf, but 0.0 meets the requirements
77       //  of Simple_spacer and creates fewer cases of 0.0*inf to handle.
78       blocking_force_ = 0.0;
79   else if (inverse_compress_strength_ > 0.0)
80     blocking_force_ = (min_distance_ - distance_) / inverse_compress_strength_;
81   else
82     blocking_force_ = 0.0;
83 }
84
85 /* scale a spring, but in a way that doesn't violate min_distance */
86 void
87 Spring::operator *= (Real r)
88 {
89   distance_ = max (min_distance_, distance_ * r);
90   inverse_compress_strength_ = max (0.0, distance_ - min_distance_);
91   inverse_stretch_strength_ *= r;
92   update_blocking_force ();
93 }
94
95 bool
96 Spring::operator > (Spring const &other) const
97 {
98   return blocking_force_ > other.blocking_force_;
99 }
100
101 /* merge springs, basically by averaging them, but leave a little headroom
102    above the largest minimum distance so that things don't get too cramped */
103 Spring
104 merge_springs (vector<Spring> const &springs)
105 {
106   Real avg_distance = 0;
107   Real min_distance = 0;
108   Real avg_stretch = 0;
109   Real avg_compress = 0;
110
111   for (vsize i = 0; i < springs.size (); i++)
112     {
113       avg_distance += springs[i].distance ();
114       avg_stretch += springs[i].inverse_stretch_strength ();
115       avg_compress += 1 / springs[i].inverse_compress_strength ();
116       min_distance = max (springs[i].min_distance (), min_distance);
117     }
118
119   avg_stretch /= Real (springs.size ());
120   avg_compress /= Real (springs.size ());
121   avg_distance /= Real (springs.size ());
122   avg_distance = max (min_distance + 0.3, avg_distance);
123
124   Spring ret = Spring (avg_distance, min_distance);
125   ret.set_inverse_stretch_strength (avg_stretch);
126   ret.set_inverse_compress_strength (1 / avg_compress);
127
128   return ret;
129 }
130
131 void
132 Spring::set_distance (Real d)
133 {
134   if (d < 0 || isinf (d) || isnan (d))
135     programming_error ("insane spring distance requested, ignoring it");
136   else
137     {
138       distance_ = d;
139       update_blocking_force ();
140     }
141 }
142
143 void
144 Spring::set_min_distance (Real d)
145 {
146   if (d < 0 || isinf (d) || isnan (d))
147     programming_error ("insane spring min_distance requested, ignoring it");
148   else
149     {
150       min_distance_ = d;
151       update_blocking_force ();
152     }
153 }
154
155 void
156 Spring::ensure_min_distance (Real d)
157 {
158   set_min_distance (max (d, min_distance_));
159 }
160
161 void
162 Spring::set_inverse_stretch_strength (Real f)
163 {
164   if (isinf (f) || isnan (f) || f < 0)
165     programming_error ("insane spring constant");
166   else
167     inverse_stretch_strength_ = f;
168
169   update_blocking_force ();
170 }
171
172 void
173 Spring::set_inverse_compress_strength (Real f)
174 {
175   if (isinf (f) || isnan (f) || f < 0)
176     programming_error ("insane spring constant");
177   else
178     inverse_compress_strength_ = f;
179
180   update_blocking_force ();
181 }
182
183 void
184 Spring::set_blocking_force (Real f)
185 {
186   if (isinf (f) || isnan (f))
187     {
188       programming_error ("insane blocking force");
189       return;
190     }
191
192   blocking_force_ = -infinity_f;
193   min_distance_ = length (f);
194   update_blocking_force ();
195 }
196
197 void
198 Spring::set_default_strength ()
199 {
200   set_default_stretch_strength ();
201   set_default_compress_strength ();
202 }
203
204 void
205 Spring::set_default_compress_strength ()
206 {
207   inverse_compress_strength_ = (distance_ >= min_distance_) ? distance_ - min_distance_ : 0;
208   update_blocking_force ();
209 }
210
211 void
212 Spring::set_default_stretch_strength ()
213 {
214   inverse_stretch_strength_ = distance_;
215 }
216
217 Real
218 Spring::length (Real f) const
219 {
220   Real force = max (f, blocking_force_);
221   Real inv_k = force < 0.0 ? inverse_compress_strength_ : inverse_stretch_strength_;
222
223   if (isinf (force))
224     {
225       programming_error ("cruelty to springs");
226       force = 0.0;
227     }
228
229   // There is a corner case here: if min_distance_ is larger than
230   // distance_ but the spring is fixed, then inv_k will be zero
231   // and we need to make sure that we return min_distance_.
232   return max (min_distance_, distance_ + force * inv_k);
233 }
234