2 bezier.cc -- implement Bezier and Bezier_bow
4 source file of the GNU LilyPond music typesetter
6 (c) 1998 Jan Nieuwenhuizen <janneke@gnu.org>
14 #include "direction.hh"
15 #include "dimension.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 if (sign (h) != sign (h1))
174 if (sign (h) != sign (h1))
177 control_[1].y () += -h1 +h;
178 control_[2].y () += -h1 +h;
179 return_[1].y () += -h1 +h;
180 return_[2].y () += -h1 +h;
184 Bezier_bow::calc_bezier ()
186 Real s = sqrt (control_[3].x () * control_[3].x ()
187 + control_[1].y () * control_[2].y ());
189 Real internote = paper_l_->internote_f ();
191 Real internote = STAFFHEIGHT / 8;
193 int steps = (int)rint (s / internote);
194 Bezier::calc (steps);
198 Bezier_bow::calc_f (Real height)
201 calc_default (height);
205 Real dy = check_fit_f ();
225 * document in Documentation/fonts.tex
231 This function tries to address two issues:
232 * the tangents of the slur should always point inwards
233 in the actual slur, i.e. *after rotating back*.
235 * slurs shouldn't be too high
236 let's try : h <= 1.2 b && h <= 3 staffheight?
238 We could calculate the tangent of the bezier curve from
239 both ends going inward, and clip the slur at the point
240 where the tangent (after rotation) points up (or inward
241 with a certain maximum angle).
243 However, we assume that real clipping is not the best
244 answer. We expect that moving the outer control point up
245 if the slur becomes too high will result in a nicer slur
248 Knowing that the tangent is the line through the first
249 two control points, we'll clip (move the outer control
250 point upwards) too if the tangent points outwards.
254 Bezier_bow::calc_clipping ()
257 Real clip_height = paper_l_->get_var ("slur_clip_height");
258 Real clip_ratio = paper_l_->get_var ("slur_clip_ratio");
259 Real clip_angle = paper_l_->get_var ("slur_clip_angle");
261 Real staffsize_f = STAFFHEIGHT;
262 Real clip_height = 3.0 * staffsize_f;
263 Real clip_ratio = 1.2;
264 Real clip_angle = 100;
267 Real b = control_[3].x () - control_[0].x ();
268 Real clip_h = clip_ratio * b <? clip_height;
269 Real begin_h = control_[1].y () - control_[0].y ();
270 Real end_h = control_[2].y () - control_[3].y ();
271 Real begin_dy = 0 >? begin_h - clip_h;
272 Real end_dy = 0 >? end_h - clip_h;
275 Real begin_alpha = (control_[1] - control_[0]).arg () + dir_ * alpha_;
276 Real end_alpha = pi - (control_[2] - control_[3]).arg () - dir_ * alpha_;
278 Real max_alpha = clip_angle / 90 * pi / 2;
279 if ((begin_dy < 0) && (end_dy < 0)
280 && (begin_alpha < max_alpha) && (end_alpha < max_alpha))
287 if ((begin_dy > 0) || (end_dy > 0))
289 Real dy = (begin_dy + end_dy) / 4;
291 encompass_[0].y () += dir_ * dy;
292 encompass_[encompass_.size () - 1].y () += dir_ * dy;
298 if (begin_alpha >= max_alpha)
299 begin_dy = 0 >? c * begin_alpha / max_alpha * begin_h;
300 if (end_alpha >= max_alpha)
301 end_dy = 0 >? c * end_alpha / max_alpha * end_h;
303 encompass_[0].y () += dir_ * begin_dy;
304 encompass_[encompass_.size () - 1].y () += dir_ * end_dy;
306 Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
307 alpha_ = delta.arg ();
316 Bezier_bow::calc_controls ()
318 for (int i = 0; i < 3; i++)
320 if (i && !calc_clipping ())
331 calc_tangent_controls ();
340 Bezier_bow::calc_return (Real begin_alpha, Real end_alpha)
343 Real thick = paper_l_->get_var ("slur_thickness");
345 Real thick = 1.8 * 0.4 PT;
348 return_[0] = control_[3];
349 return_[3] = control_[0];
351 return_[1] = control_[2] - thick * complex_exp (Offset (0, 90 + end_alpha));
352 return_[2] = control_[1]
353 - thick * complex_exp (Offset (0, 90 - begin_alpha));
357 See Documentation/fonts.tex
360 Bezier_bow::calc_tangent_controls ()
362 Offset ijk_p (control_[3].x () / 2, control_[1].y ());
363 BEZIER_BOW_DOUT << "ijk: " << ijk_p.x () << ", " << ijk_p.y () << endl;
365 Real default_rc = ijk_p.y () / ijk_p.x ();
367 int begin_disturb = encompass_.largest_disturbing ();
368 Offset begin_p = begin_disturb ? Offset (encompass_[begin_disturb].x (),
369 encompass_[begin_disturb].y ()) : ijk_p;
370 Real begin_rc = begin_p.y () / begin_p.x ();
371 if (default_rc > begin_rc)
374 begin_rc = default_rc;
378 reversed.set_size (encompass_.size ());
379 Real b = control_[3].x ();
380 for (int i = 0; i < encompass_.size (); i++ )
385 reversed[i].x () = b - encompass_[encompass_.size () - i - 1].x ();
386 reversed[i].y () = encompass_[encompass_.size () - i - 1].y ();
389 int end_disturb = reversed.largest_disturbing ();
390 end_disturb = end_disturb ? encompass_.size () - end_disturb - 1 : 0;
391 Offset end_p = end_disturb ? Offset (encompass_[end_disturb].x (),
392 encompass_[end_disturb].y ()) : ijk_p;
393 Real end_rc = end_p.y () / (control_[3].x () - end_p.x ());
394 if (default_rc > end_rc)
399 BEZIER_BOW_DOUT << "begin " << begin_p.x () << ", " << begin_p.y () << endl;
400 BEZIER_BOW_DOUT << "end " << end_p.x () << ", " << end_p.y () << endl;
402 Real height =control_[1].y ();
403 for (int i = 0; i < encompass_.size (); i++ )
404 height = height >? encompass_[i].y ();
406 // emperic computer science:
407 // * tangents somewhat steeper than minimal line
409 Real internote = paper_l_->internote_f ();
410 Real rc_correct = paper_l_->get_var ("slur_rc_factor");
412 Real internote = STAFFHEIGHT / 8;
413 Real rc_correct = 2.4;
416 begin_rc *= rc_correct;
417 end_rc *= rc_correct;
421 Real begin_alpha = atan (begin_rc);
422 Real end_alpha = atan (-end_rc);
423 Real theta = (begin_alpha - end_alpha) / 2;
425 Real epsilon = internote / 5;
427 // if we have two disturbing points, have height line through those...
428 if (!((abs (begin_p.x () - end_p.x ()) < epsilon)
429 && (abs (begin_p.y () - end_p.y ()) < epsilon)))
430 theta = atan (end_p.y () - begin_p.y ()) / (end_p.x () - begin_p.x ());
432 Real rc3 = tan (theta);
433 // ugh: be less steep
436 Real c2 = -rc2 * control_[3].x ();
437 Real c3 = begin_p.y () > end_p.y () ? begin_p.y ()
438 - rc3 * begin_p.x () : end_p.y () - rc3 * end_p.x ();
440 BEZIER_BOW_DOUT << "y1 = " << rc1 << " x + 0" << endl;
441 BEZIER_BOW_DOUT << "y2 = " << rc2 << " x + " << c2 << endl;
442 BEZIER_BOW_DOUT << "y3 = " << rc3 << " x + " << c3 << endl;
443 control_[1].x () = c3 / (rc1 - rc3);
444 control_[1].y () = rc1 * control_[1].x ();
445 control_[2].x () = (c3 - c2) / (rc2 - rc3);
446 BEZIER_BOW_DOUT << "c2.x () = " << control_[2].x () << endl;
447 BEZIER_BOW_DOUT << "(c3 - c2) = " << (c3 - c2) << endl;
448 BEZIER_BOW_DOUT << "(rc2 - rc3) = " << (rc2 - rc3) << endl;
449 control_[2].y () = rc2 * control_[2].x () + c2;
450 BEZIER_BOW_DOUT << "c2.y ()" << control_[2].y () << endl;
452 calc_return (begin_alpha, end_alpha);
456 Bezier_bow::check_fit_bo ()
458 for (int i = 1; i < encompass_.size () - 1; i++)
459 if ((encompass_[i].x () > encompass_[0].x ())
460 && (encompass_[i].x () < encompass_[encompass_.size () -1].x ()))
461 if (encompass_[i].y () > y (encompass_[i].x ()))
467 Bezier_bow::check_fit_f ()
470 for (int i = 1; i < encompass_.size () - 1; i++)
471 if ((encompass_[i].x () > encompass_[0].x ())
472 && (encompass_[i].x () < encompass_[encompass_.size () -1].x ()))
473 dy = dy >? (encompass_[i].y () - y (encompass_[i].x ()));
478 Bezier_bow::set (Array<Offset> points, int dir)
485 Bezier_bow::transform ()
487 origin_ = encompass_[0];
488 encompass_.translate (-origin_);
490 Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
491 alpha_ = delta.arg ();
493 encompass_.rotate (-alpha_);
500 Bezier_bow::transform_back ()
509 control_.rotate (alpha_);
510 control_.translate (origin_);
512 return_.rotate (alpha_);
513 return_.translate (origin_);
515 encompass_.rotate (alpha_);
516 encompass_.translate (origin_);
520 See Documentation/fonts.tex
523 Bezier_bow::calc_default (Real h)
527 Real height_limit = paper_l_->get_var ("slur_height_limit");
528 Real ratio = paper_l_->get_var ("slur_ratio");
530 Real staffsize_f = STAFFHEIGHT;
531 Real height_limit = staffsize_f;
532 Real ratio = 1.0/3.0;
535 Real alpha = height_limit * 2.0 / pi;
536 Real beta = pi * ratio / (2.0 * height_limit);
538 Offset delta (encompass_[encompass_.size () - 1].x ()
539 - encompass_[0].x (), 0);
540 Real b = delta.length ();
541 Real indent = alpha * atan (beta * b);
542 Real height = indent + h;
546 Array<Offset> control;
547 control.push (Offset (0, 0));
548 control.push (Offset (indent, height));
549 control.push (Offset (b - indent, height));
550 control.push (Offset (b, 0));
552 Array<Offset> control (4);
553 control[0] = Offset (0, 0);
554 control[1] = Offset (indent, height);
555 control[2] = Offset (b - indent, height);
556 control[3] = Offset (b, 0);
558 Bezier::set (control);