2 bezier.cc -- implement Bezier and Bezier_bow
4 source file of the GNU LilyPond music typesetter
6 (c) 1998 Jan Nieuwenhuizen <jan@digicash.com>
14 #include "direction.hh"
16 #include "paper-def.hh"
19 #define BEZIER_BOW_DOUT if (check_debug && !monitor->silent_b ("Bezier_bow")) cout
21 #define BEZIER_BOW_DOUT cerr
27 for (int i = 0; i < size (); i++)
28 (*this)[i].mirror (Y_AXIS);
32 Curve::largest_disturbing ()
36 for (int i = 1; i < size (); i++)
38 if ((*this)[i].y () > 0)
40 Real phi = (*this)[i].y () / (*this)[i].x ();
52 Curve::rotate (Real phi)
54 Offset rot (complex_exp (Offset (0, phi)));
55 for (int i = 0; i < size (); i++)
56 (*this)[i] = complex_multiply (rot, (*this)[i]);
60 Curve::translate (Offset o)
62 for (int i = 0; i < size (); i++)
68 control_.set_size (4);
72 Bezier::calc (int steps)
75 curve_.set_size (steps);
76 Real dt = 1.0 / curve_.size ();
77 Offset c = 3.0 * (control_[1] - control_[0]);
78 Offset b = 3.0 * (control_[2] - control_[1]) - c;
79 Offset a = control_[3] - (control_[0] + c + b);
81 for (int i = 0; i < curve_.size (); i++ )
83 curve_[i] = ((a * t + b) * t + c) * t + control_[0];
89 Bezier::set (Array<Offset> points)
91 assert (points.size () == 4);
99 // bounds func should be templatised to take array of offsets too?
100 Array<Real> positions;
101 for (int i = 0; i < curve_.size (); i++)
102 positions.push (curve_[i].x ());
104 Slice slice = get_bounds_slice (positions, x);
106 Offset z1 = curve_[0 >? slice.max () - 1];
107 Offset z2 = curve_[1 >? slice.max ()];
108 Real multiplier = (x - z2.x ()) / (z1.x () - z2.x ());
109 Real y = z1.y () * multiplier + (1.0 - multiplier) * z2.y();
115 Bezier_bow::Bezier_bow (Paper_def* paper_l)
118 return_.set_size (4);
122 Bezier_bow::blow_fit ()
124 Real dy1 = check_fit_f ();
128 // be careful not to take too big step
131 control_[1].y () += h1;
132 control_[2].y () += h1;
133 return_[1].y () += h1;
134 return_[2].y () += h1;
137 Real dy2 = check_fit_f ();
142 Real internote_f = paper_l_->internote_f ();
144 Real internote_f = STAFFHEIGHT / 8;
147 Real epsilon = internote_f / 4;
148 if (abs (dy2 - dy1) < epsilon)
157 Then we get for h : B (h) = 0
159 B(0) = dy1 = a * 0 + b => b = dy1
160 B(h1) = dy2 = a * h1 + b => a * f * dy1 + b = dy2
164 a * dy1 / 2 + dy1 = dy2 => a = (dy2 - dy1) / (f * dy1)
167 Real a = (dy2 - dy1) / (f * dy1);
171 control_[1].y () += -h1 +h;
172 control_[2].y () += -h1 +h;
173 return_[1].y () += -h1 +h;
174 return_[2].y () += -h1 +h;
178 Bezier_bow::calc_bezier ()
180 Real s = sqrt (control_[3].x () * control_[3].x ()
181 + control_[1].y () * control_[2].y ());
183 Real internote = paper_l_->internote_f ();
185 Real internote = STAFFHEIGHT / 8;
187 int steps = (int)rint (s / internote);
188 Bezier::calc (steps);
192 Bezier_bow::calc_f (Real height)
195 calc_default (height);
199 Real dy = check_fit_f ();
219 * document in Documentation/fonts.tex
225 This function tries to address two issues:
226 * the tangents of the slur should always point inwards
227 in the actual slur, i.e. *after rotating back*.
229 * slurs shouldn't be too high
230 let's try : h <= 1.2 b && h <= 3 staffheight?
232 We could calculate the tangent of the bezier curve from
233 both ends going inward, and clip the slur at the point
234 where the tangent (after rotation) points up (or inward
235 with a certain maximum angle).
237 However, we assume that real clipping is not the best
238 answer. We expect that moving the outer control point up
239 if the slur becomes too high will result in a nicer slur
242 Knowing that the tangent is the line through the first
243 two control points, we'll clip (move the outer control
244 point upwards) too if the tangent points outwards.
248 Bezier_bow::calc_clipping ()
251 Real clip_height = paper_l_->get_var ("slur_clip_height");
252 Real clip_ratio = paper_l_->get_var ("slur_clip_ratio");
253 Real clip_angle = paper_l_->get_var ("slur_clip_angle");
255 Real staffsize_f = STAFFHEIGHT;
256 Real clip_height = 3.0 * staffsize_f;
257 Real clip_ratio = 1.2;
258 Real clip_angle = 100;
261 Real b = control_[3].x () - control_[0].x ();
262 Real clip_h = clip_ratio * b <? clip_height;
263 Real begin_h = control_[1].y () - control_[0].y ();
264 Real end_h = control_[2].y () - control_[3].y ();
265 Real begin_dy = 0 >? begin_h - clip_h;
266 Real end_dy = 0 >? end_h - clip_h;
269 Real begin_alpha = (control_[1] - control_[0]).arg () + dir_ * alpha_;
270 Real end_alpha = pi - (control_[2] - control_[3]).arg () - dir_ * alpha_;
272 Real max_alpha = clip_angle / 90 * pi / 2;
273 if ((begin_dy < 0) && (end_dy < 0)
274 && (begin_alpha < max_alpha) && (end_alpha < max_alpha))
281 if ((begin_dy > 0) || (end_dy > 0))
283 Real dy = (begin_dy + end_dy) / 4;
285 encompass_[0].y () += dir_ * dy;
286 encompass_[encompass_.size () - 1].y () += dir_ * dy;
292 if (begin_alpha >= max_alpha)
293 begin_dy = 0 >? c * begin_alpha / max_alpha * begin_h;
294 if (end_alpha >= max_alpha)
295 end_dy = 0 >? c * end_alpha / max_alpha * end_h;
297 encompass_[0].y () += dir_ * begin_dy;
298 encompass_[encompass_.size () - 1].y () += dir_ * end_dy;
300 Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
301 alpha_ = delta.arg ();
310 Bezier_bow::calc_controls ()
312 for (int i = 0; i < 3; i++)
314 if (i && !calc_clipping ())
325 calc_tangent_controls ();
334 Bezier_bow::calc_return (Real begin_alpha, Real end_alpha)
337 Real thick = paper_l_->get_var ("slur_thickness");
339 Real thick = 1.8 * 0.4 PT;
342 return_[0] = control_[3];
343 return_[3] = control_[0];
345 return_[1] = control_[2] - thick * complex_exp (Offset (0, 90 + end_alpha));
346 return_[2] = control_[1]
347 - thick * complex_exp (Offset (0, 90 - begin_alpha));
351 See Documentation/fonts.tex
354 Bezier_bow::calc_tangent_controls ()
356 Offset ijk_p (control_[3].x () / 2, control_[1].y ());
357 BEZIER_BOW_DOUT << "ijk: " << ijk_p.x () << ", " << ijk_p.y () << endl;
359 Real default_rc = ijk_p.y () / ijk_p.x ();
361 int begin_disturb = encompass_.largest_disturbing ();
362 Offset begin_p = begin_disturb ? Offset (encompass_[begin_disturb].x (),
363 encompass_[begin_disturb].y ()) : ijk_p;
364 Real begin_rc = begin_p.y () / begin_p.x ();
365 if (default_rc > begin_rc)
368 begin_rc = default_rc;
372 reversed.set_size (encompass_.size ());
373 Real b = control_[3].x ();
374 for (int i = 0; i < encompass_.size (); i++ )
379 reversed[i].x () = b - encompass_[encompass_.size () - i - 1].x ();
380 reversed[i].y () = encompass_[encompass_.size () - i - 1].y ();
383 int end_disturb = reversed.largest_disturbing ();
384 end_disturb = end_disturb ? encompass_.size () - end_disturb - 1 : 0;
385 Offset end_p = end_disturb ? Offset (encompass_[end_disturb].x (),
386 encompass_[end_disturb].y ()) : ijk_p;
387 Real end_rc = end_p.y () / (control_[3].x () - end_p.x ());
388 if (default_rc > end_rc)
393 BEZIER_BOW_DOUT << "begin " << begin_p.x () << ", " << begin_p.y () << endl;
394 BEZIER_BOW_DOUT << "end " << end_p.x () << ", " << end_p.y () << endl;
396 Real height =control_[1].y ();
397 for (int i = 0; i < encompass_.size (); i++ )
398 height = height >? encompass_[i].y ();
400 // emperic computer science:
401 // * tangents somewhat steeper than minimal line
403 Real internote = paper_l_->internote_f ();
404 Real rc_correct = paper_l_->get_var ("slur_rc_factor");
406 Real internote = STAFFHEIGHT / 8;
407 Real rc_correct = 2.4;
410 begin_rc *= rc_correct;
411 end_rc *= rc_correct;
415 Real begin_alpha = atan (begin_rc);
416 Real end_alpha = atan (-end_rc);
417 Real theta = (begin_alpha - end_alpha) / 2;
419 Real epsilon = internote / 5;
421 // if we have two disturbing points, have height line through those...
422 if (!((abs (begin_p.x () - end_p.x ()) < epsilon)
423 && (abs (begin_p.y () - end_p.y ()) < epsilon)))
424 theta = atan (end_p.y () - begin_p.y ()) / (end_p.x () - begin_p.x ());
426 Real rc3 = tan (theta);
427 // ugh: be less steep
430 Real c2 = -rc2 * control_[3].x ();
431 Real c3 = begin_p.y () > end_p.y () ? begin_p.y ()
432 - rc3 * begin_p.x () : end_p.y () - rc3 * end_p.x ();
434 BEZIER_BOW_DOUT << "y1 = " << rc1 << " x + 0" << endl;
435 BEZIER_BOW_DOUT << "y2 = " << rc2 << " x + " << c2 << endl;
436 BEZIER_BOW_DOUT << "y3 = " << rc3 << " x + " << c3 << endl;
437 control_[1].x () = c3 / (rc1 - rc3);
438 control_[1].y () = rc1 * control_[1].x ();
439 control_[2].x () = (c3 - c2) / (rc2 - rc3);
440 BEZIER_BOW_DOUT << "c2.x () = " << control_[2].x () << endl;
441 BEZIER_BOW_DOUT << "(c3 - c2) = " << (c3 - c2) << endl;
442 BEZIER_BOW_DOUT << "(rc2 - rc3) = " << (rc2 - rc3) << endl;
443 control_[2].y () = rc2 * control_[2].x () + c2;
444 BEZIER_BOW_DOUT << "c2.y ()" << control_[2].y () << endl;
446 calc_return (begin_alpha, end_alpha);
450 Bezier_bow::check_fit_bo ()
452 for (int i = 1; i < encompass_.size () - 1; i++)
453 if ((encompass_[i].x () > encompass_[0].x ())
454 && (encompass_[i].x () < encompass_[encompass_.size () -1].x ()))
455 if (encompass_[i].y () > y (encompass_[i].x ()))
461 Bezier_bow::check_fit_f ()
464 for (int i = 1; i < encompass_.size () - 1; i++)
465 if ((encompass_[i].x () > encompass_[0].x ())
466 && (encompass_[i].x () < encompass_[encompass_.size () -1].x ()))
467 dy = dy >? (encompass_[i].y () - y (encompass_[i].x ()));
472 Bezier_bow::set (Array<Offset> points, int dir)
479 Bezier_bow::transform ()
481 origin_ = encompass_[0];
482 encompass_.translate (-origin_);
484 Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
485 alpha_ = delta.arg ();
487 encompass_.rotate (-alpha_);
494 Bezier_bow::transform_back ()
503 control_.rotate (alpha_);
504 control_.translate (origin_);
506 return_.rotate (alpha_);
507 return_.translate (origin_);
509 encompass_.rotate (alpha_);
510 encompass_.translate (origin_);
514 See Documentation/fonts.tex
517 Bezier_bow::calc_default (Real h)
521 Real height_limit = paper_l_->get_var ("slur_height_limit");
522 Real ratio = paper_l_->get_var ("slur_ratio");
524 Real staffsize_f = STAFFHEIGHT;
525 Real height_limit = staffsize_f;
526 Real ratio = 1.0/3.0;
529 Real alpha = height_limit * 2.0 / pi;
530 Real beta = pi * ratio / (2.0 * height_limit);
532 Offset delta (encompass_[encompass_.size () - 1].x ()
533 - encompass_[0].x (), 0);
534 Real b = delta.length ();
535 Real indent = alpha * atan (beta * b);
536 Real height = indent + h;
540 Array<Offset> control;
541 control.push (Offset (0, 0));
542 control.push (Offset (indent, height));
543 control.push (Offset (b - indent, height));
544 control.push (Offset (b, 0));
546 Array<Offset> control (4);
547 control[0] = Offset (0, 0);
548 control[1] = Offset (indent, height);
549 control[2] = Offset (b - indent, height);
550 control[3] = Offset (b, 0);
552 Bezier::set (control);