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