2 bezier.cc -- implement Bezier and Bezier_bow
4 source file of the GNU LilyPond music typesetter
6 (c) 1998--2000 Jan Nieuwenhuizen <janneke@gnu.org>
10 #include "bezier-bow.hh"
13 #include "dimensions.hh"
14 #include "direction.hh"
17 #include "lily-guile.hh"
20 flipy (Array<Offset> &c)
22 for (int i = c.size (); i--;)
23 c[i][Y_AXIS] = - c[i][Y_AXIS];
27 rotate (Array<Offset> &c, Real phi)
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]);
35 translate (Array<Offset> &c, Offset o)
37 for (int i = 0; i < c.size (); i++)
42 Bezier_bow::Bezier_bow (Array<Offset> points, Direction dir)
54 Bezier_bow::blow_fit ()
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;
60 // ugh. Unhardcode this
66 if (h > 0.8 + -2 * ind)
71 curve_.control_[1][Y_AXIS] = h * len;
72 curve_.control_[2][Y_AXIS] = h * len;
74 curve_.check_sanity ();
78 Bezier_bow::calc_enclosed_area_f () const
81 for (int i=0; i < encompass_.size (); i++)
86 x = Interval (0, encompass_[1][X_AXIS]/2);
87 else if (i == encompass_.size () - 1)
88 x = Interval ((encompass_[i-1][X_AXIS] + encompass_[i][X_AXIS])/2,
89 encompass_[i][X_AXIS]);
91 x = Interval ((encompass_[i-1][X_AXIS] + encompass_[i][X_AXIS])/2,
92 (encompass_[i][X_AXIS] + encompass_[i+1][X_AXIS])/2);
94 y[MIN] = encompass_[i][Y_AXIS];
95 // solve_point (X, 0|Xmax) has no solutions...
96 y[MAX] = (x[MIN] ? curve_.get_other_coordinate (X_AXIS, x[MIN]) : 0
97 + curve_.get_other_coordinate (X_AXIS, (x[MIN] + x[MAX])/2)
98 + x[MAX] != encompass_.top ()[X_AXIS] ? curve_.get_other_coordinate (X_AXIS, x[MAX]) : 0)/3;
99 Real da = x.length () * y.length ();
107 Bezier_bow::minimise_enclosed_area ()
109 Real area = calc_enclosed_area_f ();
111 Array<Offset> da (2);
112 for (int i=1; i < 3; i++)
114 for (Axis a=X_AXIS; a < NO_AXES; incr (a))
116 Real r = curve_.control_[i][a];
117 curve_.control_[i][a] += 1;
118 da[i-1][a] = (calc_enclosed_area_f () - area) / area;
119 curve_.control_[i][a] = r;
123 for (int i=1; i < 3; i++)
125 da[i-1] *= da[i-1].length () ? 1.0 / da[i-1].length () : 1.0;
126 curve_.control_[i] -= da[i-1] * curve_.control_[i].length ();
131 Bezier_bow::calculate ()
134 if (fit_factor () > 1.0)
136 // calc_tangent_controls ();
138 minimise_enclosed_area ();
145 Bezier_bow::get_curve ()const
154 rv.translate (origin_);
159 static Real const FUDGE = 1e-8;
162 This function calculates 2 center control points,
163 based on lines through c_0 --> left disturbing
164 and c_3--> right disturbing encompass points.
166 See Documentation/fonts.tex
169 Bezier_bow::calc_tangent_controls ()
171 Real b = curve_.control_[3][X_AXIS];
172 Real h = curve_.control_[1][Y_AXIS];
175 Drul_array<Offset> disturb;
176 Drul_array<Real> maxtan;
177 maxtan[LEFT] = maxtan[RIGHT] = h/(b/2);
178 disturb[LEFT] = disturb[RIGHT] = Offset (b / 2, h);
180 for (int i = 1; i < encompass_.size () -1; i++)
182 Real y= encompass_[i][Y_AXIS];
185 Real x = encompass_[i][X_AXIS];
192 Real tan = y / ((1-k)* b - d * x);
197 disturb[d] = Offset (x,y);
200 while (flip (&d)!=LEFT);
204 for (int i = 0; i < encompass_.size (); i++ )
205 h = h >? encompass_[i][Y_AXIS];
208 The curve will always be under line between curve_.control_0 -> curve_.control_1, so
209 make it extra steep by slur_rc_factor
213 Drul_array<Real> angles;
217 maxtan[d] *= -d * rc_factor_;
218 angles[d] = atan (maxtan[d]);
220 while (flip(&d) != LEFT);
225 if we have two disturbing points, have line through those...
226 in order to get a sane line, make sure points are reasonably far apart
227 X distance must be reasonably(!) big (division)
229 if (abs (disturb[LEFT][X_AXIS] - disturb[RIGHT][X_AXIS]) > FUDGE)
230 rc3 = (disturb[RIGHT][Y_AXIS] - disturb[LEFT][Y_AXIS]) / (disturb[RIGHT][X_AXIS] - disturb[LEFT][X_AXIS]);
233 rc3 = tan ((angles[LEFT] - angles[RIGHT]) / 2);
236 // ugh: be less steep
240 Real c2 = -maxtan[RIGHT] * curve_.control_[3][X_AXIS];
242 // use highest because rc3 is damped.
243 Real maxy = disturb[LEFT][Y_AXIS] >? disturb[RIGHT][Y_AXIS];
244 Real c3 = disturb[LEFT][Y_AXIS] > disturb[RIGHT][Y_AXIS] ?
245 maxy - rc3 * disturb[LEFT][X_AXIS] :
246 maxy - rc3 * disturb[RIGHT][X_AXIS];
248 curve_.control_[1][X_AXIS] = c3 / (maxtan[LEFT] - rc3);
249 curve_.control_[1][Y_AXIS] = maxtan[LEFT] * curve_.control_[1][X_AXIS];
251 curve_.control_[2][X_AXIS] = (c3 - c2) / (maxtan[RIGHT] - rc3);
252 curve_.control_[2][Y_AXIS] = maxtan[RIGHT] * curve_.control_[2][X_AXIS] + c2;
255 curve_.check_sanity();
259 The maximum amount that the encompass points stick out above the bezier curve.
262 Bezier_bow::fit_factor () const
264 Real x1 = encompass_[0][X_AXIS];
265 Real x2 = encompass_.top ()[X_AXIS];
268 for (int i=1; i < encompass_.size ()-1; i++)
270 if (encompass_[i][X_AXIS] > x1 && encompass_[i][X_AXIS] < x2)
272 Real y = curve_.get_other_coordinate (X_AXIS, encompass_[i][X_AXIS]);
275 Real f = encompass_[i][Y_AXIS] / y;
276 factor = factor >? f;
289 Bezier_bow::to_canonic_form ()
291 origin_ = encompass_[0];
292 translate (encompass_,-origin_);
294 Offset delta = encompass_.top () - encompass_[0];
295 alpha_ = delta.arg ();
297 rotate (encompass_, -alpha_);
303 while (encompass_.size () > 1 && encompass_[1][X_AXIS] <= 0.0)
305 programming_error ("Degenerate slur: infinite steepness reqd");
309 Real l = encompass_.top ()[X_AXIS];
310 while (encompass_.size () > 1 && encompass_.top (1)[X_AXIS] >= l)
312 programming_error ("Degenerate slur: infinite steepness reqd");
313 encompass_.del (encompass_.size ()-2);
320 See Documentation/fonts.tex
323 Bezier_bow::calc_default ()
327 Real alpha = height_limit_ * 2.0 / pi;
328 Real beta = pi * ratio_ / (2.0 * height_limit_);
330 Offset delta (encompass_.top ()[X_AXIS]
331 - encompass_[0][X_AXIS], 0);
333 Real b = delta.length ();
334 Real indent = alpha * atan (beta * b);
335 Real height = indent;
337 curve_.control_ [0] = Offset (0, 0);
338 curve_.control_ [1] = Offset (indent, height);
339 curve_.control_ [2] = Offset (b - indent, height);
340 curve_.control_ [3] = Offset (b, 0);