lilypond-1.1.67
[lilypond.git] / lily / bezier.cc
blobd72163f24a320ff6aa159822bb68a543cc02bce8
1 /*
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>
7 */
9 #include <math.h>
10 #include "bezier.hh"
11 #include "misc.hh"
13 #ifndef STANDALONE
14 #include "dimensions.hh"
15 #include "direction.hh"
16 #include "paper-def.hh"
17 #include "debug.hh"
18 #include "main.hh"
19 #define BEZIER_BOW_DOUT if (check_debug && !monitor->silent_b ("Bezier_bow")) cout
20 #else
21 #define BEZIER_BOW_DOUT cerr
22 #endif
26 [TODO]
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
34 void
35 Curve::flipy ()
37 for (int i = 0; i < size (); i++)
38 (*this)[i].mirror (Y_AXIS);
41 int
42 Curve::largest_disturbing ()
44 Real alpha = 0;
45 int j = 0;
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];
51 if (phi > alpha)
53 alpha = phi;
54 j = i;
58 return j;
61 void
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]);
69 void
70 Curve::translate (Offset o)
72 for (int i = 0; i < size (); i++)
73 (*this)[i] += o;
76 Bezier::Bezier ()
78 control_.set_size (4);
81 void
82 Bezier::calc (int steps)
84 steps = steps >? 10;
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);
90 Real t = 0.0;
91 for (int i = 0; i < curve_.size (); i++ )
93 curve_[i] = ((a * t + b) * t + c) * t + control_[0];
94 t += dt;
98 void
99 Bezier::print () const
101 #ifndef NPRINT
102 if (check_debug && !monitor->silent_b ("Bezier_controls"))
104 if (control_[1].length ())
106 cout << "Bezier\n";
107 cout << "Controls: ";
108 for (int i=0; i < control_.size (); i++)
109 cout << control_[i].str () << ", ";
110 // cout << "\n";
113 #endif
116 void
117 Bezier::set (Array<Offset> points)
119 assert (points.size () == 4);
120 control_ = points;
123 Real
124 Bezier::y (Real x)
126 // ugh
127 // bounds func should be templatised to take array of offsets too?
128 Array<Real> positions;
129 for (int i = 0; i < curve_.size (); i++)
130 positions.push (curve_[i][X_AXIS]);
132 Slice slice = get_bounds_slice (positions, x);
133 // ugh
134 Offset z1 = curve_[0 >? slice[BIGGER] - 1];
135 Offset z2 = curve_[1 >? slice[BIGGER]];
136 Real multiplier = (x - z2[X_AXIS]) / (z1[X_AXIS] - z2[X_AXIS]);
137 Real y = z1[Y_AXIS] * multiplier + (1.0 - multiplier) * z2[Y_AXIS];
139 return y;
143 Bezier_bow::Bezier_bow (Paper_def* paper_l)
145 paper_l_ = paper_l;
146 return_.set_size (4);
149 void
150 Bezier_bow::blow_fit ()
152 Real dy1 = check_fit_f ();
153 if (!dy1)
154 return;
156 // be careful not to take too big step
157 Real f = 0.3;
158 Real h1 = dy1 * f;
159 control_[1][Y_AXIS] += h1;
160 control_[2][Y_AXIS] += h1;
161 return_[1][Y_AXIS] += h1;
162 return_[2][Y_AXIS] += h1;
164 calc_bezier ();
165 Real dy2 = check_fit_f ();
166 if (!dy2)
167 return;
169 #ifndef STANDALONE
170 Real internote_f = paper_l_->get_realvar (interline_scm_sym)/2.0;
171 #else
172 Real internote_f = STAFFHEIGHT / 8;
173 #endif
175 Real epsilon = internote_f / 4;
176 if (abs (dy2 - dy1) < epsilon)
177 return;
180 Assume
181 dy = B (h)
182 with
183 B (h) = a * h + b;
185 Then we get for h : B (h) = 0
187 B(0) = dy1 = a * 0 + b => b = dy1
188 B(h1) = dy2 = a * h1 + b => a * f * dy1 + b = dy2
192 a * dy1 / 2 + dy1 = dy2 => a = (dy2 - dy1) / (f * dy1)
195 Real a = (dy2 - dy1) / (f * dy1);
196 Real b = dy1;
197 Real h = -b / a;
199 if (sign (h) != sign (h1))
200 return;
202 if (sign (h) != sign (h1))
203 return;
205 control_[1][Y_AXIS] += -h1 +h;
206 control_[2][Y_AXIS] += -h1 +h;
207 return_[1][Y_AXIS] += -h1 +h;
208 return_[2][Y_AXIS] += -h1 +h;
211 void
212 Bezier_bow::calc_bezier ()
214 Real s = sqrt (control_[3][X_AXIS] * control_[3][X_AXIS]
215 + control_[1][Y_AXIS] * control_[2][Y_AXIS]);
216 #ifndef STANDALONE
217 Real internote = paper_l_->get_realvar (interline_scm_sym)/2.0;
218 #else
219 Real internote = STAFFHEIGHT / 8;
220 #endif
221 int steps = (int)rint (s / internote);
222 Bezier::calc (steps);
225 Real
226 Bezier_bow::calc_f (Real height)
228 transform ();
229 calc_default (height);
231 calc_bezier ();
233 Real dy = check_fit_f ();
234 calc_return (0, 0);
236 transform_back ();
237 return dy;
240 void
241 Bezier_bow::calc ()
243 #ifndef NPRINT
244 // if (check_debug && !monitor->silent_b ("Bezier_bow_controls"))
245 if (check_debug && !(monitor->silent_b ("Bezier_controls")
246 && monitor->silent_b ("Bezier_bow_controls")))
248 cout << "Before transform*********\n";
249 print ();
250 cout << "************************\n";
252 #endif
253 transform ();
254 print ();
256 calc_controls ();
259 duh, this is crude (control-points)
260 perhaps it's even better to check the amount of blow_fit ()
262 for (int i=0; i < control_.size (); i++)
264 Real y = control_[i][Y_AXIS];
265 curve_extent_drul_[Y].unite (Interval (y, y));
266 Real x = control_[i][X_AXIS];
267 curve_extent_drul_[X].unite (Interval (x, x));
270 print ();
271 transform_back ();
272 #ifndef NPRINT
273 // if (check_debug && !monitor->silent_b ("Bezier_bow_controls"))
274 if (check_debug && !(monitor->silent_b ("Bezier_controls")
275 && monitor->silent_b ("Bezier_bow_controls")))
277 cout << "After transform*********\n";
278 print ();
279 cout << "************************\n";
281 #endif
285 [TODO]
286 * see if it works
287 * document in Documentation/fonts.tex
291 Clipping
293 This function tries to address two issues:
294 * the tangents of the slur should always point inwards
295 in the actual slur, i.e. *after rotating back*.
297 * slurs shouldn't be too high
298 let's try : h <= 1.2 b && h <= 3 staffheight?
300 We could calculate the tangent of the bezier curve from
301 both ends going inward, and clip the slur at the point
302 where the tangent (after rotation) points up (or inward
303 with a certain maximum angle).
305 However, we assume that real clipping is not the best
306 answer. We expect that moving the outer control point up
307 if the slur becomes too high will result in a nicer slur
308 after recalculation.
310 Knowing that the tangent is the line through the first
311 two control points, we'll clip (move the outer control
312 point upwards) too if the tangent points outwards.
315 bool
316 Bezier_bow::calc_clipping ()
318 #ifndef STANDALONE
319 Real clip_height = paper_l_->get_var ("slur_clip_height");
320 Real clip_ratio = paper_l_->get_var ("slur_clip_ratio");
321 Real clip_angle = paper_l_->get_var ("slur_clip_angle");
322 #else
323 Real staffsize_f = STAFFHEIGHT;
324 Real clip_height = 3.0 * staffsize_f;
325 Real clip_ratio = 1.2;
326 Real clip_angle = 100;
327 #endif
329 Real b = control_[3][X_AXIS] - control_[0][X_AXIS];
330 Real clip_h = clip_ratio * b <? clip_height;
331 Real begin_h = control_[1][Y_AXIS] - control_[0][Y_AXIS];
332 Real end_h = control_[2][Y_AXIS] - control_[3][Y_AXIS];
333 Real begin_dy = 0 >? begin_h - clip_h;
334 Real end_dy = 0 >? end_h - clip_h;
336 Real pi = M_PI;
337 Real begin_alpha = (control_[1] - control_[0]).arg () + dir_ * alpha_;
338 Real end_alpha = pi - (control_[2] - control_[3]).arg () - dir_ * alpha_;
340 Real max_alpha = clip_angle / 90 * pi / 2;
341 if ((begin_dy < 0) && (end_dy < 0)
342 && (begin_alpha < max_alpha) && (end_alpha < max_alpha))
343 return false;
345 transform_back ();
347 bool again = true;
349 if ((begin_dy > 0) || (end_dy > 0))
351 Real dy = (begin_dy + end_dy) / 4;
352 dy *= cos (alpha_);
353 encompass_[0][Y_AXIS] += dir_ * dy;
354 encompass_[encompass_.size () - 1][Y_AXIS] += dir_ * dy;
356 else
358 //ugh
359 Real c = 0.4;
360 if (begin_alpha >= max_alpha)
361 begin_dy = 0 >? c * begin_alpha / max_alpha * begin_h;
362 if (end_alpha >= max_alpha)
363 end_dy = 0 >? c * end_alpha / max_alpha * end_h;
365 encompass_[0][Y_AXIS] += dir_ * begin_dy;
366 encompass_[encompass_.size () - 1][Y_AXIS] += dir_ * end_dy;
368 Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
369 alpha_ = delta.arg ();
372 transform ();
374 return again;
377 void
378 Bezier_bow::calc_controls ()
380 for (int i = 0; i < 3; i++)
382 if (i && !calc_clipping ())
383 return;
386 why do we always recalc from 0?
387 shouldn't calc_f () be used (too), rather than blow_fit () (only)?
389 calc_default (0);
390 calc_bezier ();
392 if (check_fit_bo ())
394 calc_return (0, 0);
395 return;
397 calc_tangent_controls ();
399 blow_fit ();
400 // ugh
401 blow_fit ();
405 void
406 Bezier_bow::calc_return (Real begin_alpha, Real end_alpha)
408 #ifndef STANDALONE
409 Real thick = paper_l_->get_var ("slur_thickness");
410 #else
411 Real thick = 1.8 * 0.4 PT;
412 #endif
414 return_[0] = control_[3];
415 return_[3] = control_[0];
417 return_[1] = control_[2] - thick * complex_exp (Offset (0, 90 + end_alpha));
418 return_[2] = control_[1]
419 - thick * complex_exp (Offset (0, 90 - begin_alpha));
423 See Documentation/fonts.tex
425 void
426 Bezier_bow::calc_tangent_controls ()
428 Offset ijk_p (control_[3][X_AXIS] / 2, control_[1][Y_AXIS]);
429 BEZIER_BOW_DOUT << "ijk: " << ijk_p[X_AXIS] << ", " << ijk_p[Y_AXIS] << endl;
431 Real default_rc = ijk_p[Y_AXIS] / ijk_p[X_AXIS];
433 int begin_disturb = encompass_.largest_disturbing ();
434 Offset begin_p = begin_disturb ? Offset (encompass_[begin_disturb][X_AXIS],
435 encompass_[begin_disturb][Y_AXIS]) : ijk_p;
436 Real begin_rc = begin_p[Y_AXIS] / begin_p[X_AXIS];
437 if (default_rc > begin_rc)
439 begin_p = ijk_p;
440 begin_rc = default_rc;
443 Curve reversed;
444 reversed.set_size (encompass_.size ());
445 Real b = control_[3][X_AXIS];
446 for (int i = 0; i < encompass_.size (); i++ )
448 // b 1 0
449 // r = - * c
450 // 0 0 -1
451 reversed[i][X_AXIS] = b - encompass_[encompass_.size () - i - 1][X_AXIS];
452 reversed[i][Y_AXIS] = encompass_[encompass_.size () - i - 1][Y_AXIS];
455 int end_disturb = reversed.largest_disturbing ();
456 end_disturb = end_disturb ? encompass_.size () - end_disturb - 1 : 0;
457 Offset end_p = end_disturb ? Offset (encompass_[end_disturb][X_AXIS],
458 encompass_[end_disturb][Y_AXIS]) : ijk_p;
459 Real end_rc = end_p[Y_AXIS] / (control_[3][X_AXIS] - end_p[X_AXIS]);
460 if (default_rc > end_rc)
462 end_p = ijk_p;
463 end_rc = default_rc;
465 BEZIER_BOW_DOUT << "begin " << begin_p[X_AXIS] << ", " << begin_p[Y_AXIS] << endl;
466 BEZIER_BOW_DOUT << "end " << end_p[X_AXIS] << ", " << end_p[Y_AXIS] << endl;
468 Real height =control_[1][Y_AXIS];
469 for (int i = 0; i < encompass_.size (); i++ )
470 height = height >? encompass_[i][Y_AXIS];
472 // emperic computer science:
473 // * tangents somewhat steeper than minimal line
474 #ifndef STANDALONE
475 Real internote = paper_l_->get_realvar (interline_scm_sym)/2.0;
476 Real rc_correct = paper_l_->get_var ("slur_rc_factor");
477 #else
478 Real internote = STAFFHEIGHT / 8;
479 Real rc_correct = 2.4;
480 #endif
482 begin_rc *= rc_correct;
483 end_rc *= rc_correct;
484 Real rc1 = begin_rc;
485 Real rc2 = -end_rc;
487 Real begin_alpha = atan (begin_rc);
488 Real end_alpha = atan (-end_rc);
489 Real theta = (begin_alpha - end_alpha) / 2;
491 Real epsilon = internote / 5;
493 // if we have two disturbing points, have height line through those...
494 if (!((abs (begin_p[X_AXIS] - end_p[X_AXIS]) < epsilon)
495 && (abs (begin_p[Y_AXIS] - end_p[Y_AXIS]) < epsilon)))
496 theta = atan (end_p[Y_AXIS] - begin_p[Y_AXIS]) / (end_p[X_AXIS] - begin_p[X_AXIS]);
498 Real rc3 = tan (theta);
499 // ugh: be less steep
500 rc3 /= 2*rc_correct;
502 Real c2 = -rc2 * control_[3][X_AXIS];
503 Real c3 = begin_p[Y_AXIS] > end_p[Y_AXIS] ? begin_p[Y_AXIS]
504 - rc3 * begin_p[X_AXIS] : end_p[Y_AXIS] - rc3 * end_p[X_AXIS];
506 BEZIER_BOW_DOUT << "y1 = " << rc1 << " x + 0" << endl;
507 BEZIER_BOW_DOUT << "y2 = " << rc2 << " x + " << c2 << endl;
508 BEZIER_BOW_DOUT << "y3 = " << rc3 << " x + " << c3 << endl;
509 control_[1][X_AXIS] = c3 / (rc1 - rc3);
510 control_[1][Y_AXIS] = rc1 * control_[1][X_AXIS];
511 control_[2][X_AXIS] = (c3 - c2) / (rc2 - rc3);
512 BEZIER_BOW_DOUT << "c2[X_AXIS] = " << control_[2][X_AXIS] << endl;
513 BEZIER_BOW_DOUT << "(c3 - c2) = " << (c3 - c2) << endl;
514 BEZIER_BOW_DOUT << "(rc2 - rc3) = " << (rc2 - rc3) << endl;
515 control_[2][Y_AXIS] = rc2 * control_[2][X_AXIS] + c2;
516 BEZIER_BOW_DOUT << "c2[Y_AXIS]" << control_[2][Y_AXIS] << endl;
518 calc_return (begin_alpha, end_alpha);
521 bool
522 Bezier_bow::check_fit_bo ()
524 for (int i = 1; i < encompass_.size () - 1; i++)
525 if ((encompass_[i][X_AXIS] > encompass_[0][X_AXIS])
526 && (encompass_[i][X_AXIS] < encompass_[encompass_.size () -1][X_AXIS]))
527 if (encompass_[i][Y_AXIS] > y (encompass_[i][X_AXIS]))
528 return false;
529 return true;
532 Real
533 Bezier_bow::check_fit_f ()
535 Real dy = 0;
536 for (int i = 1; i < encompass_.size () - 1; i++)
537 if ((encompass_[i][X_AXIS] > encompass_[0][X_AXIS])
538 && (encompass_[i][X_AXIS] < encompass_[encompass_.size () -1][X_AXIS]))
539 dy = dy >? (encompass_[i][Y_AXIS] - y (encompass_[i][X_AXIS]));
540 return dy;
543 void
544 Bezier_bow::print () const
546 #ifndef NPRINT
547 Bezier::print ();
548 if (check_debug && !monitor->silent_b ("Bezier_bow_controls"))
550 cout << "Bezier_bow\n";
551 cout << "Encompass: ";
552 for (int i=0; i < encompass_.size (); i++)
553 cout << encompass_[i].str () << ", ";
554 // cout << "\n";
556 #endif
559 void
560 Bezier_bow::set (Array<Offset> points, int dir)
562 dir_ = dir;
563 encompass_ = points;
566 void
567 Bezier_bow::transform ()
569 origin_ = encompass_[0];
570 encompass_.translate (-origin_);
572 Offset delta = encompass_[encompass_.size () - 1] - encompass_[0];
573 alpha_ = delta.arg ();
575 encompass_.rotate (-alpha_);
577 if (dir_ == DOWN)
578 encompass_.flipy ();
581 void
582 Bezier_bow::transform_back ()
584 if (dir_ == DOWN)
586 control_.flipy ();
587 return_.flipy ();
588 encompass_.flipy ();
591 control_.rotate (alpha_);
592 control_.translate (origin_);
594 return_.rotate (alpha_);
595 return_.translate (origin_);
597 encompass_.rotate (alpha_);
598 encompass_.translate (origin_);
602 See Documentation/fonts.tex
604 void
605 Bezier_bow::calc_default (Real h)
607 Real pi = M_PI;
608 #ifndef STANDALONE
609 Real height_limit = paper_l_->get_var ("slur_height_limit");
610 Real ratio = paper_l_->get_var ("slur_ratio");
611 #else
612 Real staffsize_f = STAFFHEIGHT;
613 Real height_limit = staffsize_f;
614 Real ratio = 1.0/3.0;
615 #endif
617 Real alpha = height_limit * 2.0 / pi;
618 Real beta = pi * ratio / (2.0 * height_limit);
620 Offset delta (encompass_[encompass_.size () - 1][X_AXIS]
621 - encompass_[0][X_AXIS], 0);
622 Real b = delta.length ();
623 Real indent = alpha * atan (beta * b);
624 Real height = indent + h;
626 Array<Offset> control;
627 control.push (Offset (0, 0));
628 control.push (Offset (indent, height));
629 control.push (Offset (b - indent, height));
630 control.push (Offset (b, 0));
631 Bezier::set (control);