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>
13 #include "direction.hh"
15 #include "paper-def.hh"
18 #define BEZIER_BOW_DOUT if (check_debug && !monitor->silent_b ("Bezier_bow")) cout
20 #define BEZIER_BOW_DOUT cerr
26 for (int i = 0; i < size (); i++)
27 (*this)[i].mirror (Y_AXIS);
31 Curve::largest_disturbing ()
35 for (int i = 1; i < size (); i++)
37 if ((*this)[i].y () > 0)
39 Real phi = (*this)[i].y () / (*this)[i].x ();
51 Curve::rotate (Real phi)
53 Offset rot (complex_exp (Offset (0, phi)));
54 for (int i = 0; i < size (); i++)
55 (*this)[i] = complex_multiply (rot, (*this)[i]);
59 Curve::translate (Offset o)
61 for (int i = 0; i < size (); i++)
67 control_.set_size (4);
71 Bezier::calc (int steps)
74 curve_.set_size (steps);
75 Real dt = 1.0 / curve_.size ();
76 Offset c = 3.0 * (control_[1] - control_[0]);
77 Offset b = 3.0 * (control_[2] - control_[1]) - c;
78 Offset a = control_[3] - (control_[0] + c + b);
80 for (int i = 0; i < curve_.size (); i++ )
82 curve_[i] = ((a * t + b) * t + c) * t + control_[0];
88 Bezier::set (Array<Offset> points)
90 assert (points.size () == 4);
97 for (int i = 1; i < curve_.size (); i++ )
99 if (x < curve_[i].x () || (i == curve_.size () - 1))
101 Offset z1 = curve_[i-1];
102 Offset z2 = curve_[i];
103 Real multiplier = (x - z2.x ()) / (z1.x () - z2.x ());
104 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 epsilon = paper_l_->rule_thickness ();
144 Real epsilon = 1.5 * 0.4 PT;
146 if (abs (dy2 - dy1) < epsilon)
155 Then we get for h : B (h) = 0
157 B(0) = dy1 = a * 0 + b => b = dy1
158 B(h1) = dy2 = a * h1 + b => a * f * dy1 + b = dy2
162 a * dy1 / 2 + dy1 = dy2 => a = (dy2 - dy1) / (f * dy1)
165 Real a = (dy2 - dy1) / (f * dy1);
169 control_[1].y () += -h1 +h;
170 control_[2].y () += -h1 +h;
171 return_[1].y () += -h1 +h;
172 return_[2].y () += -h1 +h;
176 Bezier_bow::calc_bezier ()
178 Real s = sqrt (control_[3].x () * control_[3].x ()
179 + control_[1].y () * control_[2].y ());
181 Real internote = paper_l_->internote_f ();
183 Real internote = STAFFHEIGHT / 8;
185 int steps = (int)rint (s / internote);
186 Bezier::calc (steps);
190 Bezier_bow::calc_f (Real height)
193 calc_default (height);
197 Real dy = check_fit_f ();
200 transform_controls_back ();
211 transform_controls_back ();
217 * document in Documentation/fonts.tex
223 This function tries to address two issues:
224 * the tangents of the slur should always point inwards
225 in the actual slur, i.e. *after rotating back*.
226 * slurs shouldn't be too high ( <= 1.5 staffheight?)
228 We could calculate the tangent of the bezier curve from
229 both ends going inward, and clip the slur at the point
230 where the tangent (after rotation) points up (or inward
231 with a certain maximum angle).
233 However, we assume that real clipping is not the best
234 answer. We expect that moving the outer control point up
235 if the slur becomes too high will result in a nicer slur
238 Knowing that the tangent is the line through the first
239 two control points, we'll clip (move the outer control
240 point upwards) too if the tangent points outwards.
244 Bezier_bow::calc_clipping ()
247 Real staffsize_f = paper_l_->get_var ("barsize");
249 Real staffsize_f = STAFFHEIGHT;
252 Real clip_h = staffsize_f;
253 Real begin_h = control_[1].y () - control_[0].y ();
254 Real end_h = control_[2].y () - control_[3].y ();
255 Real begin_dy = begin_h - clip_h;
256 Real end_dy = end_h - clip_h;
259 Real begin_alpha = (control_[1] - control_[0]).arg () + alpha_;
260 Real end_alpha = pi - (control_[2] - control_[3]).arg () - alpha_;
262 Real max_alpha = 1.1 * pi/2;
263 if ((begin_dy < 0) && (end_dy < 0)
264 && (begin_alpha < max_alpha) && (end_alpha < max_alpha))
267 encompass_.rotate (alpha_);
269 origin_.y () *= dir_;
270 encompass_.translate (origin_);
274 if ((begin_dy > 0) || (end_dy > 0))
276 Real dy = (begin_dy + end_dy) / 4;
278 encompass_[0].y () += dy;
279 encompass_[encompass_.size () - 1].y () += dy;
285 if (begin_alpha >= max_alpha)
286 begin_dy = c * begin_alpha / max_alpha * begin_h;
287 if (end_alpha >= max_alpha)
288 end_dy = c * end_alpha / max_alpha * end_h;
290 Real dy = end_dy >? begin_dy;
292 if (!experimental_features_global_b)
294 encompass_[0].y () += dy;
295 encompass_[encompass_.size () - 1].y () += dy;
299 encompass_[0].y () += begin_dy;
300 encompass_[encompass_.size () - 1].y () += end_dy;
302 Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
303 alpha_ = delta.arg ();
308 origin_ = encompass_[0];
309 encompass_.translate (-origin_);
311 origin_.y () *= dir_;
312 encompass_.rotate (-alpha_);
318 Bezier_bow::calc_controls ()
320 // try clipping twice
321 for (int i = 0; i < 3; i++)
323 if (i && !calc_clipping ())
334 calc_tangent_controls ();
343 Bezier_bow::calc_return (Real begin_alpha, Real end_alpha)
346 Real thick = 1.8 * paper_l_->rule_thickness ();
348 Real thick = 1.8 * 0.4 PT;
351 return_[0] = control_[3];
352 return_[3] = control_[0];
354 return_[1] = control_[2] - thick * complex_exp (Offset (0, 90 + end_alpha));
355 return_[2] = control_[1]
356 - thick * complex_exp (Offset (0, 90 - begin_alpha));
360 See Documentation/fonts.tex
363 Bezier_bow::calc_tangent_controls ()
365 Offset ijk_p (control_[3].x () / 2, control_[1].y ());
366 BEZIER_BOW_DOUT << "ijk: " << ijk_p.x () << ", " << ijk_p.y () << endl;
368 Real default_rc = ijk_p.y () / ijk_p.x ();
370 int begin_disturb = encompass_.largest_disturbing ();
371 Offset begin_p = begin_disturb ? Offset (encompass_[begin_disturb].x (),
372 encompass_[begin_disturb].y ()) : ijk_p;
373 Real begin_rc = begin_p.y () / begin_p.x ();
374 if (default_rc > begin_rc)
377 begin_rc = default_rc;
381 reversed.set_size (encompass_.size ());
382 Real b = control_[3].x ();
383 for (int i = 0; i < encompass_.size (); i++ )
388 reversed[i].x () = b - encompass_[encompass_.size () - i - 1].x ();
389 reversed[i].y () = encompass_[encompass_.size () - i - 1].y ();
392 int end_disturb = reversed.largest_disturbing ();
393 end_disturb = end_disturb ? encompass_.size () - end_disturb - 1 : 0;
394 Offset end_p = end_disturb ? Offset (encompass_[end_disturb].x (),
395 encompass_[end_disturb].y ()) : ijk_p;
396 Real end_rc = end_p.y () / (control_[3].x () - end_p.x ());
397 if (default_rc > end_rc)
402 BEZIER_BOW_DOUT << "begin " << begin_p.x () << ", " << begin_p.y () << endl;
403 BEZIER_BOW_DOUT << "end " << end_p.x () << ", " << end_p.y () << endl;
405 Real height =control_[1].y ();
406 for (int i = 0; i < encompass_.size (); i++ )
407 height = height >? encompass_[i].y ();
409 // emperic computer science:
410 // * tangents somewhat steeper than minimal line
411 Real rc_correct = 2.4;
413 begin_rc *= rc_correct;
414 end_rc *= rc_correct;
418 Real begin_alpha = atan (begin_rc);
419 Real end_alpha = atan (-end_rc);
420 Real theta = (begin_alpha - end_alpha) / 2;
423 Real internote = paper_l_->internote_f ();
425 Real internote = STAFFHEIGHT / 8;
427 Real epsilon = internote / 5;
429 // if we have two disturbing points, have height line through those...
430 if (!((abs (begin_p.x () - end_p.x ()) < epsilon)
431 && (abs (begin_p.y () - end_p.y ()) < epsilon)))
432 theta = atan (end_p.y () - begin_p.y ()) / (end_p.x () - begin_p.x ());
434 Real rc3 = tan (theta);
435 // ugh: be less steep
438 Real c2 = -rc2 * control_[3].x ();
439 Real c3 = begin_p.y () > end_p.y () ? begin_p.y ()
440 - rc3 * begin_p.x () : end_p.y () - rc3 * end_p.x ();
442 BEZIER_BOW_DOUT << "y1 = " << rc1 << " x + 0" << endl;
443 BEZIER_BOW_DOUT << "y2 = " << rc2 << " x + " << c2 << endl;
444 BEZIER_BOW_DOUT << "y3 = " << rc3 << " x + " << c3 << endl;
445 control_[1].x () = c3 / (rc1 - rc3);
446 control_[1].y () = rc1 * control_[1].x ();
447 control_[2].x () = (c3 - c2) / (rc2 - rc3);
448 BEZIER_BOW_DOUT << "c2.x () = " << control_[2].x () << endl;
449 BEZIER_BOW_DOUT << "(c3 - c2) = " << (c3 - c2) << endl;
450 BEZIER_BOW_DOUT << "(rc2 - rc3) = " << (rc2 - rc3) << endl;
451 control_[2].y () = rc2 * control_[2].x () + c2;
452 BEZIER_BOW_DOUT << "c2.y ()" << control_[2].y () << endl;
454 calc_return (begin_alpha, end_alpha);
458 Bezier_bow::check_fit_bo ()
460 for (int i = 1; i < encompass_.size () - 1; i++)
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 dy = dy >? (encompass_[i].y () - y (encompass_[i].x ()));
476 Bezier_bow::set (Array<Offset> points, int dir)
483 Bezier_bow::transform ()
485 origin_ = encompass_[0];
486 encompass_.translate (-origin_);
488 Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
489 alpha_ = delta.arg ();
491 encompass_.rotate (-alpha_);
498 Bezier_bow::transform_controls_back ()
500 // silly name; let's transform encompass back too
501 // to allow recalculation without re-set()ting encompass array
510 control_.rotate (alpha_);
511 control_.translate (origin_);
513 return_.rotate (alpha_);
514 return_.translate (origin_);
516 encompass_.rotate (alpha_);
517 encompass_.translate (origin_);
522 See Documentation/fonts.tex
525 Bezier_bow::calc_default (Real h)
529 Real staffsize_f = paper_l_->get_var ("barsize");
531 Real staffsize_f = STAFFHEIGHT;
534 Real height_limit = staffsize_f;
535 Real ratio = 1.0/3.0;
537 Real alpha = height_limit * 2.0 / pi;
538 Real beta = pi * ratio / (2.0 * height_limit);
540 Offset delta (encompass_[encompass_.size () - 1].x ()
541 - encompass_[0].x (), 0);
542 Real b = delta.length ();
543 Real indent = alpha * atan (beta * b);
544 Real height = indent + h;
548 Array<Offset> control;
549 control.push (Offset (0, 0));
550 control.push (Offset (indent, height));
551 control.push (Offset (b - indent, height));
552 control.push (Offset (b, 0));
554 Array<Offset> control (4);
555 control[0] = Offset (0, 0);
556 control[1] = Offset (indent, height);
557 control[2] = Offset (b - indent, height);
558 control[3] = Offset (b, 0);
560 Bezier::set (control);