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>
14 #include "dimensions.hh"
15 #include "direction.hh"
16 #include "paper-def.hh"
19 #define BEZIER_BOW_DOUT if (check_debug && !lily_monitor->silent_b ("Bezier_bow")) cout
21 #define BEZIER_BOW_DOUT cerr
28 * better names, esp. for all calc_foo functions
29 * blow_fit vs calc_default (Real) and calc_f (Real)
30 * exact height / tangent calculation
36 UGH. Clean up debugging junk.
43 for (int i = 0; i < size (); i++)
44 (*this)[i].mirror (Y_AXIS);
48 Curve::largest_disturbing ()
52 for (int i = 1; i < size (); i++)
54 if ((*this)[i][Y_AXIS] > 0)
56 Real phi = (*this)[i][Y_AXIS] / (*this)[i][X_AXIS];
68 Curve::rotate (Real phi)
70 Offset rot (complex_exp (Offset (0, phi)));
71 for (int i = 0; i < size (); i++)
72 (*this)[i] = complex_multiply (rot, (*this)[i]);
76 Curve::translate (Offset o)
78 for (int i = 0; i < size (); i++)
84 control_.set_size (4);
88 Bezier::calc (int steps)
91 curve_.set_size (steps);
92 Real dt = 1.0 / curve_.size ();
93 Offset c = 3.0 * (control_[1] - control_[0]);
94 Offset b = 3.0 * (control_[2] - control_[1]) - c;
95 Offset a = control_[3] - (control_[0] + c + b);
97 for (int i = 0; i < curve_.size (); i++ )
99 curve_[i] = ((a * t + b) * t + c) * t + control_[0];
105 Bezier::print () const
108 if (check_debug && !lily_monitor->silent_b ("Bezier_controls"))
110 if (control_[1].length ())
113 cout << "Controls: ";
114 for (int i=0; i < control_.size (); i++)
115 cout << control_[i].str () << ", ";
122 Bezier::set (Array<Offset> points)
124 assert (points.size () == 4);
132 // bounds func should be templatised to take array of offsets too?
133 Array<Real> positions;
134 for (int i = 0; i < curve_.size (); i++)
135 positions.push (curve_[i][X_AXIS]);
137 Slice slice = get_bounds_slice (positions, x);
139 Offset z1 = curve_[0 >? slice[BIGGER] - 1];
140 Offset z2 = curve_[1 >? slice[BIGGER]];
141 Real multiplier = (x - z2[X_AXIS]) / (z1[X_AXIS] - z2[X_AXIS]);
142 Real y = z1[Y_AXIS] * multiplier + (1.0 - multiplier) * z2[Y_AXIS];
148 Bezier_bow::Bezier_bow (Paper_def* paper_l)
151 return_.set_size (4);
155 Bezier_bow::blow_fit ()
157 Real dy1 = check_fit_f ();
161 // be careful not to take too big step
164 control_[1][Y_AXIS] += h1;
165 control_[2][Y_AXIS] += h1;
166 return_[1][Y_AXIS] += h1;
167 return_[2][Y_AXIS] += h1;
170 Real dy2 = check_fit_f ();
175 Real internote_f = paper_l_->get_realvar (interline_scm_sym)/2.0;
177 Real internote_f = STAFFHEIGHT / 8;
180 Real epsilon = internote_f / 4;
181 if (abs (dy2 - dy1) < epsilon)
190 Then we get for h : B (h) = 0
192 B(0) = dy1 = a * 0 + b => b = dy1
193 B(h1) = dy2 = a * h1 + b => a * f * dy1 + b = dy2
197 a * dy1 / 2 + dy1 = dy2 => a = (dy2 - dy1) / (f * dy1)
200 Real a = (dy2 - dy1) / (f * dy1);
204 if (sign (h) != sign (h1))
207 if (sign (h) != sign (h1))
210 control_[1][Y_AXIS] += -h1 +h;
211 control_[2][Y_AXIS] += -h1 +h;
212 return_[1][Y_AXIS] += -h1 +h;
213 return_[2][Y_AXIS] += -h1 +h;
217 Bezier_bow::calc_bezier ()
219 Real s = sqrt (control_[3][X_AXIS] * control_[3][X_AXIS]
220 + control_[1][Y_AXIS] * control_[2][Y_AXIS]);
222 Real internote = paper_l_->get_realvar (interline_scm_sym)/2.0;
224 Real internote = STAFFHEIGHT / 8;
226 int steps = (int)rint (s / internote);
227 Bezier::calc (steps);
231 Bezier_bow::calc_f (Real height)
234 calc_default (height);
238 Real dy = check_fit_f ();
249 // if (check_debug && !lily_monitor->silent_b ("Bezier_bow_controls"))
250 if (check_debug && !(lily_monitor->silent_b ("Bezier_controls")
251 && lily_monitor->silent_b ("Bezier_bow_controls")))
253 cout << "Before transform*********\n";
255 cout << "************************\n";
264 duh, this is crude (control-points)
265 perhaps it's even better to check the amount of blow_fit ()
267 for (int i=0; i < control_.size (); i++)
269 Real y = control_[i][Y_AXIS];
270 curve_extent_drul_[Y].unite (Interval (y, y));
271 Real x = control_[i][X_AXIS];
272 curve_extent_drul_[X].unite (Interval (x, x));
278 // if (check_debug && !lily_monitor->silent_b ("Bezier_bow_controls"))
279 if (check_debug && !(lily_monitor->silent_b ("Bezier_controls")
280 && lily_monitor->silent_b ("Bezier_bow_controls")))
282 cout << "After transform*********\n";
284 cout << "************************\n";
292 * document in Documentation/fonts.tex
298 This function tries to address two issues:
299 * the tangents of the slur should always point inwards
300 in the actual slur, i.e. *after rotating back*.
302 * slurs shouldn't be too high
303 let's try : h <= 1.2 b && h <= 3 staffheight?
305 We could calculate the tangent of the bezier curve from
306 both ends going inward, and clip the slur at the point
307 where the tangent (after rotation) points up (or inward
308 with a certain maximum angle).
310 However, we assume that real clipping is not the best
311 answer. We expect that moving the outer control point up
312 if the slur becomes too high will result in a nicer slur
315 Knowing that the tangent is the line through the first
316 two control points, we'll clip (move the outer control
317 point upwards) too if the tangent points outwards.
321 Bezier_bow::calc_clipping ()
324 Real clip_height = paper_l_->get_var ("slur_clip_height");
325 Real clip_ratio = paper_l_->get_var ("slur_clip_ratio");
326 Real clip_angle = paper_l_->get_var ("slur_clip_angle");
328 Real staffsize_f = STAFFHEIGHT;
329 Real clip_height = 3.0 * staffsize_f;
330 Real clip_ratio = 1.2;
331 Real clip_angle = 100;
334 Real b = control_[3][X_AXIS] - control_[0][X_AXIS];
335 Real clip_h = clip_ratio * b <? clip_height;
336 Real begin_h = control_[1][Y_AXIS] - control_[0][Y_AXIS];
337 Real end_h = control_[2][Y_AXIS] - control_[3][Y_AXIS];
338 Real begin_dy = 0 >? begin_h - clip_h;
339 Real end_dy = 0 >? end_h - clip_h;
342 Real begin_alpha = (control_[1] - control_[0]).arg () + dir_ * alpha_;
343 Real end_alpha = pi - (control_[2] - control_[3]).arg () - dir_ * alpha_;
345 Real max_alpha = clip_angle / 90 * pi / 2;
346 if ((begin_dy < 0) && (end_dy < 0)
347 && (begin_alpha < max_alpha) && (end_alpha < max_alpha))
354 if ((begin_dy > 0) || (end_dy > 0))
356 Real dy = (begin_dy + end_dy) / 4;
358 encompass_[0][Y_AXIS] += dir_ * dy;
359 encompass_[encompass_.size () - 1][Y_AXIS] += dir_ * dy;
365 if (begin_alpha >= max_alpha)
366 begin_dy = 0 >? c * begin_alpha / max_alpha * begin_h;
367 if (end_alpha >= max_alpha)
368 end_dy = 0 >? c * end_alpha / max_alpha * end_h;
370 encompass_[0][Y_AXIS] += dir_ * begin_dy;
371 encompass_[encompass_.size () - 1][Y_AXIS] += dir_ * end_dy;
373 Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
374 alpha_ = delta.arg ();
383 Bezier_bow::calc_controls ()
385 for (int i = 0; i < 3; i++)
387 if (i && !calc_clipping ())
391 why do we always recalc from 0?
392 shouldn't calc_f () be used (too), rather than blow_fit () (only)?
402 calc_tangent_controls ();
411 Bezier_bow::calc_return (Real begin_alpha, Real end_alpha)
414 Real thick = paper_l_->get_var ("slur_thickness");
416 Real thick = 1.8 * 0.4 PT;
419 return_[0] = control_[3];
420 return_[3] = control_[0];
422 return_[1] = control_[2] - thick * complex_exp (Offset (0, 90 + end_alpha));
423 return_[2] = control_[1]
424 - thick * complex_exp (Offset (0, 90 - begin_alpha));
428 See Documentation/fonts.tex
431 Bezier_bow::calc_tangent_controls ()
433 Offset ijk_p (control_[3][X_AXIS] / 2, control_[1][Y_AXIS]);
434 BEZIER_BOW_DOUT << "ijk: " << ijk_p[X_AXIS] << ", " << ijk_p[Y_AXIS] << endl;
436 Real default_rc = ijk_p[Y_AXIS] / ijk_p[X_AXIS];
438 int begin_disturb = encompass_.largest_disturbing ();
439 Offset begin_p = begin_disturb ? Offset (encompass_[begin_disturb][X_AXIS],
440 encompass_[begin_disturb][Y_AXIS]) : ijk_p;
441 Real begin_rc = begin_p[Y_AXIS] / begin_p[X_AXIS];
442 if (default_rc > begin_rc)
445 begin_rc = default_rc;
449 reversed.set_size (encompass_.size ());
450 Real b = control_[3][X_AXIS];
451 for (int i = 0; i < encompass_.size (); i++ )
456 reversed[i][X_AXIS] = b - encompass_[encompass_.size () - i - 1][X_AXIS];
457 reversed[i][Y_AXIS] = encompass_[encompass_.size () - i - 1][Y_AXIS];
460 int end_disturb = reversed.largest_disturbing ();
461 end_disturb = end_disturb ? encompass_.size () - end_disturb - 1 : 0;
462 Offset end_p = end_disturb ? Offset (encompass_[end_disturb][X_AXIS],
463 encompass_[end_disturb][Y_AXIS]) : ijk_p;
464 Real end_rc = end_p[Y_AXIS] / (control_[3][X_AXIS] - end_p[X_AXIS]);
465 if (default_rc > end_rc)
470 BEZIER_BOW_DOUT << "begin " << begin_p[X_AXIS] << ", " << begin_p[Y_AXIS] << endl;
471 BEZIER_BOW_DOUT << "end " << end_p[X_AXIS] << ", " << end_p[Y_AXIS] << endl;
473 Real height =control_[1][Y_AXIS];
474 for (int i = 0; i < encompass_.size (); i++ )
475 height = height >? encompass_[i][Y_AXIS];
477 // emperic computer science:
478 // * tangents somewhat steeper than minimal line
480 Real internote = paper_l_->get_realvar (interline_scm_sym)/2.0;
481 Real rc_correct = paper_l_->get_var ("slur_rc_factor");
483 Real internote = STAFFHEIGHT / 8;
484 Real rc_correct = 2.4;
487 begin_rc *= rc_correct;
488 end_rc *= rc_correct;
492 Real begin_alpha = atan (begin_rc);
493 Real end_alpha = atan (-end_rc);
494 Real theta = (begin_alpha - end_alpha) / 2;
496 Real epsilon = internote / 5;
498 // if we have two disturbing points, have height line through those...
499 if (!((abs (begin_p[X_AXIS] - end_p[X_AXIS]) < epsilon)
500 && (abs (begin_p[Y_AXIS] - end_p[Y_AXIS]) < epsilon)))
501 theta = atan (end_p[Y_AXIS] - begin_p[Y_AXIS]) / (end_p[X_AXIS] - begin_p[X_AXIS]);
503 Real rc3 = tan (theta);
504 // ugh: be less steep
507 Real c2 = -rc2 * control_[3][X_AXIS];
508 Real c3 = begin_p[Y_AXIS] > end_p[Y_AXIS] ? begin_p[Y_AXIS]
509 - rc3 * begin_p[X_AXIS] : end_p[Y_AXIS] - rc3 * end_p[X_AXIS];
511 BEZIER_BOW_DOUT << "y1 = " << rc1 << " x + 0" << endl;
512 BEZIER_BOW_DOUT << "y2 = " << rc2 << " x + " << c2 << endl;
513 BEZIER_BOW_DOUT << "y3 = " << rc3 << " x + " << c3 << endl;
514 control_[1][X_AXIS] = c3 / (rc1 - rc3);
515 control_[1][Y_AXIS] = rc1 * control_[1][X_AXIS];
516 control_[2][X_AXIS] = (c3 - c2) / (rc2 - rc3);
517 BEZIER_BOW_DOUT << "c2[X_AXIS] = " << control_[2][X_AXIS] << endl;
518 BEZIER_BOW_DOUT << "(c3 - c2) = " << (c3 - c2) << endl;
519 BEZIER_BOW_DOUT << "(rc2 - rc3) = " << (rc2 - rc3) << endl;
520 control_[2][Y_AXIS] = rc2 * control_[2][X_AXIS] + c2;
521 BEZIER_BOW_DOUT << "c2[Y_AXIS]" << control_[2][Y_AXIS] << endl;
523 calc_return (begin_alpha, end_alpha);
527 Bezier_bow::check_fit_bo ()
529 for (int i = 1; i < encompass_.size () - 1; i++)
530 if ((encompass_[i][X_AXIS] > encompass_[0][X_AXIS])
531 && (encompass_[i][X_AXIS] < encompass_[encompass_.size () -1][X_AXIS]))
532 if (encompass_[i][Y_AXIS] > y (encompass_[i][X_AXIS]))
538 Bezier_bow::check_fit_f ()
541 for (int i = 1; i < encompass_.size () - 1; i++)
542 if ((encompass_[i][X_AXIS] > encompass_[0][X_AXIS])
543 && (encompass_[i][X_AXIS] < encompass_[encompass_.size () -1][X_AXIS]))
544 dy = dy >? (encompass_[i][Y_AXIS] - y (encompass_[i][X_AXIS]));
549 Bezier_bow::print () const
553 if (check_debug && !lily_monitor->silent_b ("Bezier_bow_controls"))
555 cout << "Bezier_bow\n";
556 cout << "Encompass: ";
557 for (int i=0; i < encompass_.size (); i++)
558 cout << encompass_[i].str () << ", ";
565 Bezier_bow::set (Array<Offset> points, int dir)
572 Bezier_bow::transform ()
574 origin_ = encompass_[0];
575 encompass_.translate (-origin_);
577 Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
578 alpha_ = delta.arg ();
580 encompass_.rotate (-alpha_);
587 Bezier_bow::transform_back ()
596 control_.rotate (alpha_);
597 control_.translate (origin_);
599 return_.rotate (alpha_);
600 return_.translate (origin_);
602 encompass_.rotate (alpha_);
603 encompass_.translate (origin_);
607 See Documentation/fonts.tex
610 Bezier_bow::calc_default (Real h)
614 Real height_limit = paper_l_->get_var ("slur_height_limit");
615 Real ratio = paper_l_->get_var ("slur_ratio");
617 Real staffsize_f = STAFFHEIGHT;
618 Real height_limit = staffsize_f;
619 Real ratio = 1.0/3.0;
622 Real alpha = height_limit * 2.0 / pi;
623 Real beta = pi * ratio / (2.0 * height_limit);
625 Offset delta (encompass_[encompass_.size () - 1][X_AXIS]
626 - encompass_[0][X_AXIS], 0);
627 Real b = delta.length ();
628 Real indent = alpha * atan (beta * b);
629 Real height = indent + h;
631 Array<Offset> control;
632 control.push (Offset (0, 0));
633 control.push (Offset (indent, height));
634 control.push (Offset (b - indent, height));
635 control.push (Offset (b, 0));
636 Bezier::set (control);