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 default_height (Real len)
58 Real staff_space = 5.0;
59 Real h_inf = 2.0* staff_space;
61 return h_inf * 2.0 / M_PI * atan ( M_PI * r_0 / (2.0 * h_inf) * len);
65 Bezier_bow::blow_fit ()
67 Real len = curve_.control_[3][X_AXIS];
68 Real h = curve_.control_[1][Y_AXIS] * fit_factor () / len;
69 curve_.control_[1][Y_AXIS] = h * len;
70 curve_.control_[2][Y_AXIS] = h * len;
71 curve_.check_sanity ();
75 Bezier_bow::de_uglyfy ()
77 Real len = curve_.control_[3][X_AXIS] ;
78 Real ff = fit_factor ();
79 for (int i = 1; i < 3; i++)
81 Real ind = abs (curve_.control_[(i-1)*3][X_AXIS]
82 - curve_.control_[i][X_AXIS]) / len;
83 Real h = curve_.control_[i][Y_AXIS] * ff / len;
85 // ugh. Unhardcode this
93 Real f = default_height (len) / len;
100 if (h > 0.8 + -2 * ind)
105 curve_.control_[i][Y_AXIS] = h * len;
108 curve_.check_sanity ();
112 Bezier_bow::calc_enclosed_area_f () const
115 for (int i=0; i < encompass_.size (); i++)
121 x = Interval (0, encompass_[1][X_AXIS] / 2);
123 curve_.get_other_coordinate (X_AXIS,
124 encompass_[1][X_AXIS]
127 else if (i == encompass_.size () - 1)
129 x = Interval ((encompass_[i-1][X_AXIS] + encompass_[i][X_AXIS])/2,
130 encompass_[i][X_AXIS]);
132 (curve_.get_other_coordinate (X_AXIS,
133 (x[MIN] + x[MAX]) / 2)));
137 x = Interval ((encompass_[i-1][X_AXIS] + encompass_[i][X_AXIS]) / 2,
138 (encompass_[i][X_AXIS] + encompass_[i+1][X_AXIS]) / 2);
139 y = Interval (encompass_[i][Y_AXIS],
140 (curve_.get_other_coordinate (X_AXIS, x[MIN])
141 + curve_.get_other_coordinate (X_AXIS,
142 (x[MIN] + x[MAX]) / 2)
143 + curve_.get_other_coordinate (X_AXIS, x[MAX])) / 3);
146 Real da = x.length () * y.length ();
153 Bezier_bow::area_gradient_offset_arr ()
155 Real len = curve_.control_[3][X_AXIS];
156 Real area = calc_enclosed_area_f ();
158 Real grow = len / 10.0;
159 Array<Offset> da (2);
160 for (int i=1; i < 3; i++)
162 for (Axis a=X_AXIS; a < NO_AXES; incr (a))
164 Real r = curve_.control_[i][a];
165 curve_.control_[i][a] += grow;
166 da[i-1][a] = (calc_enclosed_area_f () - area) / grow;
168 curve_.control_[i][a] = r;
175 Bezier_bow::minimise_enclosed_area ()
177 Real len = curve_.control_[3][X_AXIS];
178 Real beautiful = len * default_height (len) / 2.0;
180 DEBUG_OUT << to_str ("Beautiful: %f\n", beautiful);
181 DEBUG_OUT << to_str ("Length: %f\n", len);
183 for (int i=0; i < steps; i++)
185 Real ff = fit_factor ();
189 DEBUG_OUT << to_str ("FitFac: %f\n", ff);
191 // slur must be higher at every point
195 DEBUG_OUT << to_str ("Blown area: %f\n", calc_enclosed_area_f ());
198 DEBUG_OUT << to_str ("Init area: %f\n", calc_enclosed_area_f ());
200 Real area = calc_enclosed_area_f ();
203 if (area <= beautiful)
206 Array<Offset> da = area_gradient_offset_arr ();
210 Small slurs are easily too asymmetric,
211 while big slurs are too symmetric
213 This makes short slurs strictly x-bound,
214 long slurs become y-bound.
217 //Real xpct = (0.07 * len * len / 1000.0) <? 0.80;
218 Real xpct = (0.1 * len * len * len / 100000.0) <? 0.80;
220 Real yu = (abs (curve_.control_[1][Y_AXIS] / da[0][Y_AXIS])
221 <? abs (curve_.control_[2][Y_AXIS] / da[1][Y_AXIS]))
223 Real xu = (abs (curve_.control_[1][X_AXIS] / da[0][X_AXIS])
224 <? abs ((curve_.control_[3][X_AXIS]
225 - curve_.control_[2][X_AXIS]) / da[1][X_AXIS]))
228 DEBUG_OUT << to_str ("u (xu, yu): %f (%f, %f)\n", u, xu, yu);
229 DEBUG_OUT << to_str ("pct (x, y): (%f, %f)\n", xpct, ypct);
231 DEBUG_OUT << to_str ("da1: (%f, %f)\n", da[0][X_AXIS], da[0][Y_AXIS]);
232 DEBUG_OUT << to_str ("da2: (%f, %f)\n", da[1][X_AXIS], da[1][Y_AXIS]);
234 curve_.control_[1] -= da[0] * u;
235 curve_.control_[2] -= da[1] * u;
239 if (fit_factor () > 1.5)
242 DEBUG_OUT << to_str ("Exarea: %f\n", calc_enclosed_area_f ());
243 Real area = calc_enclosed_area_f ();
245 Slurs that fit beautifully are not ugly
247 if (area > beautiful)
249 DEBUG_OUT << "DE-UGLYFY\n";
256 Bezier_bow::calculate ()
259 if (fit_factor () > 1.0)
261 // calc_tangent_controls ();
263 minimise_enclosed_area ();
270 Bezier_bow::get_curve ()const
279 rv.translate (origin_);
284 static Real const FUDGE = 1e-8;
287 This function calculates 2 center control points,
288 based on lines through c_0 --> left disturbing
289 and c_3--> right disturbing encompass points.
291 See Documentation/fonts.tex
294 Bezier_bow::calc_tangent_controls ()
296 Real b = curve_.control_[3][X_AXIS];
297 Real h = curve_.control_[1][Y_AXIS];
300 Drul_array<Offset> disturb;
301 Drul_array<Real> maxtan;
302 maxtan[LEFT] = maxtan[RIGHT] = h/(b/2);
303 disturb[LEFT] = disturb[RIGHT] = Offset (b / 2, h);
305 for (int i = 1; i < encompass_.size () -1; i++)
307 Real y= encompass_[i][Y_AXIS];
310 Real x = encompass_[i][X_AXIS];
317 Real tan = y / ((1-k)* b - d * x);
322 disturb[d] = Offset (x,y);
325 while (flip (&d)!=LEFT);
329 for (int i = 0; i < encompass_.size (); i++ )
330 h = h >? encompass_[i][Y_AXIS];
333 The curve will always be under line between curve_.control_0 -> curve_.control_1, so
334 make it extra steep by slur_rc_factor
338 Drul_array<Real> angles;
342 maxtan[d] *= -d * rc_factor_;
343 angles[d] = atan (maxtan[d]);
345 while (flip(&d) != LEFT);
350 if we have two disturbing points, have line through those...
351 in order to get a sane line, make sure points are reasonably far apart
352 X distance must be reasonably(!) big (division)
354 if (abs (disturb[LEFT][X_AXIS] - disturb[RIGHT][X_AXIS]) > FUDGE)
355 rc3 = (disturb[RIGHT][Y_AXIS] - disturb[LEFT][Y_AXIS]) / (disturb[RIGHT][X_AXIS] - disturb[LEFT][X_AXIS]);
358 rc3 = tan ((angles[LEFT] - angles[RIGHT]) / 2);
361 // ugh: be less steep
365 Real c2 = -maxtan[RIGHT] * curve_.control_[3][X_AXIS];
367 // use highest because rc3 is damped.
368 Real maxy = disturb[LEFT][Y_AXIS] >? disturb[RIGHT][Y_AXIS];
369 Real c3 = disturb[LEFT][Y_AXIS] > disturb[RIGHT][Y_AXIS] ?
370 maxy - rc3 * disturb[LEFT][X_AXIS] :
371 maxy - rc3 * disturb[RIGHT][X_AXIS];
373 curve_.control_[1][X_AXIS] = c3 / (maxtan[LEFT] - rc3);
374 curve_.control_[1][Y_AXIS] = maxtan[LEFT] * curve_.control_[1][X_AXIS];
376 curve_.control_[2][X_AXIS] = (c3 - c2) / (maxtan[RIGHT] - rc3);
377 curve_.control_[2][Y_AXIS] = maxtan[RIGHT] * curve_.control_[2][X_AXIS] + c2;
380 curve_.check_sanity();
384 max ( encompass.y / curve.y )
388 Bezier_bow::fit_factor () const
390 Real x1 = encompass_[0][X_AXIS];
391 Real x2 = encompass_.top ()[X_AXIS];
394 for (int i=1; i < encompass_.size ()-1; i++)
396 if (encompass_[i][X_AXIS] > x1 && encompass_[i][X_AXIS] < x2)
398 Real y = curve_.get_other_coordinate (X_AXIS, encompass_[i][X_AXIS]);
401 Real f = encompass_[i][Y_AXIS] / y;
402 factor = factor >? f;
415 Bezier_bow::to_canonic_form ()
417 origin_ = encompass_[0];
418 translate (encompass_,-origin_);
420 Offset delta = encompass_.top () - encompass_[0];
421 alpha_ = delta.arg ();
423 rotate (encompass_, -alpha_);
429 while (encompass_.size () > 1 && encompass_[1][X_AXIS] <= 0.0)
431 programming_error ("Degenerate slur: infinite steepness reqd");
435 Real l = encompass_.top ()[X_AXIS];
436 while (encompass_.size () > 1 && encompass_.top (1)[X_AXIS] >= l)
438 programming_error ("Degenerate slur: infinite steepness reqd");
439 encompass_.del (encompass_.size ()-2);
446 See Documentation/fonts.tex
449 Bezier_bow::calc_default ()
453 Real alpha = height_limit_ * 2.0 / pi;
454 Real beta = pi * ratio_ / (2.0 * height_limit_);
456 Offset delta (encompass_.top ()[X_AXIS]
457 - encompass_[0][X_AXIS], 0);
459 Real b = delta.length ();
460 Real indent = alpha * atan (beta * b);
461 Real height = indent;
463 curve_.control_ [0] = Offset (0, 0);
464 curve_.control_ [1] = Offset (indent, height);
465 curve_.control_ [2] = Offset (b - indent, height);
466 curve_.control_ [3] = Offset (b, 0);