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 && !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
37 for (int i = 0; i < size (); i++)
38 (*this)[i].mirror (Y_AXIS);
42 Curve::largest_disturbing ()
46 for (int i = 1; i < size (); i++)
48 if ((*this)[i][Y_AXIS] > 0)
50 Real phi = (*this)[i][Y_AXIS] / (*this)[i][X_AXIS];
62 Curve::rotate (Real phi)
64 Offset rot (complex_exp (Offset (0, phi)));
65 for (int i = 0; i < size (); i++)
66 (*this)[i] = complex_multiply (rot, (*this)[i]);
70 Curve::translate (Offset o)
72 for (int i = 0; i < size (); i++)
78 control_.set_size (4);
82 Bezier::calc (int steps)
85 curve_.set_size (steps);
86 Real dt = 1.0 / curve_.size ();
87 Offset c = 3.0 * (control_[1] - control_[0]);
88 Offset b = 3.0 * (control_[2] - control_[1]) - c;
89 Offset a = control_[3] - (control_[0] + c + b);
91 for (int i = 0; i < curve_.size (); i++ )
93 curve_[i] = ((a * t + b) * t + c) * t + control_[0];
99 Bezier::print () const
102 if (check_debug && !monitor->silent_b ("Bezier_controls"))
104 if (control_[1].length ())
107 cout << "Controls: ";
108 for (int i=0; i < control_.size (); i++)
109 cout << control_[i].str () << ", ";
116 Bezier::set (Array<Offset> points)
118 assert (points.size () == 4);
126 // bounds func should be templatised to take array of offsets too?
127 Array<Real> positions;
128 for (int i = 0; i < curve_.size (); i++)
129 positions.push (curve_[i][X_AXIS]);
131 Slice slice = get_bounds_slice (positions, x);
133 Offset z1 = curve_[0 >? slice[BIGGER] - 1];
134 Offset z2 = curve_[1 >? slice[BIGGER]];
135 Real multiplier = (x - z2[X_AXIS]) / (z1[X_AXIS] - z2[X_AXIS]);
136 Real y = z1[Y_AXIS] * multiplier + (1.0 - multiplier) * z2[Y_AXIS];
142 Bezier_bow::Bezier_bow (Paper_def* paper_l)
145 return_.set_size (4);
149 Bezier_bow::blow_fit ()
151 Real dy1 = check_fit_f ();
155 // be careful not to take too big step
158 control_[1][Y_AXIS] += h1;
159 control_[2][Y_AXIS] += h1;
160 return_[1][Y_AXIS] += h1;
161 return_[2][Y_AXIS] += h1;
164 Real dy2 = check_fit_f ();
169 Real internote_f = paper_l_->get_realvar (interline_scm_sym)/2.0;
171 Real internote_f = STAFFHEIGHT / 8;
174 Real epsilon = internote_f / 4;
175 if (abs (dy2 - dy1) < epsilon)
184 Then we get for h : B (h) = 0
186 B(0) = dy1 = a * 0 + b => b = dy1
187 B(h1) = dy2 = a * h1 + b => a * f * dy1 + b = dy2
191 a * dy1 / 2 + dy1 = dy2 => a = (dy2 - dy1) / (f * dy1)
194 Real a = (dy2 - dy1) / (f * dy1);
198 if (sign (h) != sign (h1))
201 if (sign (h) != sign (h1))
204 control_[1][Y_AXIS] += -h1 +h;
205 control_[2][Y_AXIS] += -h1 +h;
206 return_[1][Y_AXIS] += -h1 +h;
207 return_[2][Y_AXIS] += -h1 +h;
211 Bezier_bow::calc_bezier ()
213 Real s = sqrt (control_[3][X_AXIS] * control_[3][X_AXIS]
214 + control_[1][Y_AXIS] * control_[2][Y_AXIS]);
216 Real internote = paper_l_->get_realvar (interline_scm_sym)/2.0;
218 Real internote = STAFFHEIGHT / 8;
220 int steps = (int)rint (s / internote);
221 Bezier::calc (steps);
225 Bezier_bow::calc_f (Real height)
228 calc_default (height);
232 Real dy = check_fit_f ();
243 // if (check_debug && !monitor->silent_b ("Bezier_bow_controls"))
244 if (check_debug && !(monitor->silent_b ("Bezier_controls")
245 && monitor->silent_b ("Bezier_bow_controls")))
247 cout << "Before transform*********\n";
249 cout << "************************\n";
258 duh, this is crude (control-points)
259 perhaps it's even better to check the amount of blow_fit ()
261 for (int i=0; i < control_.size (); i++)
263 Real y = control_[i][Y_AXIS];
264 curve_extent_drul_[Y].unite (Interval (y, y));
265 Real x = control_[i][X_AXIS];
266 curve_extent_drul_[X].unite (Interval (x, x));
272 // if (check_debug && !monitor->silent_b ("Bezier_bow_controls"))
273 if (check_debug && !(monitor->silent_b ("Bezier_controls")
274 && monitor->silent_b ("Bezier_bow_controls")))
276 cout << "After transform*********\n";
278 cout << "************************\n";
286 * document in Documentation/fonts.tex
292 This function tries to address two issues:
293 * the tangents of the slur should always point inwards
294 in the actual slur, i.e. *after rotating back*.
296 * slurs shouldn't be too high
297 let's try : h <= 1.2 b && h <= 3 staffheight?
299 We could calculate the tangent of the bezier curve from
300 both ends going inward, and clip the slur at the point
301 where the tangent (after rotation) points up (or inward
302 with a certain maximum angle).
304 However, we assume that real clipping is not the best
305 answer. We expect that moving the outer control point up
306 if the slur becomes too high will result in a nicer slur
309 Knowing that the tangent is the line through the first
310 two control points, we'll clip (move the outer control
311 point upwards) too if the tangent points outwards.
315 Bezier_bow::calc_clipping ()
318 Real clip_height = paper_l_->get_var ("slur_clip_height");
319 Real clip_ratio = paper_l_->get_var ("slur_clip_ratio");
320 Real clip_angle = paper_l_->get_var ("slur_clip_angle");
322 Real staffsize_f = STAFFHEIGHT;
323 Real clip_height = 3.0 * staffsize_f;
324 Real clip_ratio = 1.2;
325 Real clip_angle = 100;
328 Real b = control_[3][X_AXIS] - control_[0][X_AXIS];
329 Real clip_h = clip_ratio * b <? clip_height;
330 Real begin_h = control_[1][Y_AXIS] - control_[0][Y_AXIS];
331 Real end_h = control_[2][Y_AXIS] - control_[3][Y_AXIS];
332 Real begin_dy = 0 >? begin_h - clip_h;
333 Real end_dy = 0 >? end_h - clip_h;
336 Real begin_alpha = (control_[1] - control_[0]).arg () + dir_ * alpha_;
337 Real end_alpha = pi - (control_[2] - control_[3]).arg () - dir_ * alpha_;
339 Real max_alpha = clip_angle / 90 * pi / 2;
340 if ((begin_dy < 0) && (end_dy < 0)
341 && (begin_alpha < max_alpha) && (end_alpha < max_alpha))
348 if ((begin_dy > 0) || (end_dy > 0))
350 Real dy = (begin_dy + end_dy) / 4;
352 encompass_[0][Y_AXIS] += dir_ * dy;
353 encompass_[encompass_.size () - 1][Y_AXIS] += dir_ * dy;
359 if (begin_alpha >= max_alpha)
360 begin_dy = 0 >? c * begin_alpha / max_alpha * begin_h;
361 if (end_alpha >= max_alpha)
362 end_dy = 0 >? c * end_alpha / max_alpha * end_h;
364 encompass_[0][Y_AXIS] += dir_ * begin_dy;
365 encompass_[encompass_.size () - 1][Y_AXIS] += dir_ * end_dy;
367 Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
368 alpha_ = delta.arg ();
377 Bezier_bow::calc_controls ()
379 for (int i = 0; i < 3; i++)
381 if (i && !calc_clipping ())
385 why do we always recalc from 0?
386 shouldn't calc_f () be used (too), rather than blow_fit () (only)?
396 calc_tangent_controls ();
405 Bezier_bow::calc_return (Real begin_alpha, Real end_alpha)
408 Real thick = paper_l_->get_var ("slur_thickness");
410 Real thick = 1.8 * 0.4 PT;
413 return_[0] = control_[3];
414 return_[3] = control_[0];
416 return_[1] = control_[2] - thick * complex_exp (Offset (0, 90 + end_alpha));
417 return_[2] = control_[1]
418 - thick * complex_exp (Offset (0, 90 - begin_alpha));
422 See Documentation/fonts.tex
425 Bezier_bow::calc_tangent_controls ()
427 Offset ijk_p (control_[3][X_AXIS] / 2, control_[1][Y_AXIS]);
428 BEZIER_BOW_DOUT << "ijk: " << ijk_p[X_AXIS] << ", " << ijk_p[Y_AXIS] << endl;
430 Real default_rc = ijk_p[Y_AXIS] / ijk_p[X_AXIS];
432 int begin_disturb = encompass_.largest_disturbing ();
433 Offset begin_p = begin_disturb ? Offset (encompass_[begin_disturb][X_AXIS],
434 encompass_[begin_disturb][Y_AXIS]) : ijk_p;
435 Real begin_rc = begin_p[Y_AXIS] / begin_p[X_AXIS];
436 if (default_rc > begin_rc)
439 begin_rc = default_rc;
443 reversed.set_size (encompass_.size ());
444 Real b = control_[3][X_AXIS];
445 for (int i = 0; i < encompass_.size (); i++ )
450 reversed[i][X_AXIS] = b - encompass_[encompass_.size () - i - 1][X_AXIS];
451 reversed[i][Y_AXIS] = encompass_[encompass_.size () - i - 1][Y_AXIS];
454 int end_disturb = reversed.largest_disturbing ();
455 end_disturb = end_disturb ? encompass_.size () - end_disturb - 1 : 0;
456 Offset end_p = end_disturb ? Offset (encompass_[end_disturb][X_AXIS],
457 encompass_[end_disturb][Y_AXIS]) : ijk_p;
458 Real end_rc = end_p[Y_AXIS] / (control_[3][X_AXIS] - end_p[X_AXIS]);
459 if (default_rc > end_rc)
464 BEZIER_BOW_DOUT << "begin " << begin_p[X_AXIS] << ", " << begin_p[Y_AXIS] << endl;
465 BEZIER_BOW_DOUT << "end " << end_p[X_AXIS] << ", " << end_p[Y_AXIS] << endl;
467 Real height =control_[1][Y_AXIS];
468 for (int i = 0; i < encompass_.size (); i++ )
469 height = height >? encompass_[i][Y_AXIS];
471 // emperic computer science:
472 // * tangents somewhat steeper than minimal line
474 Real internote = paper_l_->get_realvar (interline_scm_sym)/2.0;
475 Real rc_correct = paper_l_->get_var ("slur_rc_factor");
477 Real internote = STAFFHEIGHT / 8;
478 Real rc_correct = 2.4;
481 begin_rc *= rc_correct;
482 end_rc *= rc_correct;
486 Real begin_alpha = atan (begin_rc);
487 Real end_alpha = atan (-end_rc);
488 Real theta = (begin_alpha - end_alpha) / 2;
490 Real epsilon = internote / 5;
492 // if we have two disturbing points, have height line through those...
493 if (!((abs (begin_p[X_AXIS] - end_p[X_AXIS]) < epsilon)
494 && (abs (begin_p[Y_AXIS] - end_p[Y_AXIS]) < epsilon)))
495 theta = atan (end_p[Y_AXIS] - begin_p[Y_AXIS]) / (end_p[X_AXIS] - begin_p[X_AXIS]);
497 Real rc3 = tan (theta);
498 // ugh: be less steep
501 Real c2 = -rc2 * control_[3][X_AXIS];
502 Real c3 = begin_p[Y_AXIS] > end_p[Y_AXIS] ? begin_p[Y_AXIS]
503 - rc3 * begin_p[X_AXIS] : end_p[Y_AXIS] - rc3 * end_p[X_AXIS];
505 BEZIER_BOW_DOUT << "y1 = " << rc1 << " x + 0" << endl;
506 BEZIER_BOW_DOUT << "y2 = " << rc2 << " x + " << c2 << endl;
507 BEZIER_BOW_DOUT << "y3 = " << rc3 << " x + " << c3 << endl;
508 control_[1][X_AXIS] = c3 / (rc1 - rc3);
509 control_[1][Y_AXIS] = rc1 * control_[1][X_AXIS];
510 control_[2][X_AXIS] = (c3 - c2) / (rc2 - rc3);
511 BEZIER_BOW_DOUT << "c2[X_AXIS] = " << control_[2][X_AXIS] << endl;
512 BEZIER_BOW_DOUT << "(c3 - c2) = " << (c3 - c2) << endl;
513 BEZIER_BOW_DOUT << "(rc2 - rc3) = " << (rc2 - rc3) << endl;
514 control_[2][Y_AXIS] = rc2 * control_[2][X_AXIS] + c2;
515 BEZIER_BOW_DOUT << "c2[Y_AXIS]" << control_[2][Y_AXIS] << endl;
517 calc_return (begin_alpha, end_alpha);
521 Bezier_bow::check_fit_bo ()
523 for (int i = 1; i < encompass_.size () - 1; i++)
524 if ((encompass_[i][X_AXIS] > encompass_[0][X_AXIS])
525 && (encompass_[i][X_AXIS] < encompass_[encompass_.size () -1][X_AXIS]))
526 if (encompass_[i][Y_AXIS] > y (encompass_[i][X_AXIS]))
532 Bezier_bow::check_fit_f ()
535 for (int i = 1; i < encompass_.size () - 1; i++)
536 if ((encompass_[i][X_AXIS] > encompass_[0][X_AXIS])
537 && (encompass_[i][X_AXIS] < encompass_[encompass_.size () -1][X_AXIS]))
538 dy = dy >? (encompass_[i][Y_AXIS] - y (encompass_[i][X_AXIS]));
543 Bezier_bow::print () const
547 if (check_debug && !monitor->silent_b ("Bezier_bow_controls"))
549 cout << "Bezier_bow\n";
550 cout << "Encompass: ";
551 for (int i=0; i < encompass_.size (); i++)
552 cout << encompass_[i].str () << ", ";
559 Bezier_bow::set (Array<Offset> points, int dir)
566 Bezier_bow::transform ()
568 origin_ = encompass_[0];
569 encompass_.translate (-origin_);
571 Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
572 alpha_ = delta.arg ();
574 encompass_.rotate (-alpha_);
581 Bezier_bow::transform_back ()
590 control_.rotate (alpha_);
591 control_.translate (origin_);
593 return_.rotate (alpha_);
594 return_.translate (origin_);
596 encompass_.rotate (alpha_);
597 encompass_.translate (origin_);
601 See Documentation/fonts.tex
604 Bezier_bow::calc_default (Real h)
608 Real height_limit = paper_l_->get_var ("slur_height_limit");
609 Real ratio = paper_l_->get_var ("slur_ratio");
611 Real staffsize_f = STAFFHEIGHT;
612 Real height_limit = staffsize_f;
613 Real ratio = 1.0/3.0;
616 Real alpha = height_limit * 2.0 / pi;
617 Real beta = pi * ratio / (2.0 * height_limit);
619 Offset delta (encompass_[encompass_.size () - 1][X_AXIS]
620 - encompass_[0][X_AXIS], 0);
621 Real b = delta.length ();
622 Real indent = alpha * atan (beta * b);
623 Real height = indent + h;
625 Array<Offset> control;
626 control.push (Offset (0, 0));
627 control.push (Offset (indent, height));
628 control.push (Offset (b - indent, height));
629 control.push (Offset (b, 0));
630 Bezier::set (control);