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));
224 static Real const FUDGE = 1e-8;
227 This function calculates 2 center control points,
228 based on lines through c_0 --> left disturbing
229 and c_3--> right disturbing encompass points.
231 See Documentation/fonts.tex
234 Bezier_bow::calc_tangent_controls ()
236 Real b = curve_.control_[3][X_AXIS];
237 Real h = curve_.control_[1][Y_AXIS];
240 Drul_array<Offset> disturb;
241 Drul_array<Real> maxtan;
242 maxtan[LEFT] = maxtan[RIGHT] = h/(b/2);
243 disturb[LEFT] = disturb[RIGHT] = Offset (b / 2, h);
245 for (int i = 1; i < encompass_.size () -1; i++)
247 Real y= encompass_[i][Y_AXIS];
250 Real x = encompass_[i][X_AXIS];
257 Real tan = y / ((1-k)* b - d * x);
262 disturb[d] = Offset (x,y);
265 while (flip (&d)!=LEFT);
269 for (int i = 0; i < encompass_.size (); i++ )
270 h = h >? encompass_[i][Y_AXIS];
273 The curve will always be under line between curve_.control_0 -> curve_.control_1, so
274 make it extra steep by slur_rc_factor
276 Real rc_correct = paper_l_->get_var ("slur_rc_factor");
278 Drul_array<Real> angles;
282 maxtan[d] *= -d * rc_correct;
283 angles[d] = atan (maxtan[d]);
285 while (flip(&d) != LEFT);
290 if we have two disturbing points, have line through those...
291 in order to get a sane line, make sure points are reasonably far apart
292 X distance must be reasonably(!) big (division)
294 if (abs (disturb[LEFT][X_AXIS] - disturb[RIGHT][X_AXIS]) > FUDGE)
295 rc3 = (disturb[RIGHT][Y_AXIS] - disturb[LEFT][Y_AXIS]) / (disturb[RIGHT][X_AXIS] - disturb[LEFT][X_AXIS]);
298 rc3 = tan ((angles[LEFT] - angles[RIGHT]) / 2);
301 // ugh: be less steep
305 Real c2 = -maxtan[RIGHT] * curve_.control_[3][X_AXIS];
307 // use highest because rc3 is damped.
308 Real maxy = disturb[LEFT][Y_AXIS] >? disturb[RIGHT][Y_AXIS];
309 Real c3 = disturb[LEFT][Y_AXIS] > disturb[RIGHT][Y_AXIS] ?
310 maxy - rc3 * disturb[LEFT][X_AXIS] :
311 maxy - rc3 * disturb[RIGHT][X_AXIS];
313 curve_.control_[1][X_AXIS] = c3 / (maxtan[LEFT] - rc3);
314 curve_.control_[1][Y_AXIS] = maxtan[LEFT] * curve_.control_[1][X_AXIS];
316 curve_.control_[2][X_AXIS] = (c3 - c2) / (maxtan[RIGHT] - rc3);
317 curve_.control_[2][Y_AXIS] = maxtan[RIGHT] * curve_.control_[2][X_AXIS] + c2;
320 curve_.check_sanity();
322 calc_return (angles[LEFT], angles[RIGHT]);
326 The maximum amount that the encompass points stick out above the bezier curve.
329 Bezier_bow::check_fit_f () const
332 Real x1 = encompass_[0][X_AXIS];
333 Real x2 = encompass_.top ()[X_AXIS];
334 for (int i = 1; i < encompass_.size () - 1; i++)
336 Real x = encompass_[i][X_AXIS];
338 dy = dy >? (encompass_[i][Y_AXIS] - curve_.get_other_coordinate (X_AXIS, x));
345 Bezier_bow::set (Array<Offset> points, Direction dir)
352 Bezier_bow::transform ()
354 origin_ = encompass_[0];
355 translate (encompass_,-origin_);
357 Offset delta = encompass_.top () - encompass_[0];
358 alpha_ = delta.arg ();
360 rotate (encompass_, -alpha_);
367 Bezier_bow::transform_back ()
371 curve_.flip (Y_AXIS);
372 return_.flip (Y_AXIS);
376 curve_.rotate (alpha_);
377 curve_.translate (origin_);
379 return_.rotate (alpha_);
380 return_.translate (origin_);
382 rotate (encompass_,alpha_);
383 translate (encompass_,origin_);
387 See Documentation/fonts.tex
390 Bezier_bow::calc_default (Real h)
394 Real height_limit = paper_l_->get_var ("slur_height_limit");
395 Real ratio = paper_l_->get_var ("slur_ratio");
397 Real alpha = height_limit * 2.0 / pi;
398 Real beta = pi * ratio / (2.0 * height_limit);
400 Offset delta (encompass_.top ()[X_AXIS]
401 - encompass_[0][X_AXIS], 0);
403 Real b = delta.length ();
404 Real indent = alpha * atan (beta * b);
405 Real height = indent + h;
407 curve_.control_ [0] = Offset (0, 0);
408 curve_.control_ [1] = Offset (indent, height);
409 curve_.control_ [2] = Offset (b - indent, height);
410 curve_.control_ [3] = Offset (b, 0);