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
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_AXIS] > 0)
40 Real phi = (*this)[i][Y_AXIS] / (*this)[i][X_AXIS];
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::print () const
92 if (check_debug && !monitor->silent_b ("Bezier_controls"))
94 if (control_[1].length ())
98 for (int i=0; i < control_.size (); i++)
99 cout << control_[i].str () << ", ";
107 Bezier::set (Array<Offset> points)
109 assert (points.size () == 4);
117 // bounds func should be templatised to take array of offsets too?
118 Array<Real> positions;
119 for (int i = 0; i < curve_.size (); i++)
120 positions.push (curve_[i][X_AXIS]);
122 Slice slice = get_bounds_slice (positions, x);
124 Offset z1 = curve_[0 >? slice[BIGGER] - 1];
125 Offset z2 = curve_[1 >? slice[BIGGER]];
126 Real multiplier = (x - z2[X_AXIS]) / (z1[X_AXIS] - z2[X_AXIS]);
127 Real y = z1[Y_AXIS] * multiplier + (1.0 - multiplier) * z2[Y_AXIS];
133 Bezier_bow::Bezier_bow (Paper_def* paper_l)
136 return_.set_size (4);
140 Bezier_bow::blow_fit ()
142 Real dy1 = check_fit_f ();
146 // be careful not to take too big step
149 control_[1][Y_AXIS] += h1;
150 control_[2][Y_AXIS] += h1;
151 return_[1][Y_AXIS] += h1;
152 return_[2][Y_AXIS] += h1;
155 Real dy2 = check_fit_f ();
160 Real internote_f = paper_l_->get_realvar (interline_scm_sym)/2.0;
162 Real internote_f = STAFFHEIGHT / 8;
165 Real epsilon = internote_f / 4;
166 if (abs (dy2 - dy1) < epsilon)
175 Then we get for h : B (h) = 0
177 B(0) = dy1 = a * 0 + b => b = dy1
178 B(h1) = dy2 = a * h1 + b => a * f * dy1 + b = dy2
182 a * dy1 / 2 + dy1 = dy2 => a = (dy2 - dy1) / (f * dy1)
185 Real a = (dy2 - dy1) / (f * dy1);
189 if (sign (h) != sign (h1))
192 if (sign (h) != sign (h1))
195 control_[1][Y_AXIS] += -h1 +h;
196 control_[2][Y_AXIS] += -h1 +h;
197 return_[1][Y_AXIS] += -h1 +h;
198 return_[2][Y_AXIS] += -h1 +h;
202 Bezier_bow::calc_bezier ()
204 Real s = sqrt (control_[3][X_AXIS] * control_[3][X_AXIS]
205 + control_[1][Y_AXIS] * control_[2][Y_AXIS]);
207 Real internote = paper_l_->get_realvar (interline_scm_sym)/2.0;
209 Real internote = STAFFHEIGHT / 8;
211 int steps = (int)rint (s / internote);
212 Bezier::calc (steps);
216 Bezier_bow::calc_f (Real height)
219 calc_default (height);
223 Real dy = check_fit_f ();
234 // if (check_debug && !monitor->silent_b ("Bezier_bow_controls"))
235 if (check_debug && !(monitor->silent_b ("Bezier_controls")
236 && monitor->silent_b ("Bezier_bow_controls")))
238 cout << "Before transform*********\n";
240 cout << "************************\n";
251 // if (check_debug && !monitor->silent_b ("Bezier_bow_controls"))
252 if (check_debug && !(monitor->silent_b ("Bezier_controls")
253 && monitor->silent_b ("Bezier_bow_controls")))
255 cout << "After transform*********\n";
257 cout << "************************\n";
265 * document in Documentation/fonts.tex
271 This function tries to address two issues:
272 * the tangents of the slur should always point inwards
273 in the actual slur, i.e. *after rotating back*.
275 * slurs shouldn't be too high
276 let's try : h <= 1.2 b && h <= 3 staffheight?
278 We could calculate the tangent of the bezier curve from
279 both ends going inward, and clip the slur at the point
280 where the tangent (after rotation) points up (or inward
281 with a certain maximum angle).
283 However, we assume that real clipping is not the best
284 answer. We expect that moving the outer control point up
285 if the slur becomes too high will result in a nicer slur
288 Knowing that the tangent is the line through the first
289 two control points, we'll clip (move the outer control
290 point upwards) too if the tangent points outwards.
294 Bezier_bow::calc_clipping ()
297 Real clip_height = paper_l_->get_var ("slur_clip_height");
298 Real clip_ratio = paper_l_->get_var ("slur_clip_ratio");
299 Real clip_angle = paper_l_->get_var ("slur_clip_angle");
301 Real staffsize_f = STAFFHEIGHT;
302 Real clip_height = 3.0 * staffsize_f;
303 Real clip_ratio = 1.2;
304 Real clip_angle = 100;
307 Real b = control_[3][X_AXIS] - control_[0][X_AXIS];
308 Real clip_h = clip_ratio * b <? clip_height;
309 Real begin_h = control_[1][Y_AXIS] - control_[0][Y_AXIS];
310 Real end_h = control_[2][Y_AXIS] - control_[3][Y_AXIS];
311 Real begin_dy = 0 >? begin_h - clip_h;
312 Real end_dy = 0 >? end_h - clip_h;
315 Real begin_alpha = (control_[1] - control_[0]).arg () + dir_ * alpha_;
316 Real end_alpha = pi - (control_[2] - control_[3]).arg () - dir_ * alpha_;
318 Real max_alpha = clip_angle / 90 * pi / 2;
319 if ((begin_dy < 0) && (end_dy < 0)
320 && (begin_alpha < max_alpha) && (end_alpha < max_alpha))
327 if ((begin_dy > 0) || (end_dy > 0))
329 Real dy = (begin_dy + end_dy) / 4;
331 encompass_[0][Y_AXIS] += dir_ * dy;
332 encompass_[encompass_.size () - 1][Y_AXIS] += dir_ * dy;
338 if (begin_alpha >= max_alpha)
339 begin_dy = 0 >? c * begin_alpha / max_alpha * begin_h;
340 if (end_alpha >= max_alpha)
341 end_dy = 0 >? c * end_alpha / max_alpha * end_h;
343 encompass_[0][Y_AXIS] += dir_ * begin_dy;
344 encompass_[encompass_.size () - 1][Y_AXIS] += dir_ * end_dy;
346 Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
347 alpha_ = delta.arg ();
356 Bezier_bow::calc_controls ()
358 for (int i = 0; i < 3; i++)
360 if (i && !calc_clipping ())
371 calc_tangent_controls ();
380 Bezier_bow::calc_return (Real begin_alpha, Real end_alpha)
383 Real thick = paper_l_->get_var ("slur_thickness");
385 Real thick = 1.8 * 0.4 PT;
388 return_[0] = control_[3];
389 return_[3] = control_[0];
391 return_[1] = control_[2] - thick * complex_exp (Offset (0, 90 + end_alpha));
392 return_[2] = control_[1]
393 - thick * complex_exp (Offset (0, 90 - begin_alpha));
397 See Documentation/fonts.tex
400 Bezier_bow::calc_tangent_controls ()
402 Offset ijk_p (control_[3][X_AXIS] / 2, control_[1][Y_AXIS]);
403 BEZIER_BOW_DOUT << "ijk: " << ijk_p[X_AXIS] << ", " << ijk_p[Y_AXIS] << endl;
405 Real default_rc = ijk_p[Y_AXIS] / ijk_p[X_AXIS];
407 int begin_disturb = encompass_.largest_disturbing ();
408 Offset begin_p = begin_disturb ? Offset (encompass_[begin_disturb][X_AXIS],
409 encompass_[begin_disturb][Y_AXIS]) : ijk_p;
410 Real begin_rc = begin_p[Y_AXIS] / begin_p[X_AXIS];
411 if (default_rc > begin_rc)
414 begin_rc = default_rc;
418 reversed.set_size (encompass_.size ());
419 Real b = control_[3][X_AXIS];
420 for (int i = 0; i < encompass_.size (); i++ )
425 reversed[i][X_AXIS] = b - encompass_[encompass_.size () - i - 1][X_AXIS];
426 reversed[i][Y_AXIS] = encompass_[encompass_.size () - i - 1][Y_AXIS];
429 int end_disturb = reversed.largest_disturbing ();
430 end_disturb = end_disturb ? encompass_.size () - end_disturb - 1 : 0;
431 Offset end_p = end_disturb ? Offset (encompass_[end_disturb][X_AXIS],
432 encompass_[end_disturb][Y_AXIS]) : ijk_p;
433 Real end_rc = end_p[Y_AXIS] / (control_[3][X_AXIS] - end_p[X_AXIS]);
434 if (default_rc > end_rc)
439 BEZIER_BOW_DOUT << "begin " << begin_p[X_AXIS] << ", " << begin_p[Y_AXIS] << endl;
440 BEZIER_BOW_DOUT << "end " << end_p[X_AXIS] << ", " << end_p[Y_AXIS] << endl;
442 Real height =control_[1][Y_AXIS];
443 for (int i = 0; i < encompass_.size (); i++ )
444 height = height >? encompass_[i][Y_AXIS];
446 // emperic computer science:
447 // * tangents somewhat steeper than minimal line
449 Real internote = paper_l_->get_realvar (interline_scm_sym)/2.0;
450 Real rc_correct = paper_l_->get_var ("slur_rc_factor");
452 Real internote = STAFFHEIGHT / 8;
453 Real rc_correct = 2.4;
456 begin_rc *= rc_correct;
457 end_rc *= rc_correct;
461 Real begin_alpha = atan (begin_rc);
462 Real end_alpha = atan (-end_rc);
463 Real theta = (begin_alpha - end_alpha) / 2;
465 Real epsilon = internote / 5;
467 // if we have two disturbing points, have height line through those...
468 if (!((abs (begin_p[X_AXIS] - end_p[X_AXIS]) < epsilon)
469 && (abs (begin_p[Y_AXIS] - end_p[Y_AXIS]) < epsilon)))
470 theta = atan (end_p[Y_AXIS] - begin_p[Y_AXIS]) / (end_p[X_AXIS] - begin_p[X_AXIS]);
472 Real rc3 = tan (theta);
473 // ugh: be less steep
476 Real c2 = -rc2 * control_[3][X_AXIS];
477 Real c3 = begin_p[Y_AXIS] > end_p[Y_AXIS] ? begin_p[Y_AXIS]
478 - rc3 * begin_p[X_AXIS] : end_p[Y_AXIS] - rc3 * end_p[X_AXIS];
480 BEZIER_BOW_DOUT << "y1 = " << rc1 << " x + 0" << endl;
481 BEZIER_BOW_DOUT << "y2 = " << rc2 << " x + " << c2 << endl;
482 BEZIER_BOW_DOUT << "y3 = " << rc3 << " x + " << c3 << endl;
483 control_[1][X_AXIS] = c3 / (rc1 - rc3);
484 control_[1][Y_AXIS] = rc1 * control_[1][X_AXIS];
485 control_[2][X_AXIS] = (c3 - c2) / (rc2 - rc3);
486 BEZIER_BOW_DOUT << "c2[X_AXIS] = " << control_[2][X_AXIS] << endl;
487 BEZIER_BOW_DOUT << "(c3 - c2) = " << (c3 - c2) << endl;
488 BEZIER_BOW_DOUT << "(rc2 - rc3) = " << (rc2 - rc3) << endl;
489 control_[2][Y_AXIS] = rc2 * control_[2][X_AXIS] + c2;
490 BEZIER_BOW_DOUT << "c2[Y_AXIS]" << control_[2][Y_AXIS] << endl;
492 calc_return (begin_alpha, end_alpha);
496 Bezier_bow::check_fit_bo ()
498 for (int i = 1; i < encompass_.size () - 1; i++)
499 if ((encompass_[i][X_AXIS] > encompass_[0][X_AXIS])
500 && (encompass_[i][X_AXIS] < encompass_[encompass_.size () -1][X_AXIS]))
501 if (encompass_[i][Y_AXIS] > y (encompass_[i][X_AXIS]))
507 Bezier_bow::check_fit_f ()
510 for (int i = 1; i < encompass_.size () - 1; i++)
511 if ((encompass_[i][X_AXIS] > encompass_[0][X_AXIS])
512 && (encompass_[i][X_AXIS] < encompass_[encompass_.size () -1][X_AXIS]))
513 dy = dy >? (encompass_[i][Y_AXIS] - y (encompass_[i][X_AXIS]));
518 Bezier_bow::print () const
522 if (check_debug && !monitor->silent_b ("Bezier_bow_controls"))
524 cout << "Bezier_bow\n";
525 cout << "Encompass: ";
526 for (int i=0; i < encompass_.size (); i++)
527 cout << encompass_[i].str () << ", ";
534 Bezier_bow::set (Array<Offset> points, int dir)
541 Bezier_bow::transform ()
543 origin_ = encompass_[0];
544 encompass_.translate (-origin_);
546 Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
547 alpha_ = delta.arg ();
549 encompass_.rotate (-alpha_);
556 Bezier_bow::transform_back ()
565 control_.rotate (alpha_);
566 control_.translate (origin_);
568 return_.rotate (alpha_);
569 return_.translate (origin_);
571 encompass_.rotate (alpha_);
572 encompass_.translate (origin_);
576 See Documentation/fonts.tex
579 Bezier_bow::calc_default (Real h)
583 Real height_limit = paper_l_->get_var ("slur_height_limit");
584 Real ratio = paper_l_->get_var ("slur_ratio");
586 Real staffsize_f = STAFFHEIGHT;
587 Real height_limit = staffsize_f;
588 Real ratio = 1.0/3.0;
591 Real alpha = height_limit * 2.0 / pi;
592 Real beta = pi * ratio / (2.0 * height_limit);
594 Offset delta (encompass_[encompass_.size () - 1][X_AXIS]
595 - encompass_[0][X_AXIS], 0);
596 Real b = delta.length ();
597 Real indent = alpha * atan (beta * b);
598 Real height = indent + h;
600 Array<Offset> control;
601 control.push (Offset (0, 0));
602 control.push (Offset (indent, height));
603 control.push (Offset (b - indent, height));
604 control.push (Offset (b, 0));
605 Bezier::set (control);