]> git.donarmstrong.com Git - lilypond.git/blob - lily/spring.cc
Web-ja: update introduction
[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 Spring::Spring ()
40 {
41   distance_ = 1.0;
42   min_distance_ = 1.0;
43   inverse_stretch_strength_ = 1.0;
44   inverse_compress_strength_ = 1.0;
45
46   update_blocking_force ();
47 }
48
49 Spring::Spring (Real dist, Real min_dist)
50 {
51   distance_ = 1.0;
52   min_distance_ = 1.0;
53   inverse_stretch_strength_ = 1.0;
54   inverse_compress_strength_ = 1.0;
55
56   set_distance (dist);
57   set_min_distance (min_dist);
58   set_default_strength ();
59   update_blocking_force ();
60 }
61
62 void
63 Spring::update_blocking_force ()
64 {
65   // blocking_force_ is the value of force
66   //   below which length(force) is constant, and
67   //   above which length(force) varies according to inverse_*_strength.
68   // Simple_spacer::compress_line() depends on the condition above.
69   // We assume inverse_*_strength are non-negative.
70   if (min_distance_ > distance_)
71     if (inverse_stretch_strength_ > 0.0)
72       blocking_force_ = (min_distance_ - distance_) / inverse_stretch_strength_;
73     else
74       // Conceptually, this should be +inf, but 0.0 meets the requirements
75       //  of Simple_spacer and creates fewer cases of 0.0*inf to handle.
76       blocking_force_ = 0.0;
77   else if (inverse_compress_strength_ > 0.0)
78     blocking_force_ = (min_distance_ - distance_) / inverse_compress_strength_;
79   else
80     blocking_force_ = 0.0;
81 }
82
83 /* scale a spring, but in a way that doesn't violate min_distance */
84 void
85 Spring::operator *= (Real r)
86 {
87   distance_ = max (min_distance_, distance_ * r);
88   inverse_compress_strength_ = max (0.0, distance_ - min_distance_);
89   inverse_stretch_strength_ *= r;
90   update_blocking_force ();
91 }
92
93 bool
94 Spring::operator > (Spring const &other) const
95 {
96   return blocking_force_ > other.blocking_force_;
97 }
98
99 /* merge springs, basically by averaging them, but leave a little headroom
100    above the largest minimum distance so that things don't get too cramped */
101 Spring
102 merge_springs (vector<Spring> const &springs)
103 {
104   Real avg_distance = 0;
105   Real min_distance = 0;
106   Real avg_stretch = 0;
107   Real avg_compress = 0;
108
109   for (vsize i = 0; i < springs.size (); i++)
110     {
111       avg_distance += springs[i].distance ();
112       avg_stretch += springs[i].inverse_stretch_strength ();
113       avg_compress += 1 / springs[i].inverse_compress_strength ();
114       min_distance = max (springs[i].min_distance (), min_distance);
115     }
116
117   avg_stretch /= Real (springs.size ());
118   avg_compress /= Real (springs.size ());
119   avg_distance /= Real (springs.size ());
120   avg_distance = max (min_distance + 0.3, avg_distance);
121
122   Spring ret = Spring (avg_distance, min_distance);
123   ret.set_inverse_stretch_strength (avg_stretch);
124   ret.set_inverse_compress_strength (1 / avg_compress);
125
126   return ret;
127 }
128
129 void
130 Spring::set_distance (Real d)
131 {
132   if (d < 0 || isinf (d) || isnan (d))
133     programming_error ("insane spring distance requested, ignoring it");
134   else
135     {
136       distance_ = d;
137       update_blocking_force ();
138     }
139 }
140
141 void
142 Spring::set_min_distance (Real d)
143 {
144   if (d < 0 || isinf (d) || isnan (d))
145     programming_error ("insane spring min_distance requested, ignoring it");
146   else
147     {
148       min_distance_ = d;
149       update_blocking_force ();
150     }
151 }
152
153 void
154 Spring::ensure_min_distance (Real d)
155 {
156   set_min_distance (max (d, min_distance_));
157 }
158
159 void
160 Spring::set_inverse_stretch_strength (Real f)
161 {
162   if (isinf (f) || isnan (f) || f < 0)
163     programming_error ("insane spring constant");
164   else
165     inverse_stretch_strength_ = f;
166
167   update_blocking_force ();
168 }
169
170 void
171 Spring::set_inverse_compress_strength (Real f)
172 {
173   if (isinf (f) || isnan (f) || f < 0)
174     programming_error ("insane spring constant");
175   else
176     inverse_compress_strength_ = f;
177
178   update_blocking_force ();
179 }
180
181 void
182 Spring::set_blocking_force (Real f)
183 {
184   if (isinf (f) || isnan (f))
185     {
186       programming_error ("insane blocking force");
187       return;
188     }
189
190   blocking_force_ = -infinity_f;
191   min_distance_ = length (f);
192   update_blocking_force ();
193 }
194
195 void
196 Spring::set_default_strength ()
197 {
198   set_default_stretch_strength ();
199   set_default_compress_strength ();
200 }
201
202 void
203 Spring::set_default_compress_strength ()
204 {
205   inverse_compress_strength_ = (distance_ >= min_distance_) ? distance_ - min_distance_ : 0;
206   update_blocking_force ();
207 }
208
209 void
210 Spring::set_default_stretch_strength ()
211 {
212   inverse_stretch_strength_ = distance_;
213 }
214
215 Real
216 Spring::length (Real f) const
217 {
218   Real force = max (f, blocking_force_);
219   Real inv_k = force < 0.0 ? inverse_compress_strength_ : inverse_stretch_strength_;
220
221   if (isinf (force))
222     {
223       programming_error ("cruelty to springs");
224       force = 0.0;
225     }
226
227   // There is a corner case here: if min_distance_ is larger than
228   // distance_ but the spring is fixed, then inv_k will be zero
229   // and we need to make sure that we return min_distance_.
230   return max (min_distance_, distance_ + force * inv_k);
231 }
232