2 bezier.cc -- implement Bezier and Bezier_bow
4 source file of the GNU LilyPond music typesetter
6 (c) 1998--1999 Jan Nieuwenhuizen <janneke@gnu.org>
10 #include "bezier-bow.hh"
13 #include "dimensions.hh"
14 #include "direction.hh"
15 #include "paper-def.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 (Paper_def* paper_l)
48 Bezier_bow::blow_fit ()
50 Real x1 = encompass_[0][X_AXIS];
51 Real x2 = encompass_.top ()[X_AXIS];
54 for (int i=1; i < encompass_.size ()-1; i++)
56 if (encompass_[i][X_AXIS] > x1 && encompass_[i][X_AXIS] < x2)
58 Real y = curve_.get_other_coordinate (X_AXIS, encompass_[i][X_AXIS]);
61 Real f = encompass_[i][Y_AXIS] / y;
67 curve_.control_[1][Y_AXIS] *= factor;
68 curve_.control_[2][Y_AXIS] *= factor;
69 return_.control_[1][Y_AXIS] *= factor;
70 return_.control_[2][Y_AXIS] *= factor;
72 curve_.check_sanity ();
76 Bezier_bow::calc_f (Real height)
79 calc_default (height);
81 Real dy = check_fit_f ();
101 * document in Documentation/fonts.tex
107 This function tries to address two issues:
108 * the tangents of the slur should always point inwards
109 in the actual slur, i.e. *after rotating back*.
111 * slurs shouldn't be too high
112 let's try : h <= 1.2 b && h <= 3 staffheight?
114 We could calculate the tangent of the bezier curve from
115 both ends going inward, and clip the slur at the point
116 where the tangent (after rotation) points up (or inward
117 with a certain maximum angle).
119 However, we assume that real clipping is not the best
120 answer. We expect that moving the outer control point up
121 if the slur becomes too high will result in a nicer slur
124 Knowing that the tangent is the line through the first
125 two control points, we'll clip (move the outer control
126 point upwards) too if the tangent points outwards.
130 Bezier_bow::calc_clipping ()
132 Real clip_height = paper_l_->get_var ("slur_clip_height");
133 Real clip_ratio = paper_l_->get_var ("slur_clip_ratio");
134 Real clip_angle = paper_l_->get_var ("slur_clip_angle");
136 Real b = curve_.control_[3][X_AXIS] - curve_.control_[0][X_AXIS];
137 Real clip_h = clip_ratio * b <? clip_height;
138 Real begin_h = curve_.control_[1][Y_AXIS] - curve_.control_[0][Y_AXIS];
139 Real end_h = curve_.control_[2][Y_AXIS] - curve_.control_[3][Y_AXIS];
140 Real begin_dy = 0 >? begin_h - clip_h;
141 Real end_dy = 0 >? end_h - clip_h;
144 Real begin_alpha = (curve_.control_[1] - curve_.control_[0]).arg () + dir_ * alpha_;
145 Real end_alpha = pi - (curve_.control_[2] - curve_.control_[3]).arg () - dir_ * alpha_;
147 Real max_alpha = clip_angle / 90 * pi / 2;
148 if ((begin_dy < 0) && (end_dy < 0)
149 && (begin_alpha < max_alpha) && (end_alpha < max_alpha))
156 if ((begin_dy > 0) || (end_dy > 0))
158 Real dy = (begin_dy + end_dy) / 4;
160 encompass_[0][Y_AXIS] += dir_ * dy;
161 encompass_.top ()[Y_AXIS] += dir_ * dy;
167 if (begin_alpha >= max_alpha)
168 begin_dy = 0 >? c * begin_alpha / max_alpha * begin_h;
169 if (end_alpha >= max_alpha)
170 end_dy = 0 >? c * end_alpha / max_alpha * end_h;
172 encompass_[0][Y_AXIS] += dir_ * begin_dy;
173 encompass_.top ()[Y_AXIS] += dir_ * end_dy;
175 Offset delta = encompass_.top () - encompass_[0];
176 alpha_ = delta.arg ();
185 Bezier_bow::calc_controls ()
187 for (int i = 0; i < 3; i++)
190 if (i && !calc_clipping ())
194 why do we always recalc from 0?
195 shouldn't calc_f () be used (too), rather than blow_fit () (only)?
198 curve_.check_sanity ();
199 if (check_fit_f () > 0)
201 calc_tangent_controls ();
213 Bezier_bow::calc_return (Real begin_alpha, Real end_alpha)
215 Real thick = paper_l_->get_var ("slur_thickness");
217 return_.control_[0] = curve_.control_[3];
218 return_.control_[3] = curve_.control_[0];
220 return_.control_[1] = curve_.control_[2] - thick * complex_exp (Offset (0, 90 + end_alpha));
221 return_.control_[2] = curve_.control_[1] - thick * complex_exp (Offset (0, 90 - begin_alpha));
225 This function calculates 2 center control points, based on
227 See Documentation/fonts.tex
230 Bezier_bow::calc_tangent_controls ()
232 Real b = curve_.control_[3][X_AXIS];
233 Real h = curve_.control_[1][Y_AXIS];
236 Drul_array<Offset> disturb;
237 Drul_array<Real> maxtan;
238 maxtan[LEFT] = maxtan[RIGHT] = h/(b/2);
239 disturb[LEFT] = disturb[RIGHT] = Offset (b / 2, h);
241 for (int i = 1; i < encompass_.size () -1; i++)
243 Real y= encompass_[i][Y_AXIS];
246 Real x = encompass_[i][X_AXIS];
253 Real tan = y / ((1-k)* b - d * x);
258 disturb[d] = Offset (x,y);
261 while (flip (&d)!=LEFT);
265 for (int i = 0; i < encompass_.size (); i++ )
266 h = h >? encompass_[i][Y_AXIS];
269 The curve will always be under line between curve_.control_0 -> curve_.control_1, so
270 make it extra steep by slur_rc_factor
272 Real rc_correct = paper_l_->get_var ("slur_rc_factor");
274 Drul_array<Real> angles;
278 maxtan[d] *= rc_correct;
279 angles[d] = atan (-d * maxtan[d]);
281 while (flip(&d) != LEFT);
285 // if we have two disturbing points, have line through those...
286 if (disturb[LEFT][Y_AXIS] != disturb[RIGHT][Y_AXIS])
287 rc3 = (disturb[RIGHT][Y_AXIS] - disturb[LEFT][Y_AXIS]) / (disturb[RIGHT][X_AXIS] - disturb[LEFT][X_AXIS]);
290 rc3 = tan ((angles[RIGHT] - angles[LEFT]) / 2);
293 // ugh: be less steep
297 Real c2 = -maxtan[RIGHT] * curve_.control_[3][X_AXIS];
299 // use highest because rc3 is damped.
300 Real maxy = disturb[LEFT][Y_AXIS] >? disturb[RIGHT][Y_AXIS];
301 Real c3 = disturb[LEFT][Y_AXIS] > disturb[RIGHT][Y_AXIS] ?
302 maxy - rc3 * disturb[LEFT][X_AXIS] :
303 maxy - rc3 * disturb[RIGHT][X_AXIS];
305 curve_.control_[1][X_AXIS] = c3 / (maxtan[LEFT] - rc3);
306 curve_.control_[1][Y_AXIS] = maxtan[LEFT] * curve_.control_[1][X_AXIS];
308 curve_.control_[2][X_AXIS] = (c3 - c2) / (maxtan[RIGHT] - rc3);
309 curve_.control_[2][Y_AXIS] = maxtan[RIGHT] * curve_.control_[2][X_AXIS] + c2;
312 curve_.check_sanity();
314 calc_return (angles[LEFT], angles[RIGHT]);
318 The maximum amount that the encompass points stick out above the bezier curve.
321 Bezier_bow::check_fit_f () const
324 Real x1 = encompass_[0][X_AXIS];
325 Real x2 = encompass_.top ()[X_AXIS];
326 for (int i = 1; i < encompass_.size () - 1; i++)
328 Real x = encompass_[i][X_AXIS];
330 dy = dy >? (encompass_[i][Y_AXIS] - curve_.get_other_coordinate (X_AXIS, x));
337 Bezier_bow::set (Array<Offset> points, Direction dir)
344 Bezier_bow::transform ()
346 origin_ = encompass_[0];
347 translate (encompass_,-origin_);
349 Offset delta = encompass_.top () - encompass_[0];
350 alpha_ = delta.arg ();
352 rotate (encompass_, -alpha_);
359 Bezier_bow::transform_back ()
363 curve_.flip (Y_AXIS);
364 return_.flip (Y_AXIS);
368 curve_.rotate (alpha_);
369 curve_.translate (origin_);
371 return_.rotate (alpha_);
372 return_.translate (origin_);
374 rotate (encompass_,alpha_);
375 translate (encompass_,origin_);
379 See Documentation/fonts.tex
382 Bezier_bow::calc_default (Real h)
386 Real height_limit = paper_l_->get_var ("slur_height_limit");
387 Real ratio = paper_l_->get_var ("slur_ratio");
389 Real alpha = height_limit * 2.0 / pi;
390 Real beta = pi * ratio / (2.0 * height_limit);
392 Offset delta (encompass_.top ()[X_AXIS]
393 - encompass_[0][X_AXIS], 0);
395 Real b = delta.length ();
396 Real indent = alpha * atan (beta * b);
397 Real height = indent + h;
399 curve_.control_ [0] = Offset (0, 0);
400 curve_.control_ [1] = Offset (indent, height);
401 curve_.control_ [2] = Offset (b - indent, height);
402 curve_.control_ [3] = Offset (b, 0);