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