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;
148 Real epsilon = internote_f / 2;
149 if (abs (dy2 - dy1) < epsilon)
158 Then we get for h : B (h) = 0
160 B(0) = dy1 = a * 0 + b => b = dy1
161 B(h1) = dy2 = a * h1 + b => a * f * dy1 + b = dy2
165 a * dy1 / 2 + dy1 = dy2 => a = (dy2 - dy1) / (f * dy1)
168 Real a = (dy2 - dy1) / (f * dy1);
172 control_[1].y () += -h1 +h;
173 control_[2].y () += -h1 +h;
174 return_[1].y () += -h1 +h;
175 return_[2].y () += -h1 +h;
179 Bezier_bow::calc_bezier ()
181 Real s = sqrt (control_[3].x () * control_[3].x ()
182 + control_[1].y () * control_[2].y ());
184 Real internote = paper_l_->internote_f ();
186 Real internote = STAFFHEIGHT / 8;
188 int steps = (int)rint (s / internote);
189 Bezier::calc (steps);
193 Bezier_bow::calc_f (Real height)
196 calc_default (height);
200 Real dy = check_fit_f ();
203 transform_controls_back ();
214 transform_controls_back ();
220 * document in Documentation/fonts.tex
226 This function tries to address two issues:
227 * the tangents of the slur should always point inwards
228 in the actual slur, i.e. *after rotating back*.
230 * slurs shouldn't be too high
231 let's try : h <= 1.2 b && h <= 3 staffheight?
233 We could calculate the tangent of the bezier curve from
234 both ends going inward, and clip the slur at the point
235 where the tangent (after rotation) points up (or inward
236 with a certain maximum angle).
238 However, we assume that real clipping is not the best
239 answer. We expect that moving the outer control point up
240 if the slur becomes too high will result in a nicer slur
243 Knowing that the tangent is the line through the first
244 two control points, we'll clip (move the outer control
245 point upwards) too if the tangent points outwards.
249 Bezier_bow::calc_clipping ()
252 Real staffsize_f = paper_l_->get_var ("barsize");
254 Real staffsize_f = STAFFHEIGHT;
257 Real b = control_[3].x () - control_[0].x ();
258 Real clip_h = 1.2 * b <? 3.0 * staffsize_f;
259 Real begin_h = control_[1].y () - control_[0].y ();
260 Real end_h = control_[2].y () - control_[3].y ();
261 Real begin_dy = 0 >? begin_h - clip_h;
262 Real end_dy = 0 >? end_h - clip_h;
265 Real begin_alpha = (control_[1] - control_[0]).arg () + alpha_;
266 Real end_alpha = pi - (control_[2] - control_[3]).arg () - alpha_;
268 Real max_alpha = (100.0 / 90) * pi/2;
269 if ((begin_dy < 0) && (end_dy < 0)
270 && (begin_alpha < max_alpha) && (end_alpha < max_alpha))
273 encompass_.rotate (alpha_);
274 origin_.y () *= dir_;
275 encompass_.translate (origin_);
279 if ((begin_dy > 0) || (end_dy > 0))
281 Real dy = (begin_dy + end_dy) / 4;
283 encompass_[0].y () += dy;
284 encompass_[encompass_.size () - 1].y () += dy;
290 if (begin_alpha >= max_alpha)
291 begin_dy = 0 >? c * begin_alpha / max_alpha * begin_h;
292 if (end_alpha >= max_alpha)
293 end_dy = 0 >? c * end_alpha / max_alpha * end_h;
295 encompass_[0].y () += begin_dy;
296 encompass_[encompass_.size () - 1].y () += end_dy;
298 Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
299 alpha_ = delta.arg ();
302 origin_ = encompass_[0];
303 encompass_.translate (-origin_);
304 origin_.y () *= dir_;
305 encompass_.rotate (-alpha_);
311 Bezier_bow::calc_controls ()
313 for (int i = 0; i < 3; i++)
315 if (i && !calc_clipping ())
326 calc_tangent_controls ();
335 Bezier_bow::calc_return (Real begin_alpha, Real end_alpha)
338 Real thick = 1.8 * paper_l_->rule_thickness ();
340 Real thick = 1.8 * 0.4 PT;
343 return_[0] = control_[3];
344 return_[3] = control_[0];
346 return_[1] = control_[2] - thick * complex_exp (Offset (0, 90 + end_alpha));
347 return_[2] = control_[1]
348 - thick * complex_exp (Offset (0, 90 - begin_alpha));
352 See Documentation/fonts.tex
355 Bezier_bow::calc_tangent_controls ()
357 Offset ijk_p (control_[3].x () / 2, control_[1].y ());
358 BEZIER_BOW_DOUT << "ijk: " << ijk_p.x () << ", " << ijk_p.y () << endl;
360 Real default_rc = ijk_p.y () / ijk_p.x ();
362 int begin_disturb = encompass_.largest_disturbing ();
363 Offset begin_p = begin_disturb ? Offset (encompass_[begin_disturb].x (),
364 encompass_[begin_disturb].y ()) : ijk_p;
365 Real begin_rc = begin_p.y () / begin_p.x ();
366 if (default_rc > begin_rc)
369 begin_rc = default_rc;
373 reversed.set_size (encompass_.size ());
374 Real b = control_[3].x ();
375 for (int i = 0; i < encompass_.size (); i++ )
380 reversed[i].x () = b - encompass_[encompass_.size () - i - 1].x ();
381 reversed[i].y () = encompass_[encompass_.size () - i - 1].y ();
384 int end_disturb = reversed.largest_disturbing ();
385 end_disturb = end_disturb ? encompass_.size () - end_disturb - 1 : 0;
386 Offset end_p = end_disturb ? Offset (encompass_[end_disturb].x (),
387 encompass_[end_disturb].y ()) : ijk_p;
388 Real end_rc = end_p.y () / (control_[3].x () - end_p.x ());
389 if (default_rc > end_rc)
394 BEZIER_BOW_DOUT << "begin " << begin_p.x () << ", " << begin_p.y () << endl;
395 BEZIER_BOW_DOUT << "end " << end_p.x () << ", " << end_p.y () << endl;
397 Real height =control_[1].y ();
398 for (int i = 0; i < encompass_.size (); i++ )
399 height = height >? encompass_[i].y ();
401 // emperic computer science:
402 // * tangents somewhat steeper than minimal line
403 Real rc_correct = 2.4;
405 begin_rc *= rc_correct;
406 end_rc *= rc_correct;
410 Real begin_alpha = atan (begin_rc);
411 Real end_alpha = atan (-end_rc);
412 Real theta = (begin_alpha - end_alpha) / 2;
415 Real internote = paper_l_->internote_f ();
417 Real internote = STAFFHEIGHT / 8;
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_controls_back ()
496 // silly name; let's transform encompass back too
497 // to allow recalculation without re-set()ting encompass array
506 control_.rotate (alpha_);
507 control_.translate (origin_);
509 return_.rotate (alpha_);
510 return_.translate (origin_);
512 encompass_.rotate (alpha_);
513 encompass_.translate (origin_);
518 See Documentation/fonts.tex
521 Bezier_bow::calc_default (Real h)
525 Real staffsize_f = paper_l_->get_var ("barsize");
527 Real staffsize_f = STAFFHEIGHT;
530 Real height_limit = staffsize_f;
531 Real ratio = 1.0/3.0;
533 Real alpha = height_limit * 2.0 / pi;
534 Real beta = pi * ratio / (2.0 * height_limit);
536 Offset delta (encompass_[encompass_.size () - 1].x ()
537 - encompass_[0].x (), 0);
538 Real b = delta.length ();
539 Real indent = alpha * atan (beta * b);
540 Real height = indent + h;
544 Array<Offset> control;
545 control.push (Offset (0, 0));
546 control.push (Offset (indent, height));
547 control.push (Offset (b - indent, height));
548 control.push (Offset (b, 0));
550 Array<Offset> control (4);
551 control[0] = Offset (0, 0);
552 control[1] = Offset (indent, height);
553 control[2] = Offset (b - indent, height);
554 control[3] = Offset (b, 0);
556 Bezier::set (control);