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_DEBUG_OUT if (flower_dstream && !flower_dstream->silent_b ("Bezier_bow")) cout
21 #define BEZIER_BOW_DEBUG_OUT 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. Remove 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 (flower_dstream && !flower_dstream->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);
237 Real dy = check_fit_f ();
248 // if (flower_dstream && !flower_dstream->silent_b ("Bezier_bow_controls"))
249 if (flower_dstream && !(flower_dstream->silent_b ("Bezier_controls")
250 && flower_dstream->silent_b ("Bezier_bow_controls")))
252 cout << "Before transform*********\n";
254 cout << "************************\n";
263 duh, this is crude (control-points)
264 perhaps it's even better to check the amount of blow_fit ()
266 for (int i=0; i < control_.size (); i++)
268 Real y = control_[i][Y_AXIS];
269 curve_extent_drul_[Y].unite (Interval (y, y));
270 Real x = control_[i][X_AXIS];
271 curve_extent_drul_[X].unite (Interval (x, x));
277 // if (flower_dstream && !flower_dstream->silent_b ("Bezier_bow_controls"))
278 if (flower_dstream && !(flower_dstream->silent_b ("Bezier_controls")
279 && flower_dstream->silent_b ("Bezier_bow_controls")))
281 cout << "After transform*********\n";
283 cout << "************************\n";
291 * document in Documentation/fonts.tex
297 This function tries to address two issues:
298 * the tangents of the slur should always point inwards
299 in the actual slur, i.e. *after rotating back*.
301 * slurs shouldn't be too high
302 let's try : h <= 1.2 b && h <= 3 staffheight?
304 We could calculate the tangent of the bezier curve from
305 both ends going inward, and clip the slur at the point
306 where the tangent (after rotation) points up (or inward
307 with a certain maximum angle).
309 However, we assume that real clipping is not the best
310 answer. We expect that moving the outer control point up
311 if the slur becomes too high will result in a nicer slur
314 Knowing that the tangent is the line through the first
315 two control points, we'll clip (move the outer control
316 point upwards) too if the tangent points outwards.
320 Bezier_bow::calc_clipping ()
323 Real clip_height = paper_l_->get_var ("slur_clip_height");
324 Real clip_ratio = paper_l_->get_var ("slur_clip_ratio");
325 Real clip_angle = paper_l_->get_var ("slur_clip_angle");
327 Real staffsize_f = STAFFHEIGHT;
328 Real clip_height = 3.0 * staffsize_f;
329 Real clip_ratio = 1.2;
330 Real clip_angle = 100;
333 Real b = control_[3][X_AXIS] - control_[0][X_AXIS];
334 Real clip_h = clip_ratio * b <? clip_height;
335 Real begin_h = control_[1][Y_AXIS] - control_[0][Y_AXIS];
336 Real end_h = control_[2][Y_AXIS] - control_[3][Y_AXIS];
337 Real begin_dy = 0 >? begin_h - clip_h;
338 Real end_dy = 0 >? end_h - clip_h;
341 Real begin_alpha = (control_[1] - control_[0]).arg () + dir_ * alpha_;
342 Real end_alpha = pi - (control_[2] - control_[3]).arg () - dir_ * alpha_;
344 Real max_alpha = clip_angle / 90 * pi / 2;
345 if ((begin_dy < 0) && (end_dy < 0)
346 && (begin_alpha < max_alpha) && (end_alpha < max_alpha))
353 if ((begin_dy > 0) || (end_dy > 0))
355 Real dy = (begin_dy + end_dy) / 4;
357 encompass_[0][Y_AXIS] += dir_ * dy;
358 encompass_[encompass_.size () - 1][Y_AXIS] += dir_ * dy;
364 if (begin_alpha >= max_alpha)
365 begin_dy = 0 >? c * begin_alpha / max_alpha * begin_h;
366 if (end_alpha >= max_alpha)
367 end_dy = 0 >? c * end_alpha / max_alpha * end_h;
369 encompass_[0][Y_AXIS] += dir_ * begin_dy;
370 encompass_[encompass_.size () - 1][Y_AXIS] += dir_ * end_dy;
372 Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
373 alpha_ = delta.arg ();
382 Bezier_bow::calc_controls ()
384 for (int i = 0; i < 3; i++)
386 if (i && !calc_clipping ())
390 why do we always recalc from 0?
391 shouldn't calc_f () be used (too), rather than blow_fit () (only)?
401 calc_tangent_controls ();
410 Bezier_bow::calc_return (Real begin_alpha, Real end_alpha)
413 Real thick = paper_l_->get_var ("slur_thickness");
415 Real thick = 1.8 * 0.4 PT;
418 return_[0] = control_[3];
419 return_[3] = control_[0];
421 return_[1] = control_[2] - thick * complex_exp (Offset (0, 90 + end_alpha));
422 return_[2] = control_[1]
423 - thick * complex_exp (Offset (0, 90 - begin_alpha));
427 See Documentation/fonts.tex
430 Bezier_bow::calc_tangent_controls ()
432 Offset ijk_p (control_[3][X_AXIS] / 2, control_[1][Y_AXIS]);
433 BEZIER_BOW_DEBUG_OUT << "ijk: " << ijk_p[X_AXIS] << ", " << ijk_p[Y_AXIS] << endl;
435 Real default_rc = ijk_p[Y_AXIS] / ijk_p[X_AXIS];
437 int begin_disturb = encompass_.largest_disturbing ();
438 Offset begin_p = begin_disturb ? Offset (encompass_[begin_disturb][X_AXIS],
439 encompass_[begin_disturb][Y_AXIS]) : ijk_p;
440 Real begin_rc = begin_p[Y_AXIS] / begin_p[X_AXIS];
441 if (default_rc > begin_rc)
444 begin_rc = default_rc;
448 reversed.set_size (encompass_.size ());
449 Real b = control_[3][X_AXIS];
450 for (int i = 0; i < encompass_.size (); i++ )
455 reversed[i][X_AXIS] = b - encompass_[encompass_.size () - i - 1][X_AXIS];
456 reversed[i][Y_AXIS] = encompass_[encompass_.size () - i - 1][Y_AXIS];
459 int end_disturb = reversed.largest_disturbing ();
460 end_disturb = end_disturb ? encompass_.size () - end_disturb - 1 : 0;
461 Offset end_p = end_disturb ? Offset (encompass_[end_disturb][X_AXIS],
462 encompass_[end_disturb][Y_AXIS]) : ijk_p;
463 Real end_rc = end_p[Y_AXIS] / (control_[3][X_AXIS] - end_p[X_AXIS]);
464 if (default_rc > end_rc)
469 BEZIER_BOW_DEBUG_OUT << "begin " << begin_p[X_AXIS] << ", " << begin_p[Y_AXIS] << endl;
470 BEZIER_BOW_DEBUG_OUT << "end " << end_p[X_AXIS] << ", " << end_p[Y_AXIS] << endl;
472 Real height =control_[1][Y_AXIS];
473 for (int i = 0; i < encompass_.size (); i++ )
474 height = height >? encompass_[i][Y_AXIS];
476 // emperic computer science:
477 // * tangents somewhat steeper than minimal line
479 Real internote = paper_l_->get_realvar (interline_scm_sym)/2.0;
480 Real rc_correct = paper_l_->get_var ("slur_rc_factor");
482 Real internote = STAFFHEIGHT / 8;
483 Real rc_correct = 2.4;
486 begin_rc *= rc_correct;
487 end_rc *= rc_correct;
491 Real begin_alpha = atan (begin_rc);
492 Real end_alpha = atan (-end_rc);
493 Real theta = (begin_alpha - end_alpha) / 2;
495 Real epsilon = internote / 5;
497 // if we have two disturbing points, have height line through those...
498 if (!((abs (begin_p[X_AXIS] - end_p[X_AXIS]) < epsilon)
499 && (abs (begin_p[Y_AXIS] - end_p[Y_AXIS]) < epsilon)))
500 theta = atan (end_p[Y_AXIS] - begin_p[Y_AXIS]) / (end_p[X_AXIS] - begin_p[X_AXIS]);
502 Real rc3 = tan (theta);
503 // ugh: be less steep
506 Real c2 = -rc2 * control_[3][X_AXIS];
507 Real c3 = begin_p[Y_AXIS] > end_p[Y_AXIS] ? begin_p[Y_AXIS]
508 - rc3 * begin_p[X_AXIS] : end_p[Y_AXIS] - rc3 * end_p[X_AXIS];
510 BEZIER_BOW_DEBUG_OUT << "y1 = " << rc1 << " x + 0" << endl;
511 BEZIER_BOW_DEBUG_OUT << "y2 = " << rc2 << " x + " << c2 << endl;
512 BEZIER_BOW_DEBUG_OUT << "y3 = " << rc3 << " x + " << c3 << endl;
513 control_[1][X_AXIS] = c3 / (rc1 - rc3);
514 control_[1][Y_AXIS] = rc1 * control_[1][X_AXIS];
515 control_[2][X_AXIS] = (c3 - c2) / (rc2 - rc3);
516 BEZIER_BOW_DEBUG_OUT << "c2[X_AXIS] = " << control_[2][X_AXIS] << endl;
517 BEZIER_BOW_DEBUG_OUT << "(c3 - c2) = " << (c3 - c2) << endl;
518 BEZIER_BOW_DEBUG_OUT << "(rc2 - rc3) = " << (rc2 - rc3) << endl;
519 control_[2][Y_AXIS] = rc2 * control_[2][X_AXIS] + c2;
520 BEZIER_BOW_DEBUG_OUT << "c2[Y_AXIS]" << control_[2][Y_AXIS] << endl;
522 calc_return (begin_alpha, end_alpha);
526 Bezier_bow::check_fit_bo ()
528 for (int i = 1; i < encompass_.size () - 1; i++)
529 if ((encompass_[i][X_AXIS] > encompass_[0][X_AXIS])
530 && (encompass_[i][X_AXIS] < encompass_[encompass_.size () -1][X_AXIS]))
531 if (encompass_[i][Y_AXIS] > y (encompass_[i][X_AXIS]))
537 Bezier_bow::check_fit_f ()
540 for (int i = 1; i < encompass_.size () - 1; i++)
541 if ((encompass_[i][X_AXIS] > encompass_[0][X_AXIS])
542 && (encompass_[i][X_AXIS] < encompass_[encompass_.size () -1][X_AXIS]))
543 dy = dy >? (encompass_[i][Y_AXIS] - y (encompass_[i][X_AXIS]));
548 Bezier_bow::print () const
552 if (flower_dstream && !flower_dstream->silent_b ("Bezier_bow_controls"))
554 cout << "Bezier_bow\n";
555 cout << "Encompass: ";
556 for (int i=0; i < encompass_.size (); i++)
557 cout << encompass_[i].str () << ", ";
564 Bezier_bow::set (Array<Offset> points, int dir)
571 Bezier_bow::transform ()
573 origin_ = encompass_[0];
574 encompass_.translate (-origin_);
576 Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
577 alpha_ = delta.arg ();
579 encompass_.rotate (-alpha_);
586 Bezier_bow::transform_back ()
595 control_.rotate (alpha_);
596 control_.translate (origin_);
598 return_.rotate (alpha_);
599 return_.translate (origin_);
601 encompass_.rotate (alpha_);
602 encompass_.translate (origin_);
606 See Documentation/fonts.tex
609 Bezier_bow::calc_default (Real h)
613 Real height_limit = paper_l_->get_var ("slur_height_limit");
614 Real ratio = paper_l_->get_var ("slur_ratio");
616 Real staffsize_f = STAFFHEIGHT;
617 Real height_limit = staffsize_f;
618 Real ratio = 1.0/3.0;
621 Real alpha = height_limit * 2.0 / pi;
622 Real beta = pi * ratio / (2.0 * height_limit);
624 Offset delta (encompass_[encompass_.size () - 1][X_AXIS]
625 - encompass_[0][X_AXIS], 0);
626 Real b = delta.length ();
627 Real indent = alpha * atan (beta * b);
628 Real height = indent + h;
630 Array<Offset> control;
631 control.push (Offset (0, 0));
632 control.push (Offset (indent, height));
633 control.push (Offset (b - indent, height));
634 control.push (Offset (b, 0));
635 Bezier::set (control);