]> git.donarmstrong.com Git - lilypond.git/blob - lily/bezier-bow.cc
release: 1.3.24
[lilypond.git] / lily / bezier-bow.cc
1 /*
2   bezier.cc -- implement Bezier and Bezier_bow
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1998--2000 Jan Nieuwenhuizen <janneke@gnu.org>
7 */
8
9 #include <math.h>
10 #include "bezier-bow.hh"
11 #include "misc.hh"
12 #include "bezier.hh"
13 #include "dimensions.hh"
14 #include "direction.hh"
15 #include "debug.hh"
16 #include "main.hh"
17 #include "lily-guile.hh"
18
19 void
20 flipy (Array<Offset>  &c)
21 {
22   for (int i = c.size (); i--;)
23     c[i][Y_AXIS] = - c[i][Y_AXIS];
24 }
25
26 void
27 rotate (Array<Offset> &c, Real phi)
28 {
29   Offset rot (complex_exp (Offset (0, phi)));
30   for (int i = 0; i < c.size (); i++)
31     c[i] = complex_multiply (rot, c[i]);
32 }
33
34 void
35 translate (Array<Offset> &c, Offset o)
36 {
37   for (int i = 0; i < c.size (); i++)
38     c[i] += o;
39 }
40
41
42 Bezier_bow::Bezier_bow (Array<Offset> points, Direction dir)
43 {
44   dir_ = dir;
45   encompass_ = points;
46   to_canonic_form ();
47
48   rc_factor_ = 1.0;
49   height_limit_ = 1.0;
50   ratio_ = 1.0;
51 }
52
53 void
54 Bezier_bow::blow_fit ()
55 {
56   Real len = curve_.control_[3][X_AXIS] ; 
57   Real ind = curve_.control_[1][X_AXIS] / len;
58   Real h = curve_.control_[1][Y_AXIS] * fit_factor () / len;
59
60   // ugh. Unhardcode this
61   if (h > 4 * ind)
62     {
63       h = 4* ind; 
64     }
65
66   if (h > 0.8 + -2 * ind)
67     {
68       h = 0.8 - 2  *ind; 
69     }
70   
71   curve_.control_[1][Y_AXIS] = h * len;
72   curve_.control_[2][Y_AXIS] = h * len;  
73
74   curve_.check_sanity ();
75 }
76
77 void
78 Bezier_bow::calculate ()
79 {
80   calc_default ();
81   if (fit_factor () > 1.0)
82     {
83       //    calc_tangent_controls ();
84       blow_fit ();
85     }
86 }
87
88
89   
90 Bezier
91 Bezier_bow::get_curve ()const
92 {
93   Bezier rv = curve_;
94   if (dir_ == DOWN)
95     {
96       rv.flip (Y_AXIS);
97     }
98
99   rv.rotate (alpha_);
100   rv.translate (origin_);
101   
102   return rv;
103 }
104
105 static Real const FUDGE = 1e-8;
106
107 /*
108   This function calculates 2 center control points,
109   based on lines through c_0 --> left disturbing
110   and c_3--> right disturbing encompass points.
111   
112   See Documentation/fonts.tex
113  */
114 void
115 Bezier_bow::calc_tangent_controls ()
116 {
117   Real b = curve_.control_[3][X_AXIS];
118   Real h = curve_.control_[1][Y_AXIS];
119   
120
121   Drul_array<Offset> disturb;
122   Drul_array<Real> maxtan;  
123   maxtan[LEFT]  = maxtan[RIGHT] = h/(b/2);
124   disturb[LEFT]  = disturb[RIGHT] =   Offset (b / 2, h);
125
126   for (int i = 1; i < encompass_.size () -1; i++)
127     {
128       Real y= encompass_[i][Y_AXIS];
129       if (y> 0)
130         {
131           Real x = encompass_[i][X_AXIS];
132           
133           Direction d = LEFT;
134           do
135             {
136               // 1 if d == LEFT
137               int k = (1 - d)/2;
138               Real tan = y /  ((1-k)* b - d * x);
139
140               if (tan > maxtan[d])
141                 {
142                   maxtan[d] = tan;
143                   disturb[d] = Offset (x,y);
144                 }
145             }
146           while (flip (&d)!=LEFT);
147         }
148     }
149
150   for (int i = 0; i < encompass_.size (); i++ )
151     h = h >? encompass_[i][Y_AXIS];
152
153   /*
154     The curve will always be under line between curve_.control_0 -> curve_.control_1, so
155     make it extra steep by slur_rc_factor
156   */
157
158
159   Drul_array<Real> angles;
160   Direction d = LEFT;
161   do
162     {
163       maxtan[d] *= -d * rc_factor_;
164       angles[d] = atan (maxtan[d]);
165     }
166   while (flip(&d) != LEFT);
167
168   Real rc3 = 0.0;
169
170   /* 
171     if we have two disturbing points, have line through those...
172     in order to get a sane line, make sure points are reasonably far apart
173     X distance must be reasonably(!) big (division)
174    */
175   if (abs (disturb[LEFT][X_AXIS] - disturb[RIGHT][X_AXIS]) > FUDGE)
176     rc3 = (disturb[RIGHT][Y_AXIS] - disturb[LEFT][Y_AXIS]) / (disturb[RIGHT][X_AXIS] - disturb[LEFT][X_AXIS]);
177
178   else
179     rc3 = tan ((angles[LEFT] - angles[RIGHT]) / 2);
180
181
182   // ugh: be less steep
183   rc3 /= 2*rc_factor_;
184   
185
186   Real c2 = -maxtan[RIGHT] * curve_.control_[3][X_AXIS];
187
188   // use highest because rc3 is damped.
189   Real maxy = disturb[LEFT][Y_AXIS] >? disturb[RIGHT][Y_AXIS];
190   Real c3 = disturb[LEFT][Y_AXIS] > disturb[RIGHT][Y_AXIS] ?
191     maxy - rc3 * disturb[LEFT][X_AXIS] :
192     maxy - rc3 * disturb[RIGHT][X_AXIS];
193
194   curve_.control_[1][X_AXIS] = c3 / (maxtan[LEFT] - rc3);
195   curve_.control_[1][Y_AXIS] = maxtan[LEFT] * curve_.control_[1][X_AXIS];
196   
197   curve_.control_[2][X_AXIS] = (c3 - c2) / (maxtan[RIGHT] - rc3);
198   curve_.control_[2][Y_AXIS] = maxtan[RIGHT] * curve_.control_[2][X_AXIS] + c2;
199
200
201   curve_.check_sanity();
202 }
203
204 /*
205   The maximum amount that the encompass points stick out above the bezier curve.
206  */
207 Real
208 Bezier_bow::fit_factor () const
209 {
210   Real x1 = encompass_[0][X_AXIS];
211   Real x2 = encompass_.top ()[X_AXIS];
212
213   Real factor = 1.0;
214   for (int i=1; i < encompass_.size ()-1; i++)
215     {
216       if (encompass_[i][X_AXIS] > x1 && encompass_[i][X_AXIS] < x2)
217         {
218          Real y = curve_.get_other_coordinate (X_AXIS, encompass_[i][X_AXIS]);
219          if (y>0)
220            {
221              Real f = encompass_[i][Y_AXIS] / y;
222              factor = factor >? f;
223            }
224         }
225     }
226
227
228   return factor;
229 }
230
231
232
233
234 void
235 Bezier_bow::to_canonic_form ()
236 {
237   origin_ = encompass_[0];
238   translate (encompass_,-origin_);
239
240   Offset delta = encompass_.top () - encompass_[0];
241   alpha_ = delta.arg ();
242
243   rotate (encompass_, -alpha_);
244   if (dir_ == DOWN)
245     {
246       flipy (encompass_);
247     }
248
249   while (encompass_.size () > 1 && encompass_[1][X_AXIS] <= 0.0)
250     {
251       programming_error ("Degenerate slur: infinite steepness reqd");
252       encompass_.del (1);
253     }
254
255   Real l = encompass_.top ()[X_AXIS];
256   while (encompass_.size () > 1 && encompass_.top (1)[X_AXIS] >= l)
257     {
258       programming_error ("Degenerate slur: infinite steepness reqd");
259       encompass_.del (encompass_.size ()-2);
260     }
261 }
262
263
264
265 /*
266  See Documentation/fonts.tex
267  */
268 void
269 Bezier_bow::calc_default ()
270 {
271   Real pi = M_PI;
272
273   Real alpha = height_limit_ * 2.0 / pi;
274   Real beta = pi * ratio_ / (2.0 * height_limit_);
275
276   Offset delta (encompass_.top ()[X_AXIS] 
277     - encompass_[0][X_AXIS], 0);
278
279   Real b = delta.length ();
280   Real indent = alpha * atan (beta * b);
281   Real height = indent;
282  
283   curve_.control_ [0] = Offset (0, 0);
284   curve_.control_ [1] = Offset (indent, height);
285   curve_.control_ [2] = Offset (b - indent, height);
286   curve_.control_ [3] = Offset (b, 0);
287 }
288
289
290